saros-proxy 0.7.3 → 0.9.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 (78) hide show
  1. package/README.md +98 -47
  2. package/dist/cli/autostart.js.map +1 -1
  3. package/dist/cli/daemon.d.ts +31 -4
  4. package/dist/cli/daemon.js +124 -22
  5. package/dist/cli/daemon.js.map +1 -1
  6. package/dist/cli/dsh-config.d.ts +32 -0
  7. package/dist/cli/dsh-config.js +122 -0
  8. package/dist/cli/dsh-config.js.map +1 -0
  9. package/dist/cli/harness-models.d.ts +26 -2
  10. package/dist/cli/harness-models.js +192 -2
  11. package/dist/cli/harness-models.js.map +1 -1
  12. package/dist/cli/harness-sync.d.ts +27 -10
  13. package/dist/cli/harness-sync.js +70 -16
  14. package/dist/cli/harness-sync.js.map +1 -1
  15. package/dist/cli/help.js +4 -2
  16. package/dist/cli/help.js.map +1 -1
  17. package/dist/cli/omp-config.js +22 -5
  18. package/dist/cli/omp-config.js.map +1 -1
  19. package/dist/cli/opencode-config.js +22 -15
  20. package/dist/cli/opencode-config.js.map +1 -1
  21. package/dist/cli/overview.d.ts +10 -0
  22. package/dist/cli/overview.js +61 -0
  23. package/dist/cli/overview.js.map +1 -0
  24. package/dist/cli/pi-config.js +21 -5
  25. package/dist/cli/pi-config.js.map +1 -1
  26. package/dist/cli/setup.d.ts +6 -10
  27. package/dist/cli/setup.js +107 -233
  28. package/dist/cli/setup.js.map +1 -1
  29. package/dist/cli/ui.d.ts +0 -1
  30. package/dist/cli/ui.js +0 -16
  31. package/dist/cli/ui.js.map +1 -1
  32. package/dist/cli/update-check.js.map +1 -1
  33. package/dist/cli/usage.d.ts +12 -0
  34. package/dist/cli/usage.js +116 -0
  35. package/dist/cli/usage.js.map +1 -0
  36. package/dist/config.d.ts +13 -17
  37. package/dist/config.js +29 -78
  38. package/dist/config.js.map +1 -1
  39. package/dist/constants.d.ts +7 -6
  40. package/dist/constants.js +10 -5
  41. package/dist/constants.js.map +1 -1
  42. package/dist/index.d.ts +4 -4
  43. package/dist/index.js +34 -34
  44. package/dist/index.js.map +1 -1
  45. package/dist/model-stats.d.ts +20 -0
  46. package/dist/model-stats.js +135 -0
  47. package/dist/model-stats.js.map +1 -0
  48. package/dist/models-sync.js +10 -4
  49. package/dist/models-sync.js.map +1 -1
  50. package/dist/probe-cache.js.map +1 -1
  51. package/dist/providers/commandcode.d.ts +17 -0
  52. package/dist/providers/commandcode.js +232 -0
  53. package/dist/providers/commandcode.js.map +1 -0
  54. package/dist/providers/index.d.ts +43 -0
  55. package/dist/providers/index.js +75 -0
  56. package/dist/providers/index.js.map +1 -0
  57. package/dist/providers/infer.d.ts +23 -0
  58. package/dist/providers/infer.js +13 -0
  59. package/dist/providers/infer.js.map +1 -0
  60. package/dist/providers/opencode-go.d.ts +4 -0
  61. package/dist/providers/opencode-go.js +179 -0
  62. package/dist/providers/opencode-go.js.map +1 -0
  63. package/dist/providers/types.d.ts +118 -0
  64. package/dist/providers/types.js +10 -0
  65. package/dist/providers/types.js.map +1 -0
  66. package/dist/proxy-logic.d.ts +46 -22
  67. package/dist/proxy-logic.js +146 -106
  68. package/dist/proxy-logic.js.map +1 -1
  69. package/dist/proxy.d.ts +20 -2
  70. package/dist/proxy.js +231 -57
  71. package/dist/proxy.js.map +1 -1
  72. package/dist/usage-client.d.ts +41 -0
  73. package/dist/usage-client.js +124 -0
  74. package/dist/usage-client.js.map +1 -0
  75. package/dist/validation.d.ts +5 -5
  76. package/dist/validation.js +7 -9
  77. package/dist/validation.js.map +1 -1
  78. package/package.json +75 -77
@@ -0,0 +1,232 @@
1
+ /**
2
+ * commandcode.ts — KeyProvider adapter for the CommandCode API
3
+ * (api.commandcode.ai).
4
+ *
5
+ * Keys: `user_` tokens (definitive) and `sk-` console keys (ambiguous with
6
+ * opencode-go → empirical smoke test required).
7
+ *
8
+ * Smoke test: GET /alpha/billing/subscriptions — cheap, read-only, no token
9
+ * spend; 200 with planId = valid, 401 = invalid. Requests must carry
10
+ * CLI-identity headers (the server gates on client version, verified live:
11
+ * without them /alpha/generate returns 403 upgrade_required).
12
+ */
13
+ import { MIN_KEY_LENGTH } from '../constants.js';
14
+ export const COMMANDCODE_BASE_URL = 'https://api.commandcode.ai';
15
+ export const COMMANDCODE_BILLING_PATH = '/alpha/billing/subscriptions';
16
+ /** Latest CLI version, resolved lazily and cached for the process lifetime. */
17
+ let cachedClientVersion = null;
18
+ /** Resolve the current command-code CLI version from the npm registry. */
19
+ export async function resolveClientVersion(timeoutMs = 5_000) {
20
+ if (cachedClientVersion)
21
+ return cachedClientVersion;
22
+ const controller = new AbortController();
23
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
24
+ try {
25
+ const res = await fetch('https://registry.npmjs.org/command-code/latest', {
26
+ signal: controller.signal,
27
+ });
28
+ if (res.ok) {
29
+ const body = (await res.json());
30
+ if (typeof body.version === 'string' && /^\d+\.\d+\.\d+/.test(body.version)) {
31
+ cachedClientVersion = body.version;
32
+ return cachedClientVersion;
33
+ }
34
+ }
35
+ }
36
+ catch {
37
+ // fall through to fallback
38
+ }
39
+ finally {
40
+ clearTimeout(timer);
41
+ }
42
+ // Conservative floor: server rejects versions below 0.18.10 (verified live).
43
+ return (cachedClientVersion = '0.18.10');
44
+ }
45
+ /** CLI-identity headers every authenticated CommandCode request needs. */
46
+ export function commandcodeClientHeaders(version) {
47
+ return {
48
+ 'User-Agent': `CommandCodeCLI/${version}`,
49
+ 'X-Command-Code-Version': version,
50
+ 'X-CLI-Environment': 'production',
51
+ 'X-Project-Slug': 'command-code',
52
+ 'X-CommandCode-Client': 'cli',
53
+ 'X-CommandCode-Client-Version': version,
54
+ 'X-CommandCode-CLI-Version': version,
55
+ 'X-Command-Code-Client-Version': version,
56
+ 'X-Command-Code-CLI-Version': version,
57
+ };
58
+ }
59
+ /** Extract planId from a billing/subscriptions response (opencommand-compatible). */
60
+ export function parsePlanId(body) {
61
+ let parsed;
62
+ try {
63
+ parsed = JSON.parse(body);
64
+ }
65
+ catch {
66
+ return undefined;
67
+ }
68
+ const planId = parsed?.data?.planId;
69
+ return typeof planId === 'string' && planId.trim() ? planId.trim() : undefined;
70
+ }
71
+ /**
72
+ * CommandCode has no per-key usage-percent API saros can query without
73
+ * browser session cookies (verified: /v1/account/usage is not a registered
74
+ * route on api.commandcode.ai). Usage-based weighted rotation is opencode-go
75
+ * only; commandcode keys report no usage data.
76
+ */
77
+ export const COMMANDCODE_PROVIDER_MODELS_PATH = '/provider/v1/models';
78
+ /** Bare-name index of the live catalog (last path segment of vendor ids). */
79
+ let catalogCache = null;
80
+ /** Ensures the background warm-up fires at most once per process. */
81
+ let warmupStarted = false;
82
+ function indexCatalog(models) {
83
+ const bareNames = new Set();
84
+ const nativeByBare = new Map();
85
+ for (const entry of models) {
86
+ const id = typeof entry.id === 'string' ? entry.id : '';
87
+ if (!id)
88
+ continue;
89
+ bareNames.add(id.toLowerCase());
90
+ const lastSegment = id.split('/').pop() ?? id;
91
+ if (lastSegment) {
92
+ bareNames.add(lastSegment.toLowerCase());
93
+ if (!nativeByBare.has(lastSegment.toLowerCase())) {
94
+ nativeByBare.set(lastSegment.toLowerCase(), id);
95
+ }
96
+ }
97
+ }
98
+ catalogCache = { bareNames, nativeByBare };
99
+ }
100
+ export const commandcodeProvider = {
101
+ id: 'commandcode',
102
+ displayName: 'CommandCode',
103
+ baseUrl: COMMANDCODE_BASE_URL,
104
+ chatBasePath: '/provider/v1',
105
+ quickMatch(key) {
106
+ if (key.startsWith('user_') && key.length >= MIN_KEY_LENGTH)
107
+ return 'yes';
108
+ // sk- shared with opencode-go → empirical test required
109
+ return key.startsWith('sk-') && key.length >= MIN_KEY_LENGTH ? 'maybe' : 'no';
110
+ },
111
+ async smokeTest(key, timeoutMs = 10_000) {
112
+ const controller = new AbortController();
113
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
114
+ try {
115
+ const version = await resolveClientVersion();
116
+ const res = await fetch(`${this.baseUrl.replace(/\/$/, '')}${COMMANDCODE_BILLING_PATH}`, {
117
+ headers: {
118
+ ...commandcodeClientHeaders(version),
119
+ Accept: 'application/json',
120
+ Authorization: `Bearer ${key}`,
121
+ },
122
+ signal: controller.signal,
123
+ });
124
+ if (res.ok) {
125
+ const planId = parsePlanId(await res.text());
126
+ return {
127
+ provider: this.id,
128
+ status: 'valid',
129
+ httpStatus: res.status,
130
+ detail: planId ? `plan: ${planId}` : undefined,
131
+ ...(planId ? { meta: { planId } } : {}),
132
+ };
133
+ }
134
+ const definitive = res.status === 401 || res.status === 403;
135
+ return {
136
+ provider: this.id,
137
+ status: definitive ? 'invalid' : 'error',
138
+ httpStatus: res.status,
139
+ detail: definitive ? 'upstream rejected the key' : 'upstream unavailable',
140
+ };
141
+ }
142
+ catch (err) {
143
+ return {
144
+ provider: this.id,
145
+ status: 'error',
146
+ detail: err instanceof Error ? err.message : String(err),
147
+ };
148
+ }
149
+ finally {
150
+ clearTimeout(timer);
151
+ }
152
+ },
153
+ usagePath() {
154
+ return ''; // no queryable per-key usage API without a Studio session cookie
155
+ },
156
+ parseUsageResponse(_body) {
157
+ return null;
158
+ },
159
+ extraUpstreamHeaders() {
160
+ return commandcodeClientHeaders(cachedClientVersion ?? '0.18.10');
161
+ },
162
+ /**
163
+ * Live provider catalog. Verified live: this route is fully public
164
+ * (200 with any or no Authorization header).
165
+ */
166
+ async fetchCatalog(timeoutMs = 5_000) {
167
+ const controller = new AbortController();
168
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
169
+ try {
170
+ const res = await fetch(`${COMMANDCODE_BASE_URL.replace(/\/$/, '')}${COMMANDCODE_PROVIDER_MODELS_PATH}`, { signal: controller.signal });
171
+ if (!res.ok)
172
+ return null;
173
+ const body = (await res.json());
174
+ if (!Array.isArray(body.data))
175
+ return null;
176
+ const models = (body.data.filter((m) => typeof m === 'object' && m !== null && typeof m.id === 'string')).filter((m) => {
177
+ // claude-* is served via the Anthropic Messages endpoint only
178
+ // (verified live: chat/completions → unsupported_model). saros relays
179
+ // chat/completions shapes, so these models are unusable through it.
180
+ const id = typeof m.id === 'string' ? m.id : '';
181
+ return !/^claude/i.test(id);
182
+ });
183
+ indexCatalog(models);
184
+ return models;
185
+ }
186
+ catch {
187
+ return null;
188
+ }
189
+ finally {
190
+ clearTimeout(timer);
191
+ }
192
+ },
193
+ /**
194
+ * Vendor-prefixed ids ('vendor/model') and Claude models are
195
+ * commandcode-specific naming (opencode-go uses bare ids like 'glm-5').
196
+ * Everything else is 'maybe' — catalogs overlap.
197
+ */
198
+ modelAffinity(modelId) {
199
+ if (modelId.includes('/'))
200
+ return 'yes';
201
+ if (/^claude/i.test(modelId))
202
+ return 'yes';
203
+ return 'maybe';
204
+ },
205
+ /**
206
+ * Bare-name affinity against the live catalog: 'deepseek-v4-flash' matches
207
+ * the catalog entry 'deepseek/deepseek-v4-flash' (last path segment).
208
+ * Cached after the first catalog fetch; 'maybe' until the catalog loads.
209
+ */
210
+ modelAffinityByName(bareName) {
211
+ if (catalogCache) {
212
+ return catalogCache.bareNames.has(bareName.toLowerCase()) ? 'yes' : 'no';
213
+ }
214
+ // Warm the cache in the background (once per process); stay non-committal meanwhile.
215
+ if (!warmupStarted) {
216
+ warmupStarted = true;
217
+ void commandcodeProvider.fetchCatalog?.();
218
+ }
219
+ return 'maybe';
220
+ },
221
+ /**
222
+ * Bare name → commandcode's native id ('deepseek-v4-flash' →
223
+ * 'deepseek/deepseek-v4-flash'). null when the catalog isn't loaded or
224
+ * doesn't serve the name.
225
+ */
226
+ resolveNativeId(bareName) {
227
+ if (!catalogCache)
228
+ return null;
229
+ return catalogCache.nativeByBare.get(bareName.toLowerCase()) ?? null;
230
+ },
231
+ };
232
+ //# sourceMappingURL=commandcode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandcode.js","sourceRoot":"","sources":["../../src/providers/commandcode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,MAAM,CAAC,MAAM,oBAAoB,GAAG,4BAA4B,CAAC;AACjE,MAAM,CAAC,MAAM,wBAAwB,GAAG,8BAA8B,CAAC;AAEvE,+EAA+E;AAC/E,IAAI,mBAAmB,GAAkB,IAAI,CAAC;AAE9C,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,SAAS,GAAG,KAAK;IAC1D,IAAI,mBAAmB;QAAE,OAAO,mBAAmB,CAAC;IACpD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,gDAAgD,EAAE;YACxE,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA0B,CAAC;YACzD,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC5E,mBAAmB,GAAG,IAAI,CAAC,OAAO,CAAC;gBACnC,OAAO,mBAAmB,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IACD,6EAA6E;IAC7E,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,OAAO;QACL,YAAY,EAAE,kBAAkB,OAAO,EAAE;QACzC,wBAAwB,EAAE,OAAO;QACjC,mBAAmB,EAAE,YAAY;QACjC,gBAAgB,EAAE,cAAc;QAChC,sBAAsB,EAAE,KAAK;QAC7B,8BAA8B,EAAE,OAAO;QACvC,2BAA2B,EAAE,OAAO;QACpC,+BAA+B,EAAE,OAAO;QACxC,4BAA4B,EAAE,OAAO;KACtC,CAAC;AACJ,CAAC;AAQD,qFAAqF;AACrF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,MAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC;IACpC,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACjF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,qBAAqB,CAAC;AAEtE,6EAA6E;AAC7E,IAAI,YAAY,GAAyE,IAAI,CAAC;AAC9F,qEAAqE;AACrE,IAAI,aAAa,GAAG,KAAK,CAAC;AAE1B,SAAS,YAAY,CAAC,MAAsC;IAC1D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC/C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,EAAE,GAAG,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,IAAI,CAAC,EAAE;YAAE,SAAS;QAClB,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QAChC,MAAM,WAAW,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QAC9C,IAAI,WAAW,EAAE,CAAC;YAChB,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;YACzC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBACjD,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IACD,YAAY,GAAG,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAgB;IAC9C,EAAE,EAAE,aAAa;IACjB,WAAW,EAAE,aAAa;IAC1B,OAAO,EAAE,oBAAoB;IAC7B,YAAY,EAAE,cAAc;IAE5B,UAAU,CAAC,GAAW;QACpB,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,MAAM,IAAI,cAAc;YAAE,OAAO,KAAK,CAAC;QAC1E,wDAAwD;QACxD,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,IAAI,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAChF,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAW,EAAE,SAAS,GAAG,MAAM;QAC7C,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,oBAAoB,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,wBAAwB,EAAE,EAAE;gBACvF,OAAO,EAAE;oBACP,GAAG,wBAAwB,CAAC,OAAO,CAAC;oBACpC,MAAM,EAAE,kBAAkB;oBAC1B,aAAa,EAAE,UAAU,GAAG,EAAE;iBAC/B;gBACD,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC7C,OAAO;oBACL,QAAQ,EAAE,IAAI,CAAC,EAAE;oBACjB,MAAM,EAAE,OAAO;oBACf,UAAU,EAAE,GAAG,CAAC,MAAM;oBACtB,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS;oBAC9C,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACxC,CAAC;YACJ,CAAC;YACD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC;YAC5D,OAAO;gBACL,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;gBACxC,UAAU,EAAE,GAAG,CAAC,MAAM;gBACtB,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,sBAAsB;aAC1E,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,MAAM,EAAE,OAAO;gBACf,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACzD,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,SAAS;QACP,OAAO,EAAE,CAAC,CAAC,iEAAiE;IAC9E,CAAC;IAED,kBAAkB,CAAC,KAAa;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,oBAAoB;QAClB,OAAO,wBAAwB,CAAC,mBAAmB,IAAI,SAAS,CAAC,CAAC;IACpE,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAAC,SAAS,GAAG,KAAK;QAClC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,GAAG,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,gCAAgC,EAAE,EAC/E,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAC9B,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC;YACzB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;YACtD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC3C,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAgC,EAAE,CAClC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAQ,CAA6B,CAAC,EAAE,KAAK,QAAQ,CAC/F,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;gBACd,8DAA8D;gBAC9D,sEAAsE;gBACtE,oEAAoE;gBACpE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChD,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;YACH,YAAY,CAAC,MAAM,CAAC,CAAC;YACrB,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,aAAa,CAAC,OAAe;QAC3B,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACxC,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;QAC3C,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;OAIG;IACH,mBAAmB,CAAC,QAAgB;QAClC,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3E,CAAC;QACD,qFAAqF;QACrF,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,aAAa,GAAG,IAAI,CAAC;YACrB,KAAK,mBAAmB,CAAC,YAAY,EAAE,EAAE,CAAC;QAC5C,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,QAAgB;QAC9B,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC;QAC/B,OAAO,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC;IACvE,CAAC;CACF,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { KeyProvider, ProviderId, SmokeResult } from './types.js';
2
+ export * from './types.js';
3
+ export { inferProvider, type KeyEntry } from './infer.js';
4
+ export { opencodeGoProvider, parseOpencodeUsage } from './opencode-go.js';
5
+ export { commandcodeProvider, commandcodeClientHeaders, resolveClientVersion, parsePlanId, COMMANDCODE_BASE_URL, COMMANDCODE_BILLING_PATH, } from './commandcode.js';
6
+ /** Get a provider by id. */
7
+ export declare function getProvider(id: ProviderId): KeyProvider | undefined;
8
+ /** All registered providers. */
9
+ export declare function allProviders(): readonly KeyProvider[];
10
+ /** Structural validity: at least one provider accepts the format. */
11
+ export declare function isPlausibleKey(key: string): boolean;
12
+ export interface KeyIdentification {
13
+ /** Provider the key belongs to, or null when unknown/invalid. */
14
+ provider: ProviderId | null;
15
+ /** 'unverified' when only the prefix matched (no definitive ping). */
16
+ confidence: 'verified' | 'unverified' | 'invalid' | 'unknown';
17
+ /** Per-provider smoke results (diagnostics for the TUI). */
18
+ attempts: SmokeResult[];
19
+ }
20
+ /**
21
+ * Identify which provider a key belongs to.
22
+ *
23
+ * Decision tree:
24
+ * 1. Providers whose prefix says 'no' are skipped entirely.
25
+ * 2. Remaining candidates are smoke-tested in parallel.
26
+ * 3. First 'valid' wins (definitive positive).
27
+ * 4. All candidates 'invalid' → the key is rejected.
28
+ * 5. Only inconclusive 'error' results → unknown (network problem).
29
+ * 6. No candidates at all → prefix not recognized by any provider.
30
+ *
31
+ * When `verify: false`, skips the network and returns the prefix-level
32
+ * answer ('unverified') — used by config validation where offline tolerance
33
+ * is required.
34
+ */
35
+ export declare function identifyKey(key: string, options?: {
36
+ verify?: boolean;
37
+ timeoutMs?: number;
38
+ }): Promise<KeyIdentification>;
39
+ /**
40
+ * Extract candidate API keys from arbitrary pasted text (TUI drop-a-key
41
+ * flow). Matches user_... and sk-... tokens of plausible length; dedupes.
42
+ */
43
+ export declare function extractKeys(text: string): string[];
@@ -0,0 +1,75 @@
1
+ /**
2
+ * index.ts — Provider registry and key identification.
3
+ *
4
+ * identifyKey() classifies a pasted key by pinging candidate providers in
5
+ * parallel: a 200 is a definitive positive, a 401/403 a definitive negative,
6
+ * network errors are inconclusive. Handles the sk- collision between
7
+ * opencode-go and commandcode empirically.
8
+ */
9
+ import { opencodeGoProvider } from './opencode-go.js';
10
+ import { commandcodeProvider } from './commandcode.js';
11
+ export * from './types.js';
12
+ export { inferProvider } from './infer.js';
13
+ export { opencodeGoProvider, parseOpencodeUsage } from './opencode-go.js';
14
+ export { commandcodeProvider, commandcodeClientHeaders, resolveClientVersion, parsePlanId, COMMANDCODE_BASE_URL, COMMANDCODE_BILLING_PATH, } from './commandcode.js';
15
+ const ALL_PROVIDERS = [opencodeGoProvider, commandcodeProvider];
16
+ /** Get a provider by id. */
17
+ export function getProvider(id) {
18
+ return ALL_PROVIDERS.find((p) => p.id === id);
19
+ }
20
+ /** All registered providers. */
21
+ export function allProviders() {
22
+ return ALL_PROVIDERS;
23
+ }
24
+ /** Structural validity: at least one provider accepts the format. */
25
+ export function isPlausibleKey(key) {
26
+ return ALL_PROVIDERS.some((p) => p.quickMatch(key) !== 'no');
27
+ }
28
+ /** Providers whose quickMatch does not rule the key out. */
29
+ function candidates(key) {
30
+ return ALL_PROVIDERS.filter((p) => p.quickMatch(key) !== 'no');
31
+ }
32
+ /**
33
+ * Identify which provider a key belongs to.
34
+ *
35
+ * Decision tree:
36
+ * 1. Providers whose prefix says 'no' are skipped entirely.
37
+ * 2. Remaining candidates are smoke-tested in parallel.
38
+ * 3. First 'valid' wins (definitive positive).
39
+ * 4. All candidates 'invalid' → the key is rejected.
40
+ * 5. Only inconclusive 'error' results → unknown (network problem).
41
+ * 6. No candidates at all → prefix not recognized by any provider.
42
+ *
43
+ * When `verify: false`, skips the network and returns the prefix-level
44
+ * answer ('unverified') — used by config validation where offline tolerance
45
+ * is required.
46
+ */
47
+ export async function identifyKey(key, options) {
48
+ const cands = candidates(key);
49
+ if (cands.length === 0) {
50
+ return { provider: null, confidence: 'unknown', attempts: [] };
51
+ }
52
+ if (options?.verify === false) {
53
+ const unique = new Set(cands.map((c) => c.id));
54
+ const provider = unique.size === 1 ? cands[0].id : null;
55
+ return { provider, confidence: 'unverified', attempts: [] };
56
+ }
57
+ const attempts = await Promise.all(cands.map((c) => c.smokeTest(key, options?.timeoutMs)));
58
+ const valid = attempts.find((a) => a.status === 'valid');
59
+ if (valid) {
60
+ return { provider: valid.provider, confidence: 'verified', attempts };
61
+ }
62
+ if (attempts.every((a) => a.status === 'invalid')) {
63
+ return { provider: null, confidence: 'invalid', attempts };
64
+ }
65
+ return { provider: null, confidence: 'unknown', attempts };
66
+ }
67
+ /**
68
+ * Extract candidate API keys from arbitrary pasted text (TUI drop-a-key
69
+ * flow). Matches user_... and sk-... tokens of plausible length; dedupes.
70
+ */
71
+ export function extractKeys(text) {
72
+ const matches = text.match(/(?:user_|sk-)[A-Za-z0-9_-]{16,}/g) ?? [];
73
+ return [...new Set(matches)];
74
+ }
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * infer.ts — Synchronous provider inference for config-time decisions.
3
+ *
4
+ * Config loading must never block on network I/O, so the provider for each
5
+ * key is inferred structurally here ('user_' → commandcode; 'sk-' →
6
+ * opencode-go, the legacy default). The authoritative, empirical answer
7
+ * comes from identifyKey() (async, network) — the setup wizard uses it and
8
+ * persists the result as an explicit `provider` field in config.yaml.
9
+ */
10
+ import type { ProviderId } from './types.js';
11
+ export interface KeyEntry {
12
+ label: string;
13
+ key: string;
14
+ /** Explicit provider from config.yaml; wins over inference when present. */
15
+ provider?: ProviderId;
16
+ }
17
+ /**
18
+ * Infer the provider for a key entry.
19
+ * Explicit `provider` field wins; 'user_' keys are commandcode; everything
20
+ * else plausible defaults to opencode-go (back-compat with every existing
21
+ * config.yaml).
22
+ */
23
+ export declare function inferProvider(entry: KeyEntry): ProviderId;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Infer the provider for a key entry.
3
+ * Explicit `provider` field wins; 'user_' keys are commandcode; everything
4
+ * else plausible defaults to opencode-go (back-compat with every existing
5
+ * config.yaml).
6
+ */
7
+ export function inferProvider(entry) {
8
+ if (entry.provider === 'commandcode' || entry.provider === 'opencode-go') {
9
+ return entry.provider;
10
+ }
11
+ return entry.key.startsWith('user_') ? 'commandcode' : 'opencode-go';
12
+ }
13
+ //# sourceMappingURL=infer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infer.js","sourceRoot":"","sources":["../../src/providers/infer.ts"],"names":[],"mappings":"AAkBA;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAe;IAC3C,IAAI,KAAK,CAAC,QAAQ,KAAK,aAAa,IAAI,KAAK,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;QACzE,OAAO,KAAK,CAAC,QAAQ,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC;AACvE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { KeyProvider, KeyUsage } from './types.js';
2
+ /** Parse opencode-go usage JSON (moved from usage-client.ts). */
3
+ export declare function parseOpencodeUsage(body: string): KeyUsage | null;
4
+ export declare const opencodeGoProvider: KeyProvider;
@@ -0,0 +1,179 @@
1
+ /**
2
+ * opencode-go.ts — KeyProvider adapter for the OpenCode-Go API (opencode.ai).
3
+ *
4
+ * Keys: `sk-` prefix (shared with commandcode → 'maybe'), ≥ 20 chars.
5
+ * Usage: GET /zen/go/v1/usage with Bearer = the key itself.
6
+ */
7
+ import { API_KEY_PREFIX, MIN_KEY_LENGTH, USAGE_PATH, } from '../constants.js';
8
+ /** Bare-name index of the live opencode-go catalog. */
9
+ let catalogCache = null;
10
+ /** Ensures the background refresh fires at most once per process. */
11
+ let refreshStarted = false;
12
+ /**
13
+ * Fetch the public opencode-go model list and index it. The endpoint needs
14
+ * no auth (verified live); failures keep the previous cache.
15
+ */
16
+ async function refreshCatalog(baseUrl, timeoutMs) {
17
+ const controller = new AbortController();
18
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
19
+ try {
20
+ const res = await fetch(`${baseUrl.replace(/\/$/, '')}${USAGE_PATH.replace(/\/usage$/, '/models')}`, {
21
+ signal: controller.signal,
22
+ });
23
+ if (!res.ok)
24
+ return false;
25
+ const body = (await res.json());
26
+ if (!Array.isArray(body.data))
27
+ return false;
28
+ const bareNames = new Set();
29
+ for (const entry of body.data) {
30
+ if (!entry || typeof entry !== 'object')
31
+ continue;
32
+ const id = entry.id;
33
+ if (typeof id !== 'string' || !id)
34
+ continue;
35
+ bareNames.add(id.toLowerCase());
36
+ const last = id.split('/').pop() ?? id;
37
+ if (last)
38
+ bareNames.add(last.toLowerCase());
39
+ }
40
+ catalogCache = { bareNames };
41
+ return true;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ finally {
47
+ clearTimeout(timer);
48
+ }
49
+ }
50
+ function parsePercent(window) {
51
+ const percent = window?.percent;
52
+ if (typeof percent !== 'number' || !Number.isFinite(percent))
53
+ return 0;
54
+ return Math.min(100, Math.max(0, percent));
55
+ }
56
+ /** Parse opencode-go usage JSON (moved from usage-client.ts). */
57
+ export function parseOpencodeUsage(body) {
58
+ let parsed;
59
+ try {
60
+ parsed = JSON.parse(body);
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ const usage = parsed?.usage;
66
+ if (!usage || typeof usage !== 'object')
67
+ return null;
68
+ const rolling = parsePercent(usage.rolling);
69
+ const weekly = parsePercent(usage.weekly);
70
+ const monthly = parsePercent(usage.monthly);
71
+ return {
72
+ rollingPercent: rolling,
73
+ weeklyPercent: weekly,
74
+ monthlyPercent: monthly,
75
+ usedPercent: Math.max(rolling, weekly, monthly),
76
+ rollingResetsAt: typeof usage.rolling?.resetsAt === 'string' ? usage.rolling.resetsAt : undefined,
77
+ weeklyResetsAt: typeof usage.weekly?.resetsAt === 'string' ? usage.weekly.resetsAt : undefined,
78
+ monthlyResetsAt: typeof usage.monthly?.resetsAt === 'string' ? usage.monthly.resetsAt : undefined,
79
+ };
80
+ }
81
+ export const opencodeGoProvider = {
82
+ id: 'opencode-go',
83
+ displayName: 'OpenCode Go',
84
+ baseUrl: 'https://opencode.ai',
85
+ chatBasePath: '/zen/go/v1',
86
+ quickMatch(key) {
87
+ // sk- is shared with commandcode → never a definitive 'yes'.
88
+ return key.startsWith(API_KEY_PREFIX) && key.length >= MIN_KEY_LENGTH
89
+ ? 'maybe'
90
+ : 'no';
91
+ },
92
+ async smokeTest(key, timeoutMs = 10_000) {
93
+ const controller = new AbortController();
94
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
95
+ try {
96
+ const res = await fetch(`${this.baseUrl}${USAGE_PATH}`, {
97
+ headers: { Authorization: `Bearer ${key}` },
98
+ signal: controller.signal,
99
+ });
100
+ if (res.ok) {
101
+ return { provider: this.id, status: 'valid', httpStatus: res.status };
102
+ }
103
+ // 401/403 = definitive rejection; 5xx/429 = inconclusive
104
+ const definitive = res.status === 401 || res.status === 403;
105
+ return {
106
+ provider: this.id,
107
+ status: definitive ? 'invalid' : 'error',
108
+ httpStatus: res.status,
109
+ detail: definitive ? 'upstream rejected the key' : 'upstream unavailable',
110
+ };
111
+ }
112
+ catch (err) {
113
+ return {
114
+ provider: this.id,
115
+ status: 'error',
116
+ detail: err instanceof Error ? err.message : String(err),
117
+ };
118
+ }
119
+ finally {
120
+ clearTimeout(timer);
121
+ }
122
+ },
123
+ usagePath() {
124
+ return USAGE_PATH;
125
+ },
126
+ parseUsageResponse(body) {
127
+ return parseOpencodeUsage(body);
128
+ },
129
+ /**
130
+ * Affinity via the live catalog when loaded; 'maybe' before the first
131
+ * fetch resolves (non-committal, keeps the key as rotation fallback).
132
+ */
133
+ modelAffinity(modelId) {
134
+ if (!catalogCache) {
135
+ if (!refreshStarted) {
136
+ refreshStarted = true;
137
+ void refreshCatalog(this.baseUrl, 5_000);
138
+ }
139
+ return 'maybe';
140
+ }
141
+ const bare = (modelId.split('/').pop() ?? modelId).toLowerCase();
142
+ return catalogCache.bareNames.has(bare) ? 'yes' : 'no';
143
+ },
144
+ /** Same verdict as modelAffinity — the catalog index is already bare-name based. */
145
+ modelAffinityByName(bareName) {
146
+ if (!catalogCache) {
147
+ if (!refreshStarted) {
148
+ refreshStarted = true;
149
+ void refreshCatalog(this.baseUrl, 5_000);
150
+ }
151
+ return 'maybe';
152
+ }
153
+ return catalogCache.bareNames.has(bareName.toLowerCase()) ? 'yes' : 'no';
154
+ },
155
+ /** opencode-go ids are already bare (1:1 naming) — the input passes through. */
156
+ resolveNativeId(bareName) {
157
+ return catalogCache?.bareNames.has(bareName.toLowerCase()) ? bareName : null;
158
+ },
159
+ /** Public catalog endpoint (no auth required, verified live). */
160
+ async fetchCatalog(timeoutMs = 5_000) {
161
+ const ok = await refreshCatalog(this.baseUrl, timeoutMs);
162
+ if (!ok)
163
+ return null;
164
+ // Re-fetch and return the raw list for catalog consumers (harness sync).
165
+ try {
166
+ const res = await fetch(`${this.baseUrl.replace(/\/$/, '')}${USAGE_PATH.replace(/\/usage$/, '/models')}`);
167
+ if (!res.ok)
168
+ return null;
169
+ const body = (await res.json());
170
+ return Array.isArray(body.data)
171
+ ? body.data
172
+ : null;
173
+ }
174
+ catch {
175
+ return null;
176
+ }
177
+ },
178
+ };
179
+ //# sourceMappingURL=opencode-go.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-go.js","sourceRoot":"","sources":["../../src/providers/opencode-go.ts"],"names":[],"mappings":"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"}