saros-proxy 0.5.7 → 0.6.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 +55 -8
- package/dist/cli/daemon.js +27 -14
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/help.d.ts +7 -0
- package/dist/cli/help.js +26 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/opencode-config.js +20 -7
- package/dist/cli/opencode-config.js.map +1 -1
- package/dist/constants.d.ts +13 -0
- package/dist/constants.js +21 -11
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +133 -5
- package/dist/index.js.map +1 -1
- package/dist/model-probe.d.ts +48 -0
- package/dist/model-probe.js +234 -0
- package/dist/model-probe.js.map +1 -0
- package/dist/models-fetcher.d.ts +42 -0
- package/dist/models-fetcher.js +137 -0
- package/dist/models-fetcher.js.map +1 -0
- package/dist/models-sync.d.ts +104 -0
- package/dist/models-sync.js +413 -0
- package/dist/models-sync.js.map +1 -0
- package/dist/probe-cache.d.ts +58 -0
- package/dist/probe-cache.js +99 -0
- package/dist/probe-cache.js.map +1 -0
- package/dist/proxy.d.ts +0 -10
- package/dist/proxy.js +4 -33
- package/dist/proxy.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* models-sync.ts — Auto-sync models from upstream to opencode.json.
|
|
3
|
+
*
|
|
4
|
+
* Tier 1 of the auto model discovery plan.
|
|
5
|
+
* Fetches upstream model IDs, diffs against opencode.json, and adds
|
|
6
|
+
* any missing models with minimal stubs.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync } from 'node:fs';
|
|
9
|
+
import { getDefaultOpencodeConfigPath } from './cli/opencode-config.js';
|
|
10
|
+
import { getModelsList } from './models-fetcher.js';
|
|
11
|
+
import { MODELS_DEV_URL, MODELS_DEV_CACHE_TTL_MS, MODELS_DEV_TIMEOUT_MS, MODELS_DEV_PROVIDER_ID, MODELS_DEV_SAFE_FIELDS, } from './constants.js';
|
|
12
|
+
import { loadModelsFromJson } from './cli/opencode-config.js';
|
|
13
|
+
import { logger } from './logger.js';
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Title-case helper
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/**
|
|
18
|
+
* Convert a model ID into a human-readable title.
|
|
19
|
+
*
|
|
20
|
+
* Splits on hyphens, capitalises the first letter of each segment,
|
|
21
|
+
* and joins with spaces. Segments with 3 or fewer characters are
|
|
22
|
+
* fully uppercased (e.g. "glm" → "GLM", "v2" → "V2").
|
|
23
|
+
*
|
|
24
|
+
* When a segment is purely numeric (e.g. "5", "3.7") it is attached
|
|
25
|
+
* to the preceding word with a hyphen (e.g. "GLM-5").
|
|
26
|
+
*
|
|
27
|
+
* Letter-number boundaries are split when 3+ letters precede a digit
|
|
28
|
+
* (e.g. "qwen3.7" → "Qwen 3.7").
|
|
29
|
+
*/
|
|
30
|
+
function toTitleCase(id) {
|
|
31
|
+
const parts = id.split('-');
|
|
32
|
+
const titled = [];
|
|
33
|
+
for (let i = 0; i < parts.length; i++) {
|
|
34
|
+
const part = parts[i];
|
|
35
|
+
// Purely numeric segments (e.g. "5", "3.7") attach to previous
|
|
36
|
+
if (/^\d+(\.\d+)?$/.test(part)) {
|
|
37
|
+
if (titled.length > 0) {
|
|
38
|
+
titled[titled.length - 1] += '-' + part;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
titled.push(part);
|
|
42
|
+
}
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Insert space before a digit when preceded by 3+ letters
|
|
46
|
+
const expanded = part.replace(/([a-z]{3,})(\d)/gi, '$1 $2');
|
|
47
|
+
const words = expanded.split(/\s+/);
|
|
48
|
+
const processed = words.map((w) => {
|
|
49
|
+
if (w.length <= 3) {
|
|
50
|
+
// Words with uppercase letters or digits are codes/abbreviations → fully uppercase
|
|
51
|
+
// All-lowercase letter-only short words → title-case (e.g. "new" → "New")
|
|
52
|
+
if (/[A-Z0-9]/.test(w)) {
|
|
53
|
+
return w.toUpperCase();
|
|
54
|
+
}
|
|
55
|
+
return w.charAt(0).toUpperCase() + w.slice(1);
|
|
56
|
+
}
|
|
57
|
+
return w.charAt(0).toUpperCase() + w.slice(1);
|
|
58
|
+
});
|
|
59
|
+
titled.push(processed.join(' '));
|
|
60
|
+
}
|
|
61
|
+
return titled.join(' ');
|
|
62
|
+
}
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Public API
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
/**
|
|
67
|
+
* Fetch model IDs from the upstream OpenCode-Go API.
|
|
68
|
+
*
|
|
69
|
+
* Calls `getModelsList(config)` to obtain the /v1/models response,
|
|
70
|
+
* then extracts `data[].id` values.
|
|
71
|
+
*
|
|
72
|
+
* @param config — Proxy configuration
|
|
73
|
+
* @returns Array of model ID strings, or empty array on any failure
|
|
74
|
+
*/
|
|
75
|
+
export async function fetchUpstreamModelIds(config) {
|
|
76
|
+
try {
|
|
77
|
+
const response = await getModelsList(config);
|
|
78
|
+
const body = await response.json();
|
|
79
|
+
if (typeof body !== 'object' || body === null) {
|
|
80
|
+
logger.warn('fetchUpstreamModelIds: upstream response is not an object');
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const data = body.data;
|
|
84
|
+
if (!Array.isArray(data)) {
|
|
85
|
+
logger.warn('fetchUpstreamModelIds: upstream response has no data array');
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
return data
|
|
89
|
+
.map((entry) => {
|
|
90
|
+
if (typeof entry === 'object' && entry !== null) {
|
|
91
|
+
return entry.id;
|
|
92
|
+
}
|
|
93
|
+
return '';
|
|
94
|
+
})
|
|
95
|
+
.filter(Boolean);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
logger.warn({ err }, 'fetchUpstreamModelIds: failed to fetch upstream models');
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Return model IDs from `upstream` that are NOT present in `current`.
|
|
104
|
+
*
|
|
105
|
+
* Preserves upstream order and deduplicates the result.
|
|
106
|
+
*
|
|
107
|
+
* @param current — Currently configured model IDs
|
|
108
|
+
* @param upstream — Model IDs from the upstream API
|
|
109
|
+
* @returns Missing model IDs (ordered and deduplicated)
|
|
110
|
+
*/
|
|
111
|
+
export function getMissingModels(current, upstream) {
|
|
112
|
+
const currentSet = new Set(current);
|
|
113
|
+
const seen = new Set();
|
|
114
|
+
const result = [];
|
|
115
|
+
for (const id of upstream) {
|
|
116
|
+
if (!currentSet.has(id) && !seen.has(id)) {
|
|
117
|
+
seen.add(id);
|
|
118
|
+
result.push(id);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Safely pick only whitelisted fields from a models.dev metadata entry.
|
|
125
|
+
*/
|
|
126
|
+
function pickSafeFields(entry) {
|
|
127
|
+
const result = {};
|
|
128
|
+
for (const key of MODELS_DEV_SAFE_FIELDS) {
|
|
129
|
+
if (key in entry) {
|
|
130
|
+
result[key] = entry[key];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Build a minimal model stub for use in opencode.json.
|
|
137
|
+
*
|
|
138
|
+
* Priority (first match wins):
|
|
139
|
+
* 1. models.dev metadata (if provided and found for modelId)
|
|
140
|
+
* 2. OPENCODE_MODELS bundled constants
|
|
141
|
+
* 3. Heuristic title-case of the ID with default limits
|
|
142
|
+
*
|
|
143
|
+
* @param modelId — The upstream model ID (e.g. "glm-5", "kimi-k2.7-code")
|
|
144
|
+
* @param modelsDevMetadata — Optional map from models.dev (takes precedence)
|
|
145
|
+
* @returns A stub object with `id`, `name`, `tool_call`, and `reasoning`
|
|
146
|
+
*/
|
|
147
|
+
export function buildMinimalStub(modelId, modelsDevMetadata) {
|
|
148
|
+
// 1. models.dev metadata (highest priority)
|
|
149
|
+
if (modelsDevMetadata && modelId in modelsDevMetadata) {
|
|
150
|
+
const raw = modelsDevMetadata[modelId];
|
|
151
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
|
|
152
|
+
// Edge case: if the dev entry exists but has none of our safe fields,
|
|
153
|
+
// the result is a valid { id } stub — Tiers 2/3 are intentionally skipped
|
|
154
|
+
// because the model is "known" via dev metadata.
|
|
155
|
+
const safe = pickSafeFields(raw);
|
|
156
|
+
safe.id = modelId;
|
|
157
|
+
return safe;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// 2. models.json bundled definitions
|
|
161
|
+
const bundled = loadModelsFromJson();
|
|
162
|
+
const known = bundled[modelId];
|
|
163
|
+
if (known && typeof known === 'object' && !Array.isArray(known)) {
|
|
164
|
+
return { ...known, id: modelId };
|
|
165
|
+
}
|
|
166
|
+
// 3. Heuristic fallback with sensible defaults
|
|
167
|
+
return {
|
|
168
|
+
id: modelId,
|
|
169
|
+
name: toTitleCase(modelId),
|
|
170
|
+
limit: { context: 262144, output: 65536 },
|
|
171
|
+
tool_call: true,
|
|
172
|
+
reasoning: true,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Read the set of currently configured model IDs from opencode.json.
|
|
177
|
+
*
|
|
178
|
+
* Extracts the keys of `provider["saros-proxy"].models`.
|
|
179
|
+
*
|
|
180
|
+
* @param configPath — Path to opencode.json
|
|
181
|
+
* @returns Array of model ID strings, or empty array on any failure
|
|
182
|
+
*/
|
|
183
|
+
export function getModelsFromOpencodeConfig(configPath) {
|
|
184
|
+
try {
|
|
185
|
+
if (!existsSync(configPath))
|
|
186
|
+
return [];
|
|
187
|
+
const raw = readFileSync(configPath, 'utf-8');
|
|
188
|
+
const config = JSON.parse(raw);
|
|
189
|
+
const provider = config.provider;
|
|
190
|
+
if (!provider)
|
|
191
|
+
return [];
|
|
192
|
+
const sarosProvider = provider['saros-proxy'];
|
|
193
|
+
if (!sarosProvider || typeof sarosProvider !== 'object')
|
|
194
|
+
return [];
|
|
195
|
+
const models = sarosProvider.models;
|
|
196
|
+
if (!models || typeof models !== 'object')
|
|
197
|
+
return [];
|
|
198
|
+
return Object.keys(models);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Add missing model stubs to the saros-proxy.models map in opencode.json.
|
|
206
|
+
*
|
|
207
|
+
* 1. Reads the config file.
|
|
208
|
+
* 2. Ensures `saros-proxy` provider exists.
|
|
209
|
+
* 3. Creates a `.backup` copy.
|
|
210
|
+
* 4. Merges `buildMinimalStub(id)` for each missing ID.
|
|
211
|
+
* 5. Writes the updated JSON.
|
|
212
|
+
* 6. Validates the written file — restores from backup on failure.
|
|
213
|
+
*
|
|
214
|
+
* @param configPath — Path to opencode.json
|
|
215
|
+
* @param missingIds — Model IDs to add
|
|
216
|
+
* @param modelsDevMetadata — Optional models.dev metadata (passed to buildMinimalStub)
|
|
217
|
+
* @returns Result with success status and optional error message
|
|
218
|
+
*/
|
|
219
|
+
export function addMissingModelsToOpencodeConfig(configPath, missingIds, modelsDevMetadata) {
|
|
220
|
+
try {
|
|
221
|
+
if (!existsSync(configPath)) {
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: `opencode.json not found at ${configPath}`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const raw = readFileSync(configPath, 'utf-8');
|
|
228
|
+
let config;
|
|
229
|
+
try {
|
|
230
|
+
config = JSON.parse(raw);
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return {
|
|
234
|
+
success: false,
|
|
235
|
+
error: 'Existing opencode.json contains invalid JSON',
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
const provider = config.provider;
|
|
239
|
+
if (!provider) {
|
|
240
|
+
return { success: false, error: 'Config has no "provider" section' };
|
|
241
|
+
}
|
|
242
|
+
const sarosProvider = provider['saros-proxy'];
|
|
243
|
+
if (!sarosProvider || typeof sarosProvider !== 'object' || Array.isArray(sarosProvider)) {
|
|
244
|
+
return {
|
|
245
|
+
success: false,
|
|
246
|
+
error: 'saros-proxy provider config is missing or malformed',
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
// Ensure models field exists
|
|
250
|
+
const sarosObj = sarosProvider;
|
|
251
|
+
if (!sarosObj.models || typeof sarosObj.models !== 'object' || Array.isArray(sarosObj.models)) {
|
|
252
|
+
sarosObj.models = {};
|
|
253
|
+
}
|
|
254
|
+
const models = sarosObj.models;
|
|
255
|
+
let changed = false;
|
|
256
|
+
// Add missing models
|
|
257
|
+
if (missingIds.length > 0) {
|
|
258
|
+
for (const id of missingIds) {
|
|
259
|
+
models[id] = buildMinimalStub(id, modelsDevMetadata);
|
|
260
|
+
}
|
|
261
|
+
changed = true;
|
|
262
|
+
}
|
|
263
|
+
// Enrich existing models with models.dev metadata (add missing fields only)
|
|
264
|
+
if (modelsDevMetadata) {
|
|
265
|
+
for (const [id, model] of Object.entries(models)) {
|
|
266
|
+
const devEntry = modelsDevMetadata[id];
|
|
267
|
+
if (!devEntry || typeof devEntry !== 'object' || Array.isArray(devEntry))
|
|
268
|
+
continue;
|
|
269
|
+
const additions = pickSafeFields(devEntry);
|
|
270
|
+
const modelObj = model;
|
|
271
|
+
let enriched = false;
|
|
272
|
+
for (const key of Object.keys(additions)) {
|
|
273
|
+
if (!(key in modelObj)) {
|
|
274
|
+
modelObj[key] = additions[key];
|
|
275
|
+
enriched = true;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (enriched) {
|
|
279
|
+
changed = true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Nothing changed — skip backup and write
|
|
284
|
+
if (!changed) {
|
|
285
|
+
return { success: true, path: configPath, error: undefined, created: undefined };
|
|
286
|
+
}
|
|
287
|
+
// Backup before modifying (never overwrite existing backup)
|
|
288
|
+
const backupPath = configPath + '.backup';
|
|
289
|
+
if (!existsSync(backupPath)) {
|
|
290
|
+
copyFileSync(configPath, backupPath);
|
|
291
|
+
}
|
|
292
|
+
// Write updated config
|
|
293
|
+
const json = JSON.stringify(config, null, 2);
|
|
294
|
+
writeFileSync(configPath, json, 'utf-8');
|
|
295
|
+
// Validate: re-read and parse to ensure we didn't corrupt it
|
|
296
|
+
try {
|
|
297
|
+
const verifyRaw = readFileSync(configPath, 'utf-8');
|
|
298
|
+
JSON.parse(verifyRaw);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// Restore from backup
|
|
302
|
+
if (existsSync(backupPath)) {
|
|
303
|
+
copyFileSync(backupPath, configPath);
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
success: false,
|
|
307
|
+
path: configPath,
|
|
308
|
+
error: 'Failed to write valid JSON. Original file restored from backup.',
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return { success: true, path: configPath };
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
// Restore from backup if available
|
|
315
|
+
const backupPath = configPath + '.backup';
|
|
316
|
+
if (existsSync(backupPath)) {
|
|
317
|
+
try {
|
|
318
|
+
copyFileSync(backupPath, configPath);
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
// Ignore restore errors — the backup file itself may be gone
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
325
|
+
return { success: false, path: configPath, error: message };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Orchestrator: sync opencode.json models with upstream.
|
|
330
|
+
*
|
|
331
|
+
* Flow:
|
|
332
|
+
* fetchModelsDevMetadata()
|
|
333
|
+
* → fetchUpstreamModelIds(config)
|
|
334
|
+
* → getModelsFromOpencodeConfig(configPath)
|
|
335
|
+
* → getMissingModels(current, upstream)
|
|
336
|
+
* → addMissingModelsToOpencodeConfig(configPath, missing, devMetadata)
|
|
337
|
+
*
|
|
338
|
+
* @param config — Proxy configuration for upstream fetch
|
|
339
|
+
* @param configPath — Path to opencode.json (defaults to platform default)
|
|
340
|
+
* @returns Result with success status and optional error message
|
|
341
|
+
*/
|
|
342
|
+
export async function syncOpencodeModelsWithUpstream(config, configPath) {
|
|
343
|
+
const path = configPath ?? getDefaultOpencodeConfigPath();
|
|
344
|
+
// Best-effort fetch of models.dev metadata (never throws, null → undefined)
|
|
345
|
+
const devMetadata = (await fetchModelsDevMetadata()) ?? undefined;
|
|
346
|
+
const upstreamIds = await fetchUpstreamModelIds(config);
|
|
347
|
+
if (upstreamIds.length === 0) {
|
|
348
|
+
logger.warn('syncOpencodeModelsWithUpstream: no upstream models returned, skipping sync');
|
|
349
|
+
return { success: false, error: 'No upstream models returned' };
|
|
350
|
+
}
|
|
351
|
+
const currentIds = getModelsFromOpencodeConfig(path);
|
|
352
|
+
const missingIds = getMissingModels(currentIds, upstreamIds);
|
|
353
|
+
if (missingIds.length > 0) {
|
|
354
|
+
logger.info('syncOpencodeModelsWithUpstream: adding %d missing models to opencode.json', missingIds.length);
|
|
355
|
+
}
|
|
356
|
+
return addMissingModelsToOpencodeConfig(path, missingIds, devMetadata);
|
|
357
|
+
}
|
|
358
|
+
let _modelsDevCache = null;
|
|
359
|
+
/**
|
|
360
|
+
* Extract the opencode-go provider's models from a models.dev API response.
|
|
361
|
+
*
|
|
362
|
+
* Pure function — callers handle JSON parsing and error recovery.
|
|
363
|
+
*
|
|
364
|
+
* @param parsed — Parsed models.dev JSON object (result of `JSON.parse`)
|
|
365
|
+
* @returns Model ID → metadata map, or empty object if provider not found
|
|
366
|
+
*/
|
|
367
|
+
export function extractOpencodeGoModels(parsed) {
|
|
368
|
+
// The API returns a flat dictionary keyed by provider ID, e.g.:
|
|
369
|
+
// { "opencode-go": { id, env, npm, api, name, doc, models: { ... } }, ... }
|
|
370
|
+
const entry = parsed[MODELS_DEV_PROVIDER_ID];
|
|
371
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
372
|
+
return {};
|
|
373
|
+
const models = entry.models;
|
|
374
|
+
if (!models || typeof models !== 'object' || Array.isArray(models))
|
|
375
|
+
return {};
|
|
376
|
+
return models;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Fetch and cache models.dev metadata for opencode-go models.
|
|
380
|
+
*
|
|
381
|
+
* Caches in memory with TTL. Never throws — returns null on any failure.
|
|
382
|
+
* Skips in-flight deduplication (YAGNI — single orchestrator path).
|
|
383
|
+
*
|
|
384
|
+
* @returns Model ID → metadata map, null on failure, empty on missing provider
|
|
385
|
+
*/
|
|
386
|
+
export async function fetchModelsDevMetadata() {
|
|
387
|
+
// Return cached data if within TTL
|
|
388
|
+
if (_modelsDevCache !== null && Date.now() - _modelsDevCache.ts < MODELS_DEV_CACHE_TTL_MS) {
|
|
389
|
+
return _modelsDevCache.data;
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
const response = await fetch(MODELS_DEV_URL, {
|
|
393
|
+
signal: AbortSignal.timeout(MODELS_DEV_TIMEOUT_MS),
|
|
394
|
+
});
|
|
395
|
+
if (!response.ok)
|
|
396
|
+
return null;
|
|
397
|
+
const text = await response.text();
|
|
398
|
+
const parsed = JSON.parse(text);
|
|
399
|
+
const data = extractOpencodeGoModels(parsed);
|
|
400
|
+
_modelsDevCache = { ts: Date.now(), data };
|
|
401
|
+
return data;
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Clear the models.dev in-memory cache (test helper).
|
|
409
|
+
*/
|
|
410
|
+
export function resetModelsDevCacheState() {
|
|
411
|
+
_modelsDevCache = null;
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=models-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models-sync.js","sourceRoot":"","sources":["../src/models-sync.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;;;;;;;;;;GAYG;AACH,SAAS,WAAW,CAAC,EAAU;IAC7B,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAEtB,+DAA+D;QAC/D,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;YACD,SAAS;QACX,CAAC;QAED,0DAA0D;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;QAC5D,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE;YACxC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBAClB,mFAAmF;gBACnF,0EAA0E;gBAC1E,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;oBACvB,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;gBACzB,CAAC;gBACD,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAChD,CAAC;YACD,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAAmB;IAC7D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAE5C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;YACzE,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,IAAI,GAAI,IAAgC,CAAC,IAAI,CAAC;QACpD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;YAC1E,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,IAAI;aACR,GAAG,CAAC,CAAC,KAAc,EAAE,EAAE;YACtB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBAChD,OAAQ,KAAiC,CAAC,EAAY,CAAC;YACzD,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;aACD,MAAM,CAAC,OAAO,CAAa,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,wDAAwD,CAAC,CAAC;QAC/E,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAiB,EAAE,QAAkB;IACpE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,KAA8B;IACpD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,sBAAsB,EAAE,CAAC;QACzC,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;YACjB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,iBAA2D;IAE3D,4CAA4C;IAC5C,IAAI,iBAAiB,IAAI,OAAO,IAAI,iBAAiB,EAAE,CAAC;QACtD,MAAM,GAAG,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1D,sEAAsE;YACtE,0EAA0E;YAC1E,iDAAiD;YACjD,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,GAAI,KAAiC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;IAChE,CAAC;IAED,+CAA+C;IAC/C,OAAO;QACL,EAAE,EAAE,OAAO;QACX,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC;QAC1B,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE;QACzC,SAAS,EAAE,IAAI;QACf,SAAS,EAAE,IAAI;KAChB,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CAAC,UAAkB;IAC5D,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;QAEvC,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAE1D,MAAM,QAAQ,GAAG,MAAM,CAAC,QAA+C,CAAC;QACxE,IAAI,CAAC,QAAQ;YAAE,OAAO,EAAE,CAAC;QAEzB,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAwC,CAAC;QACrF,IAAI,CAAC,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QAEnE,MAAM,MAAM,GAAG,aAAa,CAAC,MAA6C,CAAC;QAC3E,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QAErD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gCAAgC,CAC9C,UAAkB,EAClB,UAAoB,EACpB,iBAA2D;IAE3D,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,8BAA8B,UAAU,EAAE;aAClD,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,IAAI,MAA+B,CAAC;QAEpC,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,8CAA8C;aACtD,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAA+C,CAAC;QACxE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC;QACvE,CAAC;QAED,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;QAC9C,IAAI,CAAC,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;YACxF,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,qDAAqD;aAC7D,CAAC;QACJ,CAAC;QAED,6BAA6B;QAC7B,MAAM,QAAQ,GAAG,aAAwC,CAAC;QAC1D,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,OAAO,QAAQ,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9F,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAC;QACvB,CAAC;QAED,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAiC,CAAC;QAE1D,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,qBAAqB;QACrB,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;gBAC5B,MAAM,CAAC,EAAE,CAAC,GAAG,gBAAgB,CAAC,EAAE,EAAE,iBAAiB,CAAC,CAAC;YACvD,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QAED,4EAA4E;QAC5E,IAAI,iBAAiB,EAAE,CAAC;YACtB,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACjD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;gBACvC,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;oBAAE,SAAS;gBAEnF,MAAM,SAAS,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;gBAC3C,MAAM,QAAQ,GAAG,KAAgC,CAAC;gBAClD,IAAI,QAAQ,GAAG,KAAK,CAAC;gBAErB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;oBACzC,IAAI,CAAC,CAAC,GAAG,IAAI,QAAQ,CAAC,EAAE,CAAC;wBACvB,QAAQ,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;wBAC/B,QAAQ,GAAG,IAAI,CAAC;oBAClB,CAAC;gBACH,CAAC;gBAED,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;gBACjB,CAAC;YACH,CAAC;QACH,CAAC;QAED,0CAA0C;QAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;QACnF,CAAC;QAED,4DAA4D;QAC5D,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;QAC1C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,YAAY,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACvC,CAAC;QAED,uBAAuB;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7C,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAEzC,6DAA6D;QAC7D,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACpD,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;YACtB,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3B,YAAY,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACvC,CAAC;YACD,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,iEAAiE;aACzE,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,mCAAmC;QACnC,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;QAC1C,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,YAAY,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,6DAA6D;YAC/D,CAAC;QACH,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC9D,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,MAAmB,EACnB,UAAmB;IAEnB,MAAM,IAAI,GAAG,UAAU,IAAI,4BAA4B,EAAE,CAAC;IAE1D,4EAA4E;IAC5E,MAAM,WAAW,GAAG,CAAC,MAAM,sBAAsB,EAAE,CAAC,IAAI,SAAS,CAAC;IAElE,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACxD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;QAC1F,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,UAAU,GAAG,2BAA2B,CAAC,IAAI,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,gBAAgB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAE7D,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CACT,2EAA2E,EAC3E,UAAU,CAAC,MAAM,CAClB,CAAC;IACJ,CAAC;IAED,OAAO,gCAAgC,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;AACzE,CAAC;AAQD,IAAI,eAAe,GAA0B,IAAI,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAA+B;IAE/B,gEAAgE;IAChE,4EAA4E;IAC5E,MAAM,KAAK,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE3E,MAAM,MAAM,GAAI,KAAiC,CAAC,MAAM,CAAC;IACzD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9E,OAAO,MAAiD,CAAC;AAC3D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB;IAC1C,mCAAmC;IACnC,IAAI,eAAe,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC,EAAE,GAAG,uBAAuB,EAAE,CAAC;QAC1F,OAAO,eAAe,CAAC,IAAI,CAAC;IAC9B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE;YAC3C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,qBAAqB,CAAC;SACnD,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAE9B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,MAAM,GAA4B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEzD,MAAM,IAAI,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAC7C,eAAe,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB;IACtC,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* probe-cache.ts — Persistent cache for model probe results.
|
|
3
|
+
*
|
|
4
|
+
* Stores probe results to ~/.config/saros/probe-cache.json with a 7-day TTL.
|
|
5
|
+
* Avoids re-probing models that have been successfully probed recently.
|
|
6
|
+
*/
|
|
7
|
+
export interface ProbeResult {
|
|
8
|
+
status: 'ok' | 'error' | 'rate_limited' | 'unsupported';
|
|
9
|
+
ts: number;
|
|
10
|
+
details?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ModelProbe {
|
|
13
|
+
modelId: string;
|
|
14
|
+
liveness: ProbeResult;
|
|
15
|
+
reasoning: ProbeResult;
|
|
16
|
+
toolCalling: ProbeResult;
|
|
17
|
+
}
|
|
18
|
+
export interface ProbeCacheFile {
|
|
19
|
+
version: 1;
|
|
20
|
+
probes: Record<string, ModelProbe>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Return the file path for the probe cache.
|
|
24
|
+
* Always uses `~/.config/saros/probe-cache.json` (matches daemon PID file convention).
|
|
25
|
+
*/
|
|
26
|
+
export declare function getProbeCachePath(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Load the probe cache from disk.
|
|
29
|
+
* Returns an empty cache if the file is missing or corrupt.
|
|
30
|
+
*
|
|
31
|
+
* @param cachePath — Path to the cache file (defaults to getProbeCachePath())
|
|
32
|
+
*/
|
|
33
|
+
export declare function loadProbeCache(cachePath?: string): ProbeCacheFile;
|
|
34
|
+
/**
|
|
35
|
+
* Write the probe cache to disk.
|
|
36
|
+
* Creates the parent directory if it does not exist.
|
|
37
|
+
*
|
|
38
|
+
* @param cache — Cache data to write
|
|
39
|
+
* @param cachePath — Path to the cache file (defaults to getProbeCachePath())
|
|
40
|
+
*/
|
|
41
|
+
export declare function saveProbeCache(cache: ProbeCacheFile, cachePath?: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Retrieve a cached probe result for the given model.
|
|
44
|
+
* Returns null if the model is not cached or the entry is older than TTL.
|
|
45
|
+
*
|
|
46
|
+
* @param modelId — Model ID to look up
|
|
47
|
+
* @param cachePath — Path to the cache file (defaults to getProbeCachePath())
|
|
48
|
+
*/
|
|
49
|
+
export declare function getCachedProbe(modelId: string, cachePath?: string): ModelProbe | null;
|
|
50
|
+
/**
|
|
51
|
+
* Store a probe result for the given model.
|
|
52
|
+
* Loads existing cache, merges, and saves back to disk.
|
|
53
|
+
*
|
|
54
|
+
* @param modelId — Model ID to store
|
|
55
|
+
* @param probe — Probe result to store
|
|
56
|
+
* @param cachePath — Path to the cache file (defaults to getProbeCachePath())
|
|
57
|
+
*/
|
|
58
|
+
export declare function setCachedProbe(modelId: string, probe: ModelProbe, cachePath?: string): void;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* probe-cache.ts — Persistent cache for model probe results.
|
|
3
|
+
*
|
|
4
|
+
* Stores probe results to ~/.config/saros/probe-cache.json with a 7-day TTL.
|
|
5
|
+
* Avoids re-probing models that have been successfully probed recently.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { join, dirname } from 'node:path';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { PROBE_CACHE_TTL_MS } from './constants.js';
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Path
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/**
|
|
15
|
+
* Return the file path for the probe cache.
|
|
16
|
+
* Always uses `~/.config/saros/probe-cache.json` (matches daemon PID file convention).
|
|
17
|
+
*/
|
|
18
|
+
export function getProbeCachePath() {
|
|
19
|
+
return join(homedir(), '.config', 'saros', 'probe-cache.json');
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Read / Write
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/**
|
|
25
|
+
* Load the probe cache from disk.
|
|
26
|
+
* Returns an empty cache if the file is missing or corrupt.
|
|
27
|
+
*
|
|
28
|
+
* @param cachePath — Path to the cache file (defaults to getProbeCachePath())
|
|
29
|
+
*/
|
|
30
|
+
export function loadProbeCache(cachePath = getProbeCachePath()) {
|
|
31
|
+
if (!existsSync(cachePath)) {
|
|
32
|
+
return { version: 1, probes: {} };
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const raw = readFileSync(cachePath, 'utf-8');
|
|
36
|
+
const parsed = JSON.parse(raw);
|
|
37
|
+
// Basic structure validation
|
|
38
|
+
if (parsed &&
|
|
39
|
+
typeof parsed === 'object' &&
|
|
40
|
+
parsed.version === 1 &&
|
|
41
|
+
parsed.probes &&
|
|
42
|
+
typeof parsed.probes === 'object' &&
|
|
43
|
+
!Array.isArray(parsed.probes)) {
|
|
44
|
+
return parsed;
|
|
45
|
+
}
|
|
46
|
+
return { version: 1, probes: {} };
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Corrupt JSON or read error — return fresh cache
|
|
50
|
+
return { version: 1, probes: {} };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Write the probe cache to disk.
|
|
55
|
+
* Creates the parent directory if it does not exist.
|
|
56
|
+
*
|
|
57
|
+
* @param cache — Cache data to write
|
|
58
|
+
* @param cachePath — Path to the cache file (defaults to getProbeCachePath())
|
|
59
|
+
*/
|
|
60
|
+
export function saveProbeCache(cache, cachePath = getProbeCachePath()) {
|
|
61
|
+
mkdirSync(dirname(cachePath), { recursive: true });
|
|
62
|
+
writeFileSync(cachePath, JSON.stringify(cache, null, 2), 'utf-8');
|
|
63
|
+
}
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Accessors
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
/**
|
|
68
|
+
* Retrieve a cached probe result for the given model.
|
|
69
|
+
* Returns null if the model is not cached or the entry is older than TTL.
|
|
70
|
+
*
|
|
71
|
+
* @param modelId — Model ID to look up
|
|
72
|
+
* @param cachePath — Path to the cache file (defaults to getProbeCachePath())
|
|
73
|
+
*/
|
|
74
|
+
export function getCachedProbe(modelId, cachePath = getProbeCachePath()) {
|
|
75
|
+
const cache = loadProbeCache(cachePath);
|
|
76
|
+
const probe = cache.probes[modelId];
|
|
77
|
+
if (!probe)
|
|
78
|
+
return null;
|
|
79
|
+
// Check TTL using the liveness timestamp
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
if (now - probe.liveness.ts >= PROBE_CACHE_TTL_MS) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return probe;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Store a probe result for the given model.
|
|
88
|
+
* Loads existing cache, merges, and saves back to disk.
|
|
89
|
+
*
|
|
90
|
+
* @param modelId — Model ID to store
|
|
91
|
+
* @param probe — Probe result to store
|
|
92
|
+
* @param cachePath — Path to the cache file (defaults to getProbeCachePath())
|
|
93
|
+
*/
|
|
94
|
+
export function setCachedProbe(modelId, probe, cachePath = getProbeCachePath()) {
|
|
95
|
+
const cache = loadProbeCache(cachePath);
|
|
96
|
+
cache.probes[modelId] = probe;
|
|
97
|
+
saveProbeCache(cache, cachePath);
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=probe-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe-cache.js","sourceRoot":"","sources":["../src/probe-cache.ts"],"names":[],"mappings":"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"}
|
package/dist/proxy.d.ts
CHANGED
|
@@ -32,14 +32,4 @@ export declare function buildDownstreamHeaders(upstreamHeaders: Headers): Header
|
|
|
32
32
|
* Check if a request body indicates streaming mode.
|
|
33
33
|
*/
|
|
34
34
|
export declare function isStreamingRequest(bodyText: string): boolean;
|
|
35
|
-
/**
|
|
36
|
-
* Builds the OpenAI-compatible /v1/models response.
|
|
37
|
-
* Returns the full list of OPENCODE_MODELS so the OpenCode client can
|
|
38
|
-
* discover all available models. Without this, OpenCode falls back to a
|
|
39
|
-
* hardcoded list of ~3 models (one per series).
|
|
40
|
-
*
|
|
41
|
-
* Exported for testing. The response is lightweight (~18 models) so it's
|
|
42
|
-
* computed fresh on each request with no caching.
|
|
43
|
-
*/
|
|
44
|
-
export declare function buildModelsListResponse(): Response;
|
|
45
35
|
export declare function createProxyApp(config: ProxyConfig): Hono;
|
package/dist/proxy.js
CHANGED
|
@@ -11,7 +11,8 @@ import crypto from 'node:crypto';
|
|
|
11
11
|
import { createProxyState, selectKeyForRequest, failoverRequest, completeRequest, markKeyFailed, markKeySucceeded, classifyHttpError, } from './proxy-logic.js';
|
|
12
12
|
import { logger, maskKey } from './logger.js';
|
|
13
13
|
import { getAllUsage, isScraperRunning } from './scraper.js';
|
|
14
|
-
import { MAX_BODY_SIZE, MAX_RETRIES, RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX,
|
|
14
|
+
import { MAX_BODY_SIZE, MAX_RETRIES, RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX, } from './constants.js';
|
|
15
|
+
import { getModelsList } from './models-fetcher.js';
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
// Helpers
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
@@ -429,36 +430,6 @@ function wrapStreamWithErrorDetection(upstreamStream, state, requestId, keyLabel
|
|
|
429
430
|
});
|
|
430
431
|
}
|
|
431
432
|
// ---------------------------------------------------------------------------
|
|
432
|
-
// Models discovery (OpenAI-compatible /v1/models response)
|
|
433
|
-
// ---------------------------------------------------------------------------
|
|
434
|
-
/**
|
|
435
|
-
* Builds the OpenAI-compatible /v1/models response.
|
|
436
|
-
* Returns the full list of OPENCODE_MODELS so the OpenCode client can
|
|
437
|
-
* discover all available models. Without this, OpenCode falls back to a
|
|
438
|
-
* hardcoded list of ~3 models (one per series).
|
|
439
|
-
*
|
|
440
|
-
* Exported for testing. The response is lightweight (~18 models) so it's
|
|
441
|
-
* computed fresh on each request with no caching.
|
|
442
|
-
*/
|
|
443
|
-
export function buildModelsListResponse() {
|
|
444
|
-
const now = Math.floor(Date.now() / 1000);
|
|
445
|
-
const data = Object.values(OPENCODE_MODELS).map((model) => {
|
|
446
|
-
const m = model;
|
|
447
|
-
return {
|
|
448
|
-
id: m.id,
|
|
449
|
-
object: 'model',
|
|
450
|
-
created: now,
|
|
451
|
-
owned_by: 'saros',
|
|
452
|
-
// Spread the rich model config (tool_call, limit, modalities, name, etc.)
|
|
453
|
-
...m,
|
|
454
|
-
};
|
|
455
|
-
});
|
|
456
|
-
return new Response(JSON.stringify({ object: 'list', data }), {
|
|
457
|
-
status: 200,
|
|
458
|
-
headers: { 'content-type': 'application/json' },
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
// ---------------------------------------------------------------------------
|
|
462
433
|
// App factory
|
|
463
434
|
// ---------------------------------------------------------------------------
|
|
464
435
|
export function createProxyApp(config) {
|
|
@@ -540,8 +511,8 @@ export function createProxyApp(config) {
|
|
|
540
511
|
// available models. Without this, OpenCode falls back to a hardcoded
|
|
541
512
|
// list of ~3 models (one per series).
|
|
542
513
|
// NOTE: Rate limiting applies to these routes via the '*' middleware above.
|
|
543
|
-
app.get('/v1/models', (_c) =>
|
|
544
|
-
app.get('/zen/go/v1/models', (_c) =>
|
|
514
|
+
app.get('/v1/models', (_c) => getModelsList(config));
|
|
515
|
+
app.get('/zen/go/v1/models', (_c) => getModelsList(config));
|
|
545
516
|
// --- Upstream proxy handler (reused by both routes) ---
|
|
546
517
|
async function handleProxyRequest(c, path) {
|
|
547
518
|
const requestId = generateRequestId();
|