theclawbay 0.3.71 → 0.3.73

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.
@@ -10,23 +10,33 @@ const promises_1 = __importDefault(require("node:fs/promises"));
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
11
  const node_child_process_1 = require("node:child_process");
12
12
  const node_os_1 = __importDefault(require("node:os"));
13
- const codex_model_catalog_1 = require("./codex-model-catalog");
14
13
  const supported_models_1 = require("./supported-models");
15
14
  const MODELS_CACHE_FILE = "models_cache.json";
16
15
  const MODELS_CACHE_STATE_FILE = "theclawbay.models-cache.json";
17
16
  const STATE_DB_FILE_PATTERN = /^state_\d+\.sqlite$/;
18
- const CATALOG_MODELS = (0, codex_model_catalog_1.getHardcodedCodexModelCatalog)();
19
- const CATALOG_MODEL_IDS = (0, codex_model_catalog_1.getHardcodedCodexModelIds)();
20
- const LEGACY_REMOVED_MODEL_IDS = (0, supported_models_1.getLegacyRemovedModelIds)();
21
- const CATALOG_MODEL_ID_SET = new Set(CATALOG_MODEL_IDS);
22
- const TRACKED_MODEL_ID_SET = new Set([...CATALOG_MODEL_IDS, ...LEGACY_REMOVED_MODEL_IDS]);
23
- const LEGACY_REMOVED_MODEL_ID_SET = new Set(LEGACY_REMOVED_MODEL_IDS);
17
+ const TARGET_MODEL_IDS = (0, supported_models_1.getSupportedModelIds)();
18
+ const TARGET_MODEL_ID_SET = new Set(TARGET_MODEL_IDS);
24
19
  const SEED_MARKER_KEY = "_theclawbay_seeded";
25
20
  // Older releases stored the model id as the marker value; accept those so cleanup still works.
26
21
  const SEED_MARKER_VALUE = "theclawbay";
27
- const LEGACY_SEED_MARKER_VALUES = new Set([...TRACKED_MODEL_ID_SET, SEED_MARKER_VALUE, true]);
28
- const SEEDED_CACHE_FRESHNESS_ISO = "2099-12-31T23:59:59.000Z";
29
- const MINIMUM_CATALOG_CLIENT_VERSION = "0.124.0";
22
+ const LEGACY_SEED_MARKER_VALUES = new Set([...TARGET_MODEL_IDS, SEED_MARKER_VALUE, true]);
23
+ const SUPPORTED_MODELS = (0, supported_models_1.getSupportedModels)();
24
+ const SUPPORTED_MODEL_CONFIG = new Map(SUPPORTED_MODELS.map((model) => [model.id, model]));
25
+ const TEMPLATE_MODEL_IDS = TARGET_MODEL_IDS;
26
+ const TARGET_MODEL_LABEL_TEXT = "the current The Claw Bay model catalog";
27
+ const DEFAULT_REASONING_LEVELS = [
28
+ { effort: "low", description: "Fast responses with lighter reasoning" },
29
+ { effort: "medium", description: "Balances speed and reasoning depth for everyday tasks" },
30
+ { effort: "high", description: "Greater reasoning depth for complex problems" },
31
+ { effort: "xhigh", description: "Extra high reasoning depth for complex problems" },
32
+ ];
33
+ const FALLBACK_BASE_INSTRUCTIONS = `You are Codex, a coding agent based on GPT-5. You and the user share the same workspace and collaborate to achieve the user's goals.
34
+
35
+ ## General
36
+
37
+ - Prefer rg or rg --files for search when available.
38
+ - Make safe, minimal code changes that preserve user work.
39
+ - Explain what changed and any remaining risk clearly.`;
30
40
  function objectRecordOr(value) {
31
41
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
32
42
  return value;
@@ -65,7 +75,7 @@ function fingerprintModel(model) {
65
75
  async function readJsonIfExists(filePath) {
66
76
  try {
67
77
  const raw = await promises_1.default.readFile(filePath, "utf8");
68
- return JSON.parse(raw.replace(/^\uFEFF/, ""));
78
+ return JSON.parse(raw);
69
79
  }
70
80
  catch (error) {
71
81
  const err = error;
@@ -74,29 +84,6 @@ async function readJsonIfExists(filePath) {
74
84
  throw error;
75
85
  }
76
86
  }
77
- async function readModelsCacheIfExists(filePath) {
78
- try {
79
- const raw = await promises_1.default.readFile(filePath, "utf8");
80
- const hadBom = raw.charCodeAt(0) === 0xfeff;
81
- try {
82
- return { exists: true, parsed: JSON.parse(raw.replace(/^\uFEFF/, "")), hadBom };
83
- }
84
- catch (error) {
85
- return {
86
- exists: true,
87
- parsed: null,
88
- hadBom,
89
- parseError: error instanceof Error ? error.message : String(error),
90
- };
91
- }
92
- }
93
- catch (error) {
94
- const err = error;
95
- if (err.code === "ENOENT")
96
- return { exists: false, parsed: null, hadBom: false };
97
- throw error;
98
- }
99
- }
100
87
  async function removeFileIfExists(filePath) {
101
88
  try {
102
89
  await promises_1.default.unlink(filePath);
@@ -111,13 +98,13 @@ function normalizePatchFingerprintMap(state) {
111
98
  if (!state)
112
99
  return {};
113
100
  if (state.version === 1) {
114
- if (!TRACKED_MODEL_ID_SET.has(state.modelId))
101
+ if (!TARGET_MODEL_ID_SET.has(state.modelId))
115
102
  return {};
116
103
  return state.fingerprint ? { [state.modelId]: state.fingerprint } : {};
117
104
  }
118
105
  const output = {};
119
106
  for (const entry of state.models) {
120
- if (!TRACKED_MODEL_ID_SET.has(entry.modelId))
107
+ if (!TARGET_MODEL_ID_SET.has(entry.modelId))
121
108
  continue;
122
109
  if (entry.fingerprint)
123
110
  output[entry.modelId] = entry.fingerprint;
@@ -158,7 +145,7 @@ async function readPatchState(statePath) {
158
145
  }
159
146
  async function writePatchState(statePath, fingerprints) {
160
147
  await promises_1.default.mkdir(node_path_1.default.dirname(statePath), { recursive: true });
161
- const models = CATALOG_MODEL_IDS.flatMap((modelId) => {
148
+ const models = TARGET_MODEL_IDS.flatMap((modelId) => {
162
149
  const fingerprint = fingerprints[modelId];
163
150
  if (!fingerprint)
164
151
  return [];
@@ -170,52 +157,91 @@ async function writePatchState(statePath, fingerprints) {
170
157
  }, null, 2);
171
158
  await promises_1.default.writeFile(statePath, `${contents}\n`, "utf8");
172
159
  }
160
+ function findModel(models, slug) {
161
+ return models.find((entry) => entry.slug === slug) ?? null;
162
+ }
173
163
  function hasSeedMarker(model) {
174
164
  return LEGACY_SEED_MARKER_VALUES.has(model[SEED_MARKER_KEY]);
175
165
  }
176
- function stripSeedMarker(model) {
177
- if (!(SEED_MARKER_KEY in model))
178
- return cloneJson(model);
166
+ function applySeedMarker(model) {
179
167
  const next = cloneJson(model);
180
- delete next[SEED_MARKER_KEY];
168
+ next[SEED_MARKER_KEY] = SEED_MARKER_VALUE;
181
169
  return next;
182
170
  }
183
- function buildCatalogModels() {
184
- return cloneJson(CATALOG_MODELS).map((model) => stripSeedMarker(model));
171
+ function seedDescription(modelId) {
172
+ return SUPPORTED_MODEL_CONFIG.get(modelId)?.note ?? "The Claw Bay model.";
185
173
  }
186
- function buildCatalogModelMap() {
187
- return new Map(buildCatalogModels().flatMap((model) => {
188
- const slug = typeof model.slug === "string" ? model.slug : "";
189
- return slug ? [[slug, model]] : [];
190
- }));
191
- }
192
- function sameFingerprintMaps(left, right) {
193
- const leftKeys = Object.keys(left).sort();
194
- const rightKeys = Object.keys(right).sort();
195
- if (leftKeys.length !== rightKeys.length)
196
- return false;
197
- return leftKeys.every((key, index) => key === rightKeys[index] && left[key] === right[key]);
198
- }
199
- function hasCatalogModels(models) {
200
- return models.some((model) => {
201
- const slug = typeof model.slug === "string" ? model.slug : "";
202
- return Boolean(slug) && CATALOG_MODEL_ID_SET.has(slug);
203
- });
174
+ function normalizeSeedModel(template, modelId) {
175
+ const modelConfig = SUPPORTED_MODEL_CONFIG.get(modelId);
176
+ const seed = applySeedMarker(template ?? {});
177
+ seed.slug = modelId;
178
+ seed.display_name = modelConfig?.label ?? modelId;
179
+ seed.description = seedDescription(modelId);
180
+ if (typeof seed.default_reasoning_level !== "string") {
181
+ seed.default_reasoning_level = modelId === "gpt-5.5" ? "xhigh" : "medium";
182
+ }
183
+ if (!Array.isArray(seed.supported_reasoning_levels) || seed.supported_reasoning_levels.length === 0) {
184
+ seed.supported_reasoning_levels = cloneJson(DEFAULT_REASONING_LEVELS);
185
+ }
186
+ if (typeof seed.shell_type !== "string") {
187
+ seed.shell_type = "shell_command";
188
+ }
189
+ seed.visibility = "list";
190
+ seed.supported_in_api = true;
191
+ seed.priority = 0;
192
+ seed.upgrade = null;
193
+ if (typeof seed.base_instructions !== "string" || !seed.base_instructions.trim()) {
194
+ seed.base_instructions = FALLBACK_BASE_INSTRUCTIONS;
195
+ }
196
+ if (typeof seed.supports_reasoning_summaries !== "boolean") {
197
+ seed.supports_reasoning_summaries = true;
198
+ }
199
+ if (typeof seed.support_verbosity !== "boolean") {
200
+ seed.support_verbosity = true;
201
+ }
202
+ if (seed.default_verbosity === undefined) {
203
+ seed.default_verbosity = "low";
204
+ }
205
+ if (seed.apply_patch_tool_type === undefined) {
206
+ seed.apply_patch_tool_type = "freeform";
207
+ }
208
+ const truncationPolicy = objectRecordOr(seed.truncation_policy);
209
+ if (!truncationPolicy || typeof truncationPolicy.mode !== "string" || typeof truncationPolicy.limit !== "number") {
210
+ seed.truncation_policy = { mode: "tokens", limit: 10000 };
211
+ }
212
+ if (typeof seed.supports_parallel_tool_calls !== "boolean") {
213
+ seed.supports_parallel_tool_calls = true;
214
+ }
215
+ if (typeof seed.context_window !== "number") {
216
+ seed.context_window = 272000;
217
+ }
218
+ if (modelId === "gpt-5.5" && typeof seed.minimal_client_version !== "string") {
219
+ seed.minimal_client_version = "0.124.0";
220
+ }
221
+ if (typeof seed.effective_context_window_percent !== "number") {
222
+ seed.effective_context_window_percent = 95;
223
+ }
224
+ if (!Array.isArray(seed.input_modalities) || seed.input_modalities.length === 0) {
225
+ seed.input_modalities = ["text", "image"];
226
+ }
227
+ if (typeof seed.prefer_websockets !== "boolean") {
228
+ seed.prefer_websockets = true;
229
+ }
230
+ return seed;
204
231
  }
205
- function nextFetchedAtIsoForDocument(doc) {
206
- if (!hasCatalogModels(doc.models)) {
207
- return new Date().toISOString();
208
- }
209
- // Current Codex builds only consult models_cache.json for custom-provider
210
- // catalogs in API-key mode. Pin the cache freshness far into the future so
211
- // setup can refresh the catalog on demand without the five-minute TTL.
212
- return SEEDED_CACHE_FRESHNESS_ISO;
232
+ function buildSeedModel(models, modelId) {
233
+ for (const candidate of TEMPLATE_MODEL_IDS) {
234
+ const template = findModel(models, candidate);
235
+ if (template)
236
+ return normalizeSeedModel(template, modelId);
237
+ }
238
+ return normalizeSeedModel(null, modelId);
213
239
  }
214
240
  function setCacheFreshnessMetadata(doc, clientVersion) {
215
241
  let changed = false;
216
- const fetchedAt = nextFetchedAtIsoForDocument(doc);
217
- if (doc.fetched_at !== fetchedAt) {
218
- doc.fetched_at = fetchedAt;
242
+ const now = new Date().toISOString();
243
+ if (doc.fetched_at !== now) {
244
+ doc.fetched_at = now;
219
245
  changed = true;
220
246
  }
221
247
  if (clientVersion && doc.client_version !== clientVersion) {
@@ -237,204 +263,6 @@ function runVersionCommand(command, args) {
237
263
  return null;
238
264
  return parseCodexVersion(`${run.stdout ?? ""}\n${run.stderr ?? ""}`);
239
265
  }
240
- function compareCodexVersions(left, right) {
241
- const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
242
- const rightParts = right.split(".").map((part) => Number.parseInt(part, 10));
243
- const maxLen = Math.max(leftParts.length, rightParts.length);
244
- for (let index = 0; index < maxLen; index += 1) {
245
- const leftPart = leftParts[index] ?? 0;
246
- const rightPart = rightParts[index] ?? 0;
247
- if (leftPart !== rightPart)
248
- return leftPart - rightPart;
249
- }
250
- return 0;
251
- }
252
- function highestCodexVersion(candidates) {
253
- const versions = candidates
254
- .map((candidate) => (candidate ? parseCodexVersion(candidate) : null))
255
- .filter((candidate) => Boolean(candidate));
256
- if (versions.length === 0)
257
- return null;
258
- versions.sort((left, right) => compareCodexVersions(right, left));
259
- return versions[0] ?? null;
260
- }
261
- function uniqueStrings(values) {
262
- return Array.from(new Set(values.filter(Boolean)));
263
- }
264
- function isWslInteropRuntime() {
265
- return node_os_1.default.platform() === "linux" && Boolean(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);
266
- }
267
- function readWindowsCommandStdout(command) {
268
- const shell = node_os_1.default.platform() === "win32" ? "powershell.exe" : isWslInteropRuntime() ? "powershell.exe" : null;
269
- if (!shell)
270
- return null;
271
- const result = (0, node_child_process_1.spawnSync)(shell, ["-NoProfile", "-Command", command], {
272
- encoding: "utf8",
273
- stdio: ["ignore", "pipe", "ignore"],
274
- });
275
- if (result.status !== 0)
276
- return null;
277
- const next = result.stdout.replace(/\r/g, "").trim();
278
- return next ? next : null;
279
- }
280
- function resolveWindowsPathForHost(input) {
281
- if (!input)
282
- return null;
283
- const trimmed = input.trim();
284
- if (!trimmed)
285
- return null;
286
- if (node_os_1.default.platform() === "win32")
287
- return trimmed;
288
- const match = /^([A-Za-z]):\\(.*)$/.exec(trimmed);
289
- if (!match)
290
- return null;
291
- const drive = match[1].toLowerCase();
292
- const rest = match[2].replace(/\\/g, "/");
293
- return node_path_1.default.posix.join("/mnt", drive, rest);
294
- }
295
- function resolveWindowsHomeDirForHost() {
296
- const reported = readWindowsCommandStdout("$env:USERPROFILE");
297
- if (reported)
298
- return resolveWindowsPathForHost(reported);
299
- if (node_os_1.default.platform() === "win32")
300
- return node_os_1.default.homedir();
301
- return null;
302
- }
303
- function editorExtensionDirCandidates() {
304
- const home = node_os_1.default.homedir();
305
- const dirs = [];
306
- if (node_os_1.default.platform() === "darwin") {
307
- dirs.push(node_path_1.default.join(home, ".vscode", "extensions"), node_path_1.default.join(home, ".vscode-insiders", "extensions"), node_path_1.default.join(home, ".cursor", "extensions"), node_path_1.default.join(home, ".windsurf", "extensions"), node_path_1.default.join(home, ".vscode-oss", "extensions"));
308
- return uniqueStrings(dirs);
309
- }
310
- if (node_os_1.default.platform() === "win32") {
311
- const homeDir = process.env.USERPROFILE?.trim() || home;
312
- dirs.push(node_path_1.default.join(homeDir, ".vscode", "extensions"), node_path_1.default.join(homeDir, ".vscode-insiders", "extensions"), node_path_1.default.join(homeDir, ".cursor", "extensions"), node_path_1.default.join(homeDir, ".windsurf", "extensions"), node_path_1.default.join(homeDir, ".vscode-oss", "extensions"));
313
- return uniqueStrings(dirs);
314
- }
315
- dirs.push(node_path_1.default.join(home, ".vscode", "extensions"), node_path_1.default.join(home, ".vscode-insiders", "extensions"), node_path_1.default.join(home, ".cursor", "extensions"), node_path_1.default.join(home, ".windsurf", "extensions"), node_path_1.default.join(home, ".vscode-oss", "extensions"));
316
- if (isWslInteropRuntime()) {
317
- const windowsHome = resolveWindowsHomeDirForHost();
318
- if (windowsHome) {
319
- dirs.push(node_path_1.default.join(windowsHome, ".vscode", "extensions"), node_path_1.default.join(windowsHome, ".vscode-insiders", "extensions"), node_path_1.default.join(windowsHome, ".cursor", "extensions"), node_path_1.default.join(windowsHome, ".windsurf", "extensions"), node_path_1.default.join(windowsHome, ".vscode-oss", "extensions"));
320
- }
321
- }
322
- return uniqueStrings(dirs);
323
- }
324
- async function pathExists(filePath) {
325
- try {
326
- await promises_1.default.access(filePath);
327
- return true;
328
- }
329
- catch {
330
- return false;
331
- }
332
- }
333
- function codexBinaryCandidatesForBinRoot(binRoot) {
334
- const runtimePreferred = node_os_1.default.platform() === "win32"
335
- ? [
336
- node_path_1.default.join(binRoot, "windows-x86_64", "codex.exe"),
337
- node_path_1.default.join(binRoot, "windows-arm64", "codex.exe"),
338
- ]
339
- : node_os_1.default.platform() === "darwin"
340
- ? [
341
- node_path_1.default.join(binRoot, "darwin-aarch64", "codex"),
342
- node_path_1.default.join(binRoot, "darwin-arm64", "codex"),
343
- node_path_1.default.join(binRoot, "darwin-x86_64", "codex"),
344
- ]
345
- : [
346
- node_path_1.default.join(binRoot, "linux-x86_64", "codex"),
347
- node_path_1.default.join(binRoot, "linux-arm64", "codex"),
348
- ];
349
- return uniqueStrings([
350
- ...runtimePreferred,
351
- node_path_1.default.join(binRoot, "codex"),
352
- node_path_1.default.join(binRoot, "codex.exe"),
353
- ]);
354
- }
355
- async function inferCodexClientVersionsFromInstalledExtensions() {
356
- const versions = new Set();
357
- for (const extensionDir of editorExtensionDirCandidates()) {
358
- let entries = [];
359
- try {
360
- entries = await promises_1.default.readdir(extensionDir, { withFileTypes: true });
361
- }
362
- catch (error) {
363
- const err = error;
364
- if (err.code === "ENOENT")
365
- continue;
366
- throw error;
367
- }
368
- for (const entry of entries) {
369
- if (!entry.isDirectory())
370
- continue;
371
- if (!entry.name.toLowerCase().startsWith("openai.chatgpt-"))
372
- continue;
373
- const binRoot = node_path_1.default.join(extensionDir, entry.name, "bin");
374
- for (const candidate of codexBinaryCandidatesForBinRoot(binRoot)) {
375
- if (!(await pathExists(candidate)))
376
- continue;
377
- const parsed = runVersionCommand(candidate, ["--version"]);
378
- if (parsed)
379
- versions.add(parsed);
380
- }
381
- }
382
- }
383
- return [...versions];
384
- }
385
- function desktopAppCodexBinaryCandidates() {
386
- const candidates = [];
387
- const home = node_os_1.default.homedir();
388
- if (node_os_1.default.platform() === "darwin") {
389
- for (const appRoot of [
390
- "/Applications/Codex.app",
391
- "/Applications/OpenAI Codex.app",
392
- node_path_1.default.join(home, "Applications", "Codex.app"),
393
- node_path_1.default.join(home, "Applications", "OpenAI Codex.app"),
394
- ]) {
395
- candidates.push(node_path_1.default.join(appRoot, "Contents", "Resources", "codex"), node_path_1.default.join(appRoot, "Contents", "Resources", "app", "resources", "codex"));
396
- }
397
- }
398
- if (node_os_1.default.platform() === "win32" || isWslInteropRuntime()) {
399
- const installLocations = readWindowsCommandStdout("Get-AppxPackage OpenAI.Codex | ForEach-Object { $_.InstallLocation }");
400
- if (installLocations) {
401
- for (const installLocation of installLocations.split("\n")) {
402
- const hostPath = resolveWindowsPathForHost(installLocation);
403
- if (!hostPath)
404
- continue;
405
- candidates.push(node_path_1.default.join(hostPath, "app", "resources", "codex.exe"));
406
- }
407
- }
408
- }
409
- return uniqueStrings(candidates);
410
- }
411
- async function inferCodexClientVersionsFromInstalledDesktopApps() {
412
- const versions = new Set();
413
- for (const candidate of desktopAppCodexBinaryCandidates()) {
414
- if (!(await pathExists(candidate)))
415
- continue;
416
- const parsed = runVersionCommand(candidate, ["--version"]);
417
- versions.add(parsed ?? MINIMUM_CATALOG_CLIENT_VERSION);
418
- }
419
- return [...versions];
420
- }
421
- function preferredPickerCacheClientVersion(pickerVersions) {
422
- const compatible = pickerVersions.some((version) => compareCodexVersions(version, MINIMUM_CATALOG_CLIENT_VERSION) >= 0);
423
- if (!compatible)
424
- return null;
425
- // The shared models_cache.json is accepted by newer picker builds when stamped
426
- // with the oldest model-catalog version, but older app builds reject a cache
427
- // stamped with a newer VS Code/CLI version.
428
- return MINIMUM_CATALOG_CLIENT_VERSION;
429
- }
430
- async function inferCodexClientVersionFromVersionJson(codexHome) {
431
- const parsed = await readJsonIfExists(node_path_1.default.join(codexHome, "version.json"));
432
- const obj = objectRecordOr(parsed);
433
- if (!obj)
434
- return null;
435
- const latestVersion = typeof obj.latest_version === "string" ? obj.latest_version : "";
436
- return latestVersion ? parseCodexVersion(latestVersion) : null;
437
- }
438
266
  function resolvePythonCommand() {
439
267
  const candidates = [
440
268
  { command: "python3", args: ["-c", "import sqlite3"] },
@@ -525,35 +353,20 @@ async function inferCodexClientVersionFromStateDb(codexHome) {
525
353
  return null;
526
354
  }
527
355
  async function inferCodexClientVersion(codexHome) {
528
- const versions = [];
529
356
  if (node_os_1.default.platform() === "win32") {
530
357
  for (const commandLine of ["codex --version", "codex-cli --version", "codex.cmd --version", "codex-cli.cmd --version"]) {
531
358
  const parsed = runVersionCommand("cmd.exe", ["/d", "/s", "/c", commandLine]);
532
359
  if (parsed)
533
- versions.push(parsed);
360
+ return parsed;
534
361
  }
362
+ return await inferCodexClientVersionFromStateDb(codexHome);
535
363
  }
536
- else {
537
- for (const command of ["codex", "codex-cli"]) {
538
- const parsed = runVersionCommand(command, ["--version"]);
539
- if (parsed)
540
- versions.push(parsed);
541
- }
364
+ for (const command of ["codex", "codex-cli"]) {
365
+ const parsed = runVersionCommand(command, ["--version"]);
366
+ if (parsed)
367
+ return parsed;
542
368
  }
543
- const pickerVersions = [
544
- ...await inferCodexClientVersionsFromInstalledDesktopApps(),
545
- ...await inferCodexClientVersionsFromInstalledExtensions(),
546
- ];
547
- const pickerCacheClientVersion = preferredPickerCacheClientVersion(pickerVersions);
548
- if (pickerCacheClientVersion)
549
- return pickerCacheClientVersion;
550
- const versionJsonVersion = await inferCodexClientVersionFromVersionJson(codexHome);
551
- if (versionJsonVersion)
552
- versions.push(versionJsonVersion);
553
- const stateDbVersion = await inferCodexClientVersionFromStateDb(codexHome);
554
- if (stateDbVersion)
555
- versions.push(stateDbVersion);
556
- return highestCodexVersion(versions);
369
+ return await inferCodexClientVersionFromStateDb(codexHome);
557
370
  }
558
371
  function normalizeCacheDocument(value) {
559
372
  const doc = objectRecordOr(value);
@@ -569,126 +382,122 @@ async function ensureCodexModelCacheHasGpt54(params) {
569
382
  const statePath = node_path_1.default.join(params.codexHome, MODELS_CACHE_STATE_FILE);
570
383
  try {
571
384
  const existingState = await readPatchState(statePath);
572
- const cacheRead = await readModelsCacheIfExists(cachePath);
573
- const parsed = cacheRead.parsed;
574
- const catalogModels = buildCatalogModels();
575
- const catalogModelMap = buildCatalogModelMap();
576
- if (!cacheRead.exists || parsed === null) {
385
+ const parsed = await readJsonIfExists(cachePath);
386
+ if (parsed === null) {
577
387
  const clientVersion = await inferCodexClientVersion(params.codexHome);
578
388
  if (!clientVersion) {
579
389
  await removeFileIfExists(statePath);
580
390
  return {
581
391
  action: "skipped",
582
- warning: cacheRead.exists
583
- ? "Codex models cache is not valid JSON, and Codex version could not be inferred for a safe The Claw Bay model catalog repair."
584
- : "Codex models cache was not found, and Codex version could not be inferred for a safe The Claw Bay model catalog refresh.",
392
+ warning: `Codex models cache was not found, and Codex version could not be inferred for a safe ${TARGET_MODEL_LABEL_TEXT} seed.`,
585
393
  };
586
394
  }
587
- const docModels = cloneJson(catalogModels);
588
- const nextState = Object.fromEntries(docModels.flatMap((model) => {
589
- const slug = typeof model.slug === "string" ? model.slug : "";
590
- return slug ? [[slug, fingerprintModel(model)]] : [];
591
- }));
395
+ const docModels = [];
396
+ const nextState = {};
397
+ for (const modelId of TARGET_MODEL_IDS) {
398
+ const seed = buildSeedModel(docModels, modelId);
399
+ docModels.push(seed);
400
+ nextState[modelId] = fingerprintModel(seed);
401
+ }
592
402
  const doc = {
593
- fetched_at: nextFetchedAtIsoForDocument({ models: docModels }),
403
+ fetched_at: new Date().toISOString(),
594
404
  client_version: clientVersion,
595
405
  models: docModels,
596
406
  };
597
407
  await promises_1.default.mkdir(params.codexHome, { recursive: true });
598
408
  await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
599
409
  await writePatchState(statePath, nextState);
600
- return { action: cacheRead.exists ? "refreshed" : "created" };
410
+ return { action: "created" };
601
411
  }
602
412
  const doc = normalizeCacheDocument(parsed);
603
413
  if (!doc) {
604
414
  return {
605
415
  action: "skipped",
606
- warning: "Codex models cache exists but is not valid JSON in the expected format; skipped The Claw Bay model catalog refresh.",
416
+ warning: `Codex models cache exists but is not valid JSON in the expected format; skipped ${TARGET_MODEL_LABEL_TEXT} seed.`,
607
417
  };
608
418
  }
609
419
  const clientVersion = (await inferCodexClientVersion(params.codexHome)) ??
610
420
  (typeof doc.client_version === "string" && doc.client_version ? doc.client_version : null);
611
- let changed = cacheRead.hadBom;
421
+ let changed = false;
612
422
  let seeded = false;
613
- const currentCatalogEntries = new Map();
614
- const rest = [];
615
- for (const entry of doc.models) {
616
- const slug = typeof entry.slug === "string" ? entry.slug : "";
617
- if (!slug) {
618
- rest.push(entry);
619
- continue;
620
- }
621
- if (LEGACY_REMOVED_MODEL_ID_SET.has(slug) && hasSeedMarker(entry)) {
622
- changed = true;
623
- continue;
624
- }
625
- if (CATALOG_MODEL_ID_SET.has(slug)) {
626
- if (!currentCatalogEntries.has(slug)) {
627
- currentCatalogEntries.set(slug, entry);
423
+ let stateChanged = false;
424
+ const nextState = { ...existingState };
425
+ for (const modelId of TARGET_MODEL_IDS) {
426
+ const existingModel = findModel(doc.models, modelId);
427
+ const trackedFingerprint = existingState[modelId];
428
+ if (existingModel) {
429
+ const currentFingerprint = fingerprintModel(existingModel);
430
+ if (hasSeedMarker(existingModel)) {
431
+ if (!trackedFingerprint || trackedFingerprint !== currentFingerprint) {
432
+ nextState[modelId] = currentFingerprint;
433
+ stateChanged = true;
434
+ }
435
+ continue;
628
436
  }
629
- else {
437
+ if (trackedFingerprint && trackedFingerprint === currentFingerprint) {
438
+ const seededModel = applySeedMarker(existingModel);
439
+ doc.models = doc.models.map((entry) => (entry.slug === modelId ? seededModel : entry));
440
+ nextState[modelId] = fingerprintModel(seededModel);
441
+ seeded = true;
630
442
  changed = true;
443
+ stateChanged = true;
444
+ continue;
445
+ }
446
+ if (trackedFingerprint && trackedFingerprint !== currentFingerprint) {
447
+ delete nextState[modelId];
448
+ stateChanged = true;
631
449
  }
632
450
  continue;
633
451
  }
634
- const normalized = stripSeedMarker(entry);
635
- if (fingerprintModel(normalized) !== fingerprintModel(entry)) {
636
- changed = true;
637
- }
638
- rest.push(normalized);
452
+ const seed = buildSeedModel(doc.models, modelId);
453
+ doc.models = [seed, ...doc.models];
454
+ nextState[modelId] = fingerprintModel(seed);
455
+ seeded = true;
456
+ changed = true;
457
+ stateChanged = true;
639
458
  }
640
- const orderedCatalog = [];
641
- const nextState = {};
642
- for (const modelId of CATALOG_MODEL_IDS) {
643
- const desiredModel = catalogModelMap.get(modelId);
644
- if (!desiredModel)
645
- continue;
646
- const desiredFingerprint = fingerprintModel(desiredModel);
647
- const trackedFingerprint = existingState[modelId];
648
- const existingModel = currentCatalogEntries.get(modelId);
649
- if (!existingModel) {
650
- orderedCatalog.push(cloneJson(desiredModel));
651
- nextState[modelId] = desiredFingerprint;
652
- changed = true;
653
- seeded = true;
654
- continue;
655
- }
656
- const normalizedExisting = stripSeedMarker(existingModel);
657
- const existingFingerprint = fingerprintModel(normalizedExisting);
658
- if (existingFingerprint === desiredFingerprint && !hasSeedMarker(existingModel)) {
659
- orderedCatalog.push(normalizedExisting);
660
- if (trackedFingerprint === desiredFingerprint) {
661
- nextState[modelId] = trackedFingerprint;
662
- }
459
+ // Keep The Claw Bay seeds grouped at the top for easy discovery in the picker.
460
+ const bySlug = new Map();
461
+ const rest = [];
462
+ for (const entry of doc.models) {
463
+ const slug = typeof entry.slug === "string" ? entry.slug : "";
464
+ if (slug && TARGET_MODEL_ID_SET.has(slug) && !bySlug.has(slug)) {
465
+ bySlug.set(slug, entry);
663
466
  continue;
664
467
  }
665
- orderedCatalog.push(cloneJson(desiredModel));
666
- nextState[modelId] = desiredFingerprint;
667
- changed = true;
668
- seeded = true;
468
+ rest.push(entry);
469
+ }
470
+ const orderedTargets = [];
471
+ for (const id of TARGET_MODEL_IDS) {
472
+ const entry = bySlug.get(id);
473
+ if (entry)
474
+ orderedTargets.push(entry);
669
475
  }
670
- doc.models = [...orderedCatalog, ...rest];
476
+ doc.models = [...orderedTargets, ...rest];
671
477
  if (setCacheFreshnessMetadata(doc, clientVersion)) {
672
478
  changed = true;
673
479
  }
674
- const stateChanged = !sameFingerprintMaps(existingState, nextState);
675
480
  if (changed) {
676
481
  await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
677
482
  }
678
- const hasAnyTracked = CATALOG_MODEL_IDS.some((id) => Boolean(nextState[id]));
483
+ const hasAnyTracked = TARGET_MODEL_IDS.some((id) => Boolean(nextState[id]));
679
484
  if (!hasAnyTracked) {
680
485
  if (Object.keys(existingState).length > 0) {
681
486
  await removeFileIfExists(statePath);
682
487
  }
683
488
  }
684
- else if (stateChanged) {
489
+ else if (stateChanged || seeded) {
685
490
  await writePatchState(statePath, nextState);
686
491
  }
687
492
  if (seeded)
688
493
  return { action: "seeded" };
689
494
  if (changed || stateChanged)
690
495
  return { action: "refreshed" };
691
- return { action: hasAnyTracked ? "already_seeded" : "already_present" };
496
+ const allSeeded = TARGET_MODEL_IDS.every((id) => {
497
+ const model = findModel(doc.models, id);
498
+ return Boolean(model && hasSeedMarker(model));
499
+ });
500
+ return { action: allSeeded ? "already_seeded" : "already_present" };
692
501
  }
693
502
  catch (error) {
694
503
  const message = error instanceof Error ? error.message : String(error);
@@ -715,24 +524,16 @@ async function cleanupSeededCodexModelCache(params) {
715
524
  if (!doc) {
716
525
  return {
717
526
  action: "none",
718
- warning: "Codex models cache exists but is not valid JSON in the expected format; left The Claw Bay model cache patch state untouched.",
527
+ warning: `Codex models cache exists but is not valid JSON in the expected format; left ${TARGET_MODEL_LABEL_TEXT} cache patch state untouched.`,
719
528
  };
720
529
  }
721
530
  let removed = 0;
722
531
  let preserved = 0;
723
532
  doc.models = doc.models.filter((entry) => {
724
533
  const slug = typeof entry.slug === "string" ? entry.slug : "";
725
- if (!slug)
726
- return true;
727
- if (LEGACY_REMOVED_MODEL_ID_SET.has(slug) && hasSeedMarker(entry)) {
728
- removed += 1;
729
- return false;
730
- }
731
- const trackedFingerprint = state[slug];
732
- if (!trackedFingerprint)
534
+ if (!slug || !TARGET_MODEL_ID_SET.has(slug))
733
535
  return true;
734
- const normalized = stripSeedMarker(entry);
735
- if (fingerprintModel(normalized) !== trackedFingerprint) {
536
+ if (!hasSeedMarker(entry)) {
736
537
  preserved += 1;
737
538
  return true;
738
539
  }