Compare commits

...
Author SHA1 Message Date
StanPonomarev ba9fddad9d Merge pull request 'fix: remove cost estim' (#11) from fix/remove-cost-estim into main
Reviewed-on: #11
2026-09-16 20:34:57 +02:00
StanPonomarev b4ab44eb3c fix: remove cost estim
CI / verify (pull_request) Successful in 9s
2026-09-16 20:30:22 +02:00
StanPonomarev 693589c21c Merge pull request 'fix: update deps for opencode 2.0.4' (#10) from fix/opencode204 into main
Release / release (push) Successful in 27s
Reviewed-on: #10
2026-09-16 09:51:07 +02:00
StanPonomarev 0cf63d64b6 remove opencode2 referneces
CI / verify (pull_request) Successful in 10s
2026-09-16 09:50:41 +02:00
StanPonomarev 1c48fab124 fix: update deps for opencode 2.0.4
CI / verify (pull_request) Successful in 10s
2026-09-16 09:46:01 +02:00
StanPonomarev 20f6087340 Merge pull request 'fix: reasoning effor mismatch' (#9) from fix/fix-reasoning-effort into main
Release / release (push) Successful in 8s
Reviewed-on: #9
2026-09-15 17:36:49 +02:00
16 changed files with 223 additions and 286 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
- name: Install Bun - name: Install Bun
uses: https://github.com/oven-sh/setup-bun@v2 uses: https://github.com/oven-sh/setup-bun@v2
with: with:
bun-version: "1.3.14" bun-version: "1.4.2"
- name: Verify and build - name: Verify and build
run: | run: |
bun install --frozen-lockfile bun install --frozen-lockfile
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
- name: Install Bun - name: Install Bun
uses: https://github.com/oven-sh/setup-bun@v2 uses: https://github.com/oven-sh/setup-bun@v2
with: with:
bun-version: "1.3.14" bun-version: "1.4.2"
- name: Verify and build - name: Verify and build
run: | run: |
bun install --frozen-lockfile bun install --frozen-lockfile
+3 -3
View File
@@ -4,7 +4,7 @@ stanp-opencode is an OpenCode V2 coordinator with specialist workers for fast, g
## Install the latest release ## Install the latest release
Requires OpenCode V2 2.0.2 (`opencode2`), Bun 1.3.14+, Bash, curl, jq, and `sha256sum` on Linux or WSL. Requires OpenCode 2.0.4, Bun 1.4.2+, Bash, curl, jq, and `sha256sum` on Linux or WSL.
Run the installer without sudo: Run the installer without sudo:
@@ -22,7 +22,7 @@ Rerun the command to upgrade to the latest published release.
## Install from a local checkout ## Install from a local checkout
This requires Bash, jq, and Bun 1.3.14+: This requires Bash, jq, and Bun 1.4.2+:
```sh ```sh
bash /path/to/stanp-opencode/install-local.sh bash /path/to/stanp-opencode/install-local.sh
@@ -32,7 +32,7 @@ Add `--playwright` to this command to enable automatic Playwright MCP configurat
The script installs dependencies and builds the checkout before installing it. The script installs dependencies and builds the checkout before installing it.
After either installation, restart `opencode2` or run `opencode2 service restart` for an existing service. Connect OpenAI and select `coordinate`. The required models must be available for the workers to run. After either installation, restart `opencode` or run `opencode service restart` for an existing service. Connect OpenAI and select `coordinate`. The required models must be available for the workers to run.
Both installers use `~/.config/opencode`, respect `XDG_CONFIG_HOME`, and accept `OPENCODE_CONFIG_DIR` as an override. Both installers use `~/.config/opencode`, respect `XDG_CONFIG_HOME`, and accept `OPENCODE_CONFIG_DIR` as an override.
+75 -73
View File
@@ -5,25 +5,25 @@
"": { "": {
"name": "stanp-opencode", "name": "stanp-opencode",
"dependencies": { "dependencies": {
"zod": "4.1.8", "zod": "4.6.5",
}, },
"devDependencies": { "devDependencies": {
"@opencode/client": "2.0.2", "@opencode/client": "2.0.4",
"@opencode/plugin": "2.0.2", "@opencode/plugin": "2.0.4",
"@opencode/schema": "2.0.2", "@opencode/schema": "2.0.4",
"@opentui/core": "0.5.10", "@opentui/core": "0.5.11",
"@opentui/solid": "0.5.10", "@opentui/solid": "0.5.11",
"@oxlint/plugins": "1.82.0", "@oxlint/plugins": "1.83.0",
"@types/bun": "1.3.14", "@types/bun": "1.4.2",
"oxfmt": "0.67.0", "oxfmt": "0.68.0",
"oxlint": "1.82.0", "oxlint": "1.83.0",
"solid-js": "1.9.15", "solid-js": "1.9.15",
"typescript": "7.0.2", "typescript": "7.0.2",
}, },
"peerDependencies": { "peerDependencies": {
"@opencode/plugin": "2.0.2", "@opencode/plugin": "2.0.4",
"@opentui/core": ">=0.5.10", "@opentui/core": ">=0.5.11",
"@opentui/solid": ">=0.5.10", "@opentui/solid": ">=0.5.11",
"solid-js": ">=1.9.0", "solid-js": ">=1.9.0",
}, },
}, },
@@ -201,17 +201,17 @@
"@npmcli/run-script": ["@npmcli/run-script@10.0.4", "", { "dependencies": { "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "node-gyp": "^12.1.0", "proc-log": "^6.0.0" } }, "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg=="], "@npmcli/run-script": ["@npmcli/run-script@10.0.4", "", { "dependencies": { "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "node-gyp": "^12.1.0", "proc-log": "^6.0.0" } }, "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg=="],
"@opencode/ai": ["@opencode/ai@2.0.2", "", { "dependencies": { "@aws-sdk/credential-providers": "3.1057.0", "@opencode/schema": "2.0.2", "@smithy/eventstream-codec": "4.2.14", "@smithy/util-utf8": "4.2.2", "aws4fetch": "1.0.20", "effect": "4.0.0-rc.112", "google-auth-library": "10.5.0" } }, "sha512-Byq9c3kRdIl+AfGidPkd3JUC6DnjaNXGem77sAFRSxYwA0AMJMImPm+ua6ibnRqJ+6HlEq9JSh+Z1nQ8s8xzrQ=="], "@opencode/ai": ["@opencode/ai@2.0.4", "", { "dependencies": { "@aws-sdk/credential-providers": "3.1057.0", "@opencode/schema": "2.0.4", "@smithy/eventstream-codec": "4.2.14", "@smithy/util-utf8": "4.2.2", "aws4fetch": "1.0.20", "effect": "4.0.0-rc.112", "google-auth-library": "10.5.0" } }, "sha512-T/2wTuhy/I1Wxdy/GdhtMqugpl1OWKgVp+8A45mtS1jDrTSeHq2x5YhkComkJZ6BwdG/OvU6afTGDPA7/vd4RA=="],
"@opencode/client": ["@opencode/client@2.0.2", "", { "dependencies": { "@opencode/protocol": "2.0.2", "@opencode/schema": "2.0.2" }, "peerDependencies": { "effect": "4.0.0-rc.112", "solid-js": ">=1.9.0" }, "optionalPeers": ["effect", "solid-js"] }, "sha512-w8rrSsxY9qnuFrsQAHdLDE6mrEqUKQeo7fNsw2i/tFtimgspT53/8/3ckEVRuRUM5k6v68KiU2deG0W2gEIVDQ=="], "@opencode/client": ["@opencode/client@2.0.4", "", { "dependencies": { "@opencode/protocol": "2.0.4", "@opencode/schema": "2.0.4" }, "peerDependencies": { "effect": "4.0.0-rc.112", "solid-js": ">=1.9.0" }, "optionalPeers": ["effect", "solid-js"] }, "sha512-GBFWT2os+gEtGXpHRvDUNkf0H9x9njDUVbUvvc4drH2CuAihVcHqdi9Wm4UiwkBrSLjrKPs4U1NJBh2Z7/x4tQ=="],
"@opencode/plugin": ["@opencode/plugin@2.0.2", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@opencode/ai": "2.0.2", "@opencode/client": "2.0.2", "@opencode/protocol": "2.0.2", "@opencode/schema": "2.0.2", "@opencode/util": "2.0.2", "@standard-schema/spec": "1.1.0", "effect": "4.0.0-rc.112", "zod": "4.1.8" }, "peerDependencies": { "@opencode/theme": "2.0.2", "@opentui/core": ">=0.5.10", "@opentui/solid": ">=0.5.10", "solid-js": ">=1.9.0" }, "optionalPeers": ["@opencode/theme", "@opentui/core", "@opentui/solid", "solid-js"] }, "sha512-lFXcsyg1fhzCpzuMqBhb2yNddMeynXZwXL3aHs1T9ftbFu4buFAdtE9ox09Q+CL/r7yGbbX4mdtjKYdnt0dn5Q=="], "@opencode/plugin": ["@opencode/plugin@2.0.4", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@opencode/ai": "2.0.4", "@opencode/client": "2.0.4", "@opencode/protocol": "2.0.4", "@opencode/schema": "2.0.4", "@opencode/util": "2.0.4", "@standard-schema/spec": "1.1.0", "effect": "4.0.0-rc.112", "zod": "4.1.8" }, "peerDependencies": { "@opencode/theme": "2.0.4", "@opentui/core": ">=0.5.10", "@opentui/solid": ">=0.5.10", "solid-js": ">=1.9.0" }, "optionalPeers": ["@opencode/theme", "@opentui/core", "@opentui/solid", "solid-js"] }, "sha512-NsC1STARfXxe+ZoXjXzpxvtS9l4h9Aen8ZccaCd1d9+1eyYdp+q8DU3Rxh7Ch0jiwiQMybS2Z+KgAfZJBnTBbA=="],
"@opencode/protocol": ["@opencode/protocol@2.0.2", "", { "dependencies": { "@opencode/schema": "2.0.2", "effect": "4.0.0-rc.112" } }, "sha512-FxTxdHPNz46NkG8ZBB2S60QzXbZDlVEyZ6KeYTt2G7hcWB5JlMYBh8jeqHlsLbquKinvMKA2iuEsA1NaccN+YQ=="], "@opencode/protocol": ["@opencode/protocol@2.0.4", "", { "dependencies": { "@opencode/schema": "2.0.4", "effect": "4.0.0-rc.112" } }, "sha512-uWe2dmMouprSkfBu29XLR6GCWckkhm0YOo9ve07kTRqJCSoJzgQDF/8pouWG0cDCJ6oWj3bRQajXriBQyWSpUw=="],
"@opencode/schema": ["@opencode/schema@2.0.2", "", { "dependencies": { "@standard-schema/spec": "1.1.0", "effect": "4.0.0-rc.112" } }, "sha512-jF23H41j6zM2Az1pYLD6NFfzpoQ79CX0AxRkfNxTZFbPfjGGc+EPSOzVQP/r4Y/5bkSSqVga0Vn7MyoP8QhwDQ=="], "@opencode/schema": ["@opencode/schema@2.0.4", "", { "dependencies": { "@standard-schema/spec": "1.1.0", "effect": "4.0.0-rc.112" } }, "sha512-UyGcCsRb2hTH98rnBqfJogFve8kJsIbsEreRxqhkBIEGrtDTqcUd5NnqB7qlJp2khesMvpOH0GmCHlgz44C+uQ=="],
"@opencode/util": ["@opencode/util@2.0.2", "", { "dependencies": { "@effect/opentelemetry": "4.0.0-rc.112", "@effect/platform-node": "4.0.0-rc.112", "@effect/platform-node-shared": "4.0.0-rc.112", "@npmcli/arborist": "9.4.0", "@npmcli/config": "10.8.1", "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/exporter-trace-otlp-http": "0.214.0", "@opentelemetry/sdk-trace-base": "2.6.1", "@opentelemetry/sdk-trace-node": "2.6.1", "cross-spawn": "7.0.6", "effect": "4.0.0-rc.112", "glob": "13.0.5", "mime-types": "3.0.2", "minimatch": "10.2.5", "npm-package-arg": "13.0.2", "pacote": "21.5.1" } }, "sha512-AKnFr44s1Q7/Ud+Pl4QMCEJoIp33xyDiTi693BofXn+HBDHdJmKrwq87YpG+Kwt9t48gVv6Cx1glmqJ4lVJpHg=="], "@opencode/util": ["@opencode/util@2.0.4", "", { "dependencies": { "@effect/opentelemetry": "4.0.0-rc.112", "@effect/platform-node": "4.0.0-rc.112", "@effect/platform-node-shared": "4.0.0-rc.112", "@npmcli/arborist": "9.4.0", "@npmcli/config": "10.8.1", "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/exporter-trace-otlp-http": "0.214.0", "@opentelemetry/sdk-trace-base": "2.6.1", "@opentelemetry/sdk-trace-node": "2.6.1", "cross-spawn": "7.0.6", "effect": "4.0.0-rc.112", "glob": "13.0.5", "mime-types": "3.0.2", "minimatch": "10.2.5", "npm-package-arg": "13.0.2", "pacote": "21.5.1" } }, "sha512-jo6zhWqJ4WaU+fUjM8/Ea4i06gxTQFEFgfq2XG9J8gSDUhfsj4l9wJ3cLtSuKFQGeUeWqJJwRgDhCW+rUbis6Q=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
@@ -239,103 +239,103 @@
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="], "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="],
"@opentui/core": ["@opentui/core@0.5.10", "", { "dependencies": { "bun-ffi-structs": "0.3.1", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.5.10", "@opentui/core-darwin-x64": "0.5.10", "@opentui/core-linux-arm64": "0.5.10", "@opentui/core-linux-arm64-musl": "0.5.10", "@opentui/core-linux-x64": "0.5.10", "@opentui/core-linux-x64-musl": "0.5.10", "@opentui/core-win32-arm64": "0.5.10", "@opentui/core-win32-x64": "0.5.10" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-C3a2UbmefeAjIxAgm4BqjuSxKT4oqutfvYFwVvUgMxmGRHkNbBc/s7sukV0JgwcxFcV3uMFrXxo+E+BQtvuOiw=="], "@opentui/core": ["@opentui/core@0.5.11", "", { "dependencies": { "bun-ffi-structs": "0.3.1", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.5.11", "@opentui/core-darwin-x64": "0.5.11", "@opentui/core-linux-arm64": "0.5.11", "@opentui/core-linux-arm64-musl": "0.5.11", "@opentui/core-linux-x64": "0.5.11", "@opentui/core-linux-x64-musl": "0.5.11", "@opentui/core-win32-arm64": "0.5.11", "@opentui/core-win32-x64": "0.5.11" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-pImMfjCNx7JUp9Df1LRZBDLisWqgzOLXZKXO+hh3jA9ujBFPQnZOqbG+/N5uVAAX8IZGaqhYSQRwSZIFBmcfbQ=="],
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.5.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Vyb+nTbhab8ZcRy5gg1loEEGwRcIbjAeVRIBfHBcbFDqmITBOg7x2gqJ+x/TnoOy4uwMhCmICUN2wiyREw3r1Q=="], "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.5.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DRXY5ioq+n1ZNAMAcaFaBunr0cmi2gqucjbTW7lgFp8t9uN3fNZnLTDOqkCTQtT2XhrU4GXqySaPSQ4qFG/EAQ=="],
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.5.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tTFLcM7Oj1gTyhm/bUdAt3C6grZdCxPk6+/g2azcZBUlI3/62LwbeRS6HbQKFFmm+1fUmX8cq6kWrtul885mVg=="], "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.5.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-yP/8GliJDiJNm8YYJKvgWuy6xyCEd8d4GwBVOIzCFOI7ZIbGP8GOTvmwIjRW4Paw20pTttWMWyRoQiCvOXvH2g=="],
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.5.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-ncJXcgudhBf2GdJyF3xVQN/Ec+1F7GOL+pRrURmgBYSj2v1w6EyoDQFAACtPTK2c3R38W6fvZwL4JSLlm4EFXQ=="], "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.5.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-zBIsRFHlLUYFNhapRSNt9dz4mC8gZ4Wxcfy3A+2AwqsgCipcr2FkIuAXYqN08q+IvqFX7DfqgIFGWDNedHTPUg=="],
"@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.5.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-dGMphDKexSdeYqwl0wgoFBP88Ta/cdi1Zc1mk29/ENkSCGz+74zlCHgqTHRNGLmI8W5TfuUtCyktQH11/Z+TBQ=="], "@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.5.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-x+xeR2LYibvIi/qQetRjJR008sFRve60QuDcO8ItxUwzFeKTDzl5CEiZpBXfm5I4FhRNuyuj0TSPIFadMvrjFQ=="],
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.5.10", "", { "os": "linux", "cpu": "x64" }, "sha512-5qtYaOgwVycZD1GaGshTRsi0rXPAmVExO03N1JQaHu+NYxK/vXSOc7Bu4QW0sPXx3Sp0SpzpP+FHjXABfoK66g=="], "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.5.11", "", { "os": "linux", "cpu": "x64" }, "sha512-pSOXqOADrv+zINOgR3FDFA9zVRaim3zl8/yhtO+X9rEJ6f34z3gDund0Gf88hNJSMpZK5xWtipEVm28RY5VF8w=="],
"@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.5.10", "", { "os": "linux", "cpu": "x64" }, "sha512-Oj4H9hApuvuTKPWxh4SoZAgGJorR7vbvnrZA/cAkSMAk2VGSoHRRcqeXQbcH8IcdjVZ0KFpv8Zkl/D5Ye+2mew=="], "@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.5.11", "", { "os": "linux", "cpu": "x64" }, "sha512-MyqOnSs8pTYG2xmFr1xt6xZIuHu2Xu4pkle9my9JdE+WClmusHf0YN9Eas6jQLAq5XUi34r22wMeK0juk93zyw=="],
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.5.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-A9VhgvTxQoUdZ+8LmUumEng1sQNbj9QQQT3NYG9mSxI54qTANi7vOWNSphMiY6RMVsr22pgm6nUvSSvJXv7Jog=="], "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.5.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-MGGRXIDJ//HyaqC5ndSr7/CUl+ICdYEAMcjbA00UWsthh4ZO/rYhPkyqOaTn5ck6+G4ca3/+M76J+jImMYTjNg=="],
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.5.10", "", { "os": "win32", "cpu": "x64" }, "sha512-u3KHa7kEeWrmKVDRJYpxSGO+g5E9cMGlrmTsPN3GVPHUmQMiREUawLXUvsU8+IHaQnqG3Q5nuE1yf4fPBzS+Qw=="], "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.5.11", "", { "os": "win32", "cpu": "x64" }, "sha512-sMEGX9rhiPd1gBa190jzj5uIdzKCMImxHmr22hJLxIRWqejxWvqGnRBkW68wT8NfMViDiMtZWavgWL7GgmYCAQ=="],
"@opentui/solid": ["@opentui/solid@0.5.10", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.5.10", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-KrmMIsHiKBHOABTC0brOwqWm+sGq1ZX2sGCAx6WgtBbE3STMup9n8TAy/6gUYhwcjC9zugT53ytfSVwCwVWZUg=="], "@opentui/solid": ["@opentui/solid@0.5.11", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.5.11", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-u8RJ4UMwzi+r9M1sopZdnT+7XFSHmLzVmhPhc6N2LfBrA9Db4lZ9sQA+ywgdSDkzdANpAOwGLNUQlY/F1OOCLA=="],
"@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.67.0", "", { "os": "android", "cpu": "arm" }, "sha512-2olh3ioEmc4gRzQm7jxyB1b/PFBoFvTq8KdgYySeNpysDtA6DEg2Mvya4/I6flhL7G0eOrE8RD7JCNCIMhE16Q=="], "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.68.0", "", { "os": "android", "cpu": "arm" }, "sha512-dhfYPbzv/h9JgHjNkl2R6sOjUfxDyLGOZVb3g8/ScaTNwwJcYgmHh8kcYFDUhinuy1QAoANCWUvw1jlk+z6gAg=="],
"@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.67.0", "", { "os": "android", "cpu": "arm64" }, "sha512-ulfw8EHN1MBq/MFFDXw2/M1VAFu5mRUcnuZ8Hqbv9viAnFzO9t1jKSAsDqKYYDGMlytF/uj6Z5z5n/tHupnKhw=="], "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.68.0", "", { "os": "android", "cpu": "arm64" }, "sha512-v3Njdi6qY0O/5eGfg01ww2w6gTn2mUvZ72Bnx1/UN53A9wruh3Nk6otc3WkgJLkXD4Qgz1SOcVQieH1oD03V9Q=="],
"@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.67.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MfONZx/O2o9M5v2jDFol556G9+A+P9xCuJ4DZ+qhE+RnaCdoscy6Eu5nq1dbuNxhwdJyZ6kLI7fnG9mwEeOeGg=="], "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.68.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ei4MCMzHFREmZwPJ7KuWUB4kBuHdsgDrnXGJVcEAopU7fj7S42I8BChdFILWdHvhFqR08FLJtOfbZIr2CDw0cA=="],
"@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.67.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-CYnIx5LvFVJnyJcCqwH2jxMKjFjqo5678MPjdmNFoSGMhlOvZ/xRZqvhDcolKrXc8fezW3AKh+C4wyoFuWOSSg=="], "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.68.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-UrKgzZxYhwB9DSvTX+vdgl9M32wLUNKJcAKIoiyx/Kzn/zveqi7W6kYVcRroFMhS3Kwz0KhTk3WBeSuQn4YCTg=="],
"@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.67.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-7/iF1orvIS9mxhKUqnmtMgm+OrSQ5acPwuvdQrm6ECgqbwPmC+Pw9cdke3sNfVN6pT2hbJ58+jP8BCThl5HXOg=="], "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.68.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6jrEKgpJbilM1QaRv7hEtKXr4p4AK4jvvyOtajwyhu0kOz3e0O7OLnSTk6tBotRqCcUC4ehZRJ1Zx+Y99wieLw=="],
"@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.67.0", "", { "os": "linux", "cpu": "arm" }, "sha512-yy+OGys07IZOpOmYPZoObKyUQLkfxeQqeCypk+1jaZd8HGo77hzvU1Jg8X3+W75o+9lszOjBfg0nkGtlwYywXw=="], "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.68.0", "", { "os": "linux", "cpu": "arm" }, "sha512-YOIVnKOBaLeGullskS179N12hjSAdFYnzLjOaKiLhAKNWgnShq9w4xRdtmUm6BlnP65l2/EA9Aw/KlftNxDM7Q=="],
"@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.67.0", "", { "os": "linux", "cpu": "arm" }, "sha512-wPIeeigXgJpwNw3wydYRt3U9iN9Y/ejpOZuYL9IA7igxWs7LIQMOkhKxTumRvy6dIv0iXKk3RTw3Vmjg0i+2sg=="], "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.68.0", "", { "os": "linux", "cpu": "arm" }, "sha512-xW5XoEHVNqydPBv2KXvk9lmEzyAlOQHVEazKoXUuAacqekjya+OdiaFjjEBl0oJD02raG8g3TRl9OVCh9PDIHA=="],
"@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.67.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0+XNxcdbkTfxdcD4qW6Ci9n+mBNJ8xTBumnxKvKBmRFOdx0Wf8/KiHjCJayooXmYkqRpRVd98Q5egvzx5BLSgQ=="], "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.68.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-QCvYwVVQieu6oyJglAgV9vH/YMDxZyR4cwVSYtoq9oOXd5N+D3TDUBjNwxFrLn5AdcJZOcvn/7IB17vJGp+2Og=="],
"@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.67.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-I75LKPJyNOYUzkqAiAMIE31+Ye7xtQXZdoty1IXn4B+bw5Zpmez5wfG19ejGpNnS/BzQ7LFS+7jxuTPb+vHiZw=="], "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.68.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-4TVz5iFQ8ndrHnhX50UXiz9BIWAtUSOHJ6Nus4qWFfJBXq/Ed/krXbF/ehJu42BXM5tIvBs99jNIM22s9agY5A=="],
"@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.67.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-c2M5iRpe1QMZSRE/UvZoPdXBWb5Ic/ycvOyNiKCqPwQ/OyOKIMiJs02ynlNnjb7ZZJnRXYLmGcohoINOcwDK3w=="], "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.68.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-qLe3ao0RP84bnPxBvRI+GnlK/jybo538NWu0Xrm+zYeTmJtpzqLhnnd5BH21NafqKnplbGZjtN1cnrOlWF73lw=="],
"@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.67.0", "", { "os": "linux", "cpu": "none" }, "sha512-dQzzYlV24Udhfm5ECuSdgqRvFJU/CGHzcYYEO3dLM6W6+CHiBFrq9OjIllkdCcPhsoSQ8o223Dja84MOSzed9A=="], "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.68.0", "", { "os": "linux", "cpu": "none" }, "sha512-Yvyl7a6gbb0vM6r925KW2dO+/CmXySO5TVbcX7o/uZJ+d108HFOG0TxIyHApmn5USuj5mhDPxzhiVwOlGP7uSA=="],
"@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.67.0", "", { "os": "linux", "cpu": "none" }, "sha512-rFNq1CgX4qMJANOq42LkAs90JE80GpiaEohAV2qn/gT2hGjQTW1zBO5zQBxArI4926pM1OSzo3CN0tBszGBIaA=="], "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.68.0", "", { "os": "linux", "cpu": "none" }, "sha512-mJlFuFVCxzrYM5sFStN433D/s/mb6Wq4aCQAM02vs/OudHywnaSAd2rb1vlYUJtqdYIciJtiasuxvfbYkv5fLg=="],
"@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.67.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-Sky6rEdz2o5IGq01lPhS12yEvDdChVEcaYrcLHkveh4Fx0qPjljE/Iul6SX/bRMl6lNc8J7J/mDQdzgBdA++Pg=="], "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.68.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-RlfSg++qs1hbKltRR6lYvV9EoI3MdlfSQD9w1hdHVYjHqjIn1tkH4FWOpMSmjKGN20zr+nI+W9o4ARogCDudGQ=="],
"@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.67.0", "", { "os": "linux", "cpu": "x64" }, "sha512-vPXmlNORV8AZq2Ocxh07pxwMjfENUWCV/eZArnao0qC3NO/hDeTVkQvee7SJJUbIiF5PZbBa4kYmaXnu7Rk58w=="], "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.68.0", "", { "os": "linux", "cpu": "x64" }, "sha512-nyzRB9U+dlYUKu3pMo3afHzZBUv/oTHZMG36ZfJViNVfOIzp70Q4GS8FFRGgYJ/p0zcyDCgpBvYISOdJOMh+jQ=="],
"@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.67.0", "", { "os": "linux", "cpu": "x64" }, "sha512-x/WAtFqYtVr3vZ9ni8nr4kn9whSitg8fOljq/pZzBpxopRdY1BMLZCZkrbIbaBcYkm46qGbqVea2FCWmtQ2P9w=="], "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.68.0", "", { "os": "linux", "cpu": "x64" }, "sha512-iCx3sbZRIvGrL1RafphEiUKBaW1lc0/tAjKOIB/Wjw2+STRBEdu5+fH1Gc1faEWEmc2k5Ks4iUUV54Zd5C9a1A=="],
"@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.67.0", "", { "os": "none", "cpu": "arm64" }, "sha512-eRw9Neh4/aA6i+q/R3WU1gGQINhVM0J4fXIm6t27caOamkr/37uAkp1IdBx4zlJH97hmXR63z/q9n5c5dN7MzA=="], "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.68.0", "", { "os": "none", "cpu": "arm64" }, "sha512-x2X5AZez7OgyLLFpwIgItoXBUqudDM7yiaTsxv8R8vKQ6e81l0jVw0NFeUCXzcl1sAJq8h+tC8N4mY8EiMeL4w=="],
"@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.67.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-YIMvb+sGNYN2uc6+QK2HLPeEKM2vl7QZ5onQzpAJRb6pnf0DwUFP5R8tdS9R0l8hdUil2gu4Uxd0Yxrop0iT4w=="], "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.68.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-AHVPjXkenLPQUh6kB8zSC8pX2ct9r4T1Edk9r/RNJyov6wPsS5uAfYtipwG4chn6+3bPFG5rI/3DxEeH8vib1w=="],
"@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.67.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-LzmU9MyACPzwNDIK0ItMedHPz735Ug7ELWguxo4/kuy6zWuDoeglOAEFCY8jLg0PzRpFO3hDyLFe2Gu2eFDeGA=="], "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.68.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-n09SjEk5VH7z8Hl4WVP7hho+cCwGViENkQFiM45vbW85dJd7kEhWaHRUobaPzEmrWyu6uumd4EuNfNyDKLtzDA=="],
"@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.67.0", "", { "os": "win32", "cpu": "x64" }, "sha512-sbQOIDNLUEeVZcAJcSL5VURn7kfjvilPviody4Yl5n8lQCDtUm+C9oHTTwZS/m4d/Z6Vv3jNEiAofH932NPPCg=="], "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.68.0", "", { "os": "win32", "cpu": "x64" }, "sha512-gPe+dJLXaPuWPWqlpklDAJp0k+K9KhQPYiQLHfb+i2rmFuUGfJ/5Qlj6tr1mO6of5g0DiLjG/XCFHIaPhotqqA=="],
"@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.82.0", "", { "os": "android", "cpu": "arm" }, "sha512-a3LB+C5Dsj5b/qtmG/mv5WrzuiXEpg1KF5nXWcEvaoN5TYAqkIvxPOwTPp3Jy/FoGpRo8zsTFhMElMXfeoOEzA=="], "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.83.0", "", { "os": "android", "cpu": "arm" }, "sha512-0yGY24EwsLk5YDe6F+VkmZyRHSwJDALa3nIrPpq7FXmp2lV2d0TzvBCGeZk+wgiULRGr5blhyr4QMp5KCXJUqA=="],
"@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.82.0", "", { "os": "android", "cpu": "arm64" }, "sha512-OBlhRgNqFblGpGenno/aqOfJLOkQ2B8Ig3iDAalfn0H8hJGZKXPeexCRTDm6uwv6YUjSA9Xnwt1y/Bgj5ZH8uw=="], "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.83.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hHfJ0vc17A4iUjH5p9BsTUPYbYRNxGpvD2lbu1aBRk54bzNIx9o5TtYF39QPZcV95DagZd+4DEAw2RH3G2ZsMg=="],
"@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.82.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-dsopxqtY5ZdyT9uLHyGt1SyiLop6hi7hWI3PKpePodkRQOkLaCm+OE4fR9CAz9qdfjiFO8531tX/QDyP/psjFg=="], "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.83.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hsOjYjszLb/3zym/TkzUMPAoQlTJcuzSyEPOAyA+skXJIX9M0o+4JfOtqopX/Vf4hSLrJ98j0nvFo23gzk8auQ=="],
"@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.82.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-94Lu0SgTClKColU66g1VDuigV3HkcbkJBnTtZjGYfE8UPugaWDgKrm2icjC6HJVUYler2OXaHP/X0TBy8+CowQ=="], "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.83.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-mjh5oH2EA+wl5yRJYT9K9G61O2zFlpuv+yf2JwZOi0+dq2FnTUtm1h8i+5Ik0fXPWIu/k84I1psZR9aQsLAnyA=="],
"@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.82.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hne/V06ewhh1i0w8+l7GDNROAGCGPmyFuOwiP7YTRu0JycyStJ4785dmF8xU5p0uUwt2emvIF9vc7Xjis+cJ0g=="], "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.83.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fNHr64/YaO8YssuoDVC8+F4Uk5enR86q5uxfHkQrjAPs1dbAILOrD2uaud+J7MO8Fx774g44ERLD0IGIvZE48w=="],
"@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.82.0", "", { "os": "linux", "cpu": "arm" }, "sha512-aWY2xtbZf1LneW9Qsv/n2Sp8gOu74JrlQzEtj4coHX2SHFrCfhmAumaU+sI/A5nr+yoTRTSmI/pL2s6ADlNSkw=="], "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.83.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Qpwy3zzAwMj+8/lyYItHmkSMwbkprFNWTK7jPYDOxSyxEhaSLOWYUTCMkjF334J8/WD0nznCCsoBbIH6hpsuIw=="],
"@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.82.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Fe+TtXCXMh/5f7kWlZ2VAwsMumZWtraFlKVk1NJlL52/beGwfDE7ov+/8gVirHzWokzGu7X65hSPq0ucPDskWQ=="], "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.83.0", "", { "os": "linux", "cpu": "arm" }, "sha512-s+BirYLFq7JL2k9sP0XI3ZXJ9dYvJ8sX3jLCLoag7tt+zrSHpZxP0jqznfL+Gdgwu7ay0dYgGYJXrQvq3iWloA=="],
"@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.82.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-6azCZ6OJudlvipNttXCCQcyeFfcJ/NvUZdSN1z8elo73kCHtyQC7WTiUcSjWYvJ1jaq9KDUyMAoAS/vNzhBomA=="], "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.83.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-7lihXt3vKr+GIyapNbHrnFHm/biiW30le6Zv/DExbAFPF6YwCQXVFlONPFehxs0CpGO4CBfYPM9rdDT+XMoIlg=="],
"@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.82.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-PLEaSD8IAIIlwW4dwOd9YaxuxeOpwiXL4J24rcnE4iNtyM5j9Q9/3+gti08oXpx0u2ygNjRDx9xjWWpQonuJEw=="], "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.83.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-q63JalLYVkZiZvls1z3PPUnpmQluOMXp0khqQMznCeAPLGydfNY8JhvuA4WlK57JfrvikU8wB5lPVveqpIXvew=="],
"@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.82.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-D94em/BwknNTn4vqxjHh5wb2oL566eFhArabqKIr0cNZMHOJuiraFp1A8tXpH05bbE5tqwEfLXTI0MWEGtn3Dw=="], "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.83.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-krQmDF+dRbxvdqVPV88ZuOoPPu8X5BuqDA8Hd+qcS4YMRQCb+nexA57DazgGsc/rGdKBe3QmV0mnv0bdpW/p5g=="],
"@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.82.0", "", { "os": "linux", "cpu": "none" }, "sha512-MOprxBaoYU2D4VgxXCl3ghydThWtx7Um1lL51kGYNeQ5Al7WzsH7/tqGdNtbLrIWnjq3bsm13+nz/gRIxjrOXw=="], "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.83.0", "", { "os": "linux", "cpu": "none" }, "sha512-MmOl8Y6txEAXZU1RG8Rr264jQ6D7VPmqFsU/45x/FeWsGe32hklTqGrLE6UxHzp5Rjt0wP+20tY8YXKgSFB3mw=="],
"@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.82.0", "", { "os": "linux", "cpu": "none" }, "sha512-5h55QsfJ/luDXZzC20k6SNOY1Az+dCP9WvntKtcUWh2JhckAdwApY2ZusaBTwLENnReXU+A2fJtSrYvZJNKNPg=="], "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.83.0", "", { "os": "linux", "cpu": "none" }, "sha512-u1rMymh0W3JZkq370kzQsYPULGWqhE09pZRqnZvUSoYaI9pVO5yVX+iYIslmWuEgwuzH9YAaOsScJiobWCHoOw=="],
"@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.82.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-IE8NJNLlHr0CaXyGJPGVn0eTkUyoj1I2UfA8x7I4PSOYKsQ/6btVC7Pywrj5onk0cMH25r6Z38SoN3AvE5Zuog=="], "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.83.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-y0zK3HNwGysu7rqtE+BQG/d0bx5gh/KwlOtghN8oWeK1KcWzeaLqtZrbm8owqdma1lFyrce/hTO5ismuNu+INQ=="],
"@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.82.0", "", { "os": "linux", "cpu": "x64" }, "sha512-XUUUxaBo9XKl+J1B9EmP1cTGQPddzeURvoGkfwh/94PGnbW+hBprDljneoI2M1jzC1bzrIV3ihc7iM9UXl8+tg=="], "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.83.0", "", { "os": "linux", "cpu": "x64" }, "sha512-rS5gM0NgD7ngmuJmbIehsidtrOwKkLFwCQbKEeb9KuyQrrWNq5Zkn0uV6AYdXOMJ0grrWEiLwBuvMxt8w5vsNw=="],
"@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.82.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SWLSFulX9TDuH6yvbPYp4+VNn6jkkIvvI+KiujDM5rWBRHEfkesCC/pCneIIUr6ovkxZ5fRtpi2v5Cz5FrMJZg=="], "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.83.0", "", { "os": "linux", "cpu": "x64" }, "sha512-W2IH4EtpcPaWcvNGCA95YoDg4vxqE/ZiPCi3arrxEEpsK7+JQN9WYwrlYFx9pcdP6KPXqRqkv3zdQPHcx7b6YQ=="],
"@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.82.0", "", { "os": "none", "cpu": "arm64" }, "sha512-BQy35f6ZUdNr9a6c7B7orxQTcLjByGT2z3WAgmRovpRwmPYAaJ+NTplmMzhdjdJ4qSchfMNZy/Ukg+qRg6zseQ=="], "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.83.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6LyKkUyoajssTPLlZmDbZIbu4IZ5B4bGuRUnBgCGpEvHP3FQMaYITncHA/unPUo7q+Z+pIu2HhdkQ+8d1SG7iA=="],
"@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.82.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-V4QhSTg5gctZue8RJjsGi7NpQPThr/p1/HfmiMC5kfe1KFEup9SQRVub4A6kijQjdHfxj7bLL1KO3QO7/5bwMQ=="], "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.83.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Uz/fObEtF0jmNJQJ8CGRBKfefYstS0/wjD3s6IGzP8nUwsJykHQJBiN3npHwKiGRGn/vvBEgNr4B3cCzmmatvg=="],
"@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.82.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-TUSCLaKB2yktpFAJ/r3HAUYsaV/3DT7JS4iNKyoh3a9YNwD0UG7Ezh4D8m23654vQcU6P/RQrCAjRPKe4peP/A=="], "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.83.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-u7XcvPW6Bk58tY5iWs2ESb0vJjoE/kuSpHxopbwp/p3ZtWVQXZ6wor5w3ssVTHOqd/v8b+QdhSFWQ4grEUNWpA=="],
"@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.82.0", "", { "os": "win32", "cpu": "x64" }, "sha512-VTVoRIWJTb+wvUX8EYoPArfFH02whuR10goFXE/LHRRX33ajRrFgqbcONXZMiF4C5rnattfkm87HqYn8jb8hmQ=="], "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.83.0", "", { "os": "win32", "cpu": "x64" }, "sha512-LZRubd7ph13QmAg4fFecTYVZkiYbROR2Htaxh/ufWRkDhPOm2wrwaEYR89e0YpPFD3dqBrPoxS7myBw5hmYA7Q=="],
"@oxlint/plugins": ["@oxlint/plugins@1.82.0", "", {}, "sha512-IDF4UXBNOaCeLoEpVxf5Bg85KOWdeuBEkbo1rGzS2vXcbD+GT1asBSeeTC+5BgW6NFTHVByd6N5giHsFPGtwAA=="], "@oxlint/plugins": ["@oxlint/plugins@1.83.0", "", {}, "sha512-qD64wr6SS1DL7YbJQ8WWjyUfFfrJsvAy9Vo0sG/PYUQy30w0uemN2h6j4xuvj4noyETDpzcGC20sv+FH7GrN5A=="],
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
@@ -407,7 +407,7 @@
"@tufjs/models": ["@tufjs/models@4.1.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^10.1.1" } }, "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww=="], "@tufjs/models": ["@tufjs/models@4.1.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^10.1.1" } }, "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww=="],
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], "@types/bun": ["@types/bun@1.4.2", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="],
"@types/node": ["@types/node@26.5.0", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-dVSGpriSoCgz8WnDNTuSSuSv1PC/ALXihO4ulRZt7Md8k9mlbdin3lGOcDE8SnWOgf513ByWlXd7BK4azmyg/A=="], "@types/node": ["@types/node@26.5.0", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-dVSGpriSoCgz8WnDNTuSSuSv1PC/ALXihO4ulRZt7Md8k9mlbdin3lGOcDE8SnWOgf513ByWlXd7BK4azmyg/A=="],
@@ -489,7 +489,7 @@
"bun-ffi-structs": ["bun-ffi-structs@0.3.1", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-3gM7PpVWLyrwxWjcilSiGuhWanhZivvo6l0u573NziPH6f/gwk6McbaYgn7oJWov6pKGRTDbrg94W5DcJsKTtQ=="], "bun-ffi-structs": ["bun-ffi-structs@0.3.1", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-3gM7PpVWLyrwxWjcilSiGuhWanhZivvo6l0u573NziPH6f/gwk6McbaYgn7oJWov6pKGRTDbrg94W5DcJsKTtQ=="],
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], "bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="],
"cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" } }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="], "cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" } }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="],
@@ -705,9 +705,9 @@
"npm-registry-fetch": ["npm-registry-fetch@19.1.1", "", { "dependencies": { "@npmcli/redact": "^4.0.0", "jsonparse": "^1.3.1", "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minizlib": "^3.0.1", "npm-package-arg": "^13.0.0", "proc-log": "^6.0.0" } }, "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw=="], "npm-registry-fetch": ["npm-registry-fetch@19.1.1", "", { "dependencies": { "@npmcli/redact": "^4.0.0", "jsonparse": "^1.3.1", "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minizlib": "^3.0.1", "npm-package-arg": "^13.0.0", "proc-log": "^6.0.0" } }, "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw=="],
"oxfmt": ["oxfmt@0.67.0", "", { "dependencies": { "tinypool": "2.1.2" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.67.0", "@oxfmt/binding-android-arm64": "0.67.0", "@oxfmt/binding-darwin-arm64": "0.67.0", "@oxfmt/binding-darwin-x64": "0.67.0", "@oxfmt/binding-freebsd-x64": "0.67.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.67.0", "@oxfmt/binding-linux-arm-musleabihf": "0.67.0", "@oxfmt/binding-linux-arm64-gnu": "0.67.0", "@oxfmt/binding-linux-arm64-musl": "0.67.0", "@oxfmt/binding-linux-ppc64-gnu": "0.67.0", "@oxfmt/binding-linux-riscv64-gnu": "0.67.0", "@oxfmt/binding-linux-riscv64-musl": "0.67.0", "@oxfmt/binding-linux-s390x-gnu": "0.67.0", "@oxfmt/binding-linux-x64-gnu": "0.67.0", "@oxfmt/binding-linux-x64-musl": "0.67.0", "@oxfmt/binding-openharmony-arm64": "0.67.0", "@oxfmt/binding-win32-arm64-msvc": "0.67.0", "@oxfmt/binding-win32-ia32-msvc": "0.67.0", "@oxfmt/binding-win32-x64-msvc": "0.67.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-vV7sSiPsaO0mSxdoUdayipVDFPzW/UQ+hrezEHa20+Tx1dnMdZLSRHMT0PdS67FFbhd74M1n08asW21aLGeCrA=="], "oxfmt": ["oxfmt@0.68.0", "", { "dependencies": { "tinypool": "2.1.2" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.68.0", "@oxfmt/binding-android-arm64": "0.68.0", "@oxfmt/binding-darwin-arm64": "0.68.0", "@oxfmt/binding-darwin-x64": "0.68.0", "@oxfmt/binding-freebsd-x64": "0.68.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.68.0", "@oxfmt/binding-linux-arm-musleabihf": "0.68.0", "@oxfmt/binding-linux-arm64-gnu": "0.68.0", "@oxfmt/binding-linux-arm64-musl": "0.68.0", "@oxfmt/binding-linux-ppc64-gnu": "0.68.0", "@oxfmt/binding-linux-riscv64-gnu": "0.68.0", "@oxfmt/binding-linux-riscv64-musl": "0.68.0", "@oxfmt/binding-linux-s390x-gnu": "0.68.0", "@oxfmt/binding-linux-x64-gnu": "0.68.0", "@oxfmt/binding-linux-x64-musl": "0.68.0", "@oxfmt/binding-openharmony-arm64": "0.68.0", "@oxfmt/binding-win32-arm64-msvc": "0.68.0", "@oxfmt/binding-win32-ia32-msvc": "0.68.0", "@oxfmt/binding-win32-x64-msvc": "0.68.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-Z0XMofcXCGUXbcpBHnWyUiX93BGiw1B+lcHNbQDWEtOhX06ewoFfu4zXkyiLhRrNnMq0twqXRHUcJetf+GsiQQ=="],
"oxlint": ["oxlint@1.82.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.82.0", "@oxlint/binding-android-arm64": "1.82.0", "@oxlint/binding-darwin-arm64": "1.82.0", "@oxlint/binding-darwin-x64": "1.82.0", "@oxlint/binding-freebsd-x64": "1.82.0", "@oxlint/binding-linux-arm-gnueabihf": "1.82.0", "@oxlint/binding-linux-arm-musleabihf": "1.82.0", "@oxlint/binding-linux-arm64-gnu": "1.82.0", "@oxlint/binding-linux-arm64-musl": "1.82.0", "@oxlint/binding-linux-ppc64-gnu": "1.82.0", "@oxlint/binding-linux-riscv64-gnu": "1.82.0", "@oxlint/binding-linux-riscv64-musl": "1.82.0", "@oxlint/binding-linux-s390x-gnu": "1.82.0", "@oxlint/binding-linux-x64-gnu": "1.82.0", "@oxlint/binding-linux-x64-musl": "1.82.0", "@oxlint/binding-openharmony-arm64": "1.82.0", "@oxlint/binding-win32-arm64-msvc": "1.82.0", "@oxlint/binding-win32-ia32-msvc": "1.82.0", "@oxlint/binding-win32-x64-msvc": "1.82.0" }, "peerDependencies": { "oxlint-tsgolint": ">=7.0.2001", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-+iFM1BGw1ntYJt3QngbJmjbrGxPaKMUADOXOijpWGnYcBPq8YZnQftSS1C+pVcDYy9YxqDVJKQqQkTazTQMboQ=="], "oxlint": ["oxlint@1.83.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.83.0", "@oxlint/binding-android-arm64": "1.83.0", "@oxlint/binding-darwin-arm64": "1.83.0", "@oxlint/binding-darwin-x64": "1.83.0", "@oxlint/binding-freebsd-x64": "1.83.0", "@oxlint/binding-linux-arm-gnueabihf": "1.83.0", "@oxlint/binding-linux-arm-musleabihf": "1.83.0", "@oxlint/binding-linux-arm64-gnu": "1.83.0", "@oxlint/binding-linux-arm64-musl": "1.83.0", "@oxlint/binding-linux-ppc64-gnu": "1.83.0", "@oxlint/binding-linux-riscv64-gnu": "1.83.0", "@oxlint/binding-linux-riscv64-musl": "1.83.0", "@oxlint/binding-linux-s390x-gnu": "1.83.0", "@oxlint/binding-linux-x64-gnu": "1.83.0", "@oxlint/binding-linux-x64-musl": "1.83.0", "@oxlint/binding-openharmony-arm64": "1.83.0", "@oxlint/binding-win32-arm64-msvc": "1.83.0", "@oxlint/binding-win32-ia32-msvc": "1.83.0", "@oxlint/binding-win32-x64-msvc": "1.83.0" }, "peerDependencies": { "oxlint-tsgolint": ">=7.0.2001", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-cyDzSzaw3uzP0TeCeq3lLRPPoaUxkbB4ZOXj+kn+5r+BX9V+4bNVGk9lxer+WrgcpebH4JxLlJ3KQjveVztOLQ=="],
"p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], "p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
@@ -851,7 +851,7 @@
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
"zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="], "zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="],
"@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
@@ -879,6 +879,8 @@
"@npmcli/promise-spawn/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], "@npmcli/promise-spawn/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="],
"@opencode/plugin/zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="], "babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"$schema": "https://opencode.ai/config.json", "$schema": "https://opencode.ai/config.json",
// Use OpenCode V2 (opencode2), not the V1 opencode CLI. // Requires OpenCode 2.0.4 or newer.
// Availability uses the native catalog, including integrations and earlier plugins. // Availability uses the native catalog, including integrations and earlier plugins.
"plugins": [ "plugins": [
{ {
+1 -1
View File
@@ -89,4 +89,4 @@ rm -rf "$backup"
backup="" backup=""
rm -f "$config/plugins/stanp-opencode.js" rm -f "$config/plugins/stanp-opencode.js"
mv "$install/opencode.json" "$config_file" mv "$install/opencode.json" "$config_file"
printf 'Built and installed stanp-opencode from %s into %s/plugins/stanp-opencode and updated %s\nRestart opencode2 to load it.\n' "$checkout" "$config" "$config_file" printf 'Built and installed stanp-opencode from %s into %s/plugins/stanp-opencode and updated %s\nRestart opencode to load it.\n' "$checkout" "$config" "$config_file"
+1 -1
View File
@@ -100,4 +100,4 @@ fi
rm -rf "$backup" rm -rf "$backup"
rm -f "$config/plugins/stanp-opencode.js" rm -f "$config/plugins/stanp-opencode.js"
mv "$install/opencode.json" "$config_file" mv "$install/opencode.json" "$config_file"
printf 'Installed stanp-opencode %s into %s/plugins/stanp-opencode and updated %s\nRestart opencode2 to load it.\n' "$tag" "$config" "$config_file" printf 'Installed stanp-opencode %s into %s/plugins/stanp-opencode and updated %s\nRestart opencode to load it.\n' "$tag" "$config" "$config_file"
+15 -15
View File
@@ -24,9 +24,9 @@
"dist", "dist",
"examples" "examples"
], ],
"packageManager": "bun@1.3.14", "packageManager": "bun@1.4.2",
"engines": { "engines": {
"bun": ">=1.3.14" "bun": ">=1.4.2"
}, },
"scripts": { "scripts": {
"check": "tsc --noEmit", "check": "tsc --noEmit",
@@ -37,25 +37,25 @@
"build": "bun scripts/build.ts && tsc --project tsconfig.build.json --emitDeclarationOnly" "build": "bun scripts/build.ts && tsc --project tsconfig.build.json --emitDeclarationOnly"
}, },
"devDependencies": { "devDependencies": {
"@opencode/client": "2.0.2", "@opencode/client": "2.0.4",
"@opencode/plugin": "2.0.2", "@opencode/plugin": "2.0.4",
"@opencode/schema": "2.0.2", "@opencode/schema": "2.0.4",
"@opentui/core": "0.5.10", "@opentui/core": "0.5.11",
"@opentui/solid": "0.5.10", "@opentui/solid": "0.5.11",
"@oxlint/plugins": "1.82.0", "@oxlint/plugins": "1.83.0",
"@types/bun": "1.3.14", "@types/bun": "1.4.2",
"oxfmt": "0.67.0", "oxfmt": "0.68.0",
"oxlint": "1.82.0", "oxlint": "1.83.0",
"solid-js": "1.9.15", "solid-js": "1.9.15",
"typescript": "7.0.2" "typescript": "7.0.2"
}, },
"peerDependencies": { "peerDependencies": {
"@opencode/plugin": "2.0.2", "@opencode/plugin": "2.0.4",
"@opentui/core": ">=0.5.10", "@opentui/core": ">=0.5.11",
"@opentui/solid": ">=0.5.10", "@opentui/solid": ">=0.5.11",
"solid-js": ">=1.9.0" "solid-js": ">=1.9.0"
}, },
"dependencies": { "dependencies": {
"zod": "4.1.8" "zod": "4.6.5"
} }
} }
+10 -7
View File
@@ -59,20 +59,23 @@ export default Plugin.define({
await quotaRegistration?.events.emit("updated", {}); await quotaRegistration?.events.emit("updated", {});
}); });
quotaRegistration = await context.rpc.register(QuotaRpc, { quotaRegistration = await context.rpc.register(QuotaRpc, {
status: async ({ sessionID }) => (await quota.status(sessionID)) ?? null, status: async () => quota.status() ?? null,
}); });
await quota.init(); await quota.init();
// Subscribe before the first read: configuration can publish its catalog while // Subscribe before the first read: configuration can publish its models while
// asynchronous setup is running. // asynchronous setup is running.
const updates = (async () => { const updates = (async () => {
for await (const event of context.event.subscribe({ signal: controller.signal })) { for await (const event of context.event.subscribe({ signal: controller.signal })) {
if (event.location) {
const workspaceID =
"workspaceID" in event.location ? event.location.workspaceID : undefined;
if ( if (
event.location && event.location.directory !== context.location.directory ||
(event.location.directory !== context.location.directory || workspaceID !== context.location.workspaceID
event.location.workspaceID !== context.location.workspaceID)
) )
continue; continue;
if (event.type === "catalog.updated") { }
if (event.type === "model.updated") {
models = await discover(context, selected.provider); models = await discover(context, selected.provider);
await context.agent.reload(); await context.agent.reload();
} else if (event.type === "agent.updated") { } else if (event.type === "agent.updated") {
@@ -105,7 +108,7 @@ export default Plugin.define({
} }
})().catch((error) => { })().catch((error) => {
if (!controller.signal.aborted) if (!controller.signal.aborted)
console.error("stanp-opencode catalog update failed", error); console.error("stanp-opencode model update failed", error);
}); });
models = await discover(context, selected.provider); models = await discover(context, selected.provider);
const { data: initialAgents } = await context.agent.list(); const { data: initialAgents } = await context.agent.list();
+2 -2
View File
@@ -21,8 +21,8 @@ function priority(model: ModelInfo) {
} }
export async function discover(context: Plugin.Context, providerID: string) { export async function discover(context: Plugin.Context, providerID: string) {
// Native lists already apply activation, integrations, configuration, and catalog transforms. // Native lists already apply activation, integrations, configuration, and model transforms.
const { data } = await context.catalog.model.list(); const { data } = await context.model.list();
const models = new Map<string, { model: ModelInfo; serviceTier: string | undefined }>(); const models = new Map<string, { model: ModelInfo; serviceTier: string | undefined }>();
for (const model of data) { for (const model of data) {
if (model.providerID === providerID && model.enabled) if (model.providerID === providerID && model.enabled)
+1 -2
View File
@@ -8,14 +8,13 @@ const window = z.object({
used: z.number(), used: z.number(),
resetsAt: z.number(), resetsAt: z.number(),
remaining: z.number(), remaining: z.number(),
sessionConsumed: z.number(),
}); });
export const QuotaRpc = Rpc.define({ export const QuotaRpc = Rpc.define({
id: "stanp-opencode.quota", id: "stanp-opencode.quota",
methods: { methods: {
status: { status: {
input: z.object({ sessionID: z.string() }), input: z.object({}),
output: z output: z
.object({ plan: z.string(), fetchedAt: z.number(), windows: z.array(window) }) .object({ plan: z.string(), fetchedAt: z.number(), windows: z.array(window) })
.nullable(), .nullable(),
+28 -106
View File
@@ -54,7 +54,7 @@ export interface QuotaSnapshot {
export interface QuotaStatus { export interface QuotaStatus {
plan: string; plan: string;
fetchedAt: number; fetchedAt: number;
windows: Array<QuotaWindow & { remaining: number; sessionConsumed: number }>; windows: Array<QuotaWindow & { remaining: number }>;
} }
const storedState = z.object({ const storedState = z.object({
@@ -74,14 +74,8 @@ const storedState = z.object({
), ),
}) })
.optional(), .optional(),
sessions: z.record(
z.string(),
z.record(
z.string(),
z.object({ resetsAt: z.number(), lastUsed: z.number(), consumed: z.number() }),
),
),
}); });
const legacyStoredState = storedState.extend({ sessions: z.unknown() });
type StoredState = z.infer<typeof storedState>; type StoredState = z.infer<typeof storedState>;
function withoutExcludedState(state: StoredState): StoredState { function withoutExcludedState(state: StoredState): StoredState {
@@ -89,20 +83,10 @@ function withoutExcludedState(state: StoredState): StoredState {
return { return {
...state, ...state,
latest: state.latest && windows?.length ? { ...state.latest, windows } : undefined, latest: state.latest && windows?.length ? { ...state.latest, windows } : undefined,
sessions: Object.fromEntries(
Object.entries(state.sessions).map(([session, tracked]) => [
session,
Object.fromEntries(
Object.entries(tracked).filter(
([id]) => !isExcludedLimit(id.split(":", 1)[0], undefined),
),
),
]),
),
}; };
} }
type QuotaContext = Pick<Plugin.Context, "integration" | "session" | "storage">; type QuotaContext = Pick<Plugin.Context, "integration" | "storage">;
type QuotaFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>; type QuotaFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
const quotaWindow = z.object({ const quotaWindow = z.object({
@@ -239,7 +223,8 @@ export class QuotaTracker {
private timer: ReturnType<typeof setInterval> | undefined; private timer: ReturnType<typeof setInterval> | undefined;
private request: Promise<QuotaSnapshot | undefined> | undefined; private request: Promise<QuotaSnapshot | undefined> | undefined;
private readonly requests = new Set<Promise<QuotaSnapshot | undefined>>(); private readonly requests = new Set<Promise<QuotaSnapshot | undefined>>();
private readonly active = new Map<string, Set<string>>(); private readonly migrations = new Set<Promise<void>>();
private readonly active = new Set<string>();
private controller: AbortController | undefined; private controller: AbortController | undefined;
private generation = 0; private generation = 0;
private disposed = false; private disposed = false;
@@ -276,14 +261,22 @@ export class QuotaTracker {
: (this.state?.account ?? (await fingerprint(`credential:${credential.refresh}`))); : (this.state?.account ?? (await fingerprint(`credential:${credential.refresh}`)));
if (generation !== this.generation) return; if (generation !== this.generation) return;
if (this.state?.account !== account) { if (this.state?.account !== account) {
const parsed = storedState.safeParse(await this.context.storage.get(STORAGE_KEY)); const value = await this.context.storage.get(STORAGE_KEY);
const parsed = storedState.safeParse(value);
if (generation !== this.generation) return; if (generation !== this.generation) return;
const stored = parsed.success ? parsed.data : undefined; const stored = parsed.success ? parsed.data : undefined;
if (generation !== this.generation) return; if (generation !== this.generation) return;
this.state = this.state = stored?.account === account ? withoutExcludedState(stored) : { account };
stored?.account === account const legacy = legacyStoredState.safeParse(value);
? withoutExcludedState(stored) if (stored?.account === account && legacy.success) {
: { account, sessions: {} }; const migration = this.persist();
this.migrations.add(migration);
try {
await migration;
} finally {
this.migrations.delete(migration);
}
}
} }
if (generation !== this.generation) return; if (generation !== this.generation) return;
if (!this.timer) this.timer = setInterval(() => void this.refresh(), this.pollMs); if (!this.timer) this.timer = setInterval(() => void this.refresh(), this.pollMs);
@@ -345,7 +338,7 @@ export class QuotaTracker {
if (parsed.success && parsed.data.account_id) { if (parsed.success && parsed.data.account_id) {
const account = await fingerprint(`account:${parsed.data.account_id}`); const account = await fingerprint(`account:${parsed.data.account_id}`);
if (generation !== this.generation || !this.state) return; if (generation !== this.generation || !this.state) return;
if (account !== this.state.account) this.state = { account, sessions: {} }; if (account !== this.state.account) this.state = { account };
} }
const snapshot = normalizeQuota(payload); const snapshot = normalizeQuota(payload);
if (!snapshot) return; if (!snapshot) return;
@@ -360,102 +353,30 @@ export class QuotaTracker {
private apply(snapshot: QuotaSnapshot) { private apply(snapshot: QuotaSnapshot) {
if (!this.state) return; if (!this.state) return;
for (const root of this.active.keys()) {
const tracked = (this.state.sessions[root] ??= {});
for (const window of snapshot.windows) {
const previous = tracked[window.id];
if (!previous) {
tracked[window.id] = {
resetsAt: window.resetsAt,
lastUsed: window.used,
consumed: 0,
};
continue;
}
if (previous.resetsAt !== window.resetsAt) {
previous.resetsAt = window.resetsAt;
previous.lastUsed = window.used;
previous.consumed += window.used;
} else if (window.used >= previous.lastUsed) {
previous.consumed += window.used - previous.lastUsed;
previous.lastUsed = window.used;
}
}
}
this.state.latest = snapshot; this.state.latest = snapshot;
} }
private async root(sessionID: string) {
let current = sessionID;
const seen = new Set<string>();
while (!seen.has(current)) {
seen.add(current);
const session = await this.context.session.get({ sessionID: current });
if (!session.parentID) return current;
current = session.parentID;
}
return sessionID;
}
async start(sessionID: string) { async start(sessionID: string) {
if (this.active.has(sessionID)) return;
const generation = this.generation; const generation = this.generation;
try {
const root = await this.root(sessionID);
if (generation !== this.generation) return;
const family = this.active.get(root);
if (family?.has(sessionID)) return;
const snapshot = await this.refresh(); const snapshot = await this.refresh();
if (!snapshot || !this.state || generation !== this.generation) return; if (!snapshot || generation !== this.generation) return;
if (!family) { this.active.add(sessionID);
const previous = this.state.sessions[root] ?? {};
this.state.sessions[root] = Object.fromEntries(
snapshot.windows.map((window) => [
window.id,
{
resetsAt: window.resetsAt,
lastUsed: window.used,
consumed: previous[window.id]?.consumed ?? 0,
},
]),
);
}
await this.persist();
if (generation !== this.generation) return;
await this.changed();
if (generation !== this.generation) return;
const active = family ?? new Set<string>();
active.add(sessionID);
this.active.set(root, active);
} catch {
return;
}
} }
async complete(sessionID: string) { async complete(sessionID: string) {
try { if (!this.active.delete(sessionID)) return;
const root = await this.root(sessionID);
const family = this.active.get(root);
if (!family?.has(sessionID)) return;
await this.refresh(); await this.refresh();
family.delete(sessionID);
if (family.size === 0) this.active.delete(root);
await this.persist();
await this.changed();
} catch {
return;
}
} }
async status(sessionID: string): Promise<QuotaStatus | undefined> { status(): QuotaStatus | undefined {
if (!this.state?.latest) return; if (!this.state?.latest) return;
const tracked = this.state.sessions[await this.root(sessionID)];
return { return {
plan: this.state.latest.plan, plan: this.state.latest.plan,
fetchedAt: this.state.latest.fetchedAt, fetchedAt: this.state.latest.fetchedAt,
windows: this.state.latest.windows.map((window) => ({ windows: this.state.latest.windows.map((window) => ({
...window, ...window,
remaining: Math.max(0, 100 - window.used), remaining: Math.max(0, 100 - window.used),
sessionConsumed: tracked?.[window.id]?.consumed ?? 0,
})), })),
}; };
} }
@@ -465,10 +386,9 @@ export class QuotaTracker {
const value: Schema.Json = this.state.latest const value: Schema.Json = this.state.latest
? { ? {
account: this.state.account, account: this.state.account,
sessions: this.state.sessions,
latest: this.state.latest, latest: this.state.latest,
} }
: { account: this.state.account, sessions: this.state.sessions }; : { account: this.state.account };
await this.context.storage.set(STORAGE_KEY, value); await this.context.storage.set(STORAGE_KEY, value);
} }
@@ -478,6 +398,8 @@ export class QuotaTracker {
this.controller?.abort(); this.controller?.abort();
this.controller = undefined; this.controller = undefined;
this.request = undefined; this.request = undefined;
this.active.clear();
await Promise.all(this.migrations);
if (this.state) await this.clear(); if (this.state) await this.clear();
} }
await this.refresh(); await this.refresh();
@@ -489,6 +411,6 @@ export class QuotaTracker {
this.controller?.abort(); this.controller?.abort();
if (this.timer) clearInterval(this.timer); if (this.timer) clearInterval(this.timer);
this.timer = undefined; this.timer = undefined;
await Promise.all(this.requests); await Promise.all([...this.requests, ...this.migrations]);
} }
} }
+3 -6
View File
@@ -33,14 +33,11 @@ function resetTiming(resetsAt: number, now: number) {
return `${minutes}m`; return `${minutes}m`;
} }
export function formatQuotaWindow( export function formatQuotaWindow(window: QuotaWindow & { remaining: number }, now = Date.now()) {
window: QuotaWindow & { remaining: number; sessionConsumed: number },
now = Date.now(),
) {
const label = window.label const label = window.label
? `${window.label} ${duration(window.seconds)}` ? `${window.label} ${duration(window.seconds)}`
: duration(window.seconds); : duration(window.seconds);
return `${label}: ${percent(window.remaining)}% · ~${percent(window.sessionConsumed)}pp · ${resetTiming(window.resetsAt, now)}`; return `${label}: ${percent(window.remaining)}% · ${resetTiming(window.resetsAt, now)}`;
} }
export async function loadQuotaStatus(load: () => Promise<QuotaStatus | null>) { export async function loadQuotaStatus(load: () => Promise<QuotaStatus | null>) {
@@ -66,7 +63,7 @@ export default Plugin.define({
const Slot = (props: { sessionID: string }) => { const Slot = (props: { sessionID: string }) => {
const [status] = createResource( const [status] = createResource(
() => `${props.sessionID}:${revision()}`, () => `${props.sessionID}:${revision()}`,
() => loadQuotaStatus(() => quota.status({ sessionID: props.sessionID })), () => loadQuotaStatus(() => quota.status({})),
); );
return ( return (
<Show when={status()}> <Show when={status()}>
+35 -5
View File
@@ -52,6 +52,7 @@ async function host(
> = {}, > = {},
configuredMcpTools: string[] = [], configuredMcpTools: string[] = [],
directory = "/test", directory = "/test",
workspaceID?: string,
) { ) {
const agents = new Map<string, DeepMutable<Agent.Info>>(); const agents = new Map<string, DeepMutable<Agent.Info>>();
const build: DeepMutable<Agent.Info> = Agent.Info.default(Agent.ID.make("build")); const build: DeepMutable<Agent.Info> = Agent.Info.default(Agent.ID.make("build"));
@@ -131,15 +132,13 @@ async function host(
// SAFETY: This test host implements the native APIs used by setup; unrelated context // SAFETY: This test host implements the native APIs used by setup; unrelated context
// services are deliberately absent so unexpected new dependencies fail the test. // services are deliberately absent so unexpected new dependencies fail the test.
const context = { const context = {
location: { directory }, location: { directory, workspaceID },
options, options,
catalog: {
model: { model: {
async list() { async list() {
return { data: models }; return { data: models };
}, },
}, },
},
event: { event: {
subscribe({ signal }: { signal: AbortSignal }) { subscribe({ signal }: { signal: AbortSignal }) {
signal.addEventListener("abort", () => events.close(), { once: true }); signal.addEventListener("abort", () => events.close(), { once: true });
@@ -249,7 +248,16 @@ async function host(
async refresh(next: ModelInfo[]) { async refresh(next: ModelInfo[]) {
models = next; models = next;
reloaded = Promise.withResolvers<void>(); reloaded = Promise.withResolvers<void>();
events.enqueue({ type: "catalog.updated" }); events.enqueue({ type: "model.updated" });
await reloaded.promise;
},
publishModels(next: ModelInfo[], location?: { directory: string; workspaceID?: string }) {
models = next;
if (location) events.enqueue({ type: "model.updated", location });
else events.enqueue({ type: "model.updated" });
},
async waitForReload() {
reloaded = Promise.withResolvers<void>();
await reloaded.promise; await reloaded.promise;
}, },
async changeBuildPermissions(permissions: Agent.Info["permissions"]) { async changeBuildPermissions(permissions: Agent.Info["permissions"]) {
@@ -668,7 +676,12 @@ test("coordinator defaults expose their effort variant without relying on it for
expect(custom.agents.get("coordinate")?.request.settings.reasoningEffort).toBe("xhigh"); expect(custom.agents.get("coordinate")?.request.settings.reasoningEffort).toBe("xhigh");
}); });
test("catalog updates activate late models and remove unavailable agents", async () => { test("the current host model API filters discovery and reloads changed availability", async () => {
const disabled = { ...reasoningModel("gpt-5.6-luna"), enabled: false };
const other = { ...reasoningModel("gpt-5.6-sol"), providerID: Provider.ID.make("other") };
expect((await host([disabled, other, ...allModels.slice(1)])).agents.has("coordinate")).toBe(
false,
);
const result = await host([]); const result = await host([]);
await result.refresh(allModels); await result.refresh(allModels);
expect(String(result.agents.get("coordinate")?.model?.id)).toBe("gpt-6-astra"); expect(String(result.agents.get("coordinate")?.model?.id)).toBe("gpt-6-astra");
@@ -718,6 +731,23 @@ test("events from another location do not reload this location's agents", async
).toBe("ask"); ).toBe("ask");
}); });
test("model updates require an exact workspace match within the same directory", async () => {
const result = await host([], { provider: "test" }, {}, [], "/test", "workspace-a");
result.publishModels(allModels, { directory: "/test" });
await Bun.sleep(0);
expect(result.agents.has("coordinate")).toBe(false);
result.publishModels(allModels, { directory: "/test", workspaceID: "workspace-b" });
await Bun.sleep(0);
expect(result.agents.has("coordinate")).toBe(false);
const reloaded = result.waitForReload();
result.publishModels(allModels, { directory: "/test", workspaceID: "workspace-a" });
await reloaded;
expect(result.agents.has("coordinate")).toBe(true);
});
test("todo lists persist within one session without leaking to another", async () => { test("todo lists persist within one session without leaking to another", async () => {
const result = await host(); const result = await host();
const todos = [{ id: "1", content: "Implement", status: "in_progress", priority: "high" }]; const todos = [{ id: "1", content: "Implement", status: "in_progress", priority: "high" }];
+31 -47
View File
@@ -173,10 +173,6 @@ function fixture(
storageReads: Promise<Schema.Json | undefined>[] = [], storageReads: Promise<Schema.Json | undefined>[] = [],
) { ) {
const stored = new Map<string, Schema.Json>(); const stored = new Map<string, Schema.Json>();
const sessions = new Map([
["root", {}],
["child", { parentID: "root" }],
]);
let calls = 0; let calls = 0;
let storageCalls = 0; let storageCalls = 0;
let changes = 0; let changes = 0;
@@ -198,12 +194,6 @@ function fixture(
}, },
}, },
} as never, } as never,
session: {
async get({ sessionID }: { sessionID: string }) {
// SAFETY: QuotaTracker reads only parentID from this focused fixture.
return sessions.get(sessionID) as never;
},
} as never,
storage: { storage: {
async get(key: string) { async get(key: string) {
storageCalls++; storageCalls++;
@@ -242,6 +232,7 @@ function fixture(
storageCalls: () => storageCalls, storageCalls: () => storageCalls,
changes: () => changes, changes: () => changes,
headers, headers,
stored,
switch(credential: typeof current) { switch(credential: typeof current) {
current = credential; current = credential;
}, },
@@ -256,7 +247,7 @@ async function accountFingerprint(id: string) {
return Buffer.from(bytes).toString("hex"); return Buffer.from(bytes).toString("hex");
} }
test("removes cached Spark and representative reserve windows and consumption while offline", async () => { test("reads old cached snapshots without session estimates and removes excluded windows", async () => {
const cached = { const cached = {
account: await accountFingerprint("account-one"), account: await accountFingerprint("account-one"),
latest: { latest: {
@@ -331,22 +322,23 @@ test("removes cached Spark and representative reserve windows and consumption wh
); );
await result.tracker.init(); await result.tracker.init();
const status = await result.tracker.status("root"); const status = result.tracker.status();
expect(status?.windows.map((window) => window.id)).toEqual([ expect(status?.windows.map((window) => window.id)).toEqual([
"codex:primary", "codex:primary",
"codex_luna:primary", "codex_luna:primary",
]); ]);
expect(status?.windows[0]?.sessionConsumed).toBe(2); expect(status?.windows[0]).not.toHaveProperty("sessionConsumed");
expect(status?.windows[1]?.sessionConsumed).toBe(3); expect(result.stored.get("chatgpt-quota")).not.toHaveProperty("sessions");
expect(result.calls()).toBe(0);
await result.tracker.dispose(); await result.tracker.dispose();
}); });
test("activates only for OAuth and establishes an awaited root baseline shared by children", async () => { test("activates only for OAuth and refreshes quota at request start", async () => {
const key = fixture({ type: "key", key: "secret" }, [plus]); const key = fixture({ type: "key", key: "secret" }, [plus]);
await key.tracker.init(); await key.tracker.init();
await key.tracker.start("root"); await key.tracker.start("root");
expect(key.calls()).toBe(0); expect(key.calls()).toBe(0);
expect(await key.tracker.status("root")).toBeUndefined(); expect(key.tracker.status()).toBeUndefined();
await key.tracker.dispose(); await key.tracker.dispose();
const oauth = fixture( const oauth = fixture(
@@ -372,13 +364,14 @@ test("activates only for OAuth and establishes an awaited root baseline shared b
expect(oauth.calls()).toBe(1); expect(oauth.calls()).toBe(1);
expect(oauth.headers[0]?.get("chatgpt-account-id")).toBe("account-one"); expect(oauth.headers[0]?.get("chatgpt-account-id")).toBe("account-one");
await oauth.tracker.refresh(); await oauth.tracker.refresh();
const child = await oauth.tracker.status("child"); const status = oauth.tracker.status();
expect(child?.windows[0]?.sessionConsumed).toBe(5); expect(status?.windows[0]?.remaining).toBe(82.5);
expect(child?.windows[0]?.remaining).toBe(82.5); expect(status?.windows[0]).not.toHaveProperty("sessionConsumed");
expect(oauth.stored.get("chatgpt-quota")).not.toHaveProperty("sessions");
await oauth.tracker.dispose(); await oauth.tracker.dispose();
}); });
test("polls, carries consumption across quota resets, tolerates errors, and clears on logout", async () => { test("polls, tolerates errors, and clears on logout", async () => {
const reset = { const reset = {
...plus, ...plus,
rate_limit: { rate_limit: {
@@ -403,12 +396,12 @@ test("polls, carries consumption across quota resets, tolerates errors, and clea
await result.tracker.init(); await result.tracker.init();
await result.tracker.start("root"); await result.tracker.start("root");
await Bun.sleep(35); await Bun.sleep(35);
const status = await result.tracker.status("root"); const status = result.tracker.status();
expect(result.calls()).toBeGreaterThanOrEqual(3); expect(result.calls()).toBeGreaterThanOrEqual(3);
expect(status?.windows[0]?.sessionConsumed).toBe(2); expect(status?.windows[0]?.remaining).toBe(98);
result.logout(); result.logout();
await result.tracker.authChanged(true); await result.tracker.authChanged(true);
expect(await result.tracker.status("root")).toBeUndefined(); expect(result.tracker.status()).toBeUndefined();
const calls = result.calls(); const calls = result.calls();
await result.tracker.dispose(); await result.tracker.dispose();
await Bun.sleep(20); await Bun.sleep(20);
@@ -424,12 +417,11 @@ test("sidebar text places reset timing beside latest remaining quota", () => {
label: null, label: null,
used: 25, used: 25,
remaining: 75, remaining: 75,
sessionConsumed: 1.5,
resetsAt: 100_000 + 2 * 86_400_000 + 5 * 3_600_000, resetsAt: 100_000 + 2 * 86_400_000 + 5 * 3_600_000,
}, },
100_000, 100_000,
), ),
).toBe("7d: 75% · ~1.5pp · 2d 5h"); ).toBe("7d: 75% · 2d 5h");
}); });
test("sidebar reset timing uses compact future units and handles boundaries", () => { test("sidebar reset timing uses compact future units and handles boundaries", () => {
@@ -442,17 +434,16 @@ test("sidebar reset timing uses compact future units and handles boundaries", ()
label: null, label: null,
used: 25, used: 25,
remaining: 75, remaining: 75,
sessionConsumed: 1.5,
resetsAt: now + resetIn, resetsAt: now + resetIn,
}, },
now, now,
); );
expect(format(2 * 3_600_000 + 15 * 60_000)).toBe("5h: 75% · ~1.5pp · 2h 15m"); expect(format(2 * 3_600_000 + 15 * 60_000)).toBe("5h: 75% · 2h 15m");
expect(format(3_600_000)).toBe("5h: 75% · ~1.5pp · 1h"); expect(format(3_600_000)).toBe("5h: 75% · 1h");
expect(format(15 * 60_000)).toBe("5h: 75% · ~1.5pp · 15m"); expect(format(15 * 60_000)).toBe("5h: 75% · 15m");
expect(format(60_000)).toBe("5h: 75% · ~1.5pp · 1m"); expect(format(60_000)).toBe("5h: 75% · 1m");
expect(format(59_999)).toBe("5h: 75% · ~1.5pp · <1m"); expect(format(59_999)).toBe("5h: 75% · <1m");
}); });
test("sidebar reset timing preserves labels and distinguishes stale and unavailable timestamps", () => { test("sidebar reset timing preserves labels and distinguishes stale and unavailable timestamps", () => {
@@ -462,17 +453,16 @@ test("sidebar reset timing preserves labels and distinguishes stale and unavaila
label: "GPT-5.6 Luna", label: "GPT-5.6 Luna",
used: 99.95, used: 99.95,
remaining: 0.05, remaining: 0.05,
sessionConsumed: 1.5,
}; };
expect(formatQuotaWindow({ ...window, resetsAt: 999 }, 1_000)).toBe( expect(formatQuotaWindow({ ...window, resetsAt: 999 }, 1_000)).toBe(
"GPT-5.6 Luna 5h: <0.1% · ~1.5pp · due", "GPT-5.6 Luna 5h: <0.1% · due",
); );
expect(formatQuotaWindow({ ...window, resetsAt: Number.NaN }, 1_000)).toBe( expect(formatQuotaWindow({ ...window, resetsAt: Number.NaN }, 1_000)).toBe(
"GPT-5.6 Luna 5h: <0.1% · ~1.5pp · unavailable", "GPT-5.6 Luna 5h: <0.1% · unavailable",
); );
}); });
test("inactive roots freeze, resume from a cached baseline on failure, and deactivate by family", async () => { test("request completion refreshes once across terminal and idle events", async () => {
const used = (percent: number) => ({ const used = (percent: number) => ({
...plus, ...plus,
rate_limit: { rate_limit: {
@@ -492,16 +482,10 @@ test("inactive roots freeze, resume from a cached baseline on failure, and deact
await result.tracker.init(); await result.tracker.init();
await result.tracker.start("root"); await result.tracker.start("root");
await result.tracker.complete("root"); await result.tracker.complete("root");
expect((await result.tracker.status("root"))?.windows[0]?.sessionConsumed).toBe(1); expect(result.tracker.status()?.windows[0]?.remaining).toBe(89);
await result.tracker.refresh();
expect((await result.tracker.status("root"))?.windows[0]?.sessionConsumed).toBe(1);
await result.tracker.start("root");
await result.tracker.start("child");
await result.tracker.complete("root"); await result.tracker.complete("root");
await result.tracker.refresh(); expect(result.calls()).toBe(2);
await result.tracker.complete("child"); expect(result.tracker.status()?.windows[0]?.remaining).toBe(89);
expect((await result.tracker.status("root"))?.windows[0]?.sessionConsumed).toBe(6);
await result.tracker.dispose(); await result.tracker.dispose();
}); });
@@ -542,10 +526,10 @@ test("an auth switch detaches an in-flight old-account response and immediately
expect(result.calls()).toBe(2); expect(result.calls()).toBe(2);
pending.resolve(plus); pending.resolve(plus);
await old; await old;
expect((await result.tracker.status("root"))?.windows[0]?.used).toBe(70); expect(result.tracker.status()?.windows[0]?.used).toBe(70);
expect(result.headers[1]?.get("chatgpt-account-id")).toBe("account-two"); expect(result.headers[1]?.get("chatgpt-account-id")).toBe("account-two");
await result.tracker.refresh(); await result.tracker.refresh();
expect((await result.tracker.status("root"))?.windows[0]?.sessionConsumed).toBe(0); expect(result.tracker.status()?.windows[0]).not.toHaveProperty("sessionConsumed");
await result.tracker.dispose(); await result.tracker.dispose();
}); });
@@ -577,7 +561,7 @@ test("an auth switch during an old account storage read cannot restore stale sta
expect(result.calls()).toBe(1); expect(result.calls()).toBe(1);
pending.resolve(undefined); pending.resolve(undefined);
await old; await old;
expect((await result.tracker.status("root"))?.windows[0]?.used).toBe(64); expect(result.tracker.status()?.windows[0]?.used).toBe(64);
expect(result.headers[0]?.get("chatgpt-account-id")).toBe("account-two"); expect(result.headers[0]?.get("chatgpt-account-id")).toBe("account-two");
await result.tracker.dispose(); await result.tracker.dispose();
}); });
+7 -7
View File
@@ -18,7 +18,6 @@ const status = (plan: string, remaining: number): QuotaStatus => ({
used: 100 - remaining, used: 100 - remaining,
resetsAt: 2, resetsAt: 2,
remaining, remaining,
sessionConsumed: 1.5,
}, },
], ],
}); });
@@ -32,13 +31,13 @@ test("built quota slot stays reactive across pending, empty, failure, update, an
const { default: plugin } = tui; const { default: plugin } = tui;
const pending = Promise.withResolvers<QuotaStatus | null>(); const pending = Promise.withResolvers<QuotaStatus | null>();
const responses: Array<() => Promise<QuotaStatus | null>> = [() => pending.promise]; const responses: Array<() => Promise<QuotaStatus | null>> = [() => pending.promise];
const sessions: string[] = []; let statusCalls = 0;
let slotRender: SlotRender | undefined; let slotRender: SlotRender | undefined;
let notifyUpdated: (() => void) | undefined; let notifyUpdated: (() => void) | undefined;
const quota = { const quota = {
status(input: { sessionID: string }) { status() {
sessions.push(input.sessionID); statusCalls++;
const response = responses.shift(); const response = responses.shift();
if (!response) throw new Error("Missing test response"); if (!response) throw new Error("Missing test response");
return response(); return response();
@@ -84,7 +83,8 @@ test("built quota slot stays reactive across pending, empty, failure, update, an
pending.resolve(status("plus", 75)); pending.resolve(status("plus", 75));
await setup.waitForFrame((frame) => frame.includes("ChatGPT quota (plus)")); await setup.waitForFrame((frame) => frame.includes("ChatGPT quota (plus)"));
expect(setup.captureCharFrame()).toContain("5h: 75% · ~1.5pp · due"); expect(setup.captureCharFrame()).toContain("5h: 75% · due");
expect(setup.captureCharFrame()).not.toContain("~");
responses.push(async () => null); responses.push(async () => null);
notifyUpdated(); notifyUpdated();
@@ -94,13 +94,13 @@ test("built quota slot stays reactive across pending, empty, failure, update, an
throw new Error("disconnected"); throw new Error("disconnected");
}); });
notifyUpdated(); notifyUpdated();
await setup.waitFor(() => sessions.length === 3); await setup.waitFor(() => statusCalls === 3);
expect(setup.captureCharFrame()).not.toContain("ChatGPT quota"); expect(setup.captureCharFrame()).not.toContain("ChatGPT quota");
responses.push(async () => status("pro", 42)); responses.push(async () => status("pro", 42));
setSessionID("session-two"); setSessionID("session-two");
await setup.waitForFrame((frame) => frame.includes("ChatGPT quota (pro)")); await setup.waitForFrame((frame) => frame.includes("ChatGPT quota (pro)"));
expect(sessions).toEqual(["session-one", "session-one", "session-one", "session-two"]); expect(statusCalls).toBe(4);
} finally { } finally {
setup.renderer.destroy(); setup.renderer.destroy();
if (cleanup) await cleanup(); if (cleanup) await cleanup();