@slkiser/opencode-quota 1.2.0 → 1.4.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.
- package/README.md +171 -74
- package/dist/lib/api-key-resolver.d.ts +83 -0
- package/dist/lib/api-key-resolver.d.ts.map +1 -0
- package/dist/lib/api-key-resolver.js +113 -0
- package/dist/lib/api-key-resolver.js.map +1 -0
- package/dist/lib/chutes-config.d.ts +8 -7
- package/dist/lib/chutes-config.d.ts.map +1 -1
- package/dist/lib/chutes-config.js +32 -128
- package/dist/lib/chutes-config.js.map +1 -1
- package/dist/lib/chutes.d.ts.map +1 -1
- package/dist/lib/chutes.js +1 -17
- package/dist/lib/chutes.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +1 -48
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/copilot.d.ts.map +1 -1
- package/dist/lib/copilot.js +1 -24
- package/dist/lib/copilot.js.map +1 -1
- package/dist/lib/env-template.d.ts +25 -0
- package/dist/lib/env-template.d.ts.map +1 -0
- package/dist/lib/env-template.js +32 -0
- package/dist/lib/env-template.js.map +1 -0
- package/dist/lib/firmware-config.d.ts +1 -7
- package/dist/lib/firmware-config.d.ts.map +1 -1
- package/dist/lib/firmware-config.js +26 -148
- package/dist/lib/firmware-config.js.map +1 -1
- package/dist/lib/firmware.d.ts +22 -0
- package/dist/lib/firmware.d.ts.map +1 -1
- package/dist/lib/firmware.js +96 -23
- package/dist/lib/firmware.js.map +1 -1
- package/dist/lib/format-utils.d.ts +56 -0
- package/dist/lib/format-utils.d.ts.map +1 -0
- package/dist/lib/format-utils.js +101 -0
- package/dist/lib/format-utils.js.map +1 -0
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/lib/format.js +2 -67
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/google.d.ts.map +1 -1
- package/dist/lib/google.js +2 -24
- package/dist/lib/google.js.map +1 -1
- package/dist/lib/http.d.ts +14 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +34 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/jsonc.d.ts +25 -0
- package/dist/lib/jsonc.d.ts.map +1 -0
- package/dist/lib/jsonc.js +73 -0
- package/dist/lib/jsonc.js.map +1 -0
- package/dist/lib/markdown-table.d.ts +7 -0
- package/dist/lib/markdown-table.d.ts.map +1 -1
- package/dist/lib/markdown-table.js +76 -9
- package/dist/lib/markdown-table.js.map +1 -1
- package/dist/lib/openai.d.ts.map +1 -1
- package/dist/lib/openai.js +1 -17
- package/dist/lib/openai.js.map +1 -1
- package/dist/lib/opencode-storage.d.ts +27 -0
- package/dist/lib/opencode-storage.d.ts.map +1 -1
- package/dist/lib/opencode-storage.js +67 -0
- package/dist/lib/opencode-storage.js.map +1 -1
- package/dist/lib/quota-command-format.d.ts.map +1 -1
- package/dist/lib/quota-command-format.js +5 -50
- package/dist/lib/quota-command-format.js.map +1 -1
- package/dist/lib/quota-stats-format.d.ts.map +1 -1
- package/dist/lib/quota-stats-format.js +9 -3
- package/dist/lib/quota-stats-format.js.map +1 -1
- package/dist/lib/quota-stats.d.ts +1 -0
- package/dist/lib/quota-stats.d.ts.map +1 -1
- package/dist/lib/quota-stats.js +15 -5
- package/dist/lib/quota-stats.js.map +1 -1
- package/dist/lib/quota-status.d.ts +7 -0
- package/dist/lib/quota-status.d.ts.map +1 -1
- package/dist/lib/quota-status.js +10 -0
- package/dist/lib/quota-status.js.map +1 -1
- package/dist/lib/toast-format-grouped.d.ts.map +1 -1
- package/dist/lib/toast-format-grouped.js +1 -66
- package/dist/lib/toast-format-grouped.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +116 -162
- package/dist/plugin.js.map +1 -1
- package/dist/providers/firmware.d.ts.map +1 -1
- package/dist/providers/firmware.js +40 -7
- package/dist/providers/firmware.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Opencode Quota
|
|
2
2
|
|
|
3
3
|
Quota and token tracking for OpenCode providers via Toasts and Commands with no LLM calls.
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ Quota and token tracking for OpenCode providers via Toasts and Commands with no
|
|
|
10
10
|
|
|
11
11
|
**Token Report Commands** - Track token usage and estimated costs across sessions.
|
|
12
12
|
|
|
13
|
-

|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
@@ -22,9 +22,7 @@ Add to your `opencode.json`:
|
|
|
22
22
|
}
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Enable the providers you use:
|
|
25
|
+
Then enable the providers you use:
|
|
28
26
|
|
|
29
27
|
```jsonc
|
|
30
28
|
{
|
|
@@ -36,54 +34,72 @@ Enable the providers you use:
|
|
|
36
34
|
}
|
|
37
35
|
```
|
|
38
36
|
|
|
39
|
-
That's it. Toasts appear automatically after
|
|
37
|
+
That's it. Toasts appear automatically after assistant responses.
|
|
38
|
+
|
|
39
|
+
> **LLM Agents:** Ask your agent to install `https://github.com/slkiser/opencode-quota` with the providers you have enabled. See [LLM Agent Installation Instructions](#llm-agent-installation-instructions) for details.
|
|
40
40
|
|
|
41
41
|
## Commands
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
| Command | Description |
|
|
44
|
+
| ------------------------ | ------------------------------------------------ |
|
|
45
|
+
| `/quota` | Show quota toast (verbose) |
|
|
46
|
+
| `/quota_status` | Show diagnostics (config, providers, accounts) |
|
|
47
|
+
| | |
|
|
48
|
+
| `/tokens_today` | Tokens used today (calendar day) |
|
|
49
|
+
| `/tokens_daily` | Tokens used in last 24 hours |
|
|
50
|
+
| `/tokens_weekly` | Tokens used in last 7 days |
|
|
51
|
+
| `/tokens_monthly` | Tokens used in last 30 days |
|
|
52
|
+
| `/tokens_all` | Tokens used all time |
|
|
53
|
+
| `/tokens_session` | Tokens used in current session |
|
|
54
|
+
| `/tokens_between` | Tokens between two dates (YYYY-MM-DD) |
|
|
55
|
+
| | |
|
|
56
|
+
| `/firmware_reset_window` | Resets Firmware 5-hour window (requires confirm) |
|
|
44
57
|
|
|
45
|
-
|
|
46
|
-
| ----------------- | -------------------------------------------------------- |
|
|
47
|
-
| `/tokens_today` | Tokens used (Today) (/tokens_today) |
|
|
48
|
-
| `/tokens_daily` | Tokens used (Last 24 Hours) (/tokens_daily) |
|
|
49
|
-
| `/tokens_weekly` | Tokens used (Last 7 Days) (/tokens_weekly) |
|
|
50
|
-
| `/tokens_monthly` | Tokens used (Last 30 Days) (/tokens_monthly) |
|
|
51
|
-
| `/tokens_all` | Tokens used (All Time) (/tokens_all) |
|
|
52
|
-
| `/tokens_session` | Tokens used (Current Session) (/tokens_session) |
|
|
53
|
-
| `/tokens_between` | Tokens used (YYYY-MM-DD .. YYYY-MM-DD) (/tokens_between) |
|
|
58
|
+
## Supported Providers
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
| Provider | Config ID | Auth Source |
|
|
61
|
+
| ------------------ | -------------------- | --------------------------------------------- |
|
|
62
|
+
| GitHub Copilot | `copilot` | OpenCode auth (automatic) |
|
|
63
|
+
| OpenAI (Plus/Pro) | `openai` | OpenCode auth (automatic) |
|
|
64
|
+
| Firmware AI | `firmware` | OpenCode auth or API key |
|
|
65
|
+
| Chutes AI | `chutes` | OpenCode auth or API key |
|
|
66
|
+
| Google Antigravity | `google-antigravity` | Multi-account via `opencode-antigravity-auth` |
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
| --------------- | --------------------------------- |
|
|
59
|
-
| `/quota` | Quota Toast (Verbose) (/quota) |
|
|
60
|
-
| `/quota_status` | Quota Diagnostics (/quota_status) |
|
|
68
|
+
### Provider-Specific Setup
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
<details>
|
|
71
|
+
<summary><strong>GitHub Copilot</strong> (usually no setup needed)</summary>
|
|
63
72
|
|
|
64
|
-
|
|
73
|
+
Copilot works automatically if OpenCode has Copilot configured and logged in.
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
- `/quota_daily` -> `/tokens_daily`
|
|
68
|
-
- `/quota_weekly` -> `/tokens_weekly`
|
|
69
|
-
- `/quota_monthly` -> `/tokens_monthly`
|
|
70
|
-
- `/quota_all` -> `/tokens_all`
|
|
71
|
-
- `/quota_session` -> `/tokens_session`
|
|
72
|
-
- `/quota_between` -> `/tokens_between`
|
|
75
|
+
**Optional:** For more reliable quota reporting, provide a fine-grained PAT:
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
1. Create a fine-grained PAT at GitHub with **Account permissions > Plan > Read**
|
|
78
|
+
2. Create `~/.config/opencode/copilot-quota-token.json`:
|
|
75
79
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"token": "github_pat_...",
|
|
83
|
+
"username": "your-username",
|
|
84
|
+
"tier": "pro"
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Tier options: `free`, `pro`, `pro+`, `business`, `enterprise`
|
|
89
|
+
|
|
90
|
+
</details>
|
|
91
|
+
|
|
92
|
+
<details>
|
|
93
|
+
<summary><strong>OpenAI</strong> (no setup needed)</summary>
|
|
94
|
+
|
|
95
|
+
OpenAI works automatically if OpenCode has OpenAI/ChatGPT configured.
|
|
96
|
+
|
|
97
|
+
</details>
|
|
83
98
|
|
|
84
|
-
|
|
99
|
+
<details>
|
|
100
|
+
<summary><strong>Firmware AI</strong></summary>
|
|
85
101
|
|
|
86
|
-
|
|
102
|
+
Works automatically if OpenCode has Firmware configured. Alternatively, provide an API key:
|
|
87
103
|
|
|
88
104
|
```jsonc
|
|
89
105
|
{
|
|
@@ -102,11 +118,16 @@ Firmware works automatically if OpenCode has Firmware configured. Alternatively,
|
|
|
102
118
|
}
|
|
103
119
|
```
|
|
104
120
|
|
|
105
|
-
The `apiKey` field supports
|
|
121
|
+
The `apiKey` field supports `{env:VAR_NAME}` syntax or a direct key.
|
|
106
122
|
|
|
107
|
-
|
|
123
|
+
**Firmware-specific command:** Use `/firmware_reset_window confirm` to reset your 5-hour spending window (consumes 1 of 2 weekly resets). Running without `confirm` shows a warning first.
|
|
108
124
|
|
|
109
|
-
|
|
125
|
+
</details>
|
|
126
|
+
|
|
127
|
+
<details>
|
|
128
|
+
<summary><strong>Chutes AI</strong></summary>
|
|
129
|
+
|
|
130
|
+
Works automatically if OpenCode has Chutes configured. Alternatively, provide an API key:
|
|
110
131
|
|
|
111
132
|
```jsonc
|
|
112
133
|
{
|
|
@@ -125,64 +146,140 @@ Chutes works automatically if OpenCode has Chutes configured. Alternatively, you
|
|
|
125
146
|
}
|
|
126
147
|
```
|
|
127
148
|
|
|
128
|
-
|
|
149
|
+
</details>
|
|
129
150
|
|
|
130
|
-
|
|
151
|
+
<details>
|
|
152
|
+
<summary><strong>Google Antigravity</strong></summary>
|
|
131
153
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
1. Create a fine-grained PAT at GitHub with **Account permissions > Plan > Read**
|
|
135
|
-
2. Create `~/.config/opencode/copilot-quota-token.json`:
|
|
154
|
+
Requires the `opencode-antigravity-auth` plugin for multi-account support:
|
|
136
155
|
|
|
137
156
|
```json
|
|
138
157
|
{
|
|
139
|
-
"
|
|
140
|
-
"username": "your-username",
|
|
141
|
-
"tier": "pro"
|
|
158
|
+
"plugin": ["opencode-antigravity-auth", "@slkiser/opencode-quota"]
|
|
142
159
|
}
|
|
143
160
|
```
|
|
144
161
|
|
|
145
|
-
|
|
162
|
+
Account credentials are stored in `~/.config/opencode/antigravity-accounts.json`.
|
|
146
163
|
|
|
147
|
-
|
|
164
|
+
</details>
|
|
148
165
|
|
|
149
166
|
## Configuration Reference
|
|
150
167
|
|
|
151
168
|
All options go under `experimental.quotaToast` in `opencode.json`:
|
|
152
169
|
|
|
153
|
-
| Option | Default | Description
|
|
154
|
-
| ------------------- | ------------ |
|
|
155
|
-
| `enabled` | `true` | Enable/disable plugin
|
|
156
|
-
| `enableToast` | `true` | Show popup toasts
|
|
157
|
-
| `enabledProviders` | `[]` | Provider
|
|
158
|
-
| `minIntervalMs` | `300000` |
|
|
159
|
-
| `toastDurationMs` | `9000` |
|
|
160
|
-
| `onlyCurrentModel` | `false` | Only show current model
|
|
161
|
-
| `showSessionTokens` | `true` | Show per-model input/output tokens in toast
|
|
162
|
-
| `googleModels` | `["CLAUDE"]` | Google models: `CLAUDE`, `G3PRO`, `G3FLASH`
|
|
163
|
-
| `debug` | `false` | Show debug info in toasts
|
|
170
|
+
| Option | Default | Description |
|
|
171
|
+
| ------------------- | ------------ | ----------------------------------------------- |
|
|
172
|
+
| `enabled` | `true` | Enable/disable plugin |
|
|
173
|
+
| `enableToast` | `true` | Show popup toasts |
|
|
174
|
+
| `enabledProviders` | `[]` | Provider IDs to query (see table above) |
|
|
175
|
+
| `minIntervalMs` | `300000` | Minimum ms between API fetches (default: 5 min) |
|
|
176
|
+
| `toastDurationMs` | `9000` | How long toasts display (ms) |
|
|
177
|
+
| `onlyCurrentModel` | `false` | Only show quota for the current model |
|
|
178
|
+
| `showSessionTokens` | `true` | Show per-model input/output tokens in toast |
|
|
179
|
+
| `googleModels` | `["CLAUDE"]` | Google models: `CLAUDE`, `G3PRO`, `G3FLASH` |
|
|
180
|
+
| `debug` | `false` | Show debug info in toasts |
|
|
181
|
+
|
|
182
|
+
## Troubleshooting
|
|
183
|
+
|
|
184
|
+
Toast not appearing? Run `/quota_status` to check config and provider availability.
|
|
164
185
|
|
|
165
|
-
|
|
186
|
+
---
|
|
166
187
|
|
|
167
|
-
|
|
188
|
+
<details id="llm-agent-installation-instructions">
|
|
189
|
+
<summary><strong>LLM Agent Installation Instructions</strong></summary>
|
|
190
|
+
|
|
191
|
+
### For LLM Agents: How to Install and Configure
|
|
192
|
+
|
|
193
|
+
This section provides instructions for LLM agents to install and configure `opencode-quota` based on the user's current OpenCode setup.
|
|
194
|
+
|
|
195
|
+
#### Step 1: Check Current Configuration
|
|
196
|
+
|
|
197
|
+
First, determine what providers the user has connected. You can:
|
|
198
|
+
|
|
199
|
+
1. Read the user's `opencode.json` (typically at `~/.config/opencode/opencode.json`)
|
|
200
|
+
2. Run `/connected` in OpenCode to see active providers
|
|
201
|
+
3. Ask the user which providers they use
|
|
202
|
+
|
|
203
|
+
#### Step 2: Install the Plugin
|
|
204
|
+
|
|
205
|
+
Add the plugin to the user's `opencode.json`:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"plugin": ["@slkiser/opencode-quota"]
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
If the user already has plugins, append to the existing array.
|
|
214
|
+
|
|
215
|
+
#### Step 3: Configure Providers
|
|
216
|
+
|
|
217
|
+
Based on the user's connected providers, add the appropriate `enabledProviders`:
|
|
168
218
|
|
|
169
219
|
```jsonc
|
|
170
220
|
{
|
|
171
|
-
"plugin": ["@slkiser/opencode-quota"],
|
|
172
221
|
"experimental": {
|
|
173
222
|
"quotaToast": {
|
|
174
|
-
"enabledProviders": [
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
223
|
+
"enabledProviders": [
|
|
224
|
+
// Add only the providers the user has configured:
|
|
225
|
+
// "copilot" - if using GitHub Copilot
|
|
226
|
+
// "openai" - if using OpenAI/ChatGPT
|
|
227
|
+
// "firmware" - if using Firmware AI
|
|
228
|
+
// "chutes" - if using Chutes AI
|
|
229
|
+
// "google-antigravity" - if using Google Antigravity (requires opencode-antigravity-auth)
|
|
230
|
+
],
|
|
178
231
|
},
|
|
179
232
|
},
|
|
180
233
|
}
|
|
181
234
|
```
|
|
182
235
|
|
|
183
|
-
|
|
236
|
+
#### Provider Detection Guide
|
|
184
237
|
|
|
185
|
-
|
|
238
|
+
| If user's config has... | Add to enabledProviders |
|
|
239
|
+
| ------------------------------------------- | ----------------------- |
|
|
240
|
+
| `github-copilot` provider or Copilot models | `"copilot"` |
|
|
241
|
+
| `openai` / `chatgpt` provider | `"openai"` |
|
|
242
|
+
| `firmware` / `firmware-ai` provider | `"firmware"` |
|
|
243
|
+
| `chutes` provider | `"chutes"` |
|
|
244
|
+
| `google` provider with antigravity models | `"google-antigravity"` |
|
|
245
|
+
| `opencode-antigravity-auth` in plugins | `"google-antigravity"` |
|
|
246
|
+
|
|
247
|
+
#### Example: Full Configuration
|
|
248
|
+
|
|
249
|
+
For a user with Copilot and Google Antigravity:
|
|
250
|
+
|
|
251
|
+
```jsonc
|
|
252
|
+
{
|
|
253
|
+
"plugin": [
|
|
254
|
+
"opencode-antigravity-auth", // Required for google-antigravity
|
|
255
|
+
"@slkiser/opencode-quota",
|
|
256
|
+
],
|
|
257
|
+
"experimental": {
|
|
258
|
+
"quotaToast": {
|
|
259
|
+
"enabledProviders": ["copilot", "google-antigravity"],
|
|
260
|
+
"googleModels": ["CLAUDE"], // Options: CLAUDE, G3PRO, G3FLASH
|
|
261
|
+
"minIntervalMs": 300000, // 5 minutes between fetches
|
|
262
|
+
"toastDurationMs": 9000, // Toast shows for 9 seconds
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Step 4: Verify Installation
|
|
269
|
+
|
|
270
|
+
After configuration, instruct the user to:
|
|
271
|
+
|
|
272
|
+
1. Restart OpenCode (or reload the window)
|
|
273
|
+
2. Run `/quota_status` to verify providers are detected
|
|
274
|
+
3. Run `/quota` to manually trigger a toast
|
|
275
|
+
|
|
276
|
+
#### Common Issues
|
|
277
|
+
|
|
278
|
+
- **Toast not showing**: Run `/quota_status` to diagnose
|
|
279
|
+
- **Google Antigravity not working**: Ensure `opencode-antigravity-auth` plugin is installed and accounts are configured
|
|
280
|
+
- **Copilot quota unreliable**: Consider setting up a fine-grained PAT (see Provider-Specific Setup above)
|
|
281
|
+
|
|
282
|
+
</details>
|
|
186
283
|
|
|
187
284
|
## License
|
|
188
285
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic API key resolution from env vars, config files, and auth.json.
|
|
3
|
+
*
|
|
4
|
+
* Used by provider-specific config modules (firmware-config, chutes-config)
|
|
5
|
+
* to resolve API keys with consistent priority and behavior.
|
|
6
|
+
*/
|
|
7
|
+
/** A candidate config file path with its format */
|
|
8
|
+
export interface ConfigCandidate {
|
|
9
|
+
path: string;
|
|
10
|
+
isJsonc: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get candidate paths for opencode.json/opencode.jsonc files.
|
|
14
|
+
*
|
|
15
|
+
* Order: local (cwd) first, then global (~/.config/opencode).
|
|
16
|
+
* Within each location, .jsonc takes precedence over .json.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getOpencodeConfigCandidatePaths(): ConfigCandidate[];
|
|
19
|
+
/**
|
|
20
|
+
* Read and parse an opencode config file.
|
|
21
|
+
*
|
|
22
|
+
* @returns Parsed config with metadata, or null if file doesn't exist or is invalid
|
|
23
|
+
*/
|
|
24
|
+
export declare function readOpencodeConfig(filePath: string, isJsonc: boolean): Promise<{
|
|
25
|
+
config: unknown;
|
|
26
|
+
path: string;
|
|
27
|
+
isJsonc: boolean;
|
|
28
|
+
} | null>;
|
|
29
|
+
/** Result of API key resolution */
|
|
30
|
+
export interface ApiKeyResult<Source extends string> {
|
|
31
|
+
key: string;
|
|
32
|
+
source: Source;
|
|
33
|
+
}
|
|
34
|
+
/** Environment variable definition for key resolution */
|
|
35
|
+
export interface EnvVarDef<Source extends string> {
|
|
36
|
+
name: string;
|
|
37
|
+
source: Source;
|
|
38
|
+
}
|
|
39
|
+
/** Configuration for resolving an API key from multiple sources */
|
|
40
|
+
export interface ResolveApiKeyConfig<Source extends string> {
|
|
41
|
+
/** Environment variables to check (in order) */
|
|
42
|
+
envVars: EnvVarDef<Source>[];
|
|
43
|
+
/** Extract API key from parsed config object. Returns null if not found. */
|
|
44
|
+
extractFromConfig: (config: unknown) => string | null;
|
|
45
|
+
/** Source label for opencode.json */
|
|
46
|
+
configJsonSource: Source;
|
|
47
|
+
/** Source label for opencode.jsonc */
|
|
48
|
+
configJsoncSource: Source;
|
|
49
|
+
/** Extract API key from auth.json data. Returns null if not found. */
|
|
50
|
+
extractFromAuth: (auth: unknown) => string | null;
|
|
51
|
+
/** Source label for auth.json */
|
|
52
|
+
authSource: Source;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve an API key from multiple sources with consistent priority.
|
|
56
|
+
*
|
|
57
|
+
* Priority (first wins):
|
|
58
|
+
* 1. Environment variables (in order specified)
|
|
59
|
+
* 2. opencode.json/opencode.jsonc (local first, then global)
|
|
60
|
+
* 3. auth.json
|
|
61
|
+
*
|
|
62
|
+
* @returns API key and source, or null if not found
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolveApiKey<Source extends string>(config: ResolveApiKeyConfig<Source>, readAuth: () => Promise<unknown | null>): Promise<ApiKeyResult<Source> | null>;
|
|
65
|
+
/** Configuration for API key diagnostics */
|
|
66
|
+
export interface DiagnosticsConfig<Source extends string> {
|
|
67
|
+
/** Environment variable names to check */
|
|
68
|
+
envVarNames: string[];
|
|
69
|
+
/** Resolver function to get the current key result */
|
|
70
|
+
resolve: () => Promise<ApiKeyResult<Source> | null>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get diagnostic info about API key configuration.
|
|
74
|
+
*
|
|
75
|
+
* Reports which sources were checked (env vars that exist, config files that exist)
|
|
76
|
+
* and whether a key was found.
|
|
77
|
+
*/
|
|
78
|
+
export declare function getApiKeyDiagnostics<Source extends string>(config: DiagnosticsConfig<Source>): Promise<{
|
|
79
|
+
configured: boolean;
|
|
80
|
+
source: Source | null;
|
|
81
|
+
checkedPaths: string[];
|
|
82
|
+
}>;
|
|
83
|
+
//# sourceMappingURL=api-key-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key-resolver.d.ts","sourceRoot":"","sources":["../../src/lib/api-key-resolver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,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,CAUnE;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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic API key resolution from env vars, config files, and auth.json.
|
|
3
|
+
*
|
|
4
|
+
* Used by provider-specific config modules (firmware-config, chutes-config)
|
|
5
|
+
* to resolve API keys with consistent priority and behavior.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync } from "fs";
|
|
8
|
+
import { readFile } from "fs/promises";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import { parseJsonOrJsonc } from "./jsonc.js";
|
|
12
|
+
/**
|
|
13
|
+
* Get candidate paths for opencode.json/opencode.jsonc files.
|
|
14
|
+
*
|
|
15
|
+
* Order: local (cwd) first, then global (~/.config/opencode).
|
|
16
|
+
* Within each location, .jsonc takes precedence over .json.
|
|
17
|
+
*/
|
|
18
|
+
export function getOpencodeConfigCandidatePaths() {
|
|
19
|
+
const cwd = process.cwd();
|
|
20
|
+
const configBaseDir = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
21
|
+
return [
|
|
22
|
+
{ path: join(cwd, "opencode.jsonc"), isJsonc: true },
|
|
23
|
+
{ path: join(cwd, "opencode.json"), isJsonc: false },
|
|
24
|
+
{ path: join(configBaseDir, "opencode", "opencode.jsonc"), isJsonc: true },
|
|
25
|
+
{ path: join(configBaseDir, "opencode", "opencode.json"), isJsonc: false },
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Read and parse an opencode config file.
|
|
30
|
+
*
|
|
31
|
+
* @returns Parsed config with metadata, or null if file doesn't exist or is invalid
|
|
32
|
+
*/
|
|
33
|
+
export async function readOpencodeConfig(filePath, isJsonc) {
|
|
34
|
+
try {
|
|
35
|
+
if (!existsSync(filePath))
|
|
36
|
+
return null;
|
|
37
|
+
const content = await readFile(filePath, "utf-8");
|
|
38
|
+
const config = parseJsonOrJsonc(content, isJsonc);
|
|
39
|
+
return { config, path: filePath, isJsonc };
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve an API key from multiple sources with consistent priority.
|
|
47
|
+
*
|
|
48
|
+
* Priority (first wins):
|
|
49
|
+
* 1. Environment variables (in order specified)
|
|
50
|
+
* 2. opencode.json/opencode.jsonc (local first, then global)
|
|
51
|
+
* 3. auth.json
|
|
52
|
+
*
|
|
53
|
+
* @returns API key and source, or null if not found
|
|
54
|
+
*/
|
|
55
|
+
export async function resolveApiKey(config, readAuth) {
|
|
56
|
+
// 1. Check environment variables (highest priority)
|
|
57
|
+
for (const envVar of config.envVars) {
|
|
58
|
+
const value = process.env[envVar.name]?.trim();
|
|
59
|
+
if (value && value.length > 0) {
|
|
60
|
+
return { key: value, source: envVar.source };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 2. Check opencode.json/opencode.jsonc files
|
|
64
|
+
const candidates = getOpencodeConfigCandidatePaths();
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
const result = await readOpencodeConfig(candidate.path, candidate.isJsonc);
|
|
67
|
+
if (!result)
|
|
68
|
+
continue;
|
|
69
|
+
const key = config.extractFromConfig(result.config);
|
|
70
|
+
if (key) {
|
|
71
|
+
return {
|
|
72
|
+
key,
|
|
73
|
+
source: result.isJsonc ? config.configJsoncSource : config.configJsonSource,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 3. Fallback to auth.json
|
|
78
|
+
const auth = await readAuth();
|
|
79
|
+
const key = config.extractFromAuth(auth);
|
|
80
|
+
if (key) {
|
|
81
|
+
return { key, source: config.authSource };
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get diagnostic info about API key configuration.
|
|
87
|
+
*
|
|
88
|
+
* Reports which sources were checked (env vars that exist, config files that exist)
|
|
89
|
+
* and whether a key was found.
|
|
90
|
+
*/
|
|
91
|
+
export async function getApiKeyDiagnostics(config) {
|
|
92
|
+
const checkedPaths = [];
|
|
93
|
+
// Track env vars checked (only if they exist, even if empty)
|
|
94
|
+
for (const envVarName of config.envVarNames) {
|
|
95
|
+
if (process.env[envVarName] !== undefined) {
|
|
96
|
+
checkedPaths.push(`env:${envVarName}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Track config files checked (only if they exist)
|
|
100
|
+
const candidates = getOpencodeConfigCandidatePaths();
|
|
101
|
+
for (const candidate of candidates) {
|
|
102
|
+
if (existsSync(candidate.path)) {
|
|
103
|
+
checkedPaths.push(candidate.path);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const result = await config.resolve();
|
|
107
|
+
return {
|
|
108
|
+
configured: result !== null,
|
|
109
|
+
source: result?.source ?? null,
|
|
110
|
+
checkedPaths,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=api-key-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key-resolver.js","sourceRoot":"","sources":["../../src/lib/api-key-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,15 +14,16 @@ 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
|
-
|
|
18
|
-
* Get candidate paths for opencode.json/opencode.jsonc files
|
|
19
|
-
*/
|
|
20
|
-
export declare function getOpencodeConfigCandidatePaths(): Array<{
|
|
21
|
-
path: string;
|
|
22
|
-
isJsonc: boolean;
|
|
23
|
-
}>;
|
|
17
|
+
export { getOpencodeConfigCandidatePaths } from "./api-key-resolver.js";
|
|
24
18
|
/**
|
|
25
19
|
* Resolve Chutes API key from all available sources.
|
|
20
|
+
*
|
|
21
|
+
* Priority (first wins):
|
|
22
|
+
* 1. Environment variable: CHUTES_API_KEY
|
|
23
|
+
* 2. opencode.json/opencode.jsonc: provider.chutes.options.apiKey
|
|
24
|
+
* 3. auth.json: chutes.key
|
|
25
|
+
*
|
|
26
|
+
* @returns API key and source, or null if not found
|
|
26
27
|
*/
|
|
27
28
|
export declare function resolveChutesApiKey(): Promise<ChutesApiKeyResult | null>;
|
|
28
29
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chutes-config.d.ts","sourceRoot":"","sources":["../../src/lib/chutes-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
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"}
|