saros-proxy 1.0.0 → 1.1.1

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 (45) hide show
  1. package/README.md +83 -16
  2. package/assets/models.json +3349 -0
  3. package/dist/cli/autostart.d.ts +13 -6
  4. package/dist/cli/autostart.js +317 -60
  5. package/dist/cli/autostart.js.map +1 -1
  6. package/dist/cli/daemon.js +89 -14
  7. package/dist/cli/daemon.js.map +1 -1
  8. package/dist/cli/harness-models.d.ts +5 -4
  9. package/dist/cli/harness-models.js +77 -30
  10. package/dist/cli/harness-models.js.map +1 -1
  11. package/dist/cli/help.js +5 -0
  12. package/dist/cli/help.js.map +1 -1
  13. package/dist/cli/log-render.d.ts +16 -0
  14. package/dist/cli/log-render.js +82 -0
  15. package/dist/cli/log-render.js.map +1 -0
  16. package/dist/cli/opencode-config.d.ts +1 -0
  17. package/dist/cli/opencode-config.js +7 -6
  18. package/dist/cli/opencode-config.js.map +1 -1
  19. package/dist/config.js +20 -4
  20. package/dist/config.js.map +1 -1
  21. package/dist/constants.d.ts +2 -0
  22. package/dist/constants.js +2 -0
  23. package/dist/constants.js.map +1 -1
  24. package/dist/index.js +29 -36
  25. package/dist/index.js.map +1 -1
  26. package/dist/logger.d.ts +16 -0
  27. package/dist/logger.js +56 -23
  28. package/dist/logger.js.map +1 -1
  29. package/dist/model-ladder.d.ts +30 -0
  30. package/dist/model-ladder.js +45 -0
  31. package/dist/model-ladder.js.map +1 -0
  32. package/dist/models-catalog.d.ts +25 -4
  33. package/dist/models-catalog.js +41 -12
  34. package/dist/models-catalog.js.map +1 -1
  35. package/dist/models-dev.d.ts +72 -0
  36. package/dist/models-dev.js +196 -0
  37. package/dist/models-dev.js.map +1 -0
  38. package/dist/models-refresh.d.ts +144 -0
  39. package/dist/models-refresh.js +522 -0
  40. package/dist/models-refresh.js.map +1 -0
  41. package/dist/models-sync.d.ts +6 -10
  42. package/dist/models-sync.js +26 -60
  43. package/dist/models-sync.js.map +1 -1
  44. package/package.json +5 -5
  45. package/models.json +0 -1440
@@ -0,0 +1,522 @@
1
+ /**
2
+ * models-refresh.ts — Deterministic generator for the bundled model catalog.
3
+ *
4
+ * `npm run models:refresh` (scripts/refresh-models.ts) is the only writer of
5
+ * `assets/models.json`. It merges models.dev metadata for the two providers
6
+ * this proxy serves with the live catalogs, so a model that no longer exists
7
+ * upstream leaves the bundle and a new one enters without a hand edit.
8
+ *
9
+ * The merge is a pure function (`mergeModelCatalog`) so the catalog rules are
10
+ * testable without network access:
11
+ * - membership follows the live catalogs: a model the provider serves enters
12
+ * with the metadata the live payload carries even when models.dev omits it,
13
+ * and it stays while the provider keeps advertising it;
14
+ * - `opencode-go` metadata wins when both providers know a model;
15
+ * - a field already in the current file survives when models.dev is silent
16
+ * about it (models.dev drops fields occasionally and a regeneration must
17
+ * not discard the value);
18
+ * - two raw ids that collapse to the same bare key abort the refresh;
19
+ * - output keys, including nested object keys, are sorted so a refresh diff
20
+ * only shows real changes;
21
+ * - a failed fetch — or an empty/partial response — aborts before the file is
22
+ * read, and a merge that would drop more than half of the current models
23
+ * needs `allowShrink` before it writes anything.
24
+ */
25
+ import { isDeepStrictEqual } from 'node:util';
26
+ import { DEFAULT_UPSTREAM_COMMANDCODE, DEFAULT_UPSTREAM_URL, MODELS_DEV_COMMANDCODE_PROVIDER_ID, MODELS_DEV_SAFE_FIELDS, MODELS_DEV_TIMEOUT_MS, MODELS_DEV_URL, MODELS_DEV_PROVIDER_ID, MODELS_FETCH_TIMEOUT_MS, } from './constants.js';
27
+ import { OPENCODE_GO_USER_AGENT } from './providers/opencode-go.js';
28
+ import { conservativeEffortOptions } from './model-ladder.js';
29
+ import { parseCatalogObject, serializeCatalogObject } from './models-catalog.js';
30
+ import { extractProviderModels } from './models-dev.js';
31
+ // ---------------------------------------------------------------------------
32
+ // Sources
33
+ // ---------------------------------------------------------------------------
34
+ /** Public opencode-go catalog — no auth, but Cloudflare wants a CLI signature. */
35
+ export const OPENCODE_GO_MODELS_URL = `${DEFAULT_UPSTREAM_URL}/zen/go/v1/models`;
36
+ /** Public CommandCode catalog (200 with any or no Authorization header). */
37
+ export const COMMANDCODE_MODELS_URL = `${DEFAULT_UPSTREAM_COMMANDCODE}/provider/v1/models`;
38
+ /**
39
+ * models.dev sits behind Cloudflare and answers 403 `error code: 1010` to
40
+ * browser-like or missing signatures. Any stable non-browser UA works.
41
+ */
42
+ export const MODELS_REFRESH_USER_AGENT = 'saros-proxy-models-refresh';
43
+ // ---------------------------------------------------------------------------
44
+ // Fetching
45
+ // ---------------------------------------------------------------------------
46
+ /** GET a JSON document with one User-Agent header. Throws on any non-2xx. */
47
+ async function fetchJson(url, userAgent, label, fetchImpl, timeoutMs) {
48
+ const response = await fetchImpl(url, {
49
+ headers: { 'User-Agent': userAgent },
50
+ signal: AbortSignal.timeout(timeoutMs),
51
+ });
52
+ if (!response.ok)
53
+ throw new Error(`${label} returned HTTP ${response.status}`);
54
+ return response.json();
55
+ }
56
+ /** Fetch and validate the models.dev catalog. */
57
+ export async function fetchModelsDevCatalog(fetchImpl = fetch, timeoutMs = MODELS_DEV_TIMEOUT_MS) {
58
+ const parsed = await fetchJson(MODELS_DEV_URL, MODELS_REFRESH_USER_AGENT, 'models.dev', fetchImpl, timeoutMs);
59
+ if (!isPlainObject(parsed))
60
+ throw new Error('models.dev returned a non-object payload');
61
+ return parsed;
62
+ }
63
+ /**
64
+ * Fetch the opencode-go live catalog.
65
+ *
66
+ * The list is authoritative for membership: models.dev keeps models the
67
+ * provider already retired (`space-bunny`, `cat-jam-free`, …), and publishing
68
+ * those as connectable models is what this gate prevents.
69
+ */
70
+ export async function fetchOpencodeGoModels(fetchImpl = fetch, timeoutMs = MODELS_FETCH_TIMEOUT_MS) {
71
+ return fetchLiveModels(OPENCODE_GO_MODELS_URL, OPENCODE_GO_USER_AGENT, 'opencode-go models', fetchImpl, timeoutMs, () => true);
72
+ }
73
+ /**
74
+ * Fetch the CommandCode live catalog.
75
+ *
76
+ * Only models that advertise `/chat/completions` are usable through the proxy,
77
+ * which relays chat/completions shapes. `claude-*` entries list `/messages`
78
+ * only (verified live: chat/completions → unsupported_model); a vendor-prefixed
79
+ * Claude model would slip past a bare `^claude` test, so the endpoint field —
80
+ * not the id — decides.
81
+ */
82
+ export async function fetchCommandcodeModels(fetchImpl = fetch, timeoutMs = MODELS_FETCH_TIMEOUT_MS) {
83
+ return fetchLiveModels(COMMANDCODE_MODELS_URL, MODELS_REFRESH_USER_AGENT, 'commandcode models', fetchImpl, timeoutMs, supportsChatCompletions);
84
+ }
85
+ /** True when the live entry advertises the OpenAI chat/completions endpoint. */
86
+ function supportsChatCompletions(entry) {
87
+ const endpoints = entry.supported_endpoints;
88
+ return Array.isArray(endpoints) && endpoints.includes('/chat/completions');
89
+ }
90
+ /** Shared `{ object: 'list', data: [{ id }] }` parser. */
91
+ async function fetchLiveModels(url, userAgent, label, fetchImpl, timeoutMs, keep) {
92
+ const body = await fetchJson(url, userAgent, label, fetchImpl, timeoutMs);
93
+ if (!isPlainObject(body) || !Array.isArray(body.data)) {
94
+ throw new Error(`${label} returned an unexpected payload`);
95
+ }
96
+ const models = [];
97
+ for (const entry of body.data) {
98
+ if (!isPlainObject(entry) || typeof entry.id !== 'string' || entry.id.length === 0)
99
+ continue;
100
+ if (keep(entry))
101
+ models.push(toLiveModel(entry, entry.id));
102
+ }
103
+ return models;
104
+ }
105
+ /** Keep the fields the live payload can seed a catalog entry with. */
106
+ function toLiveModel(entry, id) {
107
+ const model = { id };
108
+ if (typeof entry.name === 'string' && entry.name.length > 0)
109
+ model.name = entry.name;
110
+ if (typeof entry.context_length === 'number' && Number.isFinite(entry.context_length)) {
111
+ model.context_length = entry.context_length;
112
+ }
113
+ return model;
114
+ }
115
+ /** Build the real fetchers — the only place the CLI touches the network. */
116
+ export function defaultRefreshFetchers() {
117
+ return {
118
+ modelsDev: () => fetchModelsDevCatalog(),
119
+ opencodeModels: () => fetchOpencodeGoModels(),
120
+ commandcodeModels: () => fetchCommandcodeModels(),
121
+ };
122
+ }
123
+ // ---------------------------------------------------------------------------
124
+ // models.dev extraction
125
+ // ---------------------------------------------------------------------------
126
+ /**
127
+ * Return one provider's `models` map from a parsed models.dev payload.
128
+ *
129
+ * Thin delegate: the parse lives in `models-dev.ts` so the generator and the
130
+ * runtime read agree on what a provider slice is.
131
+ */
132
+ export function extractModelsDevProviderModels(parsed, providerId) {
133
+ return extractProviderModels(parsed, providerId);
134
+ }
135
+ /**
136
+ * Merge models.dev metadata with the live catalogs and the current file.
137
+ *
138
+ * Membership is decided by upstream: only ids that the live catalog advertises
139
+ * are kept, and a live id enters even when models.dev has never heard of it.
140
+ * Fields inside a surviving entry are additive — the current file's value
141
+ * survives whenever the upstream source does not carry that field.
142
+ */
143
+ export function mergeModelCatalog(input) {
144
+ const metadata = collectCandidates(input);
145
+ const live = indexLiveModels(input);
146
+ const current = isPlainObject(input.current) ? input.current : {};
147
+ const models = {};
148
+ const added = [];
149
+ const changed = [];
150
+ const entries = [...live].sort(([left], [right]) => (left < right ? -1 : 1));
151
+ for (const [key, liveModel] of entries) {
152
+ const base = isPlainObject(current[key]) ? current[key] : {};
153
+ const fromModelsDev = metadata.get(key);
154
+ // models.dev wins over the current file (it is the richer source); the
155
+ // live payload only fills the holes models.dev leaves, so a value the user
156
+ // customized is never replaced by a bare live stub.
157
+ const entry = fromModelsDev ? overlay(base, { ...fromModelsDev, id: key }) : base;
158
+ const merged = fillMissing(entry, liveStub(key, liveModel));
159
+ models[key] = sortKeysDeep({ ...merged, id: key });
160
+ if (!(key in current))
161
+ added.push(key);
162
+ else if (!isDeepStrictEqual(current[key], models[key]))
163
+ changed.push(key);
164
+ }
165
+ return {
166
+ models,
167
+ added,
168
+ removed: Object.keys(current)
169
+ .filter((key) => !live.has(key))
170
+ .sort(),
171
+ changed,
172
+ };
173
+ }
174
+ /** Build the models.dev candidate map: opencode-go first, commandcode fills the gaps. */
175
+ function collectCandidates(input) {
176
+ const opencodeCatalog = extractModelsDevProviderModels(input.modelsDev, MODELS_DEV_PROVIDER_ID);
177
+ const commandcodeCatalog = extractModelsDevProviderModels(input.modelsDev, MODELS_DEV_COMMANDCODE_PROVIDER_ID);
178
+ const opencodeKeys = indexProviderIds(MODELS_DEV_PROVIDER_ID, input.opencodeModels);
179
+ const commandcodeKeys = indexProviderIds(MODELS_DEV_COMMANDCODE_PROVIDER_ID, input.commandcodeModels);
180
+ const candidates = new Map();
181
+ addProviderModels(candidates, MODELS_DEV_PROVIDER_ID, opencodeCatalog, opencodeKeys, false);
182
+ addProviderModels(candidates, MODELS_DEV_COMMANDCODE_PROVIDER_ID, commandcodeCatalog, commandcodeKeys, true);
183
+ return candidates;
184
+ }
185
+ /**
186
+ * Index both live catalogs by key. opencode-go is the primary provider, so its
187
+ * entry wins when the same model is served by both.
188
+ */
189
+ function indexLiveModels(input) {
190
+ const byKey = new Map();
191
+ for (const model of [...input.opencodeModels, ...input.commandcodeModels]) {
192
+ const key = normalizeModelKey(model.id);
193
+ if (key && !byKey.has(key))
194
+ byKey.set(key, model);
195
+ }
196
+ return byKey;
197
+ }
198
+ /**
199
+ * Index one provider's live ids by catalog key, rejecting ambiguity.
200
+ *
201
+ * Two different raw ids (`vendorA/foo`, `vendorB/foo`) that collapse to the
202
+ * same bare key would silently overwrite each other, so the refresh aborts
203
+ * instead of picking one.
204
+ */
205
+ function indexProviderIds(providerId, live) {
206
+ const byKey = new Map();
207
+ for (const model of live) {
208
+ const key = normalizeModelKey(model.id);
209
+ if (!key)
210
+ continue;
211
+ const existing = byKey.get(key);
212
+ if (existing)
213
+ throw collisionError(providerId, existing.id, model.id, key);
214
+ byKey.set(key, model);
215
+ }
216
+ return byKey;
217
+ }
218
+ /** The bare-name collision message: which raw ids fought over one key. */
219
+ function collisionError(providerId, first, second, key) {
220
+ return new Error(`bare-name collision in ${providerId}: "${first}" and "${second}" both map to "${key}"; `
221
+ + 'rename one of them upstream or teach normalizeModelKey about the difference');
222
+ }
223
+ /**
224
+ * Add one provider's models.dev entries that exist in its live catalog.
225
+ *
226
+ * @param fillOnly — keep the existing entry's fields and only fill the holes
227
+ * (CommandCode is secondary metadata; opencode-go is primary)
228
+ */
229
+ function addProviderModels(target, providerId, models, allowed, fillOnly) {
230
+ for (const [modelId, raw] of Object.entries(models)) {
231
+ const key = normalizeModelKey(modelId);
232
+ const liveModel = allowed.get(key);
233
+ if (!liveModel)
234
+ continue;
235
+ // The same model under a different raw id in models.dev and the live
236
+ // payload is the cross-source form of the bare-name collision.
237
+ if (liveModel.id !== modelId)
238
+ throw collisionError(providerId, liveModel.id, modelId, key);
239
+ const entry = sanitizeEntry(raw);
240
+ if (!entry)
241
+ continue;
242
+ const existing = target.get(key);
243
+ if (!existing) {
244
+ target.set(key, entry);
245
+ }
246
+ else if (fillOnly) {
247
+ target.set(key, fillMissing(existing, entry));
248
+ }
249
+ }
250
+ }
251
+ /**
252
+ * Fallback context window for a live-only model whose payload carries no
253
+ * `context_length`; same default `providerCatalogToCanonical` uses.
254
+ */
255
+ const LIVE_STUB_CONTEXT_FALLBACK = 262_144;
256
+ /** Fallback max output tokens; same default `providerCatalogToCanonical` uses. */
257
+ const LIVE_STUB_OUTPUT_FALLBACK = 65_536;
258
+ /**
259
+ * Minimal catalog entry from a live payload: id, name, context window and the
260
+ * canonical defaults the harness pipeline (`providerCatalogToCanonical`) gives
261
+ * a model with no models.dev metadata. Every value here only fills a hole, so
262
+ * models.dev and the current file still win wherever they have a field.
263
+ */
264
+ function liveStub(key, model) {
265
+ const stub = {
266
+ id: key,
267
+ tool_call: true,
268
+ reasoning: true,
269
+ limit: {
270
+ context: model.context_length ?? LIVE_STUB_CONTEXT_FALLBACK,
271
+ output: LIVE_STUB_OUTPUT_FALLBACK,
272
+ },
273
+ modalities: { input: ['text'], output: ['text'] },
274
+ // Conservative effort ladder: the commandcode gateway accepts
275
+ // low|medium|high|xhigh and rejects minimal, so declare the safe three.
276
+ reasoning_options: conservativeEffortOptions(),
277
+ };
278
+ if (model.name !== undefined)
279
+ stub.name = model.name;
280
+ return stub;
281
+ }
282
+ /**
283
+ * Normalize an id to catalog key form: bare name, lower-cased, `_` → `-`.
284
+ * Vendor-prefixed names (`zai-org/GLM-5.3`) collapse to their model name.
285
+ */
286
+ function normalizeModelKey(modelId) {
287
+ const bare = modelId.substring(modelId.lastIndexOf('/') + 1);
288
+ return bare.toLowerCase().replaceAll('_', '-');
289
+ }
290
+ /** Keep only the fields the catalog schema documents. */
291
+ function sanitizeEntry(raw) {
292
+ if (!isPlainObject(raw))
293
+ return null;
294
+ const entry = {};
295
+ for (const field of MODELS_DEV_SAFE_FIELDS) {
296
+ if (field in raw)
297
+ entry[field] = raw[field];
298
+ }
299
+ return Object.keys(entry).length > 0 ? entry : null;
300
+ }
301
+ /** Recursive overlay: `upstream` wins at every leaf. */
302
+ function overlay(base, upstream) {
303
+ const merged = { ...base };
304
+ for (const [field, value] of Object.entries(upstream)) {
305
+ const current = merged[field];
306
+ merged[field] = isPlainObject(current) && isPlainObject(value)
307
+ ? overlay(current, value)
308
+ : value;
309
+ }
310
+ return merged;
311
+ }
312
+ /** Recursive fill: values already in `base` survive, holes come from `extra`. */
313
+ function fillMissing(base, extra) {
314
+ const merged = { ...base };
315
+ for (const [field, value] of Object.entries(extra)) {
316
+ const current = merged[field];
317
+ if (!(field in merged)) {
318
+ merged[field] = value;
319
+ }
320
+ else if (isPlainObject(current) && isPlainObject(value)) {
321
+ merged[field] = fillMissing(current, value);
322
+ }
323
+ }
324
+ return merged;
325
+ }
326
+ /** Sort object keys recursively; arrays keep their order. */
327
+ function sortKeysDeep(value) {
328
+ if (Array.isArray(value))
329
+ return value.map(sortKeysDeep);
330
+ if (!isPlainObject(value))
331
+ return value;
332
+ const sorted = {};
333
+ for (const key of Object.keys(value).sort()) {
334
+ sorted[key] = sortKeysDeep(value[key]);
335
+ }
336
+ return sorted;
337
+ }
338
+ /** Plain object (not null, not an array). */
339
+ function isPlainObject(value) {
340
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
341
+ }
342
+ // ---------------------------------------------------------------------------
343
+ // Serialization & orchestration
344
+ // ---------------------------------------------------------------------------
345
+ /** Stable on-disk format: 2-space indent, sorted keys, trailing newline. */
346
+ export function serializeModelCatalog(models) {
347
+ return serializeCatalogObject(sortKeysDeep(models));
348
+ }
349
+ /** Parse a catalog file. `null`/empty content is an empty catalog. */
350
+ export function parseCatalogContent(raw) {
351
+ if (raw === null || raw.trim() === '')
352
+ return {};
353
+ return parseCatalogObject(raw);
354
+ }
355
+ /**
356
+ * Fetch, merge, and (unless dry-running) write the catalog.
357
+ *
358
+ * Sources are validated before the file is read: a 2xx with an empty body (a
359
+ * Cloudflare hiccup, a partial rollout) used to empty the catalog and mark
360
+ * every model removed. A merge that would drop more than half of the current
361
+ * models is treated the same way unless `allowShrink` opts in.
362
+ */
363
+ export async function refreshModelCatalog(io, fetchers = defaultRefreshFetchers(), options = {}) {
364
+ const [modelsDev, opencodeModels, commandcodeModels] = await Promise.all([
365
+ fetchers.modelsDev(),
366
+ fetchers.opencodeModels(),
367
+ fetchers.commandcodeModels(),
368
+ ]);
369
+ assertRefreshSources(modelsDev, opencodeModels, commandcodeModels);
370
+ const previousContent = io.read();
371
+ const current = parseCatalogContent(previousContent);
372
+ const merge = mergeModelCatalog({
373
+ modelsDev,
374
+ opencodeModels,
375
+ commandcodeModels,
376
+ current,
377
+ });
378
+ assertShrinkAllowed(merge, current, options.allowShrink);
379
+ const nextContent = serializeModelCatalog(merge.models);
380
+ const bytesChanged = nextContent !== previousContent;
381
+ const formattingOnly = bytesChanged
382
+ && merge.added.length === 0
383
+ && merge.removed.length === 0
384
+ && merge.changed.length === 0;
385
+ const written = options.dryRun !== true && bytesChanged;
386
+ if (written)
387
+ io.write(nextContent);
388
+ return { ...merge, previousContent, nextContent, written, formattingOnly };
389
+ }
390
+ /**
391
+ * Reject a source that answered 2xx with an empty or partial payload.
392
+ *
393
+ * models.dev must carry both provider maps; each live catalog must carry at
394
+ * least one usable id. Anything else is a network hiccup, not a catalog.
395
+ */
396
+ function assertRefreshSources(modelsDev, opencodeModels, commandcodeModels) {
397
+ for (const providerId of [MODELS_DEV_PROVIDER_ID, MODELS_DEV_COMMANDCODE_PROVIDER_ID]) {
398
+ const models = extractModelsDevProviderModels(modelsDev, providerId);
399
+ if (Object.keys(models).length === 0) {
400
+ throw new Error(`models.dev returned no ${providerId} models; refusing to treat an empty response as a catalog`);
401
+ }
402
+ }
403
+ if (opencodeModels.length === 0) {
404
+ throw new Error('opencode-go returned no models; refusing to treat an empty response as a catalog');
405
+ }
406
+ if (commandcodeModels.length === 0) {
407
+ throw new Error('commandcode returned no models; refusing to treat an empty response as a catalog');
408
+ }
409
+ }
410
+ /**
411
+ * Refuse a merge that removes more than half of the current models.
412
+ *
413
+ * A partial upstream response looks exactly like a mass retirement, and the
414
+ * resulting file would drop models the proxy still serves. `allowShrink` is
415
+ * the operator's statement that the removal is intentional.
416
+ */
417
+ function assertShrinkAllowed(merge, current, allowShrink) {
418
+ if (allowShrink)
419
+ return;
420
+ const total = Object.keys(current).length;
421
+ if (total === 0 || merge.removed.length * 2 <= total)
422
+ return;
423
+ const percent = Math.round((merge.removed.length / total) * 100);
424
+ throw new Error(`refusing to remove ${merge.removed.length} of ${total} models (${percent}%): `
425
+ + 'neither live catalog lists them any more, which is how a partial upstream response looks. '
426
+ + 'Rerun with --allow-shrink if the removal is intentional');
427
+ }
428
+ // ---------------------------------------------------------------------------
429
+ // Diff rendering (--dry-run)
430
+ // ---------------------------------------------------------------------------
431
+ /** Human-readable model-level diff between the old and the new catalog. */
432
+ export function formatCatalogDiff(previous, next) {
433
+ const lines = [];
434
+ const previousModels = previous ?? {};
435
+ const nextKeys = Object.keys(next);
436
+ for (const key of Object.keys(previousModels).sort()) {
437
+ if (!(key in next))
438
+ lines.push(...entryBlock('-', key, previousModels[key]));
439
+ }
440
+ for (const key of nextKeys) {
441
+ if (!(key in previousModels))
442
+ lines.push(...entryBlock('+', key, next[key]));
443
+ }
444
+ for (const key of nextKeys) {
445
+ if (!(key in previousModels))
446
+ continue;
447
+ if (isDeepStrictEqual(previousModels[key], next[key]))
448
+ continue;
449
+ lines.push(`~ ${key}`);
450
+ lines.push(...diffLines(prettyLines(previousModels[key]), prettyLines(next[key])));
451
+ }
452
+ return lines.length > 0 ? lines.join('\n') : 'no changes';
453
+ }
454
+ /** Render one model entry as a prefixed JSON block. */
455
+ function entryBlock(prefix, key, value) {
456
+ const [first, ...rest] = prettyLines(value);
457
+ return [`${prefix} ${JSON.stringify(key)}: ${first}`, ...rest.map((line) => `${prefix} ${line}`)];
458
+ }
459
+ /** Pretty-print a value as lines. */
460
+ function prettyLines(value) {
461
+ return JSON.stringify(value, null, 2).split('\n');
462
+ }
463
+ /** Line diff with common prefix/suffix trimmed (LCS over the middle). */
464
+ function diffLines(before, after) {
465
+ let start = 0;
466
+ while (start < before.length && start < after.length && before[start] === after[start])
467
+ start++;
468
+ let endBefore = before.length;
469
+ let endAfter = after.length;
470
+ while (endBefore > start &&
471
+ endAfter > start &&
472
+ before[endBefore - 1] === after[endAfter - 1]) {
473
+ endBefore--;
474
+ endAfter--;
475
+ }
476
+ const middleBefore = before.slice(start, endBefore);
477
+ const middleAfter = after.slice(start, endAfter);
478
+ const out = before.slice(0, start).map((line) => ` ${line}`);
479
+ out.push(...emitDiff(middleBefore, middleAfter));
480
+ out.push(...before.slice(endBefore).map((line) => ` ${line}`));
481
+ return out;
482
+ }
483
+ /** Walk the LCS table to emit ` `/`-`/`+` lines for the changed middle. */
484
+ function emitDiff(before, after) {
485
+ const table = lcsTable(before, after);
486
+ const out = [];
487
+ let i = 0;
488
+ let j = 0;
489
+ while (i < before.length && j < after.length) {
490
+ if (before[i] === after[j]) {
491
+ out.push(` ${before[i]}`);
492
+ i++;
493
+ j++;
494
+ }
495
+ else if (table[i + 1][j] >= table[i][j + 1]) {
496
+ out.push(`- ${before[i]}`);
497
+ i++;
498
+ }
499
+ else {
500
+ out.push(`+ ${after[j]}`);
501
+ j++;
502
+ }
503
+ }
504
+ for (; i < before.length; i++)
505
+ out.push(`- ${before[i]}`);
506
+ for (; j < after.length; j++)
507
+ out.push(`+ ${after[j]}`);
508
+ return out;
509
+ }
510
+ /** LCS length table where `table[i][j]` covers `before[i:]` × `after[j:]`. */
511
+ function lcsTable(before, after) {
512
+ const table = Array.from({ length: before.length + 1 }, () => new Array(after.length + 1).fill(0));
513
+ for (let i = before.length - 1; i >= 0; i--) {
514
+ for (let j = after.length - 1; j >= 0; j--) {
515
+ table[i][j] = before[i] === after[j]
516
+ ? table[i + 1][j + 1] + 1
517
+ : Math.max(table[i + 1][j], table[i][j + 1]);
518
+ }
519
+ }
520
+ return table;
521
+ }
522
+ //# sourceMappingURL=models-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models-refresh.js","sourceRoot":"","sources":["../src/models-refresh.ts"],"names":[],"mappings":"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"}
@@ -32,7 +32,7 @@ export declare function getMissingModels(current: string[], upstream: string[]):
32
32
  *
33
33
  * Priority (first match wins):
34
34
  * 1. models.dev metadata (if provided and found for modelId)
35
- * 2. Bundled `models.json` catalog
35
+ * 2. Bundled `assets/models.json` catalog
36
36
  * 3. Heuristic title-case of the ID with default limits
37
37
  *
38
38
  * @param modelId — The upstream model ID (e.g. "glm-5", "kimi-k2.7-code")
@@ -83,22 +83,18 @@ export declare function syncOpencodeModelsWithUpstream(config: ProxyConfig, conf
83
83
  /**
84
84
  * Extract the opencode-go provider's models from a models.dev API response.
85
85
  *
86
- * Pure function — callers handle JSON parsing and error recovery.
86
+ * Thin delegate: per-provider extraction lives in `models-dev.ts`.
87
87
  *
88
88
  * @param parsed — Parsed models.dev JSON object (result of `JSON.parse`)
89
89
  * @returns Model ID → metadata map, or empty object if provider not found
90
90
  */
91
91
  export declare function extractOpencodeGoModels(parsed: Record<string, unknown>): Record<string, Record<string, unknown>>;
92
92
  /**
93
- * Fetch and cache models.dev metadata for opencode-go models.
93
+ * Fetch opencode-go metadata from models.dev.
94
94
  *
95
- * Caches in memory with TTL. Never throws — returns null on any failure.
96
- * Skips in-flight deduplication (YAGNI — single orchestrator path).
97
- *
98
- * @returns Model ID → metadata map, null on failure, empty on missing provider
95
+ * Never throws — returns null on any failure, `{}` when the provider is absent.
96
+ * The TTL cache is shared with every other provider slice.
99
97
  */
100
98
  export declare function fetchModelsDevMetadata(): Promise<Record<string, Record<string, unknown>> | null>;
101
- /**
102
- * Clear the models.dev in-memory cache (test helper).
103
- */
99
+ /** Clear the models.dev in-memory cache (test helper). */
104
100
  export declare function resetModelsDevCacheState(): void;