@slkiser/opencode-quota 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +51 -57
  2. package/dist/data/modelsdev-pricing.min.json +4 -4
  3. package/dist/lib/api-key-resolver.d.ts +11 -0
  4. package/dist/lib/api-key-resolver.d.ts.map +1 -1
  5. package/dist/lib/api-key-resolver.js +17 -2
  6. package/dist/lib/api-key-resolver.js.map +1 -1
  7. package/dist/lib/chutes-config.d.ts +3 -3
  8. package/dist/lib/chutes-config.d.ts.map +1 -1
  9. package/dist/lib/chutes-config.js +9 -6
  10. package/dist/lib/chutes-config.js.map +1 -1
  11. package/dist/lib/chutes.d.ts.map +1 -1
  12. package/dist/lib/chutes.js +3 -2
  13. package/dist/lib/chutes.js.map +1 -1
  14. package/dist/lib/config.d.ts +7 -3
  15. package/dist/lib/config.d.ts.map +1 -1
  16. package/dist/lib/config.js +66 -28
  17. package/dist/lib/config.js.map +1 -1
  18. package/dist/lib/copilot.d.ts.map +1 -1
  19. package/dist/lib/copilot.js +4 -3
  20. package/dist/lib/copilot.js.map +1 -1
  21. package/dist/lib/cursor-pricing.d.ts.map +1 -1
  22. package/dist/lib/cursor-pricing.js +8 -0
  23. package/dist/lib/cursor-pricing.js.map +1 -1
  24. package/dist/lib/display-sanitize.d.ts +10 -0
  25. package/dist/lib/display-sanitize.d.ts.map +1 -0
  26. package/dist/lib/display-sanitize.js +17 -0
  27. package/dist/lib/display-sanitize.js.map +1 -0
  28. package/dist/lib/env-template.d.ts +3 -2
  29. package/dist/lib/env-template.d.ts.map +1 -1
  30. package/dist/lib/env-template.js +6 -2
  31. package/dist/lib/env-template.js.map +1 -1
  32. package/dist/lib/firmware-config.d.ts +3 -3
  33. package/dist/lib/firmware-config.d.ts.map +1 -1
  34. package/dist/lib/firmware-config.js +9 -6
  35. package/dist/lib/firmware-config.js.map +1 -1
  36. package/dist/lib/firmware.d.ts.map +1 -1
  37. package/dist/lib/firmware.js +3 -11
  38. package/dist/lib/firmware.js.map +1 -1
  39. package/dist/lib/google-token-cache.js +2 -2
  40. package/dist/lib/google-token-cache.js.map +1 -1
  41. package/dist/lib/nanogpt-config.d.ts +26 -0
  42. package/dist/lib/nanogpt-config.d.ts.map +1 -0
  43. package/dist/lib/nanogpt-config.js +78 -0
  44. package/dist/lib/nanogpt-config.js.map +1 -0
  45. package/dist/lib/nanogpt.d.ts +47 -0
  46. package/dist/lib/nanogpt.d.ts.map +1 -0
  47. package/dist/lib/nanogpt.js +214 -0
  48. package/dist/lib/nanogpt.js.map +1 -0
  49. package/dist/lib/openai.d.ts.map +1 -1
  50. package/dist/lib/openai.js +3 -2
  51. package/dist/lib/openai.js.map +1 -1
  52. package/dist/lib/provider-metadata.d.ts.map +1 -1
  53. package/dist/lib/provider-metadata.js +2 -0
  54. package/dist/lib/provider-metadata.js.map +1 -1
  55. package/dist/lib/quota-status.d.ts.map +1 -1
  56. package/dist/lib/quota-status.js +72 -0
  57. package/dist/lib/quota-status.js.map +1 -1
  58. package/dist/lib/types.d.ts +6 -0
  59. package/dist/lib/types.d.ts.map +1 -1
  60. package/dist/lib/types.js.map +1 -1
  61. package/dist/lib/zai.d.ts.map +1 -1
  62. package/dist/lib/zai.js +3 -2
  63. package/dist/lib/zai.js.map +1 -1
  64. package/dist/plugin.d.ts.map +1 -1
  65. package/dist/plugin.js +3 -2
  66. package/dist/plugin.js.map +1 -1
  67. package/dist/providers/nanogpt.d.ts +6 -0
  68. package/dist/providers/nanogpt.d.ts.map +1 -0
  69. package/dist/providers/nanogpt.js +121 -0
  70. package/dist/providers/nanogpt.js.map +1 -0
  71. package/dist/providers/registry.d.ts.map +1 -1
  72. package/dist/providers/registry.js +2 -0
  73. package/dist/providers/registry.js.map +1 -1
  74. package/package.json +1 -1
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  - Automatic quota toasts after assistant responses
6
6
  - Manual `/quota`, `/pricing_refresh`, and `/tokens_*` commands for deeper local reporting with zero context window pollution
7
7
 
8
- **Quota providers**: GitHub Copilot, OpenAI (Plus/Pro), Cursor, Qwen Code, Alibaba Coding Plan, Chutes AI, Firmware AI, Google Antigravity, and Z.ai coding plan.
8
+ **Quota providers**: GitHub Copilot, OpenAI (Plus/Pro), Cursor, Qwen Code, Alibaba Coding Plan, Chutes AI, Firmware AI, Google Antigravity, Z.ai coding plan, and NanoGPT.
9
9
 
10
10
  **Token reports**: All models and providers in [models.dev](https://models.dev), plus deterministic local pricing for Cursor Auto/Composer and Cursor model aliases that are not on models.dev.
11
11
 
@@ -16,15 +16,15 @@
16
16
  </tr>
17
17
  <tr>
18
18
  <td width="50%">
19
- <img src="https://github.com/slkiser/opencode-quota/blob/main/toasts.png" alt="Image of opencode-quota toast" />
19
+ <img src="https://github.com/slkiser/opencode-quota/blob/main/toasts.webp" alt="Image of opencode-quota toast" />
20
20
  </td>
21
21
  <td width="50%">
22
- <img src="https://github.com/slkiser/opencode-quota/blob/main/tokens.png" alt="Image of opencode-quota /tokens_weekly output" />
22
+ <img src="https://github.com/slkiser/opencode-quota/blob/main/tokens.webp" alt="Image of opencode-quota /tokens_weekly output" />
23
23
  </td>
24
24
  </tr>
25
25
  </table>
26
26
 
27
- Quota and `/tokens_*` output are computed from local OpenCode session history.
27
+ `/tokens_*` output is computed from local OpenCode session history. Quota rows use each provider's existing local auth plus deterministic local state or live provider quota endpoints, depending on the provider.
28
28
 
29
29
  ## Quick Start
30
30
 
@@ -78,15 +78,16 @@ That is enough for most installs. Providers are auto-detected from your existing
78
78
 
79
79
  | Provider | Auto setup | How it works |
80
80
  | --- | --- | --- |
81
- | **GitHub Copilot** | Usually | OpenCode auth; PAT only for managed billing. [**Notes**](#github-copilot-notes) |
82
- | **OpenAI** | Yes | OpenCode auth. [**Notes**](#openai-notes) |
83
- | **Cursor** | Needs [quick setup](#cursor-quick-setup) | Companion auth plugin + `provider.cursor`. [**Notes**](#cursor-notes) |
84
- | **Qwen Code** | Needs [quick setup](#qwen-code-quick-setup) | Companion auth plugin. [**Notes**](#qwen-code-notes) |
85
- | **Alibaba Coding Plan** | Yes | Native OpenCode auth with local request estimation. [**Notes**](#alibaba-coding-plan-notes) |
86
- | **Firmware AI** | Usually | OpenCode config; API key optional. [**Notes**](#firmware-ai-notes) |
87
- | **Chutes AI** | Usually | OpenCode config; API key optional. [**Notes**](#chutes-ai-notes) |
88
- | **Google Antigravity** | Needs [quick setup](#google-antigravity-quick-setup) | Companion auth plugin. [**Notes**](#google-antigravity-notes) |
89
- | **Z.ai** | Yes | OpenCode auth. [**Notes**](#zai-notes) |
81
+ | **GitHub Copilot** | Usually | OpenCode auth; PAT only for managed billing. |
82
+ | **OpenAI** | Yes | OpenCode auth. |
83
+ | **Cursor** | Needs [quick setup](#cursor-quick-setup) | Companion auth plugin + `provider.cursor`. |
84
+ | **Qwen Code** | Needs [quick setup](#qwen-code-quick-setup) | Companion auth plugin. |
85
+ | **Alibaba Coding Plan** | Yes | OpenCode auth + local request estimation. |
86
+ | **Firmware AI** | Usually | User/global OpenCode config or env; repo-local secrets ignored. |
87
+ | **Chutes AI** | Usually | User/global OpenCode config or env; repo-local secrets ignored. |
88
+ | **NanoGPT** | Usually | User/global OpenCode config, env, or auth.json; repo-local secrets ignored. |
89
+ | **Google Antigravity** | Needs [quick setup](#google-antigravity-quick-setup) | Companion auth plugin. |
90
+ | **Z.ai** | Yes | OpenCode auth. |
90
91
 
91
92
  <a id="cursor-quick-setup"></a>
92
93
  <details>
@@ -207,14 +208,6 @@ Example `copilot-quota-token.json`:
207
208
 
208
209
  </details>
209
210
 
210
- <a id="openai-notes"></a>
211
- <details>
212
- <summary><strong>OpenAI</strong></summary>
213
-
214
- No extra setup is required if OpenCode already has OpenAI or ChatGPT auth configured.
215
-
216
- </details>
217
-
218
211
  <a id="cursor-notes"></a>
219
212
  <details>
220
213
  <summary><strong>Cursor</strong></summary>
@@ -288,7 +281,11 @@ Example fallback tier:
288
281
 
289
282
  If OpenCode already has Firmware configured, it usually works automatically. Optional API key: `provider.firmware.options.apiKey`.
290
283
 
291
- `{env:FIRMWARE_API_KEY}` and literal values are both supported.
284
+ For security, provider secrets are read from environment variables or your user/global OpenCode config only. Repo-local `opencode.json` / `opencode.jsonc` is ignored for `provider.firmware.options.apiKey`.
285
+
286
+ Allowed env templates are limited to `{env:FIRMWARE_AI_API_KEY}` and `{env:FIRMWARE_API_KEY}`.
287
+
288
+ Example user/global config (`~/.config/opencode/opencode.jsonc` on Linux/macOS):
292
289
 
293
290
  ```jsonc
294
291
  {
@@ -310,7 +307,11 @@ If OpenCode already has Firmware configured, it usually works automatically. Opt
310
307
 
311
308
  If OpenCode already has Chutes configured, it usually works automatically. Optional API key: `provider.chutes.options.apiKey`.
312
309
 
313
- `{env:CHUTES_API_KEY}` and literal values are both supported.
310
+ For security, provider secrets are read from environment variables or your user/global OpenCode config only. Repo-local `opencode.json` / `opencode.jsonc` is ignored for `provider.chutes.options.apiKey`.
311
+
312
+ Allowed env templates are limited to `{env:CHUTES_API_KEY}`.
313
+
314
+ Example user/global config (`~/.config/opencode/opencode.jsonc` on Linux/macOS):
314
315
 
315
316
  ```jsonc
316
317
  {
@@ -336,11 +337,32 @@ If detection looks wrong, `/quota_status` prints the candidate paths checked for
336
337
 
337
338
  </details>
338
339
 
339
- <a id="zai-notes"></a>
340
+ <a id="nanogpt-notes"></a>
340
341
  <details>
341
- <summary><strong>Z.ai</strong></summary>
342
+ <summary><strong>NanoGPT</strong></summary>
343
+
344
+ NanoGPT uses live NanoGPT subscription usage and balance endpoints, so `/quota`, grouped/classic toasts, and `/quota_status` can show daily quota, monthly quota, and account balance in real time.
345
+
346
+ - Canonical provider id is `nanogpt`. Alias `nano-gpt` also normalizes in `enabledProviders`.
347
+ - Optional API key: `provider.nanogpt.options.apiKey` or `provider["nano-gpt"].options.apiKey`.
348
+ - For security, provider secrets are read from `NANOGPT_API_KEY`, `NANO_GPT_API_KEY`, your user/global OpenCode config, or `auth.json`. Repo-local `opencode.json` / `opencode.jsonc` is ignored for NanoGPT secrets.
349
+ - Allowed env templates are limited to `{env:NANOGPT_API_KEY}` and `{env:NANO_GPT_API_KEY}`.
350
+ - `/quota_status` prints a `nanogpt` section with API-key diagnostics, auth candidate paths, live subscription state, daily/monthly usage windows, endpoint errors, and balance details.
351
+ - NanoGPT quota reflects subscription-covered requests and account balance. It is not token-priced in `/tokens_*`.
352
+
353
+ Example user/global config (`~/.config/opencode/opencode.jsonc` on Linux/macOS):
342
354
 
343
- No extra setup is required if OpenCode already has Z.ai configured.
355
+ ```jsonc
356
+ {
357
+ "provider": {
358
+ "nanogpt": {
359
+ "options": {
360
+ "apiKey": "{env:NANOGPT_API_KEY}"
361
+ }
362
+ }
363
+ }
364
+ }
365
+ ```
344
366
 
345
367
  </details>
346
368
 
@@ -348,6 +370,8 @@ No extra setup is required if OpenCode already has Z.ai configured.
348
370
 
349
371
  All plugin settings live under `experimental.quotaToast`.
350
372
 
373
+ Workspace-local config can still customize display/report behavior, but user/global config is authoritative for network-affecting settings such as `enabled`, `enabledProviders`, `minIntervalMs`, `pricingSnapshot`, `showOnIdle`, `showOnQuestion`, and `showOnCompact`.
374
+
351
375
  | Option | Default | Meaning |
352
376
  | --- | --- | --- |
353
377
  | `enabled` | `true` | Master switch for the plugin. When `false`, `/quota`, `/quota_status`, `/pricing_refresh`, and `/tokens_*` are no-ops. |
@@ -403,37 +427,7 @@ If something is missing or looks wrong:
403
427
 
404
428
  If `opencode.db` is missing, start OpenCode once and let its local migration complete.
405
429
 
406
- ## Development
407
-
408
- ```sh
409
- npm install
410
- npm run typecheck
411
- npm test
412
- npm run build
413
- ```
414
-
415
- Maintainer workflow for tracked upstream companion plugins:
416
-
417
- ```sh
418
- npm run upstream:check
419
- npm run upstream:prepare-review
420
- npm run upstream:sync
421
- ```
422
-
423
- - `npm run upstream:check` compares the committed references in `references/upstream-plugins/lock.json` with the latest npm releases for `opencode-qwencode-auth`, `opencode-antigravity-auth`, and `opencode-cursor-oauth`.
424
- - `.github/workflows/upstream-plugin-update-check.yml` runs that check daily and keeps at most one open `[check] <plugin> had update` issue per plugin. If a newer npm release arrives before you act, that same issue is updated and gets a comment instead of piling up more issues.
425
- - `npm run upstream:prepare-review` is the normal maintainer command. It syncs the latest published package copies, runs `npm test`, runs `npm run typecheck`, and prints a ready-to-paste prompt for another agent with changed relative paths and diff previews.
426
- - `npm run upstream:sync` downloads the latest published package contents into `references/upstream-plugins/<plugin>/` and rewrites `references/upstream-plugins/lock.json`.
427
- - Known embedded upstream credentials are redacted deterministically during sync before the reference copies are committed. Update issues still come from `references/upstream-plugins/lock.json` version comparisons.
428
- - Syncing does not close the GitHub issue. The issue stays open until you finish review/fix/release work and close it manually.
429
- - These reference copies are committed for maintainer review only. They are not published in this package because `package.json` ships only `dist`, `README.md`, and `LICENSE`.
430
-
431
- If you maintain this repo through an agentic `/command` workflow, keep your normal-language maintainer prompt rules in local `AGENTS.md`. The current local prompt patterns are:
432
-
433
- - `please help me add feature: <short description>`
434
- - `please handle bug issue #<number>`
435
- - `please handle feature request issue #<number>`
436
- - `please sync our plugin updates`
430
+ ## Contribution
437
431
 
438
432
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution workflow and repository policy.
439
433
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "_meta": {
3
- "generatedAt": 1773949200336,
3
+ "generatedAt": 1774268550053,
4
4
  "providers": [
5
5
  "anthropic",
6
6
  "google",
@@ -647,17 +647,17 @@
647
647
  "output": 0.5,
648
648
  "cache_read": 0.05
649
649
  },
650
- "grok-4.20-beta-latest-non-reasoning": {
650
+ "grok-4.20-0309-non-reasoning": {
651
651
  "input": 2,
652
652
  "output": 6,
653
653
  "cache_read": 0.2
654
654
  },
655
- "grok-4.20-beta-latest-reasoning": {
655
+ "grok-4.20-0309-reasoning": {
656
656
  "input": 2,
657
657
  "output": 6,
658
658
  "cache_read": 0.2
659
659
  },
660
- "grok-4.20-multi-agent-beta-latest": {
660
+ "grok-4.20-multi-agent-0309": {
661
661
  "input": 2,
662
662
  "output": 6,
663
663
  "cache_read": 0.2
@@ -16,6 +16,13 @@ export interface ConfigCandidate {
16
16
  * Within each location, .jsonc takes precedence over .json.
17
17
  */
18
18
  export declare function getOpencodeConfigCandidatePaths(): ConfigCandidate[];
19
+ /**
20
+ * Get trusted global-only candidate paths for opencode.json/opencode.jsonc files.
21
+ *
22
+ * Provider secrets must not be sourced from repo-local config because the
23
+ * current workspace may be untrusted.
24
+ */
25
+ export declare function getGlobalOpencodeConfigCandidatePaths(): ConfigCandidate[];
19
26
  /**
20
27
  * Read and parse an opencode config file.
21
28
  *
@@ -50,6 +57,8 @@ export interface ResolveApiKeyConfig<Source extends string> {
50
57
  extractFromAuth: (auth: unknown) => string | null;
51
58
  /** Source label for auth.json */
52
59
  authSource: Source;
60
+ /** Candidate config file paths to trust for provider-secret lookup. */
61
+ getConfigCandidates?: () => ConfigCandidate[];
53
62
  }
54
63
  /**
55
64
  * Resolve an API key from multiple sources with consistent priority.
@@ -68,6 +77,8 @@ export interface DiagnosticsConfig<Source extends string> {
68
77
  envVarNames: string[];
69
78
  /** Resolver function to get the current key result */
70
79
  resolve: () => Promise<ApiKeyResult<Source> | null>;
80
+ /** Candidate config file paths to report for provider-secret lookup. */
81
+ getConfigCandidates?: () => ConfigCandidate[];
71
82
  }
72
83
  /**
73
84
  * Get diagnostic info about API key configuration.
@@ -1 +1 @@
1
- {"version":3,"file":"api-key-resolver.d.ts","sourceRoot":"","sources":["../../src/lib/api-key-resolver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,IAAI,eAAe,EAAE,CAenE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAAC,CASrE;AAED,mCAAmC;AACnC,MAAM,WAAW,YAAY,CAAC,MAAM,SAAS,MAAM;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,yDAAyD;AACzD,MAAM,WAAW,SAAS,CAAC,MAAM,SAAS,MAAM;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,mEAAmE;AACnE,MAAM,WAAW,mBAAmB,CAAC,MAAM,SAAS,MAAM;IACxD,gDAAgD;IAChD,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAE7B,4EAA4E;IAC5E,iBAAiB,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;IAEtD,qCAAqC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IAEzB,sCAAsC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAE1B,sEAAsE;IACtE,eAAe,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;IAElD,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,MAAM,SAAS,MAAM,EACvD,MAAM,EAAE,mBAAmB,CAAC,MAAM,CAAC,EACnC,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GACtC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAgCtC;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB,CAAC,MAAM,SAAS,MAAM;IACtD,0CAA0C;IAC1C,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB,sDAAsD;IACtD,OAAO,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;CACrD;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,SAAS,MAAM,EAC9D,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAChC,OAAO,CAAC;IACT,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC,CAyBD"}
1
+ {"version":3,"file":"api-key-resolver.d.ts","sourceRoot":"","sources":["../../src/lib/api-key-resolver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,IAAI,eAAe,EAAE,CAenE;AAED;;;;;GAKG;AACH,wBAAgB,qCAAqC,IAAI,eAAe,EAAE,CAUzE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAAC,CASrE;AAED,mCAAmC;AACnC,MAAM,WAAW,YAAY,CAAC,MAAM,SAAS,MAAM;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,yDAAyD;AACzD,MAAM,WAAW,SAAS,CAAC,MAAM,SAAS,MAAM;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,mEAAmE;AACnE,MAAM,WAAW,mBAAmB,CAAC,MAAM,SAAS,MAAM;IACxD,gDAAgD;IAChD,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAE7B,4EAA4E;IAC5E,iBAAiB,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;IAEtD,qCAAqC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IAEzB,sCAAsC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAE1B,sEAAsE;IACtE,eAAe,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;IAElD,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IAEnB,uEAAuE;IACvE,mBAAmB,CAAC,EAAE,MAAM,eAAe,EAAE,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,MAAM,SAAS,MAAM,EACvD,MAAM,EAAE,mBAAmB,CAAC,MAAM,CAAC,EACnC,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GACtC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAgCtC;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB,CAAC,MAAM,SAAS,MAAM;IACtD,0CAA0C;IAC1C,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB,sDAAsD;IACtD,OAAO,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAEpD,wEAAwE;IACxE,mBAAmB,CAAC,EAAE,MAAM,eAAe,EAAE,CAAC;CAC/C;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,SAAS,MAAM,EAC9D,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAChC,OAAO,CAAC;IACT,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC,CAyBD"}
@@ -29,6 +29,21 @@ export function getOpencodeConfigCandidatePaths() {
29
29
  ...global,
30
30
  ];
31
31
  }
32
+ /**
33
+ * Get trusted global-only candidate paths for opencode.json/opencode.jsonc files.
34
+ *
35
+ * Provider secrets must not be sourced from repo-local config because the
36
+ * current workspace may be untrusted.
37
+ */
38
+ export function getGlobalOpencodeConfigCandidatePaths() {
39
+ const { configDirs } = getOpencodeRuntimeDirCandidates();
40
+ const global = [];
41
+ for (const dir of configDirs) {
42
+ global.push({ path: join(dir, "opencode.jsonc"), isJsonc: true });
43
+ global.push({ path: join(dir, "opencode.json"), isJsonc: false });
44
+ }
45
+ return global;
46
+ }
32
47
  /**
33
48
  * Read and parse an opencode config file.
34
49
  *
@@ -65,7 +80,7 @@ export async function resolveApiKey(config, readAuth) {
65
80
  }
66
81
  }
67
82
  // 2. Check opencode.json/opencode.jsonc files
68
- const candidates = getOpencodeConfigCandidatePaths();
83
+ const candidates = config.getConfigCandidates?.() ?? getOpencodeConfigCandidatePaths();
69
84
  for (const candidate of candidates) {
70
85
  const result = await readOpencodeConfig(candidate.path, candidate.isJsonc);
71
86
  if (!result)
@@ -101,7 +116,7 @@ export async function getApiKeyDiagnostics(config) {
101
116
  }
102
117
  }
103
118
  // Track config files checked (only if they exist)
104
- const candidates = getOpencodeConfigCandidatePaths();
119
+ const candidates = config.getConfigCandidates?.() ?? getOpencodeConfigCandidatePaths();
105
120
  for (const candidate of candidates) {
106
121
  if (existsSync(candidate.path)) {
107
122
  checkedPaths.push(candidate.path);
@@ -1 +1 @@
1
- {"version":3,"file":"api-key-resolver.js","sourceRoot":"","sources":["../../src/lib/api-key-resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-key-resolver.js","sourceRoot":"","sources":["../../src/lib/api-key-resolver.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Resolution priority (first wins):
5
5
  * 1. Environment variable: CHUTES_API_KEY
6
- * 2. opencode.json/opencode.jsonc: provider.chutes.options.apiKey
6
+ * 2. User/global opencode.json/opencode.jsonc: provider.chutes.options.apiKey
7
7
  * - Supports {env:VAR_NAME} syntax for environment variable references
8
8
  * 3. auth.json: chutes.key (legacy/fallback)
9
9
  */
@@ -14,13 +14,13 @@ export interface ChutesApiKeyResult {
14
14
  }
15
15
  /** Source of the resolved API key */
16
16
  export type ChutesKeySource = "env:CHUTES_API_KEY" | "opencode.json" | "opencode.jsonc" | "auth.json";
17
- export { getOpencodeConfigCandidatePaths } from "./api-key-resolver.js";
17
+ export { getGlobalOpencodeConfigCandidatePaths as getOpencodeConfigCandidatePaths } from "./api-key-resolver.js";
18
18
  /**
19
19
  * Resolve Chutes API key from all available sources.
20
20
  *
21
21
  * Priority (first wins):
22
22
  * 1. Environment variable: CHUTES_API_KEY
23
- * 2. opencode.json/opencode.jsonc: provider.chutes.options.apiKey
23
+ * 2. User/global opencode.json/opencode.jsonc: provider.chutes.options.apiKey
24
24
  * 3. auth.json: chutes.key
25
25
  *
26
26
  * @returns API key and source, or null if not found
@@ -1 +1 @@
1
- {"version":3,"file":"chutes-config.d.ts","sourceRoot":"","sources":["../../src/lib/chutes-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,qCAAqC;AACrC,MAAM,MAAM,eAAe,GACvB,oBAAoB,GACpB,eAAe,GACf,gBAAgB,GAChB,WAAW,CAAC;AAyChB,OAAO,EAAE,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AAExE;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAY9E;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAGxD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC;IACvD,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC,CAKD"}
1
+ {"version":3,"file":"chutes-config.d.ts","sourceRoot":"","sources":["../../src/lib/chutes-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC;CACzB;AAID,qCAAqC;AACrC,MAAM,MAAM,eAAe,GACvB,oBAAoB,GACpB,eAAe,GACf,gBAAgB,GAChB,WAAW,CAAC;AAyChB,OAAO,EAAE,qCAAqC,IAAI,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AAEjH;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAa9E;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAGxD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC;IACvD,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC,CAMD"}
@@ -3,15 +3,16 @@
3
3
  *
4
4
  * Resolution priority (first wins):
5
5
  * 1. Environment variable: CHUTES_API_KEY
6
- * 2. opencode.json/opencode.jsonc: provider.chutes.options.apiKey
6
+ * 2. User/global opencode.json/opencode.jsonc: provider.chutes.options.apiKey
7
7
  * - Supports {env:VAR_NAME} syntax for environment variable references
8
8
  * 3. auth.json: chutes.key (legacy/fallback)
9
9
  */
10
10
  import { resolveEnvTemplate } from "./env-template.js";
11
11
  import { readAuthFile } from "./opencode-auth.js";
12
- import { resolveApiKey, getApiKeyDiagnostics, } from "./api-key-resolver.js";
12
+ import { resolveApiKey, getApiKeyDiagnostics, getGlobalOpencodeConfigCandidatePaths, } from "./api-key-resolver.js";
13
+ const ALLOWED_CHUTES_ENV_VARS = ["CHUTES_API_KEY"];
13
14
  /**
14
- * Extract Chutes API key from opencode config object
15
+ * Extract Chutes API key from trusted opencode config object
15
16
  *
16
17
  * Looks for: provider.chutes.options.apiKey
17
18
  */
@@ -31,7 +32,7 @@ function extractChutesKeyFromConfig(config) {
31
32
  const apiKey = options.apiKey;
32
33
  if (typeof apiKey !== "string" || apiKey.trim().length === 0)
33
34
  return null;
34
- return resolveEnvTemplate(apiKey.trim());
35
+ return resolveEnvTemplate(apiKey.trim(), ALLOWED_CHUTES_ENV_VARS);
35
36
  }
36
37
  /**
37
38
  * Extract Chutes API key from auth.json
@@ -46,13 +47,13 @@ function extractChutesKeyFromAuth(auth) {
46
47
  return null;
47
48
  }
48
49
  // Re-export for consumers that need path info
49
- export { getOpencodeConfigCandidatePaths } from "./api-key-resolver.js";
50
+ export { getGlobalOpencodeConfigCandidatePaths as getOpencodeConfigCandidatePaths } from "./api-key-resolver.js";
50
51
  /**
51
52
  * Resolve Chutes API key from all available sources.
52
53
  *
53
54
  * Priority (first wins):
54
55
  * 1. Environment variable: CHUTES_API_KEY
55
- * 2. opencode.json/opencode.jsonc: provider.chutes.options.apiKey
56
+ * 2. User/global opencode.json/opencode.jsonc: provider.chutes.options.apiKey
56
57
  * 3. auth.json: chutes.key
57
58
  *
58
59
  * @returns API key and source, or null if not found
@@ -65,6 +66,7 @@ export async function resolveChutesApiKey() {
65
66
  configJsoncSource: "opencode.jsonc",
66
67
  extractFromAuth: extractChutesKeyFromAuth,
67
68
  authSource: "auth.json",
69
+ getConfigCandidates: getGlobalOpencodeConfigCandidatePaths,
68
70
  }, readAuthFile);
69
71
  }
70
72
  /**
@@ -81,6 +83,7 @@ export async function getChutesKeyDiagnostics() {
81
83
  return getApiKeyDiagnostics({
82
84
  envVarNames: ["CHUTES_API_KEY"],
83
85
  resolve: resolveChutesApiKey,
86
+ getConfigCandidates: getGlobalOpencodeConfigCandidatePaths,
84
87
  });
85
88
  }
86
89
  //# sourceMappingURL=chutes-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chutes-config.js","sourceRoot":"","sources":["../../src/lib/chutes-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,aAAa,EACb,oBAAoB,GAGrB,MAAM,uBAAuB,CAAC;AAe/B;;;;GAIG;AACH,SAAS,0BAA0B,CAAC,MAAe;IACjD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,IAAI,GAAG,MAAiC,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3D,MAAM,MAAM,GAAI,QAAoC,CAAC,MAAM,CAAC;IAC5D,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,OAAO,GAAI,MAAkC,CAAC,OAAO,CAAC;IAC5D,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEzD,MAAM,MAAM,GAAI,OAAmC,CAAC,MAAM,CAAC;IAC3D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE1E,OAAO,kBAAkB,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAAC,IAAa;IAC7C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,MAAM,GAAI,IAAgC,CAAC,MAEpC,CAAC;IACd,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClF,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8CAA8C;AAC9C,OAAO,EAAE,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AAExE;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,OAAO,aAAa,CAClB;QACE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACnE,iBAAiB,EAAE,0BAA0B;QAC7C,gBAAgB,EAAE,eAAe;QACjC,iBAAiB,EAAE,gBAAgB;QACnC,eAAe,EAAE,wBAAwB;QACzC,UAAU,EAAE,WAAW;KACxB,EACD,YAAY,CACb,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAC3C,OAAO,MAAM,KAAK,IAAI,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAK3C,OAAO,oBAAoB,CAAkB;QAC3C,WAAW,EAAE,CAAC,gBAAgB,CAAC;QAC/B,OAAO,EAAE,mBAAmB;KAC7B,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"chutes-config.js","sourceRoot":"","sources":["../../src/lib/chutes-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,qCAAqC,GACtC,MAAM,uBAAuB,CAAC;AAQ/B,MAAM,uBAAuB,GAAG,CAAC,gBAAgB,CAAU,CAAC;AAS5D;;;;GAIG;AACH,SAAS,0BAA0B,CAAC,MAAe;IACjD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,IAAI,GAAG,MAAiC,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3D,MAAM,MAAM,GAAI,QAAoC,CAAC,MAAM,CAAC;IAC5D,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,OAAO,GAAI,MAAkC,CAAC,OAAO,CAAC;IAC5D,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEzD,MAAM,MAAM,GAAI,OAAmC,CAAC,MAAM,CAAC;IAC3D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE1E,OAAO,kBAAkB,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,uBAAuB,CAAC,CAAC;AACpE,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAAC,IAAa;IAC7C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,MAAM,GAAI,IAAgC,CAAC,MAEpC,CAAC;IACd,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClF,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8CAA8C;AAC9C,OAAO,EAAE,qCAAqC,IAAI,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AAEjH;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,OAAO,aAAa,CAClB;QACE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACnE,iBAAiB,EAAE,0BAA0B;QAC7C,gBAAgB,EAAE,eAAe;QACjC,iBAAiB,EAAE,gBAAgB;QACnC,eAAe,EAAE,wBAAwB;QACzC,UAAU,EAAE,WAAW;QACvB,mBAAmB,EAAE,qCAAqC;KAC3D,EACD,YAAY,CACb,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAC3C,OAAO,MAAM,KAAK,IAAI,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAK3C,OAAO,oBAAoB,CAAkB;QAC3C,WAAW,EAAE,CAAC,gBAAgB,CAAC;QAC/B,OAAO,EAAE,mBAAmB;QAC5B,mBAAmB,EAAE,qCAAqC;KAC3D,CAAC,CAAC;AACL,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"chutes.d.ts","sourceRoot":"","sources":["../../src/lib/chutes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAqC/C,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,OAAO,CAAC,CAElE;AAED,OAAO,EAAE,uBAAuB,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEnF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,YAAY,CAAC,CAwC9D"}
1
+ {"version":3,"file":"chutes.d.ts","sourceRoot":"","sources":["../../src/lib/chutes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAsC/C,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,OAAO,CAAC,CAElE;AAED,OAAO,EAAE,uBAAuB,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEnF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,YAAY,CAAC,CAwC9D"}
@@ -4,6 +4,7 @@
4
4
  * Resolves API key from multiple sources and queries:
5
5
  * https://api.chutes.ai/users/me/quota_usage/me
6
6
  */
7
+ import { sanitizeDisplaySnippet, sanitizeDisplayText } from "./display-sanitize.js";
7
8
  import { fetchWithTimeout } from "./http.js";
8
9
  import { clampPercent } from "./format-utils.js";
9
10
  import { resolveChutesApiKey, hasChutesApiKey, } from "./chutes-config.js";
@@ -39,7 +40,7 @@ export async function queryChutesQuota() {
39
40
  const text = await resp.text();
40
41
  return {
41
42
  success: false,
42
- error: `Chutes API error ${resp.status}: ${text.slice(0, 120)}`,
43
+ error: `Chutes API error ${resp.status}: ${sanitizeDisplaySnippet(text, 120)}`,
43
44
  };
44
45
  }
45
46
  const data = (await resp.json());
@@ -56,7 +57,7 @@ export async function queryChutesQuota() {
56
57
  catch (err) {
57
58
  return {
58
59
  success: false,
59
- error: err instanceof Error ? err.message : String(err),
60
+ error: sanitizeDisplayText(err instanceof Error ? err.message : String(err)),
60
61
  };
61
62
  }
62
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"chutes.js","sourceRoot":"","sources":["../../src/lib/chutes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chutes.js","sourceRoot":"","sources":["../../src/lib/chutes.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,12 @@
1
1
  /**
2
- * Configuration loader for opencode-quota plugin
2
+ * Configuration loader for opencode-quota plugin.
3
3
  *
4
- * Primary: reads configuration from OpenCode's merged config via the SDK client.
5
- * Fallback: reads local config files directly.
4
+ * Security model:
5
+ * - Config is loaded from opencode.json/opencode.jsonc files directly so we can
6
+ * enforce precedence for network-affecting fields.
7
+ * - User/global config is authoritative for fields that control whether the
8
+ * plugin performs automatic network-backed quota fetches.
9
+ * - SDK config is used only as a fallback when no config files are found.
6
10
  */
7
11
  import type { QuotaToastConfig } from "./types.js";
8
12
  export interface LoadConfigMeta {
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,UAAU,CAAC;IACrC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AA+BD;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE;IACN,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE;gBAAE,YAAY,CAAC,EAAE;oBAAE,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;iBAAE,CAAA;aAAE,CAAA;SAAE,CAAC,CAAC;KAC9F,CAAC;CACH,EACD,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAAC,gBAAgB,CAAC,CA4M3B"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,UAAU,CAAC;IACrC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AA6CD;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE;IACN,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE;gBAAE,YAAY,CAAC,EAAE;oBAAE,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;iBAAE,CAAA;aAAE,CAAA;SAAE,CAAC,CAAC;KAC9F,CAAC;CACH,EACD,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAwO3B"}
@@ -1,8 +1,12 @@
1
1
  /**
2
- * Configuration loader for opencode-quota plugin
2
+ * Configuration loader for opencode-quota plugin.
3
3
  *
4
- * Primary: reads configuration from OpenCode's merged config via the SDK client.
5
- * Fallback: reads local config files directly.
4
+ * Security model:
5
+ * - Config is loaded from opencode.json/opencode.jsonc files directly so we can
6
+ * enforce precedence for network-affecting fields.
7
+ * - User/global config is authoritative for fields that control whether the
8
+ * plugin performs automatic network-backed quota fetches.
9
+ * - SDK config is used only as a fallback when no config files are found.
6
10
  */
7
11
  import { DEFAULT_CONFIG } from "./types.js";
8
12
  import { parseJsonOrJsonc } from "./jsonc.js";
@@ -14,6 +18,18 @@ import { getOpencodeRuntimeDirCandidates } from "./opencode-runtime-paths.js";
14
18
  export function createLoadConfigMeta() {
15
19
  return { source: "defaults", paths: [] };
16
20
  }
21
+ const NETWORK_AFFECTING_KEYS = [
22
+ "enabled",
23
+ "enabledProviders",
24
+ "minIntervalMs",
25
+ "pricingSnapshot",
26
+ "showOnIdle",
27
+ "showOnQuestion",
28
+ "showOnCompact",
29
+ ];
30
+ function hasOwnKey(value, key) {
31
+ return Object.prototype.hasOwnProperty.call(value, key);
32
+ }
17
33
  /**
18
34
  * Validates and normalizes a Google model ID
19
35
  */
@@ -148,13 +164,7 @@ export async function loadConfig(client, meta) {
148
164
  return null;
149
165
  }
150
166
  }
151
- async function loadFromFiles() {
152
- const cwd = process.cwd();
153
- const { configDirs } = getOpencodeRuntimeDirCandidates();
154
- // Order: global first, then local overrides.
155
- // Within each location, load .json first, then .jsonc so that
156
- // .jsonc takes precedence on key collisions (matching the documented intent).
157
- const locations = [...configDirs, cwd];
167
+ async function loadQuotaToastFromLocations(locations) {
158
168
  const quota = {};
159
169
  const usedPaths = [];
160
170
  for (const dir of locations) {
@@ -166,26 +176,50 @@ export async function loadConfig(client, meta) {
166
176
  if (!parsed || typeof parsed !== "object")
167
177
  continue;
168
178
  const root = parsed;
169
- const picks = [
170
- { key: "experimental.quotaToast", value: root?.experimental?.quotaToast },
171
- ];
172
- const usedKeys = [];
173
- for (const pick of picks) {
174
- if (!pick.value || typeof pick.value !== "object")
175
- continue;
176
- Object.assign(quota, pick.value);
177
- usedKeys.push(pick.key);
178
- }
179
- if (usedKeys.length > 0) {
180
- usedPaths.push(`${p} (${usedKeys.join(", ")})`);
181
- }
179
+ const rawQuotaToast = root?.experimental?.quotaToast;
180
+ if (!rawQuotaToast || typeof rawQuotaToast !== "object")
181
+ continue;
182
+ Object.assign(quota, rawQuotaToast);
183
+ usedPaths.push(`${p} (experimental.quotaToast)`);
182
184
  }
183
185
  }
186
+ return { quota, usedPaths };
187
+ }
188
+ async function loadFromFiles() {
189
+ const cwd = process.cwd();
190
+ const { configDirs } = getOpencodeRuntimeDirCandidates();
191
+ const globalConfig = await loadQuotaToastFromLocations(configDirs);
192
+ const localConfig = await loadQuotaToastFromLocations([cwd]);
193
+ const usedPaths = [...globalConfig.usedPaths, ...localConfig.usedPaths];
194
+ if (usedPaths.length === 0) {
195
+ return { config: null, usedPaths: [] };
196
+ }
197
+ const quota = {
198
+ ...globalConfig.quota,
199
+ ...localConfig.quota,
200
+ };
201
+ // Repo-local config may customize display/formatting, but user/global config
202
+ // remains authoritative for settings that trigger or shape automatic network activity.
203
+ for (const key of NETWORK_AFFECTING_KEYS) {
204
+ if (hasOwnKey(globalConfig.quota, key)) {
205
+ quota[key] = globalConfig.quota[key];
206
+ }
207
+ else if (hasOwnKey(localConfig.quota, key)) {
208
+ quota[key] = localConfig.quota[key];
209
+ }
210
+ }
211
+ return {
212
+ config: normalize(quota),
213
+ usedPaths,
214
+ };
215
+ }
216
+ const fileConfig = await loadFromFiles();
217
+ if (fileConfig.config) {
184
218
  if (meta) {
185
- meta.source = usedPaths.length > 0 ? "files" : "defaults";
186
- meta.paths = usedPaths;
219
+ meta.source = "files";
220
+ meta.paths = fileConfig.usedPaths;
187
221
  }
188
- return normalize(Object.keys(quota).length > 0 ? quota : null);
222
+ return fileConfig.config;
189
223
  }
190
224
  try {
191
225
  const response = await client.config.get();
@@ -199,10 +233,14 @@ export async function loadConfig(client, meta) {
199
233
  }
200
234
  return normalize(quotaToastConfig);
201
235
  }
202
- return await loadFromFiles();
203
236
  }
204
237
  catch {
205
- return await loadFromFiles();
238
+ // ignore; fall back to defaults below
239
+ }
240
+ if (meta) {
241
+ meta.source = "defaults";
242
+ meta.paths = [];
206
243
  }
244
+ return DEFAULT_CONFIG;
207
245
  }
208
246
  //# sourceMappingURL=config.js.map