theclawbay 0.3.63 → 0.3.65

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.
@@ -2986,7 +2986,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2986
2986
  let restoredClaudeDesktop3pConfig = false;
2987
2987
  let claudeDesktop3pConfigPathManaged = null;
2988
2988
  let sessionMigration = null;
2989
- let authSeedCleanup = null;
2989
+ let authSeedResult = null;
2990
2990
  let stateDbMigration = null;
2991
2991
  let modelCacheMigration = null;
2992
2992
  let continueConfigPath = null;
@@ -3088,8 +3088,10 @@ class SetupCommand extends base_command_1.BaseCommand {
3088
3088
  neutralizeSources: HISTORY_PROVIDER_NEUTRALIZE_SOURCES,
3089
3089
  });
3090
3090
  }
3091
- authSeedCleanup = await (0, codex_auth_seeding_1.cleanupSeededCodexAuth)({
3091
+ authSeedResult = await (0, codex_auth_seeding_1.ensureCodexApiKeyAuth)({
3092
3092
  codexHome: paths_1.codexDir,
3093
+ apiKey: authCredential,
3094
+ replaceExistingAuth: true,
3093
3095
  });
3094
3096
  if (migrateCodexConversations) {
3095
3097
  stateDbMigration = await (0, codex_history_migration_1.migrateStateDbProviders)({
@@ -3229,13 +3231,11 @@ class SetupCommand extends base_command_1.BaseCommand {
3229
3231
  }
3230
3232
  if (modelCacheMigration?.warning)
3231
3233
  summaryNotes.add(modelCacheMigration.warning);
3232
- if (authSeedCleanup?.warning)
3233
- summaryNotes.add(authSeedCleanup.warning);
3234
- if (authSeedCleanup?.action === "removed") {
3235
- summaryNotes.add("Removed an older The Claw Bay Codex auth override that newer Codex releases now treat as invalid ChatGPT OAuth.");
3236
- }
3237
- else if (authSeedCleanup?.action === "restored_backup") {
3238
- summaryNotes.add("Restored the Codex auth that existed before an older The Claw Bay auth override.");
3234
+ if (authSeedResult?.warning)
3235
+ summaryNotes.add(authSeedResult.warning);
3236
+ if (authSeedResult &&
3237
+ ["seeded", "updated", "already_seeded"].includes(authSeedResult.action)) {
3238
+ summaryNotes.add("Codex desktop clients were pinned to local API-key auth so current VS Code and Codex app builds keep the The Claw Bay model picker instead of falling back to OpenAI's default model catalog.");
3239
3239
  }
3240
3240
  if (selectedSetupClients.has("codex") && !migrateCodexConversations) {
3241
3241
  summaryNotes.add("Left existing Codex conversations untouched.");
@@ -3338,17 +3338,22 @@ class SetupCommand extends base_command_1.BaseCommand {
3338
3338
  else if (modelCacheMigration) {
3339
3339
  this.log("- Codex model cache: no change.");
3340
3340
  }
3341
- if (authSeedCleanup?.action === "restored_backup") {
3342
- this.log("- Codex login state: restored the auth that existed before an older The Claw Bay override.");
3341
+ if (authSeedResult?.action === "seeded") {
3342
+ this.log(authSeedResult.replacedExistingAuth
3343
+ ? "- Codex login state: backed up the existing Codex auth and switched local desktop auth to The Claw Bay API-key mode."
3344
+ : "- Codex login state: seeded local desktop auth in The Claw Bay API-key mode.");
3345
+ }
3346
+ else if (authSeedResult?.action === "updated") {
3347
+ this.log("- Codex login state: refreshed the local The Claw Bay API-key auth seed.");
3343
3348
  }
3344
- else if (authSeedCleanup?.action === "removed") {
3345
- this.log("- Codex login state: removed the older The Claw Bay ChatGPT-token override for compatibility with current Codex releases.");
3349
+ else if (authSeedResult?.action === "already_seeded") {
3350
+ this.log("- Codex login state: local desktop auth was already pinned to The Claw Bay API-key mode.");
3346
3351
  }
3347
- else if (authSeedCleanup?.warning) {
3348
- this.log(`- Codex login state: ${authSeedCleanup.warning}`);
3352
+ else if (authSeedResult?.warning) {
3353
+ this.log(`- Codex login state: ${authSeedResult.warning}`);
3349
3354
  }
3350
3355
  else {
3351
- this.log("- Codex login state: using provider-scoped bearer auth only; no fake ChatGPT auth tokens were seeded.");
3356
+ this.log("- Codex login state: no change.");
3352
3357
  }
3353
3358
  }
3354
3359
  else if (codexDetected) {
@@ -12,10 +12,12 @@ export type CleanupCodexAuthResult = {
12
12
  export declare function ensureCodexApiKeyAuth(params: {
13
13
  codexHome: string;
14
14
  apiKey: string;
15
+ replaceExistingAuth?: boolean;
15
16
  }): Promise<EnsureCodexAuthResult>;
16
17
  export declare function ensureCodexUsageLimitAuth(params: {
17
18
  codexHome: string;
18
19
  apiKey: string;
20
+ replaceExistingAuth?: boolean;
19
21
  }): Promise<EnsureCodexAuthResult>;
20
22
  export declare function cleanupSeededCodexAuth(params: {
21
23
  codexHome: string;
@@ -236,7 +236,7 @@ async function ensureCodexApiKeyAuth(params) {
236
236
  codexHome: params.codexHome,
237
237
  apiKey: params.apiKey,
238
238
  mode: "api-key",
239
- replaceExistingAuth: false,
239
+ replaceExistingAuth: params.replaceExistingAuth ?? false,
240
240
  });
241
241
  }
242
242
  async function ensureCodexUsageLimitAuth(params) {
@@ -39,6 +39,7 @@ const FALLBACK_BASE_INSTRUCTIONS = `You are Codex, a coding agent based on GPT-5
39
39
  - Prefer rg or rg --files for search when available.
40
40
  - Make safe, minimal code changes that preserve user work.
41
41
  - Explain what changed and any remaining risk clearly.`;
42
+ const SEEDED_CACHE_FRESHNESS_YEARS = 10;
42
43
  function objectRecordOr(value) {
43
44
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
44
45
  return value;
@@ -239,11 +240,25 @@ function buildSeedModel(models, modelId) {
239
240
  }
240
241
  return normalizeSeedModel(null, modelId);
241
242
  }
243
+ function hasSeededTheClawBayModel(models) {
244
+ return models.some((model) => hasSeedMarker(model));
245
+ }
246
+ function nextFetchedAtIsoForDocument(doc) {
247
+ if (!hasSeededTheClawBayModel(doc.models)) {
248
+ return new Date().toISOString();
249
+ }
250
+ // Current desktop Codex builds only consult models_cache.json for custom-provider
251
+ // catalogs in API-key mode, then silently fall back to compiled OpenAI models once
252
+ // the five-minute TTL expires. Keep the seeded cache durable until setup refreshes it.
253
+ const next = new Date();
254
+ next.setUTCFullYear(next.getUTCFullYear() + SEEDED_CACHE_FRESHNESS_YEARS);
255
+ return next.toISOString();
256
+ }
242
257
  function setCacheFreshnessMetadata(doc, clientVersion) {
243
258
  let changed = false;
244
- const now = new Date().toISOString();
245
- if (doc.fetched_at !== now) {
246
- doc.fetched_at = now;
259
+ const fetchedAt = nextFetchedAtIsoForDocument(doc);
260
+ if (doc.fetched_at !== fetchedAt) {
261
+ doc.fetched_at = fetchedAt;
247
262
  changed = true;
248
263
  }
249
264
  if (clientVersion && doc.client_version !== clientVersion) {
@@ -265,6 +280,159 @@ function runVersionCommand(command, args) {
265
280
  return null;
266
281
  return parseCodexVersion(`${run.stdout ?? ""}\n${run.stderr ?? ""}`);
267
282
  }
283
+ function compareCodexVersions(left, right) {
284
+ const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
285
+ const rightParts = right.split(".").map((part) => Number.parseInt(part, 10));
286
+ const maxLen = Math.max(leftParts.length, rightParts.length);
287
+ for (let index = 0; index < maxLen; index += 1) {
288
+ const leftPart = leftParts[index] ?? 0;
289
+ const rightPart = rightParts[index] ?? 0;
290
+ if (leftPart !== rightPart)
291
+ return leftPart - rightPart;
292
+ }
293
+ return 0;
294
+ }
295
+ function highestCodexVersion(candidates) {
296
+ const versions = candidates
297
+ .map((candidate) => (candidate ? parseCodexVersion(candidate) : null))
298
+ .filter((candidate) => Boolean(candidate));
299
+ if (versions.length === 0)
300
+ return null;
301
+ versions.sort((left, right) => compareCodexVersions(right, left));
302
+ return versions[0] ?? null;
303
+ }
304
+ function uniqueStrings(values) {
305
+ return Array.from(new Set(values.filter(Boolean)));
306
+ }
307
+ function isWslInteropRuntime() {
308
+ return node_os_1.default.platform() === "linux" && Boolean(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);
309
+ }
310
+ function readWindowsCommandStdout(command) {
311
+ const shell = node_os_1.default.platform() === "win32" ? "powershell.exe" : isWslInteropRuntime() ? "powershell.exe" : null;
312
+ if (!shell)
313
+ return null;
314
+ const result = (0, node_child_process_1.spawnSync)(shell, ["-NoProfile", "-Command", command], {
315
+ encoding: "utf8",
316
+ stdio: ["ignore", "pipe", "ignore"],
317
+ });
318
+ if (result.status !== 0)
319
+ return null;
320
+ const next = result.stdout.replace(/\r/g, "").trim();
321
+ return next ? next : null;
322
+ }
323
+ function resolveWindowsPathForHost(input) {
324
+ if (!input)
325
+ return null;
326
+ const trimmed = input.trim();
327
+ if (!trimmed)
328
+ return null;
329
+ if (node_os_1.default.platform() === "win32")
330
+ return trimmed;
331
+ const match = /^([A-Za-z]):\\(.*)$/.exec(trimmed);
332
+ if (!match)
333
+ return null;
334
+ const drive = match[1].toLowerCase();
335
+ const rest = match[2].replace(/\\/g, "/");
336
+ return node_path_1.default.posix.join("/mnt", drive, rest);
337
+ }
338
+ function resolveWindowsHomeDirForHost() {
339
+ const reported = readWindowsCommandStdout("$env:USERPROFILE");
340
+ if (reported)
341
+ return resolveWindowsPathForHost(reported);
342
+ if (node_os_1.default.platform() === "win32")
343
+ return node_os_1.default.homedir();
344
+ return null;
345
+ }
346
+ function editorExtensionDirCandidates() {
347
+ const home = node_os_1.default.homedir();
348
+ const dirs = [];
349
+ if (node_os_1.default.platform() === "darwin") {
350
+ 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"));
351
+ return uniqueStrings(dirs);
352
+ }
353
+ if (node_os_1.default.platform() === "win32") {
354
+ const homeDir = process.env.USERPROFILE?.trim() || home;
355
+ 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"));
356
+ return uniqueStrings(dirs);
357
+ }
358
+ 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"));
359
+ if (isWslInteropRuntime()) {
360
+ const windowsHome = resolveWindowsHomeDirForHost();
361
+ if (windowsHome) {
362
+ 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"));
363
+ }
364
+ }
365
+ return uniqueStrings(dirs);
366
+ }
367
+ async function pathExists(filePath) {
368
+ try {
369
+ await promises_1.default.access(filePath);
370
+ return true;
371
+ }
372
+ catch {
373
+ return false;
374
+ }
375
+ }
376
+ function codexBinaryCandidatesForBinRoot(binRoot) {
377
+ const runtimePreferred = node_os_1.default.platform() === "win32"
378
+ ? [
379
+ node_path_1.default.join(binRoot, "windows-x86_64", "codex.exe"),
380
+ node_path_1.default.join(binRoot, "windows-arm64", "codex.exe"),
381
+ ]
382
+ : node_os_1.default.platform() === "darwin"
383
+ ? [
384
+ node_path_1.default.join(binRoot, "darwin-aarch64", "codex"),
385
+ node_path_1.default.join(binRoot, "darwin-arm64", "codex"),
386
+ node_path_1.default.join(binRoot, "darwin-x86_64", "codex"),
387
+ ]
388
+ : [
389
+ node_path_1.default.join(binRoot, "linux-x86_64", "codex"),
390
+ node_path_1.default.join(binRoot, "linux-arm64", "codex"),
391
+ ];
392
+ return uniqueStrings([
393
+ ...runtimePreferred,
394
+ node_path_1.default.join(binRoot, "codex"),
395
+ node_path_1.default.join(binRoot, "codex.exe"),
396
+ ]);
397
+ }
398
+ async function inferCodexClientVersionsFromInstalledExtensions() {
399
+ const versions = new Set();
400
+ for (const extensionDir of editorExtensionDirCandidates()) {
401
+ let entries = [];
402
+ try {
403
+ entries = await promises_1.default.readdir(extensionDir, { withFileTypes: true });
404
+ }
405
+ catch (error) {
406
+ const err = error;
407
+ if (err.code === "ENOENT")
408
+ continue;
409
+ throw error;
410
+ }
411
+ for (const entry of entries) {
412
+ if (!entry.isDirectory())
413
+ continue;
414
+ if (!entry.name.toLowerCase().startsWith("openai.chatgpt-"))
415
+ continue;
416
+ const binRoot = node_path_1.default.join(extensionDir, entry.name, "bin");
417
+ for (const candidate of codexBinaryCandidatesForBinRoot(binRoot)) {
418
+ if (!(await pathExists(candidate)))
419
+ continue;
420
+ const parsed = runVersionCommand(candidate, ["--version"]);
421
+ if (parsed)
422
+ versions.add(parsed);
423
+ }
424
+ }
425
+ }
426
+ return [...versions];
427
+ }
428
+ async function inferCodexClientVersionFromVersionJson(codexHome) {
429
+ const parsed = await readJsonIfExists(node_path_1.default.join(codexHome, "version.json"));
430
+ const obj = objectRecordOr(parsed);
431
+ if (!obj)
432
+ return null;
433
+ const latestVersion = typeof obj.latest_version === "string" ? obj.latest_version : "";
434
+ return latestVersion ? parseCodexVersion(latestVersion) : null;
435
+ }
268
436
  function resolvePythonCommand() {
269
437
  const candidates = [
270
438
  { command: "python3", args: ["-c", "import sqlite3"] },
@@ -355,20 +523,29 @@ async function inferCodexClientVersionFromStateDb(codexHome) {
355
523
  return null;
356
524
  }
357
525
  async function inferCodexClientVersion(codexHome) {
526
+ const versions = [];
358
527
  if (node_os_1.default.platform() === "win32") {
359
528
  for (const commandLine of ["codex --version", "codex-cli --version", "codex.cmd --version", "codex-cli.cmd --version"]) {
360
529
  const parsed = runVersionCommand("cmd.exe", ["/d", "/s", "/c", commandLine]);
361
530
  if (parsed)
362
- return parsed;
531
+ versions.push(parsed);
363
532
  }
364
- return await inferCodexClientVersionFromStateDb(codexHome);
365
533
  }
366
- for (const command of ["codex", "codex-cli"]) {
367
- const parsed = runVersionCommand(command, ["--version"]);
368
- if (parsed)
369
- return parsed;
534
+ else {
535
+ for (const command of ["codex", "codex-cli"]) {
536
+ const parsed = runVersionCommand(command, ["--version"]);
537
+ if (parsed)
538
+ versions.push(parsed);
539
+ }
370
540
  }
371
- return await inferCodexClientVersionFromStateDb(codexHome);
541
+ versions.push(...await inferCodexClientVersionsFromInstalledExtensions());
542
+ const versionJsonVersion = await inferCodexClientVersionFromVersionJson(codexHome);
543
+ if (versionJsonVersion)
544
+ versions.push(versionJsonVersion);
545
+ const stateDbVersion = await inferCodexClientVersionFromStateDb(codexHome);
546
+ if (stateDbVersion)
547
+ versions.push(stateDbVersion);
548
+ return highestCodexVersion(versions);
372
549
  }
373
550
  function normalizeCacheDocument(value) {
374
551
  const doc = objectRecordOr(value);
@@ -402,7 +579,7 @@ async function ensureCodexModelCacheHasGpt54(params) {
402
579
  nextState[modelId] = fingerprintModel(seed);
403
580
  }
404
581
  const doc = {
405
- fetched_at: new Date().toISOString(),
582
+ fetched_at: nextFetchedAtIsoForDocument({ models: docModels }),
406
583
  client_version: clientVersion,
407
584
  models: docModels,
408
585
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "theclawbay",
3
- "version": "0.3.63",
3
+ "version": "0.3.65",
4
4
  "description": "CLI for connecting Codex, Continue, Cline, GSD, OpenClaw, OpenCode, Kilo, Roo Code, Aider, experimental Trae, and experimental Zo to The Claw Bay.",
5
5
  "license": "MIT",
6
6
  "repository": {