Compare commits

..

2 Commits

Author SHA1 Message Date
diegosouzapw
3e56c46feb docs(changelog): point the PR-5 fragment at #13235 2026-09-10 13:35:57 -03:00
diegosouzapw
2a9364d451 chore(electron): prune the root-level files of every translated docs mirror
The desktop runtime reads exactly one slice of docs/i18n: the translated
docs/** tree, through the in-app docs route (src/lib/docsI18nPath.ts pins
the path). Each locale's root-level mirrors — README.md, llm.txt, CLAUDE.md,
GEMINI.md, CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md — exist for
GitHub readers and are never opened by the packaged app, so they leave the
bundle together with the translated CHANGELOG that was already pruned.

Measured on release/v3.8.51 with 51 locales: docs/i18n is 139 MB, of which
25.7 MB is the served docs/** tree (kept), 103.5 MB the CHANGELOG mirrors
(already pruned) and 11.4 MB these root files (pruned now). The plan's D4
called for dropping docs/i18n entirely; that would have broken localized
docs inside the app, which is why the served subtree stays.
2026-09-10 13:30:27 -03:00
16 changed files with 186 additions and 465 deletions

View File

@@ -22,7 +22,7 @@
"node": ">=22.22.3"
},
"peerDependencies": {
"@opencode-ai/plugin": ">=1.18.29 <2"
"@opencode-ai/plugin": "*"
}
},
"node_modules/@ai-sdk/provider": {
@@ -39,9 +39,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
"integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
"integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
"cpu": [
"ppc64"
],
@@ -56,9 +56,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
"integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
"integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
"cpu": [
"arm"
],
@@ -73,9 +73,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
"integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
"integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
"cpu": [
"arm64"
],
@@ -90,9 +90,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
"integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
"integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
"cpu": [
"x64"
],
@@ -107,9 +107,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
"integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
"integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
"cpu": [
"arm64"
],
@@ -124,9 +124,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
"integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
"integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
"cpu": [
"x64"
],
@@ -141,9 +141,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
"integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
"integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
"cpu": [
"arm64"
],
@@ -158,9 +158,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
"integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
"integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
"cpu": [
"x64"
],
@@ -175,9 +175,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
"integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
"integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
"cpu": [
"arm"
],
@@ -192,9 +192,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
"integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
"integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
"cpu": [
"arm64"
],
@@ -209,9 +209,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
"integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
"integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
"cpu": [
"ia32"
],
@@ -226,9 +226,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
"integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
"integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
"cpu": [
"loong64"
],
@@ -243,9 +243,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
"integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
"integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
"cpu": [
"mips64el"
],
@@ -260,9 +260,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
"integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
"integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
"cpu": [
"ppc64"
],
@@ -277,9 +277,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
"integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
"integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
"cpu": [
"riscv64"
],
@@ -294,9 +294,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
"integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
"integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
"cpu": [
"s390x"
],
@@ -311,9 +311,7 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
"integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
"version": "0.28.1",
"cpu": [
"x64"
],
@@ -328,9 +326,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
"integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
"integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
"cpu": [
"arm64"
],
@@ -345,9 +343,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
"integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
"integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
"cpu": [
"x64"
],
@@ -362,9 +360,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
"integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
"integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
"cpu": [
"arm64"
],
@@ -379,9 +377,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
"integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
"integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
"cpu": [
"x64"
],
@@ -396,9 +394,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
"integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
"integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
"cpu": [
"arm64"
],
@@ -413,9 +411,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
"integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
"integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
"cpu": [
"x64"
],
@@ -430,9 +428,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
"integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
"integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
"cpu": [
"arm64"
],
@@ -447,9 +445,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
"integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
"integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
"cpu": [
"ia32"
],
@@ -464,9 +462,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
"integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
"integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
"cpu": [
"x64"
],
@@ -1182,9 +1180,7 @@
}
},
"node_modules/esbuild": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
"integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
"version": "0.28.1",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1195,32 +1191,32 @@
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.2",
"@esbuild/android-arm": "0.28.2",
"@esbuild/android-arm64": "0.28.2",
"@esbuild/android-x64": "0.28.2",
"@esbuild/darwin-arm64": "0.28.2",
"@esbuild/darwin-x64": "0.28.2",
"@esbuild/freebsd-arm64": "0.28.2",
"@esbuild/freebsd-x64": "0.28.2",
"@esbuild/linux-arm": "0.28.2",
"@esbuild/linux-arm64": "0.28.2",
"@esbuild/linux-ia32": "0.28.2",
"@esbuild/linux-loong64": "0.28.2",
"@esbuild/linux-mips64el": "0.28.2",
"@esbuild/linux-ppc64": "0.28.2",
"@esbuild/linux-riscv64": "0.28.2",
"@esbuild/linux-s390x": "0.28.2",
"@esbuild/linux-x64": "0.28.2",
"@esbuild/netbsd-arm64": "0.28.2",
"@esbuild/netbsd-x64": "0.28.2",
"@esbuild/openbsd-arm64": "0.28.2",
"@esbuild/openbsd-x64": "0.28.2",
"@esbuild/openharmony-arm64": "0.28.2",
"@esbuild/sunos-x64": "0.28.2",
"@esbuild/win32-arm64": "0.28.2",
"@esbuild/win32-ia32": "0.28.2",
"@esbuild/win32-x64": "0.28.2"
"@esbuild/aix-ppc64": "0.28.1",
"@esbuild/android-arm": "0.28.1",
"@esbuild/android-arm64": "0.28.1",
"@esbuild/android-x64": "0.28.1",
"@esbuild/darwin-arm64": "0.28.1",
"@esbuild/darwin-x64": "0.28.1",
"@esbuild/freebsd-arm64": "0.28.1",
"@esbuild/freebsd-x64": "0.28.1",
"@esbuild/linux-arm": "0.28.1",
"@esbuild/linux-arm64": "0.28.1",
"@esbuild/linux-ia32": "0.28.1",
"@esbuild/linux-loong64": "0.28.1",
"@esbuild/linux-mips64el": "0.28.1",
"@esbuild/linux-ppc64": "0.28.1",
"@esbuild/linux-riscv64": "0.28.1",
"@esbuild/linux-s390x": "0.28.1",
"@esbuild/linux-x64": "0.28.1",
"@esbuild/netbsd-arm64": "0.28.1",
"@esbuild/netbsd-x64": "0.28.1",
"@esbuild/openbsd-arm64": "0.28.1",
"@esbuild/openbsd-x64": "0.28.1",
"@esbuild/openharmony-arm64": "0.28.1",
"@esbuild/sunos-x64": "0.28.1",
"@esbuild/win32-arm64": "0.28.1",
"@esbuild/win32-ia32": "0.28.1",
"@esbuild/win32-x64": "0.28.1"
}
},
"node_modules/fast-check": {

View File

@@ -1745,9 +1745,9 @@
}
},
"node_modules/toml": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-4.3.0.tgz",
"integrity": "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/toml/-/toml-4.1.1.tgz",
"integrity": "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw==",
"dev": true,
"license": "MIT",
"engines": {

View File

@@ -35,24 +35,16 @@ export function resolveOpencodeTarget(opts = {}) {
baseUrl = `http://localhost:${Number(opts.port ?? process.env.PORT ?? 20128) || 20128}`;
}
// Precedence: explicit --api-key flag > OMNIROUTE_API_KEY env var > active
// context's management token. A context's accessToken/apiKey is a CLI
// management credential (oma_live_...) with no /v1/* inference scope — it
// must never silently outrank a real inference key the caller supplied
// either as a flag or via the ambient env var (mirrors the explicit >
// ambient-env > context precedence documented in bin/cli/api.mjs's
// buildHeaders()). Only fall back to the context token when neither an
// explicit flag nor the env var is set.
let apiKey = opts.apiKey ?? opts["api-key"];
if (!apiKey) apiKey = process.env.OMNIROUTE_API_KEY || "";
if (!apiKey) {
try {
const c = resolveActiveContext(opts.context ?? process.env.OMNIROUTE_CONTEXT);
apiKey = c?.accessToken || c?.apiKey || "";
apiKey = c?.accessToken || c?.apiKey;
} catch {
/* no context auth */
}
}
if (!apiKey) apiKey = process.env.OMNIROUTE_API_KEY || "";
return { baseUrl: baseUrl.replace(/\/+$/, ""), apiKey };
}
@@ -185,17 +177,8 @@ export function registerSetupOpencode(program) {
"--allow-container-write",
"Write even when the target is inside a container and not mounted from the host"
)
.action(async (opts, cmd) => {
// Commander parses the ancestor program's own global --api-key option
// (bin/cli/program.mjs, bound to .env("OMNIROUTE_API_KEY")) against any
// occurrence of the flag in argv, so it wins the value even when the
// user typed --api-key AFTER `setup-opencode` — this local option's own
// `opts.apiKey` never sees it. cmd.optsWithGlobals() resolves to the
// correct value either way ("globals overwrite locals" is exactly the
// outcome we want here, since the global option is where the value
// always actually lands).
const resolvedOpts = { ...opts, apiKey: cmd.optsWithGlobals().apiKey ?? opts.apiKey };
const code = await runSetupOpencodeCommand(resolvedOpts);
.action(async (opts) => {
const code = await runSetupOpencodeCommand(opts);
if (code !== 0) process.exit(code);
});
}

View File

@@ -1 +0,0 @@
- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783)

View File

@@ -0,0 +1 @@
- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n/<locale>/docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000)

View File

@@ -2113,9 +2113,9 @@
}
},
"node_modules/js-yaml": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"funding": [
{
"type": "github",

50
package-lock.json generated
View File

@@ -8687,6 +8687,20 @@
"license": "ISC",
"optional": true
},
"node_modules/@openai/codex-security/node_modules/smol-toml": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
"integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"engines": {
"node": ">= 18"
},
"funding": {
"url": "https://github.com/sponsors/cyyynthia"
}
},
"node_modules/@openai/codex-security/node_modules/type-fest": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.9.0.tgz",
@@ -15203,9 +15217,9 @@
}
},
"node_modules/@yarnpkg/parsers/node_modules/js-yaml": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
"funding": [
{
@@ -18521,9 +18535,9 @@
"license": "MIT"
},
"node_modules/csv-parse": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-7.0.2.tgz",
"integrity": "sha512-uKZghv9UmPkMVLYy//KZ9HFAIJsl7wkhoEdIL0+rhuSY9pZQlhaeGEDPIe+/w7eh81MOql8Q/9+inAGWG6ZHYA==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-7.0.1.tgz",
"integrity": "sha512-+2z7Ar0APQ7Uu6fX4cn+pitRmxjZ1WPBcGmZFKmA74FCyi7Et/XZx8cjNQ5CjbZ4HCOxXCOpRBYvYH08Qa003A==",
"dev": true,
"license": "MIT"
},
@@ -23589,9 +23603,9 @@
"license": "MIT"
},
"node_modules/hono": {
"version": "4.13.7",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.7.tgz",
"integrity": "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==",
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.0.tgz",
"integrity": "sha512-jhunvfHWxd7J5EFfSgH4xsYJzSe/lfqbUCxiyyeaQasUsXeEHXtzVid+7EOGByc5JnFa23SSFL3Y2RV/z1T+eQ==",
"license": "MIT",
"engines": {
"node": ">=16.9.0"
@@ -26102,9 +26116,9 @@
}
},
"node_modules/joi": {
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/joi/-/joi-18.2.8.tgz",
"integrity": "sha512-G2TX62h58ZHuwqetJgP2F4ualakqAmZtBYe3jWen7gxQRw5xApX6crnFtuB91WC0c3ESBnva+kGSnb3+6pIQDQ==",
"version": "18.2.3",
"resolved": "https://registry.npmjs.org/joi/-/joi-18.2.3.tgz",
"integrity": "sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -27932,9 +27946,9 @@
}
},
"node_modules/lockfile-lint/node_modules/js-yaml": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
"funding": [
{
@@ -39606,9 +39620,9 @@
}
},
"node_modules/xmlbuilder2/node_modules/js-yaml": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
"funding": [
{

View File

@@ -486,10 +486,7 @@
"fast-uri": "^3.1.7",
"body-parser": "^2.3.0",
"@yarnpkg/parsers": {
"js-yaml": "^4.3.2"
},
"@openai/codex-security": {
"smol-toml": "^1.8.0"
"js-yaml": "^4.3.1"
},
"jsdom": {
"undici": "^7.29.0"

View File

@@ -1,8 +1,24 @@
import { existsSync, lstatSync, readdirSync, rmSync } from "node:fs";
import { join, relative, resolve, sep } from "node:path";
// The packaged app reads exactly one slice of the translated mirrors:
// `docs/i18n/<locale>/docs/**`, through the in-app docs route (the path is
// pinned by src/lib/docsI18nPath.ts). Everything at a locale's root — the
// README, llm.txt and the agent/contributor guides — is authoring material
// mirrored for GitHub readers and never opened by the runtime, so it leaves
// the bundle together with the translated CHANGELOG (~115 MB across 51
// locales, 103 MB of it CHANGELOG). The translated `docs/**` tree stays.
export const ELECTRON_RUNTIME_DOC_PRUNE_RULES = Object.freeze({
localeRootFiles: Object.freeze(["CHANGELOG.md"]),
localeRootFiles: Object.freeze([
"CHANGELOG.md",
"CLAUDE.md",
"CODE_OF_CONDUCT.md",
"CONTRIBUTING.md",
"GEMINI.md",
"README.md",
"SECURITY.md",
"llm.txt",
]),
authoringDirectories: Object.freeze(["docs/research", "docs/superpowers"]),
});

View File

@@ -19,10 +19,7 @@ export async function DELETE(request: Request) {
);
}
// Scope the sweep to the caller. Only the operator's own dashboard (session
// auth) may clear the whole instance; an API key clears only its own
// completed batches (GHSA-wvxc-jp3v-5mg5).
const result = deleteCompletedBatches(scope.isSessionAuth ? undefined : scope.apiKeyId);
const result = deleteCompletedBatches();
return NextResponse.json(
{ deleted: true, deletedBatches: result.deletedBatches, deletedFiles: result.deletedFiles },

View File

@@ -411,37 +411,15 @@ export function deleteBatch(id: string): boolean {
return result.changes > 0;
}
/**
* Bulk-delete completed batches and the files they reference.
*
* `apiKeyId` scopes EVERY statement to that owner. Omitting it keeps the
* instance-wide sweep, which is legitimate for the operator's own dashboard
* (session auth) and for nothing else: without the predicate, an ordinary
* inference key could wipe every tenant's completed batches and null out their
* file contents (GHSA-wvxc-jp3v-5mg5). Same ownership shape as `listBatches`
* and `countBatches` above.
*/
export function deleteCompletedBatches(apiKeyId?: string | null): {
deletedBatches: number;
deletedFiles: number;
} {
export function deleteCompletedBatches(): { deletedBatches: number; deletedFiles: number } {
const db = getDbInstance();
const scoped = typeof apiKeyId === "string" && apiKeyId.length > 0;
// Collect unique file IDs from the completed batches in scope
const rows = (
scoped
? db
.prepare(
"SELECT input_file_id, output_file_id, error_file_id FROM batches WHERE status = 'completed' AND api_key_id = ?"
)
.all(apiKeyId)
: db
.prepare(
"SELECT input_file_id, output_file_id, error_file_id FROM batches WHERE status = 'completed'"
)
.all()
) as Array<{
// Collect unique file IDs from all completed batches
const rows = db
.prepare(
"SELECT input_file_id, output_file_id, error_file_id FROM batches WHERE status = 'completed'"
)
.all() as Array<{
input_file_id: string | null;
output_file_id: string | null;
error_file_id: string | null;
@@ -463,16 +441,6 @@ export function deleteCompletedBatches(apiKeyId?: string | null): {
}
}
if (scoped) {
db.prepare(
"DELETE FROM batch_item_checkpoints WHERE batch_id IN (SELECT id FROM batches WHERE status = 'completed' AND api_key_id = ?)"
).run(apiKeyId);
const result = db
.prepare("DELETE FROM batches WHERE status = 'completed' AND api_key_id = ?")
.run(apiKeyId);
return { deletedBatches: result.changes, deletedFiles };
}
db.prepare(
"DELETE FROM batch_item_checkpoints WHERE batch_id IN (SELECT id FROM batches WHERE status = 'completed')"
).run();

View File

@@ -58,8 +58,6 @@
"tests/unit/account-fallback-retry-after-json.test.ts",
"tests/unit/account-fallback-route-restriction-403.test.ts",
"tests/unit/account-fallback-service.test.ts",
"tests/unit/combo-pin-implicit-allowlist.test.ts",
"tests/unit/combo/execute-target-gates.test.ts",
"tests/unit/moonshot-quota-writeback.test.ts",
"tests/unit/accountfallback-ratelimit-400-4976.test.ts",
"tests/unit/adaptive-admission-route-matrix.test.ts",
@@ -77,8 +75,6 @@
"tests/unit/api-key-policy-noauth-allowed-connections.test.ts",
"tests/unit/api-key-rotator-health.test.ts",
"tests/unit/chat-routing-synced-inventory-11089.test.ts",
"tests/unit/openai-compatible-per-upstream-402-health.test.ts",
"tests/unit/quota-window-label.test.ts",
"tests/unit/repro-combo-persisted-cooldown-preskip.test.ts",
"tests/unit/repro-glm-iso-reset-24h-cap.test.ts",
"tests/unit/security-route-guard-tiers.test.ts",

View File

@@ -1,114 +0,0 @@
/**
* GHSA-wvxc-jp3v-5mg5 — `DELETE /api/v1/batches/delete-completed` deleted the
* completed batches of EVERY api key on the instance, and nulled the contents of
* every file those batches referenced.
*
* Two defects in one endpoint:
* 1. `deleteCompletedBatches()` carried no `api_key_id` predicate — the file
* SELECT, the checkpoint DELETE and the batch DELETE were all instance-wide.
* 2. The route only checked that SOME key was present (`!scope.apiKeyId` →
* 401), never that the caller owned anything. A key with `scopes: []` —
* an ordinary inference key — could wipe another tenant's batches.
*
* The ownership pattern this restores is not new: `listBatches(apiKeyId?)` and
* `countBatches(apiKeyId?)` in the same module already scope by `api_key_id`,
* and `batches/[id]/route.ts` already gates per-record access with `scopeCheck`
* (session auth sees everything, a key sees only its own). This helper was the
* one that never got it.
*
* Run with:
* node --import tsx/esm --test tests/unit/batch-delete-completed-ownership-wvxc.test.ts
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { createFile, getFile } from "@/lib/db/files";
import { createBatch, getBatch, deleteCompletedBatches } from "@/lib/db/batches";
const KEY_A = "key-wvxc-aaaa";
const KEY_B = "key-wvxc-bbbb";
function seedCompletedBatch(apiKeyId: string | null, tag: string) {
const file = createFile({
bytes: 10,
filename: `wvxc-${tag}.jsonl`,
purpose: "batch",
content: Buffer.from("{}"),
});
const batch = createBatch({
endpoint: "/v1/chat/completions",
completionWindow: "24h",
inputFileId: file.id,
status: "completed",
apiKeyId,
});
return { file, batch };
}
describe("deleteCompletedBatches — ownership scoping (GHSA-wvxc-jp3v-5mg5)", () => {
it("scoped to one key deletes ONLY that key's completed batches", () => {
const a = seedCompletedBatch(KEY_A, "a1");
const b = seedCompletedBatch(KEY_B, "b1");
const result = deleteCompletedBatches(KEY_A);
assert.equal(getBatch(a.batch.id), null, "the caller's own batch should be gone");
assert.ok(getBatch(b.batch.id), "another key's batch must survive");
assert.equal(result.deletedBatches, 1, "must report only what it actually deleted");
});
it("scoped deletion does not touch another key's file contents", () => {
const a = seedCompletedBatch(KEY_A, "a2");
const b = seedCompletedBatch(KEY_B, "b2");
deleteCompletedBatches(KEY_A);
assert.equal(getFile(a.file.id), null, "the caller's own file should be gone");
assert.ok(getFile(b.file.id), "another key's file must survive with its content intact");
});
it("a key with no completed batches deletes nothing at all", () => {
const b = seedCompletedBatch(KEY_B, "b3");
const result = deleteCompletedBatches("key-wvxc-with-nothing");
assert.equal(result.deletedBatches, 0);
assert.equal(result.deletedFiles, 0);
assert.ok(getBatch(b.batch.id), "an unrelated key's batch must survive");
});
it("unscoped (dashboard session) still clears the whole instance", () => {
// The operator's own dashboard legitimately cleans up everything; that is
// the ONLY caller allowed to omit the key. Preserved deliberately.
seedCompletedBatch(KEY_A, "a4");
seedCompletedBatch(KEY_B, "b4");
const result = deleteCompletedBatches();
assert.ok(
result.deletedBatches >= 2,
`expected an instance-wide sweep, got ${result.deletedBatches}`
);
});
});
describe("the route passes the caller's key through", () => {
it("delete-completed scopes by api key instead of calling the helper bare", async () => {
const { readFileSync } = await import("node:fs");
const { fileURLToPath } = await import("node:url");
const src = readFileSync(
fileURLToPath(
new URL("../../src/app/api/v1/batches/delete-completed/route.ts", import.meta.url)
),
"utf8"
);
assert.ok(
!/deleteCompletedBatches\(\s*\)/.test(src),
"the route still calls deleteCompletedBatches() with no owner — every tenant's batches go"
);
assert.ok(
/deleteCompletedBatches\(\s*scope\./.test(src),
"the route must pass the caller's scope into the helper"
);
});
});

View File

@@ -13,11 +13,6 @@ type Check = {
validate?: (content: string, claim?: string) => { ok: boolean; detail: string };
};
/** Escape a value that is interpolated into a RegExp source. */
function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
describe("ToS caution heading count", () => {
it("the Caution heading carries the count the gate checks", () => {
const txt = readFileSync(join(process.cwd(), "docs/reference/FREE_TIERS.md"), "utf8");
@@ -26,24 +21,7 @@ describe("ToS caution heading count", () => {
const tos = (buildChecks() as Check[]).find((c) =>
String(c.docKey ?? "").includes("ToS caution")
);
// The backslashes must survive the TEMPLATE LITERAL to reach the regex.
// Written as `\(\s*…` they did not: JS resolves `\(` to "(" and `\s` to the
// LETTER "s" before RegExp ever sees them, so the pattern compiled to
// `(s*16s*)` — a capture group around optional "s" characters. That matched
// any heading merely containing the number, with no literal parentheses
// required at all, so this guard passed on headings it was written to reject
// (CodeQL js/useless-regexp-character-escape #994-#997).
const count = escapeRegExp(String(tos?.actual));
assert.match(heading, new RegExp(`\\(\\s*${count}\\s*\\)`));
});
it("the heading guard actually requires the parentheses", () => {
// Pins the defect above: the pattern this test builds must REJECT a heading
// that carries the count without parentheses. Before the fix it accepted it.
const pattern = new RegExp(`\\(\\s*${escapeRegExp("16")}\\s*\\)`);
assert.equal(pattern.test("### Caution — clauses worth checking 16"), false);
assert.equal(pattern.test("### Caution — clauses worth checking (16)"), true);
assert.equal(pattern.test("### Caution — clauses worth checking ( 16 )"), true);
assert.match(heading, new RegExp(`\(\s*${String(tos?.actual)}\s*\)`));
});
it("buildChecks exposes a soft ToS entry on FREE_TIERS.md with requireClaim", () => {
const checks = buildChecks() as Check[];

View File

@@ -73,6 +73,8 @@ test("electron docs manifest prunes authoring payloads without removing runtime
["docs/guides/CODEX-CLI-CONFIGURATION.md", "# Codex CLI"],
["docs/i18n/ko/docs/guides/ELECTRON_GUIDE.md", "# Electron"],
["docs/i18n/ko/CHANGELOG.md", "translated release history"],
["docs/i18n/ko/README.md", "translated readme"],
["docs/i18n/ko/llm.txt", "translated llm summary"],
["docs/i18n/fr/CHANGELOG.md", "historique traduit"],
["docs/research/desktop-notes.md", "authoring notes"],
["docs/superpowers/plans/desktop-plan.md", "implementation plan"],
@@ -87,16 +89,23 @@ test("electron docs manifest prunes authoring payloads without removing runtime
const result = pruneElectronRuntimeDocs(bundleRoot);
// Only `docs/i18n/<locale>/docs/**` is read at runtime (the in-app docs
// route, see src/lib/docsI18nPath.ts); every root-level mirror of a locale
// is authoring material and leaves the bundle with the CHANGELOG.
assert.deepEqual(result.removedPaths, [
"docs/i18n/fr/CHANGELOG.md",
"docs/i18n/ko/CHANGELOG.md",
"docs/i18n/ko/README.md",
"docs/i18n/ko/llm.txt",
"docs/research",
"docs/superpowers",
]);
assert.equal(result.removedFiles, 4);
assert.equal(result.removedFiles, 6);
assert.equal(
result.removedBytes,
Buffer.byteLength("translated release history") +
Buffer.byteLength("translated readme") +
Buffer.byteLength("translated llm summary") +
Buffer.byteLength("historique traduit") +
Buffer.byteLength("authoring notes") +
Buffer.byteLength("implementation plan")
@@ -106,6 +115,8 @@ test("electron docs manifest prunes authoring payloads without removing runtime
assert.equal(existsSync(join(bundleRoot, "docs/guides/CODEX-CLI-CONFIGURATION.md")), true);
assert.equal(existsSync(join(bundleRoot, "docs/i18n/ko/docs/guides/ELECTRON_GUIDE.md")), true);
assert.equal(existsSync(join(bundleRoot, "docs/i18n/ko/CHANGELOG.md")), false);
assert.equal(existsSync(join(bundleRoot, "docs/i18n/ko/README.md")), false);
assert.equal(existsSync(join(bundleRoot, "docs/i18n/ko/llm.txt")), false);
assert.equal(existsSync(join(bundleRoot, "docs/research")), false);
assert.equal(existsSync(join(bundleRoot, "docs/superpowers")), false);

View File

@@ -1,121 +0,0 @@
import assert from "node:assert/strict";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from "node:test";
import { resolveOpencodeTarget } from "../../bin/cli/commands/setup-opencode.mjs";
/** Point OMNIROUTE_CONTEXT config resolution at an isolated, throwaway DATA_DIR. */
function withIsolatedContext(contextConfig, fn) {
const dir = mkdtempSync(join(tmpdir(), "omniroute-setup-opencode-test-"));
const originalDataDir = process.env.DATA_DIR;
process.env.DATA_DIR = dir;
writeFileSync(
join(dir, "config.json"),
JSON.stringify({
version: 1,
currentContext: "remote",
contexts: { remote: contextConfig },
})
);
try {
return fn();
} finally {
if (originalDataDir === undefined) delete process.env.DATA_DIR;
else process.env.DATA_DIR = originalDataDir;
rmSync(dir, { recursive: true, force: true });
}
}
function withEnvApiKey(value, fn) {
const original = process.env.OMNIROUTE_API_KEY;
if (value === undefined) delete process.env.OMNIROUTE_API_KEY;
else process.env.OMNIROUTE_API_KEY = value;
try {
return fn();
} finally {
if (original === undefined) delete process.env.OMNIROUTE_API_KEY;
else process.env.OMNIROUTE_API_KEY = original;
}
}
test("setup-opencode: --api-key typed AFTER the subcommand name is not stolen by the parent program's global option", async () => {
const { createProgram } = await import("../../bin/cli/program.mjs");
const program = createProgram();
const setupOpencode = program.commands.find((c) => c.name() === "setup-opencode");
assert.ok(setupOpencode, "setup-opencode subcommand must be registered");
let capturedApiKey;
setupOpencode._actionHandler = null; // avoid the real network-calling action
setupOpencode.action((opts, cmd) => {
capturedApiKey = cmd.optsWithGlobals().apiKey ?? opts.apiKey;
});
await program.parseAsync(
[
"node",
"omniroute",
"setup-opencode",
"--remote",
"http://100.64.0.1:20128",
"--api-key",
"sk-TESTKEY123",
],
{ from: "node" }
);
assert.equal(
capturedApiKey,
"sk-TESTKEY123",
"the CLI-supplied --api-key value must reach the setup-opencode action handler"
);
});
test("resolveOpencodeTarget: (a) explicit --api-key flag wins over an active context's management token", () => {
withEnvApiKey(undefined, () => {
withIsolatedContext(
{ baseUrl: "http://100.64.0.1:20128", accessToken: "oma_live_CONTEXT_TOKEN" },
() => {
const { apiKey } = resolveOpencodeTarget({ apiKey: "sk-FLAG", context: "remote" });
assert.equal(apiKey, "sk-FLAG");
}
);
});
});
test("resolveOpencodeTarget: (b) OMNIROUTE_API_KEY env wins over an active context's management token when no flag is passed", () => {
withEnvApiKey("sk-ENVKEY", () => {
withIsolatedContext(
{ baseUrl: "http://100.64.0.1:20128", accessToken: "oma_live_CONTEXT_TOKEN" },
() => {
const { apiKey } = resolveOpencodeTarget({ context: "remote" });
assert.equal(apiKey, "sk-ENVKEY");
}
);
});
});
test("resolveOpencodeTarget: (c) the context's token is used only when neither a flag nor the env var is set", () => {
withEnvApiKey(undefined, () => {
withIsolatedContext(
{ baseUrl: "http://100.64.0.1:20128", accessToken: "oma_live_CONTEXT_TOKEN" },
() => {
const { apiKey } = resolveOpencodeTarget({ context: "remote" });
assert.equal(apiKey, "oma_live_CONTEXT_TOKEN");
}
);
});
});
test("resolveOpencodeTarget: falls back to '' when neither a flag, env var, nor a resolvable context is present", () => {
withEnvApiKey(undefined, () => {
withIsolatedContext({ baseUrl: "http://100.64.0.1:20128" }, () => {
const { apiKey } = resolveOpencodeTarget({
remote: "http://100.64.0.1:20128",
context: "__no-such-context__",
});
assert.equal(apiKey, "");
});
});
});