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.
- package/dist/commands/logout.js +3 -3
- package/dist/commands/setup.js +89 -107
- package/dist/lib/codex-auth-seeding.d.ts +0 -2
- package/dist/lib/codex-auth-seeding.js +11 -17
- package/dist/lib/codex-model-cache-migration.js +174 -373
- package/dist/lib/supported-models.d.ts +0 -2
- package/dist/lib/supported-models.js +0 -14
- package/package.json +1 -2
- package/theclawbay-supported-models.json +46 -1
- package/dist/lib/codex-desktop-model-picker.d.ts +0 -7
- package/dist/lib/codex-desktop-model-picker.js +0 -452
- package/dist/lib/codex-model-catalog.d.ts +0 -5
- package/dist/lib/codex-model-catalog.js +0 -46
- package/theclawbay-codex-model-catalog.json +0 -644
|
@@ -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
|
|
19
|
-
const
|
|
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([...
|
|
28
|
-
const
|
|
29
|
-
const
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
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
|
|
177
|
-
if (!(SEED_MARKER_KEY in model))
|
|
178
|
-
return cloneJson(model);
|
|
166
|
+
function applySeedMarker(model) {
|
|
179
167
|
const next = cloneJson(model);
|
|
180
|
-
|
|
168
|
+
next[SEED_MARKER_KEY] = SEED_MARKER_VALUE;
|
|
181
169
|
return next;
|
|
182
170
|
}
|
|
183
|
-
function
|
|
184
|
-
return
|
|
171
|
+
function seedDescription(modelId) {
|
|
172
|
+
return SUPPORTED_MODEL_CONFIG.get(modelId)?.note ?? "The Claw Bay model.";
|
|
185
173
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
|
217
|
-
if (doc.fetched_at !==
|
|
218
|
-
doc.fetched_at =
|
|
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
|
-
|
|
360
|
+
return parsed;
|
|
534
361
|
}
|
|
362
|
+
return await inferCodexClientVersionFromStateDb(codexHome);
|
|
535
363
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
-
|
|
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
|
|
573
|
-
|
|
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:
|
|
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 =
|
|
588
|
-
const nextState =
|
|
589
|
-
|
|
590
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
421
|
+
let changed = false;
|
|
612
422
|
let seeded = false;
|
|
613
|
-
|
|
614
|
-
const
|
|
615
|
-
for (const
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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
|
-
|
|
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
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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
|
-
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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 = [...
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
735
|
-
if (fingerprintModel(normalized) !== trackedFingerprint) {
|
|
536
|
+
if (!hasSeedMarker(entry)) {
|
|
736
537
|
preserved += 1;
|
|
737
538
|
return true;
|
|
738
539
|
}
|