theclawbay 0.3.30 → 0.3.32
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
CHANGED
|
@@ -19,13 +19,6 @@ theclawbay setup --api-key <apiKey>
|
|
|
19
19
|
In an interactive terminal, setup shows one picker for Codex, Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Aider, and Windows Trae.
|
|
20
20
|
Each row includes a terminal-friendly icon plus the tool's official site, and you can toggle items with arrow keys plus `Enter` or by pressing their number.
|
|
21
21
|
|
|
22
|
-
History notes:
|
|
23
|
-
|
|
24
|
-
- Continue setup only updates `~/.continue/config.yaml` and leaves `~/.continue/sessions` untouched.
|
|
25
|
-
- Cline setup only updates `~/.cline/data/globalState.json` and `~/.cline/data/secrets.json`; task history stays in `~/.cline/state` and `~/.cline/tasks`.
|
|
26
|
-
- Roo Code setup uses a managed auto-import file plus editor settings and does not touch task history. Imported Roo profiles can persist inside Roo after first launch.
|
|
27
|
-
- Aider setup appends a managed block to `~/.aider.conf.yml` and enables `restore-chat-history: true` without touching `.aider.chat.history.md`.
|
|
28
|
-
|
|
29
22
|
## Optional
|
|
30
23
|
|
|
31
24
|
`theclawbay link --api-key <apiKey>`
|
package/dist/commands/logout.js
CHANGED
|
@@ -678,10 +678,10 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
678
678
|
this.log("- Codex login state: no cleanup needed.");
|
|
679
679
|
}
|
|
680
680
|
if (modelCacheCleanup.action === "removed") {
|
|
681
|
-
this.log("- Codex model cache: removed the Claw Bay GPT-5.4 seed.");
|
|
681
|
+
this.log("- Codex model cache: removed the Claw Bay GPT-5.4 / GPT-5.4 Mini seed.");
|
|
682
682
|
}
|
|
683
683
|
else if (modelCacheCleanup.action === "preserved") {
|
|
684
|
-
this.log("- Codex model cache: preserved an existing GPT-5.4 entry.");
|
|
684
|
+
this.log("- Codex model cache: preserved an existing GPT-5.4 / GPT-5.4 Mini entry.");
|
|
685
685
|
}
|
|
686
686
|
else if (modelCacheCleanup.warning) {
|
|
687
687
|
this.log(`- Codex model cache: ${modelCacheCleanup.warning}`);
|
package/dist/commands/setup.js
CHANGED
|
@@ -460,25 +460,51 @@ async function promptForSetupClients(clients) {
|
|
|
460
460
|
const wasRaw = stdin.isRaw;
|
|
461
461
|
const applyIndex = options.length;
|
|
462
462
|
const hyperlinksEnabled = supportsTerminalHyperlinks();
|
|
463
|
+
const ansi = {
|
|
464
|
+
reset: "\x1b[0m",
|
|
465
|
+
bold: "\x1b[1m",
|
|
466
|
+
dim: "\x1b[2m",
|
|
467
|
+
inverse: "\x1b[7m",
|
|
468
|
+
red: "\x1b[31m",
|
|
469
|
+
green: "\x1b[32m",
|
|
470
|
+
yellow: "\x1b[33m",
|
|
471
|
+
gray: "\x1b[90m",
|
|
472
|
+
};
|
|
473
|
+
const paint = (text, ...codes) => `${codes.join("")}${text}${ansi.reset}`;
|
|
463
474
|
const clearScreen = () => {
|
|
464
475
|
stdout.write("\x1b[2J\x1b[H");
|
|
465
476
|
};
|
|
466
477
|
const selectedCount = () => options.filter((option) => option.checked).length;
|
|
467
478
|
const render = () => {
|
|
468
479
|
clearScreen();
|
|
469
|
-
stdout.write("Choose local clients to configure\n
|
|
470
|
-
stdout.write(`Use ↑/↓ to move, Enter to toggle the highlighted integration, or press 1-${options.length} to toggle directly
|
|
471
|
-
stdout.write("Each tool name links to its official site when your terminal supports it
|
|
472
|
-
stdout.write("Move to Apply setup and press Enter when you're ready
|
|
480
|
+
stdout.write(`${paint("Choose local clients to configure", ansi.bold)}\n`);
|
|
481
|
+
stdout.write(`${paint(`Use ↑/↓ to move, Enter to toggle the highlighted integration, or press 1-${options.length} to toggle directly.`, ansi.dim, ansi.gray)}\n`);
|
|
482
|
+
stdout.write(`${paint("Each tool name links to its official site when your terminal supports it.", ansi.dim, ansi.gray)}\n`);
|
|
483
|
+
stdout.write(`${paint("Move to Apply setup and press Enter when you're ready.", ansi.dim, ansi.gray)}\n\n`);
|
|
473
484
|
for (const [index, option] of options.entries()) {
|
|
474
|
-
const pointer = index === cursor ? ">" : " ";
|
|
475
|
-
const mark = option.detected
|
|
476
|
-
|
|
477
|
-
|
|
485
|
+
const pointer = index === cursor ? paint(">", ansi.bold) : " ";
|
|
486
|
+
const mark = option.detected
|
|
487
|
+
? option.checked
|
|
488
|
+
? paint("[x]", ansi.green, ansi.bold)
|
|
489
|
+
: paint("[ ]", ansi.gray)
|
|
490
|
+
: paint("[-]", ansi.red, ansi.bold);
|
|
491
|
+
const badge = option.detected
|
|
492
|
+
? option.recommended
|
|
493
|
+
? paint("recommended", ansi.green)
|
|
494
|
+
: paint("optional", ansi.yellow)
|
|
495
|
+
: paint("not detected", ansi.red);
|
|
496
|
+
const label = option.detected
|
|
497
|
+
? formatSetupClientLabel(option, hyperlinksEnabled)
|
|
498
|
+
: paint(formatSetupClientLabel(option, hyperlinksEnabled), ansi.dim, ansi.gray);
|
|
499
|
+
stdout.write(`${pointer} ${index + 1}. ${mark} ${label} ${badge}\n`);
|
|
478
500
|
}
|
|
479
501
|
const applyPointer = cursor === applyIndex ? ">" : " ";
|
|
480
|
-
|
|
481
|
-
|
|
502
|
+
const applyLabel = `Apply setup (${selectedCount()} selected)`;
|
|
503
|
+
const applyStyled = cursor === applyIndex
|
|
504
|
+
? paint(` ${applyLabel} `, ansi.inverse, ansi.bold, ansi.green)
|
|
505
|
+
: paint(applyLabel, ansi.bold, ansi.green);
|
|
506
|
+
stdout.write(`\n${applyPointer} ${applyStyled}\n`);
|
|
507
|
+
stdout.write(`\n${paint(hint, ansi.dim, ansi.gray)}\n`);
|
|
482
508
|
};
|
|
483
509
|
const finish = () => {
|
|
484
510
|
if (settled)
|
|
@@ -1467,19 +1493,19 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1467
1493
|
this.log(`- Conversation cache: could not update local Codex history DB.${detail}`);
|
|
1468
1494
|
}
|
|
1469
1495
|
if (modelCacheMigration?.action === "seeded") {
|
|
1470
|
-
this.log("- Codex model cache: added GPT-5.4 to the local picker cache.");
|
|
1496
|
+
this.log("- Codex model cache: added GPT-5.4 / GPT-5.4 Mini to the local picker cache.");
|
|
1471
1497
|
}
|
|
1472
1498
|
else if (modelCacheMigration?.action === "created") {
|
|
1473
|
-
this.log("- Codex model cache: created a local picker cache with GPT-5.4.");
|
|
1499
|
+
this.log("- Codex model cache: created a local picker cache with GPT-5.4 / GPT-5.4 Mini.");
|
|
1474
1500
|
}
|
|
1475
1501
|
else if (modelCacheMigration?.action === "refreshed") {
|
|
1476
|
-
this.log("- Codex model cache: refreshed the local picker cache for GPT-5.4.");
|
|
1502
|
+
this.log("- Codex model cache: refreshed the local picker cache for GPT-5.4 / GPT-5.4 Mini.");
|
|
1477
1503
|
}
|
|
1478
1504
|
else if (modelCacheMigration?.action === "already_seeded") {
|
|
1479
|
-
this.log("- Codex model cache: GPT-5.4
|
|
1505
|
+
this.log("- Codex model cache: GPT-5.4 / GPT-5.4 Mini were already seeded locally.");
|
|
1480
1506
|
}
|
|
1481
1507
|
else if (modelCacheMigration?.action === "already_present") {
|
|
1482
|
-
this.log("- Codex model cache: GPT-5.4 already existed locally.");
|
|
1508
|
+
this.log("- Codex model cache: GPT-5.4 / GPT-5.4 Mini already existed locally.");
|
|
1483
1509
|
}
|
|
1484
1510
|
else if (modelCacheMigration?.warning) {
|
|
1485
1511
|
this.log(`- Codex model cache: ${modelCacheMigration.warning}`);
|
|
@@ -14,10 +14,13 @@ const supported_models_1 = require("./supported-models");
|
|
|
14
14
|
const MODELS_CACHE_FILE = "models_cache.json";
|
|
15
15
|
const MODELS_CACHE_STATE_FILE = "theclawbay.models-cache.json";
|
|
16
16
|
const STATE_DB_FILE_PATTERN = /^state_\d+\.sqlite$/;
|
|
17
|
-
const
|
|
17
|
+
const TARGET_MODEL_IDS = ["gpt-5.4", "gpt-5.4-mini"];
|
|
18
|
+
const TARGET_MODEL_ID_SET = new Set(TARGET_MODEL_IDS);
|
|
18
19
|
const SEED_MARKER_KEY = "_theclawbay_seeded";
|
|
19
|
-
|
|
20
|
-
const
|
|
20
|
+
// Older releases stored the model id as the marker value; accept those so cleanup still works.
|
|
21
|
+
const SEED_MARKER_VALUE = "theclawbay";
|
|
22
|
+
const LEGACY_SEED_MARKER_VALUES = new Set(["gpt-5.4", SEED_MARKER_VALUE, true]);
|
|
23
|
+
const TEMPLATE_MODEL_IDS = (0, supported_models_1.getSupportedModelIds)().filter((id) => !TARGET_MODEL_ID_SET.has(id));
|
|
21
24
|
const DEFAULT_REASONING_LEVELS = [
|
|
22
25
|
{ effort: "low", description: "Fast responses with lighter reasoning" },
|
|
23
26
|
{ effort: "medium", description: "Balances speed and reasoning depth for everyday tasks" },
|
|
@@ -88,29 +91,66 @@ async function removeFileIfExists(filePath) {
|
|
|
88
91
|
throw error;
|
|
89
92
|
}
|
|
90
93
|
}
|
|
94
|
+
function normalizePatchFingerprintMap(state) {
|
|
95
|
+
if (!state)
|
|
96
|
+
return {};
|
|
97
|
+
if (state.version === 1) {
|
|
98
|
+
if (!TARGET_MODEL_ID_SET.has(state.modelId))
|
|
99
|
+
return {};
|
|
100
|
+
return state.fingerprint ? { [state.modelId]: state.fingerprint } : {};
|
|
101
|
+
}
|
|
102
|
+
const output = {};
|
|
103
|
+
for (const entry of state.models) {
|
|
104
|
+
if (!TARGET_MODEL_ID_SET.has(entry.modelId))
|
|
105
|
+
continue;
|
|
106
|
+
if (entry.fingerprint)
|
|
107
|
+
output[entry.modelId] = entry.fingerprint;
|
|
108
|
+
}
|
|
109
|
+
return output;
|
|
110
|
+
}
|
|
91
111
|
async function readPatchState(statePath) {
|
|
92
112
|
const parsed = await readJsonIfExists(statePath);
|
|
93
113
|
const obj = objectRecordOr(parsed);
|
|
94
114
|
if (!obj)
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
115
|
+
return {};
|
|
116
|
+
const version = obj.version;
|
|
117
|
+
if (version === 1) {
|
|
118
|
+
const modelId = typeof obj.modelId === "string" ? obj.modelId : "";
|
|
119
|
+
const fingerprint = typeof obj.fingerprint === "string" ? obj.fingerprint : "";
|
|
120
|
+
if (!modelId || !fingerprint)
|
|
121
|
+
return {};
|
|
122
|
+
return normalizePatchFingerprintMap({ version: 1, modelId, fingerprint });
|
|
123
|
+
}
|
|
124
|
+
if (version === 2) {
|
|
125
|
+
const modelsRaw = obj.models;
|
|
126
|
+
if (!Array.isArray(modelsRaw))
|
|
127
|
+
return {};
|
|
128
|
+
const models = [];
|
|
129
|
+
for (const entry of modelsRaw) {
|
|
130
|
+
const record = objectRecordOr(entry);
|
|
131
|
+
if (!record)
|
|
132
|
+
continue;
|
|
133
|
+
const modelId = typeof record.modelId === "string" ? record.modelId : "";
|
|
134
|
+
const fingerprint = typeof record.fingerprint === "string" ? record.fingerprint : "";
|
|
135
|
+
if (!modelId || !fingerprint)
|
|
136
|
+
continue;
|
|
137
|
+
models.push({ modelId, fingerprint });
|
|
138
|
+
}
|
|
139
|
+
return normalizePatchFingerprintMap({ version: 2, models });
|
|
140
|
+
}
|
|
141
|
+
return {};
|
|
107
142
|
}
|
|
108
|
-
async function writePatchState(statePath,
|
|
143
|
+
async function writePatchState(statePath, fingerprints) {
|
|
109
144
|
await promises_1.default.mkdir(node_path_1.default.dirname(statePath), { recursive: true });
|
|
145
|
+
const models = TARGET_MODEL_IDS.flatMap((modelId) => {
|
|
146
|
+
const fingerprint = fingerprints[modelId];
|
|
147
|
+
if (!fingerprint)
|
|
148
|
+
return [];
|
|
149
|
+
return [{ modelId, fingerprint }];
|
|
150
|
+
});
|
|
110
151
|
const contents = JSON.stringify({
|
|
111
|
-
version:
|
|
112
|
-
|
|
113
|
-
fingerprint,
|
|
152
|
+
version: 2,
|
|
153
|
+
models,
|
|
114
154
|
}, null, 2);
|
|
115
155
|
await promises_1.default.writeFile(statePath, `${contents}\n`, "utf8");
|
|
116
156
|
}
|
|
@@ -118,18 +158,23 @@ function findModel(models, slug) {
|
|
|
118
158
|
return models.find((entry) => entry.slug === slug) ?? null;
|
|
119
159
|
}
|
|
120
160
|
function hasSeedMarker(model) {
|
|
121
|
-
return model[SEED_MARKER_KEY]
|
|
161
|
+
return LEGACY_SEED_MARKER_VALUES.has(model[SEED_MARKER_KEY]);
|
|
122
162
|
}
|
|
123
163
|
function applySeedMarker(model) {
|
|
124
164
|
const next = cloneJson(model);
|
|
125
165
|
next[SEED_MARKER_KEY] = SEED_MARKER_VALUE;
|
|
126
166
|
return next;
|
|
127
167
|
}
|
|
128
|
-
function
|
|
168
|
+
function seedDescription(modelId) {
|
|
169
|
+
if (modelId === "gpt-5.4-mini")
|
|
170
|
+
return "Smaller GPT-5.4 variant for quick iterations.";
|
|
171
|
+
return "Latest frontier agentic coding model.";
|
|
172
|
+
}
|
|
173
|
+
function normalizeSeedModel(template, modelId) {
|
|
129
174
|
const seed = applySeedMarker(template ?? {});
|
|
130
|
-
seed.slug =
|
|
131
|
-
seed.display_name =
|
|
132
|
-
seed.description =
|
|
175
|
+
seed.slug = modelId;
|
|
176
|
+
seed.display_name = modelId;
|
|
177
|
+
seed.description = seedDescription(modelId);
|
|
133
178
|
if (typeof seed.default_reasoning_level !== "string") {
|
|
134
179
|
seed.default_reasoning_level = "medium";
|
|
135
180
|
}
|
|
@@ -182,13 +227,13 @@ function normalizeSeedModel(template) {
|
|
|
182
227
|
}
|
|
183
228
|
return seed;
|
|
184
229
|
}
|
|
185
|
-
function buildSeedModel(models) {
|
|
230
|
+
function buildSeedModel(models, modelId) {
|
|
186
231
|
for (const candidate of TEMPLATE_MODEL_IDS) {
|
|
187
232
|
const template = findModel(models, candidate);
|
|
188
233
|
if (template)
|
|
189
|
-
return normalizeSeedModel(template);
|
|
234
|
+
return normalizeSeedModel(template, modelId);
|
|
190
235
|
}
|
|
191
|
-
return normalizeSeedModel(null);
|
|
236
|
+
return normalizeSeedModel(null, modelId);
|
|
192
237
|
}
|
|
193
238
|
function setCacheFreshnessMetadata(doc, clientVersion) {
|
|
194
239
|
let changed = false;
|
|
@@ -342,70 +387,115 @@ async function ensureCodexModelCacheHasGpt54(params) {
|
|
|
342
387
|
await removeFileIfExists(statePath);
|
|
343
388
|
return {
|
|
344
389
|
action: "skipped",
|
|
345
|
-
warning: "Codex models cache was not found, and Codex version could not be inferred for a safe GPT-5.4 seed.",
|
|
390
|
+
warning: "Codex models cache was not found, and Codex version could not be inferred for a safe GPT-5.4 / GPT-5.4 Mini seed.",
|
|
346
391
|
};
|
|
347
392
|
}
|
|
348
|
-
const
|
|
393
|
+
const docModels = [];
|
|
394
|
+
const nextState = {};
|
|
395
|
+
for (const modelId of TARGET_MODEL_IDS) {
|
|
396
|
+
const seed = buildSeedModel(docModels, modelId);
|
|
397
|
+
docModels.push(seed);
|
|
398
|
+
nextState[modelId] = fingerprintModel(seed);
|
|
399
|
+
}
|
|
349
400
|
const doc = {
|
|
350
401
|
fetched_at: new Date().toISOString(),
|
|
351
402
|
client_version: clientVersion,
|
|
352
|
-
models:
|
|
403
|
+
models: docModels,
|
|
353
404
|
};
|
|
354
405
|
await promises_1.default.mkdir(params.codexHome, { recursive: true });
|
|
355
406
|
await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
356
|
-
await writePatchState(statePath,
|
|
407
|
+
await writePatchState(statePath, nextState);
|
|
357
408
|
return { action: "created" };
|
|
358
409
|
}
|
|
359
410
|
const doc = normalizeCacheDocument(parsed);
|
|
360
411
|
if (!doc) {
|
|
361
412
|
return {
|
|
362
413
|
action: "skipped",
|
|
363
|
-
warning: "Codex models cache exists but is not valid JSON in the expected format; skipped GPT-5.4 seed.",
|
|
414
|
+
warning: "Codex models cache exists but is not valid JSON in the expected format; skipped GPT-5.4 / GPT-5.4 Mini seed.",
|
|
364
415
|
};
|
|
365
416
|
}
|
|
366
417
|
const clientVersion = (await inferCodexClientVersion(params.codexHome)) ??
|
|
367
418
|
(typeof doc.client_version === "string" && doc.client_version ? doc.client_version : null);
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
384
|
-
return { action: "refreshed" };
|
|
419
|
+
let changed = false;
|
|
420
|
+
let seeded = false;
|
|
421
|
+
let stateChanged = false;
|
|
422
|
+
const nextState = { ...existingState };
|
|
423
|
+
for (const modelId of TARGET_MODEL_IDS) {
|
|
424
|
+
const existingModel = findModel(doc.models, modelId);
|
|
425
|
+
const trackedFingerprint = existingState[modelId];
|
|
426
|
+
if (existingModel) {
|
|
427
|
+
const currentFingerprint = fingerprintModel(existingModel);
|
|
428
|
+
if (hasSeedMarker(existingModel)) {
|
|
429
|
+
if (!trackedFingerprint || trackedFingerprint !== currentFingerprint) {
|
|
430
|
+
nextState[modelId] = currentFingerprint;
|
|
431
|
+
stateChanged = true;
|
|
432
|
+
}
|
|
433
|
+
continue;
|
|
385
434
|
}
|
|
386
|
-
if (
|
|
387
|
-
|
|
388
|
-
|
|
435
|
+
if (trackedFingerprint && trackedFingerprint === currentFingerprint) {
|
|
436
|
+
const seededModel = applySeedMarker(existingModel);
|
|
437
|
+
doc.models = doc.models.map((entry) => (entry.slug === modelId ? seededModel : entry));
|
|
438
|
+
nextState[modelId] = fingerprintModel(seededModel);
|
|
439
|
+
seeded = true;
|
|
440
|
+
changed = true;
|
|
441
|
+
stateChanged = true;
|
|
442
|
+
continue;
|
|
389
443
|
}
|
|
390
|
-
|
|
444
|
+
if (trackedFingerprint && trackedFingerprint !== currentFingerprint) {
|
|
445
|
+
delete nextState[modelId];
|
|
446
|
+
stateChanged = true;
|
|
447
|
+
}
|
|
448
|
+
continue;
|
|
391
449
|
}
|
|
392
|
-
|
|
393
|
-
|
|
450
|
+
const seed = buildSeedModel(doc.models, modelId);
|
|
451
|
+
doc.models = [seed, ...doc.models];
|
|
452
|
+
nextState[modelId] = fingerprintModel(seed);
|
|
453
|
+
seeded = true;
|
|
454
|
+
changed = true;
|
|
455
|
+
stateChanged = true;
|
|
456
|
+
}
|
|
457
|
+
// Keep The Claw Bay seeds grouped at the top for easy discovery in the picker.
|
|
458
|
+
const bySlug = new Map();
|
|
459
|
+
const rest = [];
|
|
460
|
+
for (const entry of doc.models) {
|
|
461
|
+
const slug = typeof entry.slug === "string" ? entry.slug : "";
|
|
462
|
+
if (slug && TARGET_MODEL_ID_SET.has(slug) && !bySlug.has(slug)) {
|
|
463
|
+
bySlug.set(slug, entry);
|
|
464
|
+
continue;
|
|
394
465
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
466
|
+
rest.push(entry);
|
|
467
|
+
}
|
|
468
|
+
const orderedTargets = [];
|
|
469
|
+
for (const id of TARGET_MODEL_IDS) {
|
|
470
|
+
const entry = bySlug.get(id);
|
|
471
|
+
if (entry)
|
|
472
|
+
orderedTargets.push(entry);
|
|
473
|
+
}
|
|
474
|
+
doc.models = [...orderedTargets, ...rest];
|
|
475
|
+
if (setCacheFreshnessMetadata(doc, clientVersion)) {
|
|
476
|
+
changed = true;
|
|
477
|
+
}
|
|
478
|
+
if (changed) {
|
|
479
|
+
await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
480
|
+
}
|
|
481
|
+
const hasAnyTracked = TARGET_MODEL_IDS.some((id) => Boolean(nextState[id]));
|
|
482
|
+
if (!hasAnyTracked) {
|
|
483
|
+
if (Object.keys(existingState).length > 0) {
|
|
484
|
+
await removeFileIfExists(statePath);
|
|
398
485
|
}
|
|
399
|
-
return {
|
|
400
|
-
action: existingState && existingState.fingerprint === currentFingerprint ? "already_seeded" : "already_present",
|
|
401
|
-
};
|
|
402
486
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
487
|
+
else if (stateChanged || seeded) {
|
|
488
|
+
await writePatchState(statePath, nextState);
|
|
489
|
+
}
|
|
490
|
+
if (seeded)
|
|
491
|
+
return { action: "seeded" };
|
|
492
|
+
if (changed || stateChanged)
|
|
493
|
+
return { action: "refreshed" };
|
|
494
|
+
const allSeeded = TARGET_MODEL_IDS.every((id) => {
|
|
495
|
+
const model = findModel(doc.models, id);
|
|
496
|
+
return Boolean(model && hasSeedMarker(model));
|
|
497
|
+
});
|
|
498
|
+
return { action: allSeeded ? "already_seeded" : "already_present" };
|
|
409
499
|
}
|
|
410
500
|
catch (error) {
|
|
411
501
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -420,7 +510,7 @@ async function cleanupSeededCodexModelCache(params) {
|
|
|
420
510
|
const statePath = node_path_1.default.join(params.codexHome, MODELS_CACHE_STATE_FILE);
|
|
421
511
|
try {
|
|
422
512
|
const state = await readPatchState(statePath);
|
|
423
|
-
if (
|
|
513
|
+
if (Object.keys(state).length === 0) {
|
|
424
514
|
return { action: "none" };
|
|
425
515
|
}
|
|
426
516
|
const parsed = await readJsonIfExists(cachePath);
|
|
@@ -432,27 +522,26 @@ async function cleanupSeededCodexModelCache(params) {
|
|
|
432
522
|
if (!doc) {
|
|
433
523
|
return {
|
|
434
524
|
action: "none",
|
|
435
|
-
warning: "Codex models cache exists but is not valid JSON in the expected format; left GPT-5.4 cache patch state untouched.",
|
|
525
|
+
warning: "Codex models cache exists but is not valid JSON in the expected format; left GPT-5.4 / GPT-5.4 Mini cache patch state untouched.",
|
|
436
526
|
};
|
|
437
527
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
528
|
+
let removed = 0;
|
|
529
|
+
let preserved = 0;
|
|
530
|
+
doc.models = doc.models.filter((entry) => {
|
|
531
|
+
const slug = typeof entry.slug === "string" ? entry.slug : "";
|
|
532
|
+
if (!slug || !TARGET_MODEL_ID_SET.has(slug))
|
|
533
|
+
return true;
|
|
534
|
+
if (!hasSeedMarker(entry)) {
|
|
535
|
+
preserved += 1;
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
removed += 1;
|
|
539
|
+
return false;
|
|
540
|
+
});
|
|
541
|
+
if (removed === 0) {
|
|
452
542
|
await removeFileIfExists(statePath);
|
|
453
|
-
return { action: "
|
|
543
|
+
return { action: preserved > 0 ? "preserved" : "none" };
|
|
454
544
|
}
|
|
455
|
-
doc.models = doc.models.filter((entry) => entry.slug !== TARGET_MODEL_ID);
|
|
456
545
|
await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
457
546
|
await removeFileIfExists(statePath);
|
|
458
547
|
return { action: "removed" };
|
package/package.json
CHANGED
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
"cachedInputPer1M": 0.25,
|
|
9
9
|
"outputPer1M": 15.0
|
|
10
10
|
},
|
|
11
|
+
{
|
|
12
|
+
"id": "gpt-5.4-mini",
|
|
13
|
+
"label": "GPT-5.4 Mini",
|
|
14
|
+
"note": "Smaller GPT-5.4 variant for quick iterations.",
|
|
15
|
+
"tone": "sea",
|
|
16
|
+
"inputPer1M": 1.25,
|
|
17
|
+
"cachedInputPer1M": 0.125,
|
|
18
|
+
"outputPer1M": 10.0
|
|
19
|
+
},
|
|
11
20
|
{
|
|
12
21
|
"id": "gpt-5.3-codex",
|
|
13
22
|
"label": "GPT-5.3 Codex",
|