theclawbay 0.3.2 → 0.3.4
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
CHANGED
|
@@ -8,6 +8,7 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const base_command_1 = require("../lib/base-command");
|
|
10
10
|
const codex_history_migration_1 = require("../lib/codex-history-migration");
|
|
11
|
+
const codex_model_cache_migration_1 = require("../lib/codex-model-cache-migration");
|
|
11
12
|
const paths_1 = require("../lib/config/paths");
|
|
12
13
|
const OPENAI_PROVIDER_ID = "openai";
|
|
13
14
|
const DEFAULT_PROVIDER_ID = "theclawbay";
|
|
@@ -245,6 +246,9 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
245
246
|
targetProvider: OPENAI_PROVIDER_ID,
|
|
246
247
|
sourceProviders: HISTORY_PROVIDER_DB_MIGRATE_SOURCES,
|
|
247
248
|
});
|
|
249
|
+
const modelCacheCleanup = await (0, codex_model_cache_migration_1.cleanupSeededCodexModelCache)({
|
|
250
|
+
codexHome: paths_1.codexDir,
|
|
251
|
+
});
|
|
248
252
|
delete process.env[ENV_KEY_NAME];
|
|
249
253
|
this.log("Logout complete");
|
|
250
254
|
this.log(`- Managed configs removed: ${deletedManagedPaths}`);
|
|
@@ -271,6 +275,18 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
271
275
|
const detail = stateDbMigration.warning ? ` ${stateDbMigration.warning}` : "";
|
|
272
276
|
this.log(`- Conversation cache: could not update local Codex history DB.${detail}`);
|
|
273
277
|
}
|
|
278
|
+
if (modelCacheCleanup.action === "removed") {
|
|
279
|
+
this.log("- Codex model cache: removed the Claw Bay GPT-5.4 seed.");
|
|
280
|
+
}
|
|
281
|
+
else if (modelCacheCleanup.action === "preserved") {
|
|
282
|
+
this.log("- Codex model cache: preserved an existing GPT-5.4 entry.");
|
|
283
|
+
}
|
|
284
|
+
else if (modelCacheCleanup.warning) {
|
|
285
|
+
this.log(`- Codex model cache: ${modelCacheCleanup.warning}`);
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
this.log("- Codex model cache: no cleanup needed.");
|
|
289
|
+
}
|
|
274
290
|
this.log(`- OpenClaw config cleaned: ${updatedOpenClawConfig ? "yes" : "no"}`);
|
|
275
291
|
this.log(`- OpenCode config cleaned: ${updatedOpenCodeConfig ? "yes" : "no"}`);
|
|
276
292
|
this.log("Note: restart terminals/VS Code windows to clear already-loaded shell environment.");
|
package/dist/commands/setup.js
CHANGED
|
@@ -10,6 +10,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
10
10
|
const core_1 = require("@oclif/core");
|
|
11
11
|
const base_command_1 = require("../lib/base-command");
|
|
12
12
|
const codex_history_migration_1 = require("../lib/codex-history-migration");
|
|
13
|
+
const codex_model_cache_migration_1 = require("../lib/codex-model-cache-migration");
|
|
13
14
|
const paths_1 = require("../lib/config/paths");
|
|
14
15
|
const api_key_1 = require("../lib/managed/api-key");
|
|
15
16
|
const config_1 = require("../lib/managed/config");
|
|
@@ -460,6 +461,9 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
460
461
|
targetProvider: DEFAULT_PROVIDER_ID,
|
|
461
462
|
sourceProviders: HISTORY_PROVIDER_DB_MIGRATE_SOURCES,
|
|
462
463
|
});
|
|
464
|
+
const modelCacheMigration = await (0, codex_model_cache_migration_1.ensureCodexModelCacheHasGpt54)({
|
|
465
|
+
codexHome: paths_1.codexDir,
|
|
466
|
+
});
|
|
463
467
|
const hasOpenClaw = hasCommand("openclaw");
|
|
464
468
|
const hasOpenCode = hasCommand("opencode");
|
|
465
469
|
let openClawConfigPath = null;
|
|
@@ -509,6 +513,24 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
509
513
|
const detail = stateDbMigration.warning ? ` ${stateDbMigration.warning}` : "";
|
|
510
514
|
this.log(`- Conversation cache: could not update local Codex history DB.${detail}`);
|
|
511
515
|
}
|
|
516
|
+
if (modelCacheMigration.action === "seeded") {
|
|
517
|
+
this.log("- Codex model cache: added GPT-5.4 to the local picker cache.");
|
|
518
|
+
}
|
|
519
|
+
else if (modelCacheMigration.action === "created") {
|
|
520
|
+
this.log("- Codex model cache: created a local picker cache with GPT-5.4.");
|
|
521
|
+
}
|
|
522
|
+
else if (modelCacheMigration.action === "already_seeded") {
|
|
523
|
+
this.log("- Codex model cache: GPT-5.4 was already seeded locally.");
|
|
524
|
+
}
|
|
525
|
+
else if (modelCacheMigration.action === "already_present") {
|
|
526
|
+
this.log("- Codex model cache: GPT-5.4 already existed locally.");
|
|
527
|
+
}
|
|
528
|
+
else if (modelCacheMigration.warning) {
|
|
529
|
+
this.log(`- Codex model cache: ${modelCacheMigration.warning}`);
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
this.log("- Codex model cache: no change.");
|
|
533
|
+
}
|
|
512
534
|
this.log(`- API key env: ${ENV_FILE}`);
|
|
513
535
|
this.log(`- Shell profiles updated: ${updatedShellFiles.join(", ")}`);
|
|
514
536
|
this.log(`- VS Code env hooks updated: ${updatedVsCodeEnvFiles.join(", ")}`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type EnsureCodexModelCacheResult = {
|
|
2
|
+
action: "seeded" | "created" | "already_present" | "already_seeded" | "skipped";
|
|
3
|
+
warning?: string;
|
|
4
|
+
};
|
|
5
|
+
export type CleanupCodexModelCacheResult = {
|
|
6
|
+
action: "removed" | "preserved" | "none";
|
|
7
|
+
warning?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function ensureCodexModelCacheHasGpt54(params: {
|
|
10
|
+
codexHome: string;
|
|
11
|
+
}): Promise<EnsureCodexModelCacheResult>;
|
|
12
|
+
export declare function cleanupSeededCodexModelCache(params: {
|
|
13
|
+
codexHome: string;
|
|
14
|
+
}): Promise<CleanupCodexModelCacheResult>;
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ensureCodexModelCacheHasGpt54 = ensureCodexModelCacheHasGpt54;
|
|
7
|
+
exports.cleanupSeededCodexModelCache = cleanupSeededCodexModelCache;
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
9
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
|
+
const MODELS_CACHE_FILE = "models_cache.json";
|
|
13
|
+
const MODELS_CACHE_STATE_FILE = "theclawbay.models-cache.json";
|
|
14
|
+
const TARGET_MODEL_ID = "gpt-5.4";
|
|
15
|
+
const SEED_MARKER_KEY = "_theclawbay_seeded";
|
|
16
|
+
const SEED_MARKER_VALUE = "gpt-5.4";
|
|
17
|
+
const TEMPLATE_MODEL_IDS = [
|
|
18
|
+
"gpt-5.3-codex",
|
|
19
|
+
"gpt-5.3-codex-spark",
|
|
20
|
+
"gpt-5.2-codex",
|
|
21
|
+
"gpt-5.1-codex-max",
|
|
22
|
+
"gpt-5.1-codex-mini",
|
|
23
|
+
"gpt-5.1-codex",
|
|
24
|
+
];
|
|
25
|
+
const DEFAULT_REASONING_LEVELS = [
|
|
26
|
+
{ effort: "low", description: "Fast responses with lighter reasoning" },
|
|
27
|
+
{ effort: "medium", description: "Balances speed and reasoning depth for everyday tasks" },
|
|
28
|
+
{ effort: "high", description: "Greater reasoning depth for complex problems" },
|
|
29
|
+
{ effort: "xhigh", description: "Extra high reasoning depth for complex problems" },
|
|
30
|
+
];
|
|
31
|
+
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.
|
|
32
|
+
|
|
33
|
+
## General
|
|
34
|
+
|
|
35
|
+
- Prefer rg or rg --files for search when available.
|
|
36
|
+
- Make safe, minimal code changes that preserve user work.
|
|
37
|
+
- Explain what changed and any remaining risk clearly.`;
|
|
38
|
+
function objectRecordOr(value) {
|
|
39
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function arrayOfObjects(value) {
|
|
45
|
+
if (!Array.isArray(value))
|
|
46
|
+
return null;
|
|
47
|
+
const output = [];
|
|
48
|
+
for (const entry of value) {
|
|
49
|
+
const obj = objectRecordOr(entry);
|
|
50
|
+
if (!obj)
|
|
51
|
+
return null;
|
|
52
|
+
output.push(obj);
|
|
53
|
+
}
|
|
54
|
+
return output;
|
|
55
|
+
}
|
|
56
|
+
function cloneJson(value) {
|
|
57
|
+
return JSON.parse(JSON.stringify(value));
|
|
58
|
+
}
|
|
59
|
+
function stableStringify(value) {
|
|
60
|
+
if (Array.isArray(value)) {
|
|
61
|
+
return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
|
|
62
|
+
}
|
|
63
|
+
if (value && typeof value === "object") {
|
|
64
|
+
const obj = value;
|
|
65
|
+
const keys = Object.keys(obj).sort();
|
|
66
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(obj[key])}`).join(",")}}`;
|
|
67
|
+
}
|
|
68
|
+
return JSON.stringify(value);
|
|
69
|
+
}
|
|
70
|
+
function fingerprintModel(model) {
|
|
71
|
+
return (0, node_crypto_1.createHash)("sha256").update(stableStringify(model)).digest("hex");
|
|
72
|
+
}
|
|
73
|
+
async function readJsonIfExists(filePath) {
|
|
74
|
+
try {
|
|
75
|
+
const raw = await promises_1.default.readFile(filePath, "utf8");
|
|
76
|
+
return JSON.parse(raw);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const err = error;
|
|
80
|
+
if (err.code === "ENOENT")
|
|
81
|
+
return null;
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function removeFileIfExists(filePath) {
|
|
86
|
+
try {
|
|
87
|
+
await promises_1.default.unlink(filePath);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
const err = error;
|
|
91
|
+
if (err.code !== "ENOENT")
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function readPatchState(statePath) {
|
|
96
|
+
const parsed = await readJsonIfExists(statePath);
|
|
97
|
+
const obj = objectRecordOr(parsed);
|
|
98
|
+
if (!obj)
|
|
99
|
+
return null;
|
|
100
|
+
if (obj.version !== 1)
|
|
101
|
+
return null;
|
|
102
|
+
if (obj.modelId !== TARGET_MODEL_ID)
|
|
103
|
+
return null;
|
|
104
|
+
if (typeof obj.fingerprint !== "string" || !obj.fingerprint)
|
|
105
|
+
return null;
|
|
106
|
+
return {
|
|
107
|
+
version: 1,
|
|
108
|
+
modelId: TARGET_MODEL_ID,
|
|
109
|
+
fingerprint: obj.fingerprint,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function writePatchState(statePath, fingerprint) {
|
|
113
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(statePath), { recursive: true });
|
|
114
|
+
const contents = JSON.stringify({
|
|
115
|
+
version: 1,
|
|
116
|
+
modelId: TARGET_MODEL_ID,
|
|
117
|
+
fingerprint,
|
|
118
|
+
}, null, 2);
|
|
119
|
+
await promises_1.default.writeFile(statePath, `${contents}\n`, "utf8");
|
|
120
|
+
}
|
|
121
|
+
function findModel(models, slug) {
|
|
122
|
+
return models.find((entry) => entry.slug === slug) ?? null;
|
|
123
|
+
}
|
|
124
|
+
function hasSeedMarker(model) {
|
|
125
|
+
return model[SEED_MARKER_KEY] === SEED_MARKER_VALUE;
|
|
126
|
+
}
|
|
127
|
+
function applySeedMarker(model) {
|
|
128
|
+
const next = cloneJson(model);
|
|
129
|
+
next[SEED_MARKER_KEY] = SEED_MARKER_VALUE;
|
|
130
|
+
return next;
|
|
131
|
+
}
|
|
132
|
+
function normalizeSeedModel(template) {
|
|
133
|
+
const seed = applySeedMarker(template ?? {});
|
|
134
|
+
seed.slug = TARGET_MODEL_ID;
|
|
135
|
+
seed.display_name = TARGET_MODEL_ID;
|
|
136
|
+
seed.description = "Latest frontier agentic coding model.";
|
|
137
|
+
if (typeof seed.default_reasoning_level !== "string") {
|
|
138
|
+
seed.default_reasoning_level = "medium";
|
|
139
|
+
}
|
|
140
|
+
if (!Array.isArray(seed.supported_reasoning_levels) || seed.supported_reasoning_levels.length === 0) {
|
|
141
|
+
seed.supported_reasoning_levels = cloneJson(DEFAULT_REASONING_LEVELS);
|
|
142
|
+
}
|
|
143
|
+
if (typeof seed.shell_type !== "string") {
|
|
144
|
+
seed.shell_type = "shell_command";
|
|
145
|
+
}
|
|
146
|
+
seed.visibility = "list";
|
|
147
|
+
seed.supported_in_api = true;
|
|
148
|
+
seed.priority = 0;
|
|
149
|
+
seed.upgrade = null;
|
|
150
|
+
if (typeof seed.base_instructions !== "string" || !seed.base_instructions.trim()) {
|
|
151
|
+
seed.base_instructions = FALLBACK_BASE_INSTRUCTIONS;
|
|
152
|
+
}
|
|
153
|
+
if (typeof seed.supports_reasoning_summaries !== "boolean") {
|
|
154
|
+
seed.supports_reasoning_summaries = true;
|
|
155
|
+
}
|
|
156
|
+
if (typeof seed.support_verbosity !== "boolean") {
|
|
157
|
+
seed.support_verbosity = true;
|
|
158
|
+
}
|
|
159
|
+
if (seed.default_verbosity === undefined) {
|
|
160
|
+
seed.default_verbosity = "low";
|
|
161
|
+
}
|
|
162
|
+
if (seed.apply_patch_tool_type === undefined) {
|
|
163
|
+
seed.apply_patch_tool_type = "freeform";
|
|
164
|
+
}
|
|
165
|
+
const truncationPolicy = objectRecordOr(seed.truncation_policy);
|
|
166
|
+
if (!truncationPolicy || typeof truncationPolicy.mode !== "string" || typeof truncationPolicy.limit !== "number") {
|
|
167
|
+
seed.truncation_policy = { mode: "tokens", limit: 10000 };
|
|
168
|
+
}
|
|
169
|
+
if (typeof seed.supports_parallel_tool_calls !== "boolean") {
|
|
170
|
+
seed.supports_parallel_tool_calls = true;
|
|
171
|
+
}
|
|
172
|
+
if (typeof seed.context_window !== "number") {
|
|
173
|
+
seed.context_window = 272000;
|
|
174
|
+
}
|
|
175
|
+
if (typeof seed.effective_context_window_percent !== "number") {
|
|
176
|
+
seed.effective_context_window_percent = 95;
|
|
177
|
+
}
|
|
178
|
+
if (!Array.isArray(seed.experimental_supported_tools)) {
|
|
179
|
+
seed.experimental_supported_tools = [];
|
|
180
|
+
}
|
|
181
|
+
if (!Array.isArray(seed.input_modalities) || seed.input_modalities.length === 0) {
|
|
182
|
+
seed.input_modalities = ["text", "image"];
|
|
183
|
+
}
|
|
184
|
+
if (typeof seed.prefer_websockets !== "boolean") {
|
|
185
|
+
seed.prefer_websockets = true;
|
|
186
|
+
}
|
|
187
|
+
return seed;
|
|
188
|
+
}
|
|
189
|
+
function buildSeedModel(models) {
|
|
190
|
+
for (const candidate of TEMPLATE_MODEL_IDS) {
|
|
191
|
+
const template = findModel(models, candidate);
|
|
192
|
+
if (template)
|
|
193
|
+
return normalizeSeedModel(template);
|
|
194
|
+
}
|
|
195
|
+
return normalizeSeedModel(null);
|
|
196
|
+
}
|
|
197
|
+
function parseCodexVersion(stdout) {
|
|
198
|
+
const match = stdout.match(/\b(\d+\.\d+\.\d+)\b/);
|
|
199
|
+
return match ? match[1] : null;
|
|
200
|
+
}
|
|
201
|
+
function inferCodexClientVersion() {
|
|
202
|
+
for (const command of ["codex", "codex-cli"]) {
|
|
203
|
+
const run = (0, node_child_process_1.spawnSync)(command, ["--version"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
204
|
+
if (run.status !== 0)
|
|
205
|
+
continue;
|
|
206
|
+
const parsed = parseCodexVersion(run.stdout ?? "");
|
|
207
|
+
if (parsed)
|
|
208
|
+
return parsed;
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
function normalizeCacheDocument(value) {
|
|
213
|
+
const doc = objectRecordOr(value);
|
|
214
|
+
if (!doc)
|
|
215
|
+
return null;
|
|
216
|
+
const models = arrayOfObjects(doc.models);
|
|
217
|
+
if (!models)
|
|
218
|
+
return null;
|
|
219
|
+
return { ...doc, models };
|
|
220
|
+
}
|
|
221
|
+
async function ensureCodexModelCacheHasGpt54(params) {
|
|
222
|
+
const cachePath = node_path_1.default.join(params.codexHome, MODELS_CACHE_FILE);
|
|
223
|
+
const statePath = node_path_1.default.join(params.codexHome, MODELS_CACHE_STATE_FILE);
|
|
224
|
+
try {
|
|
225
|
+
const existingState = await readPatchState(statePath);
|
|
226
|
+
const parsed = await readJsonIfExists(cachePath);
|
|
227
|
+
if (parsed === null) {
|
|
228
|
+
const clientVersion = inferCodexClientVersion();
|
|
229
|
+
if (!clientVersion) {
|
|
230
|
+
await removeFileIfExists(statePath);
|
|
231
|
+
return {
|
|
232
|
+
action: "skipped",
|
|
233
|
+
warning: "Codex models cache was not found, and Codex version could not be inferred for a safe GPT-5.4 seed.",
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const seed = buildSeedModel([]);
|
|
237
|
+
const doc = {
|
|
238
|
+
fetched_at: new Date().toISOString(),
|
|
239
|
+
client_version: clientVersion,
|
|
240
|
+
models: [seed],
|
|
241
|
+
};
|
|
242
|
+
await promises_1.default.mkdir(params.codexHome, { recursive: true });
|
|
243
|
+
await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
244
|
+
await writePatchState(statePath, fingerprintModel(seed));
|
|
245
|
+
return { action: "created" };
|
|
246
|
+
}
|
|
247
|
+
const doc = normalizeCacheDocument(parsed);
|
|
248
|
+
if (!doc) {
|
|
249
|
+
return {
|
|
250
|
+
action: "skipped",
|
|
251
|
+
warning: "Codex models cache exists but is not valid JSON in the expected format; skipped GPT-5.4 seed.",
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
const existingModel = findModel(doc.models, TARGET_MODEL_ID);
|
|
255
|
+
if (existingModel) {
|
|
256
|
+
const currentFingerprint = fingerprintModel(existingModel);
|
|
257
|
+
if (existingState && existingState.fingerprint === currentFingerprint && !hasSeedMarker(existingModel)) {
|
|
258
|
+
const seededModel = applySeedMarker(existingModel);
|
|
259
|
+
doc.models = doc.models.map((entry) => (entry.slug === TARGET_MODEL_ID ? seededModel : entry));
|
|
260
|
+
await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
261
|
+
await writePatchState(statePath, fingerprintModel(seededModel));
|
|
262
|
+
return { action: "seeded" };
|
|
263
|
+
}
|
|
264
|
+
if (hasSeedMarker(existingModel)) {
|
|
265
|
+
if (!existingState || existingState.fingerprint !== currentFingerprint) {
|
|
266
|
+
await writePatchState(statePath, currentFingerprint);
|
|
267
|
+
}
|
|
268
|
+
return { action: "already_seeded" };
|
|
269
|
+
}
|
|
270
|
+
if (existingState && existingState.fingerprint !== currentFingerprint) {
|
|
271
|
+
await removeFileIfExists(statePath);
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
action: existingState && existingState.fingerprint === currentFingerprint ? "already_seeded" : "already_present",
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const seed = buildSeedModel(doc.models);
|
|
278
|
+
doc.models = [seed, ...doc.models];
|
|
279
|
+
await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
280
|
+
await writePatchState(statePath, fingerprintModel(seed));
|
|
281
|
+
return { action: "seeded" };
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
285
|
+
return {
|
|
286
|
+
action: "skipped",
|
|
287
|
+
warning: `Could not update Codex models cache: ${message}`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function cleanupSeededCodexModelCache(params) {
|
|
292
|
+
const cachePath = node_path_1.default.join(params.codexHome, MODELS_CACHE_FILE);
|
|
293
|
+
const statePath = node_path_1.default.join(params.codexHome, MODELS_CACHE_STATE_FILE);
|
|
294
|
+
try {
|
|
295
|
+
const state = await readPatchState(statePath);
|
|
296
|
+
if (!state) {
|
|
297
|
+
return { action: "none" };
|
|
298
|
+
}
|
|
299
|
+
const parsed = await readJsonIfExists(cachePath);
|
|
300
|
+
if (parsed === null) {
|
|
301
|
+
await removeFileIfExists(statePath);
|
|
302
|
+
return { action: "none" };
|
|
303
|
+
}
|
|
304
|
+
const doc = normalizeCacheDocument(parsed);
|
|
305
|
+
if (!doc) {
|
|
306
|
+
return {
|
|
307
|
+
action: "none",
|
|
308
|
+
warning: "Codex models cache exists but is not valid JSON in the expected format; left GPT-5.4 cache patch state untouched.",
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const existingModel = findModel(doc.models, TARGET_MODEL_ID);
|
|
312
|
+
if (!existingModel) {
|
|
313
|
+
await removeFileIfExists(statePath);
|
|
314
|
+
return { action: "none" };
|
|
315
|
+
}
|
|
316
|
+
const currentFingerprint = fingerprintModel(existingModel);
|
|
317
|
+
if (!hasSeedMarker(existingModel)) {
|
|
318
|
+
await removeFileIfExists(statePath);
|
|
319
|
+
return { action: "preserved" };
|
|
320
|
+
}
|
|
321
|
+
if (currentFingerprint !== state.fingerprint) {
|
|
322
|
+
await writePatchState(statePath, currentFingerprint);
|
|
323
|
+
doc.models = doc.models.filter((entry) => entry.slug !== TARGET_MODEL_ID);
|
|
324
|
+
await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
325
|
+
await removeFileIfExists(statePath);
|
|
326
|
+
return { action: "removed" };
|
|
327
|
+
}
|
|
328
|
+
doc.models = doc.models.filter((entry) => entry.slug !== TARGET_MODEL_ID);
|
|
329
|
+
await promises_1.default.writeFile(cachePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
330
|
+
await removeFileIfExists(statePath);
|
|
331
|
+
return { action: "removed" };
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
335
|
+
return {
|
|
336
|
+
action: "none",
|
|
337
|
+
warning: `Could not clean up Codex models cache patch: ${message}`,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
}
|