theclawbay 0.3.79 → 0.3.81

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.
@@ -672,6 +672,54 @@ function shellQuote(value) {
672
672
  function powerShellQuote(value) {
673
673
  return `'${value.replace(/'/g, "''")}'`;
674
674
  }
675
+ function shellCommandForCurrentPlatform(command) {
676
+ if (node_os_1.default.platform() === "win32") {
677
+ return { executable: "cmd.exe", args: ["/c", command] };
678
+ }
679
+ return { executable: "bash", args: ["-lc", command] };
680
+ }
681
+ function installPlanForSetupClient(clientId) {
682
+ if (clientId === "codex") {
683
+ return {
684
+ summary: "Install Codex CLI",
685
+ command: "npm install -g @openai/codex",
686
+ };
687
+ }
688
+ if (clientId === "claude") {
689
+ return {
690
+ summary: "Install Claude Code CLI",
691
+ command: "npm install -g @anthropic-ai/claude-code",
692
+ };
693
+ }
694
+ if (clientId === "opencode") {
695
+ return {
696
+ summary: "Install OpenCode CLI",
697
+ command: "npm install -g opencode-ai",
698
+ };
699
+ }
700
+ if (clientId === "aider") {
701
+ return {
702
+ summary: "Install Aider",
703
+ command: node_os_1.default.platform() === "win32"
704
+ ? "py -m pip install --user --upgrade aider-chat"
705
+ : "python3 -m pip install --user --upgrade aider-chat",
706
+ };
707
+ }
708
+ return null;
709
+ }
710
+ function isSetupClientInstallable(clientId) {
711
+ return installPlanForSetupClient(clientId) !== null;
712
+ }
713
+ function runInstallPlan(plan) {
714
+ const { executable, args } = shellCommandForCurrentPlatform(plan.command);
715
+ const run = (0, node_child_process_1.spawnSync)(executable, args, {
716
+ stdio: "inherit",
717
+ env: process.env,
718
+ });
719
+ if (run.status === 0)
720
+ return;
721
+ throw new Error(`${plan.summary} failed.`);
722
+ }
675
723
  function modelDisplayName(modelId) {
676
724
  return MODEL_DISPLAY_NAMES[modelId] ?? prettyModelId(modelId);
677
725
  }
@@ -1149,17 +1197,18 @@ async function promptForSetupClients(clients) {
1149
1197
  stdout.write(`${paint("Move to Apply setup and press Enter when you're ready.", ansi.dim, ansi.gray)}\n\n`);
1150
1198
  for (const [index, option] of options.entries()) {
1151
1199
  const pointer = index === cursor ? paint(">", ansi.bold) : " ";
1152
- const mark = option.detected
1200
+ const selectable = option.detected || option.installable;
1201
+ const mark = selectable
1153
1202
  ? option.checked
1154
1203
  ? paint("[x]", ansi.green, ansi.bold)
1155
1204
  : paint("[ ]", ansi.gray)
1156
1205
  : paint("[-]", ansi.red, ansi.bold);
1157
1206
  const badge = option.detected
1158
- ? option.recommended
1159
- ? paint("recommended", ansi.green)
1160
- : paint("optional", ansi.yellow)
1161
- : paint("not detected", ansi.red);
1162
- const label = option.detected
1207
+ ? paint("found", ansi.green)
1208
+ : option.installable
1209
+ ? paint(option.checked ? "will install" : "available to install", ansi.yellow)
1210
+ : paint("not found", ansi.red);
1211
+ const label = selectable
1163
1212
  ? formatSetupClientLabel(option, hyperlinksEnabled)
1164
1213
  : paint(formatSetupClientLabel(option, hyperlinksEnabled), ansi.dim, ansi.gray);
1165
1214
  stdout.write(`${pointer} ${index + 1}. ${mark} ${label} ${badge}\n`);
@@ -1189,13 +1238,18 @@ async function promptForSetupClients(clients) {
1189
1238
  if (!option)
1190
1239
  return;
1191
1240
  cursor = index;
1192
- if (!option.detected) {
1241
+ if (!option.detected && !option.installable) {
1193
1242
  hint = `${option.label} is not detected on this machine yet, so it cannot be selected.`;
1194
1243
  render();
1195
1244
  return;
1196
1245
  }
1197
1246
  option.checked = !option.checked;
1198
- hint = `${option.label} ${option.checked ? "selected" : "cleared"}.`;
1247
+ if (option.checked && !option.detected && option.installable) {
1248
+ hint = `${option.label} selected. Setup will install it first.`;
1249
+ }
1250
+ else {
1251
+ hint = `${option.label} ${option.checked ? "selected" : "cleared"}.`;
1252
+ }
1199
1253
  render();
1200
1254
  };
1201
1255
  const onKeypress = (_str, key) => {
@@ -1252,6 +1306,7 @@ async function promptForSetupClients(clients) {
1252
1306
  }
1253
1307
  };
1254
1308
  (0, node_readline_1.emitKeypressEvents)(stdin);
1309
+ stdin.resume();
1255
1310
  if (stdin.isTTY)
1256
1311
  stdin.setRawMode(true);
1257
1312
  stdout.write("\x1b[?25l");
@@ -1265,7 +1320,7 @@ function resolveSetupClientSelection(params) {
1265
1320
  for (const client of setupClients) {
1266
1321
  if (!flagSelection.has(client.id))
1267
1322
  continue;
1268
- if (!client.detected) {
1323
+ if (!client.detected && !client.installable) {
1269
1324
  throw new Error(`${client.label} is not detected on this machine, so it cannot be selected yet.`);
1270
1325
  }
1271
1326
  }
@@ -1276,6 +1331,20 @@ function resolveSetupClientSelection(params) {
1276
1331
  return Promise.resolve(defaults);
1277
1332
  return promptForSetupClients(setupClients);
1278
1333
  }
1334
+ function installSelectedMissingClients(params) {
1335
+ const installed = [];
1336
+ for (const client of params.setupClients) {
1337
+ if (!params.selectedClientIds.has(client.id) || client.detected || !client.installable)
1338
+ continue;
1339
+ const plan = installPlanForSetupClient(client.id);
1340
+ if (!plan)
1341
+ continue;
1342
+ params.log(`Installing ${client.summaryLabel}...`);
1343
+ runInstallPlan(plan);
1344
+ installed.push(client.summaryLabel);
1345
+ }
1346
+ return installed;
1347
+ }
1279
1348
  async function promptForCodexConversationMigration() {
1280
1349
  if (!process.stdin.isTTY || !process.stdout.isTTY)
1281
1350
  return false;
@@ -1290,6 +1359,7 @@ async function promptForCodexConversationMigration() {
1290
1359
  }
1291
1360
  finally {
1292
1361
  rl.close();
1362
+ process.stdin.resume();
1293
1363
  }
1294
1364
  }
1295
1365
  async function resolveCodexConversationMigrationSelection(params) {
@@ -1319,6 +1389,7 @@ async function resolveDeviceLabel(params) {
1319
1389
  }
1320
1390
  finally {
1321
1391
  rl.close();
1392
+ process.stdin.resume();
1322
1393
  }
1323
1394
  }
1324
1395
  async function promptForGroupedModelSelection(params) {
@@ -1480,6 +1551,7 @@ async function promptForGroupedModelSelection(params) {
1480
1551
  resolvePromise = resolve;
1481
1552
  });
1482
1553
  (0, node_readline_1.emitKeypressEvents)(stdin);
1554
+ stdin.resume();
1483
1555
  if (stdin.isTTY)
1484
1556
  stdin.setRawMode(true);
1485
1557
  stdout.write("\x1b[?25l");
@@ -1562,6 +1634,7 @@ async function promptForPrimaryModel(models, defaultModel) {
1562
1634
  resolvePromise = resolve;
1563
1635
  });
1564
1636
  (0, node_readline_1.emitKeypressEvents)(stdin);
1637
+ stdin.resume();
1565
1638
  if (stdin.isTTY)
1566
1639
  stdin.setRawMode(true);
1567
1640
  stdout.write("\x1b[?25l");
@@ -1950,7 +2023,7 @@ function isOpenAiCompatibleSetupModelId(modelId) {
1950
2023
  const trimmed = modelId.trim();
1951
2024
  if (!trimmed)
1952
2025
  return false;
1953
- return !trimmed.startsWith("claude-");
2026
+ return !trimmed.startsWith("claude-") && SUPPORTED_MODEL_IDS.includes(trimmed);
1954
2027
  }
1955
2028
  function formatDotEnvValue(value) {
1956
2029
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
@@ -2033,7 +2106,7 @@ async function writeHermesConfig(params) {
2033
2106
  }
2034
2107
  async function resolveModels(backendUrl, apiKey) {
2035
2108
  const { ids, failure } = await fetchBackendModelIds(backendUrl, apiKey);
2036
- const filteredIds = (ids ?? []).filter(isOpenAiCompatibleSetupModelId);
2109
+ const filteredIds = uniqueStrings((ids ?? []).filter(isOpenAiCompatibleSetupModelId));
2037
2110
  const available = new Set(filteredIds);
2038
2111
  let selected = DEFAULT_CODEX_MODEL;
2039
2112
  let note;
@@ -2060,10 +2133,14 @@ async function resolveModels(backendUrl, apiKey) {
2060
2133
  unique.push(modelId);
2061
2134
  };
2062
2135
  pushUnique(selected);
2063
- for (const modelId of DEFAULT_MODELS)
2064
- pushUnique(modelId);
2065
- for (const modelId of filteredIds)
2066
- pushUnique(modelId);
2136
+ if (filteredIds.length > 0) {
2137
+ for (const modelId of filteredIds)
2138
+ pushUnique(modelId);
2139
+ }
2140
+ else {
2141
+ for (const modelId of DEFAULT_MODELS)
2142
+ pushUnique(modelId);
2143
+ }
2067
2144
  return {
2068
2145
  model: selected,
2069
2146
  models: unique.map((modelId) => ({ id: modelId, name: modelDisplayName(modelId) || prettyModelId(modelId) })),
@@ -3485,6 +3562,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3485
3562
  summaryLabel: "Codex",
3486
3563
  detected: codexDetected,
3487
3564
  recommended: true,
3565
+ installable: true,
3488
3566
  icon: "◎",
3489
3567
  siteUrl: "https://openai.com/codex",
3490
3568
  },
@@ -3494,6 +3572,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3494
3572
  summaryLabel: "Claude",
3495
3573
  detected: claudeDetected,
3496
3574
  recommended: true,
3575
+ installable: true,
3497
3576
  icon: "✳",
3498
3577
  siteUrl: "https://code.claude.com",
3499
3578
  },
@@ -3503,6 +3582,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3503
3582
  summaryLabel: "Continue",
3504
3583
  detected: continueDetected,
3505
3584
  recommended: true,
3585
+ installable: false,
3506
3586
  icon: "▶",
3507
3587
  siteUrl: "https://continue.dev",
3508
3588
  },
@@ -3512,6 +3592,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3512
3592
  summaryLabel: "Cline",
3513
3593
  detected: clineDetected,
3514
3594
  recommended: true,
3595
+ installable: false,
3515
3596
  icon: "◈",
3516
3597
  siteUrl: "https://cline.bot",
3517
3598
  },
@@ -3521,6 +3602,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3521
3602
  summaryLabel: "GSD",
3522
3603
  detected: gsdDetected,
3523
3604
  recommended: true,
3605
+ installable: false,
3524
3606
  icon: "▣",
3525
3607
  siteUrl: "https://github.com/gsd-build/gsd-2",
3526
3608
  },
@@ -3530,6 +3612,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3530
3612
  summaryLabel: "OpenClaw",
3531
3613
  detected: hasCommand("openclaw"),
3532
3614
  recommended: true,
3615
+ installable: false,
3533
3616
  icon: "🦞",
3534
3617
  siteUrl: "https://openclaw.ai",
3535
3618
  },
@@ -3539,6 +3622,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3539
3622
  summaryLabel: "OpenCode",
3540
3623
  detected: openCodeDetected,
3541
3624
  recommended: true,
3625
+ installable: true,
3542
3626
  icon: "⌘",
3543
3627
  siteUrl: "https://opencode.ai",
3544
3628
  },
@@ -3548,6 +3632,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3548
3632
  summaryLabel: "Kilo Code",
3549
3633
  detected: kiloDetected,
3550
3634
  recommended: true,
3635
+ installable: false,
3551
3636
  icon: "⚡",
3552
3637
  siteUrl: "https://kilo.ai",
3553
3638
  },
@@ -3557,6 +3642,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3557
3642
  summaryLabel: "Roo Code",
3558
3643
  detected: rooDetected,
3559
3644
  recommended: true,
3645
+ installable: false,
3560
3646
  icon: "🦘",
3561
3647
  siteUrl: "https://roocode.com",
3562
3648
  },
@@ -3566,6 +3652,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3566
3652
  summaryLabel: "Trae",
3567
3653
  detected: traeDetected,
3568
3654
  recommended: false,
3655
+ installable: false,
3569
3656
  icon: "△",
3570
3657
  siteUrl: "https://www.trae.ai",
3571
3658
  },
@@ -3575,6 +3662,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3575
3662
  summaryLabel: "Aider",
3576
3663
  detected: aiderDetected,
3577
3664
  recommended: true,
3665
+ installable: true,
3578
3666
  icon: "✦",
3579
3667
  siteUrl: "https://aider.chat",
3580
3668
  },
@@ -3584,6 +3672,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3584
3672
  summaryLabel: "Zo",
3585
3673
  detected: zoDetected,
3586
3674
  recommended: false,
3675
+ installable: false,
3587
3676
  icon: "◉",
3588
3677
  siteUrl: "https://www.zo.computer",
3589
3678
  },
@@ -3593,6 +3682,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3593
3682
  summaryLabel: "Hermes",
3594
3683
  detected: hermesDetected,
3595
3684
  recommended: true,
3685
+ installable: false,
3596
3686
  icon: "☿",
3597
3687
  siteUrl: "https://hermes-agent.nousresearch.com/docs/",
3598
3688
  },
@@ -3602,6 +3692,11 @@ class SetupCommand extends base_command_1.BaseCommand {
3602
3692
  flagSelection: parseSetupClientFlags(flags.clients),
3603
3693
  skipPrompt: flags.yes,
3604
3694
  });
3695
+ const installedMissingClients = installSelectedMissingClients({
3696
+ setupClients,
3697
+ selectedClientIds: selectedSetupClients,
3698
+ log: (message) => this.log(message),
3699
+ });
3605
3700
  const migrateCodexConversations = await resolveCodexConversationMigrationSelection({
3606
3701
  codexSelected: selectedSetupClients.has("codex"),
3607
3702
  flagValue: flags["migrate-conversations"],
@@ -3654,6 +3749,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3654
3749
  let authSeedCleanup = null;
3655
3750
  let stateDbMigration = null;
3656
3751
  let modelCacheMigration = null;
3752
+ let installedClientsSummary = installedMissingClients;
3657
3753
  let continueConfigPath = null;
3658
3754
  let clineConfigPaths = [];
3659
3755
  let gsdConfigPaths = [];
@@ -3716,6 +3812,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3716
3812
  this.log(` - ${modelId}${marker}`);
3717
3813
  }
3718
3814
  }
3815
+ return;
3719
3816
  }
3720
3817
  const claudeSelected = selectedSetupClients.has("claude");
3721
3818
  const claudeEnvEnabled = claudeSelected && claudeAccess.enabled;
@@ -3923,6 +4020,9 @@ class SetupCommand extends base_command_1.BaseCommand {
3923
4020
  if (backupSummary) {
3924
4021
  summaryNotes.add(`Created a local backup snapshot (${backupSummary.id}) before changing client configs.`);
3925
4022
  }
4023
+ if (installedClientsSummary.length > 0) {
4024
+ summaryNotes.add(`Installed missing clients: ${installedClientsSummary.join(", ")}.`);
4025
+ }
3926
4026
  if (selectedSetupClients.has("claude") && claudeAccess?.enabled) {
3927
4027
  summaryNotes.add("Claude Code: exported ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY.");
3928
4028
  if (claudeDesktop3pConfigPathManaged) {
@@ -3977,6 +4077,9 @@ class SetupCommand extends base_command_1.BaseCommand {
3977
4077
  this.log(`- Managed config: ${paths_1.managedConfigPath}`);
3978
4078
  this.log(`- Backend: ${backendUrl}`);
3979
4079
  this.log(`- Auth mode: ${authType}`);
4080
+ if (installedClientsSummary.length > 0) {
4081
+ this.log(`- Installed missing clients: ${installedClientsSummary.join(", ")}`);
4082
+ }
3980
4083
  if (backupSummary) {
3981
4084
  this.log(`- Backup: ${backupSummary.id} (${backupSummary.directory})`);
3982
4085
  }
@@ -30,6 +30,21 @@ function shellRcTargets() {
30
30
  kind: "file",
31
31
  }));
32
32
  }
33
+ function codexTargets() {
34
+ return [
35
+ ["codex-config", "Codex config", node_path_1.default.join(paths_1.codexDir, "config.toml"), "file"],
36
+ ["codex-auth", "Codex auth state", node_path_1.default.join(paths_1.codexDir, "auth.json"), "file"],
37
+ ["codex-credentials", "Codex credentials", node_path_1.default.join(paths_1.codexDir, ".credentials.json"), "file"],
38
+ ["codex-accounts", "Codex accounts directory", node_path_1.default.join(paths_1.codexDir, "accounts"), "directory"],
39
+ ["codex-managed", "Codex The Claw Bay managed state", node_path_1.default.join(paths_1.codexDir, "theclawbay.managed.json"), "file"],
40
+ ["codex-model-cache", "Codex model cache", node_path_1.default.join(paths_1.codexDir, "models_cache.json"), "file"],
41
+ ].map(([id, label, targetPath, kind]) => ({
42
+ id,
43
+ label,
44
+ targetPath,
45
+ kind: kind,
46
+ }));
47
+ }
33
48
  function editorSettingsTargets() {
34
49
  const home = node_os_1.default.homedir();
35
50
  const targets = [];
@@ -59,12 +74,6 @@ function backupTargets() {
59
74
  targetPath: paths_1.theclawbayConfigDir,
60
75
  kind: "directory",
61
76
  },
62
- {
63
- id: "codex-home",
64
- label: "Codex home directory",
65
- targetPath: paths_1.codexDir,
66
- kind: "directory",
67
- },
68
77
  {
69
78
  id: "continue-home",
70
79
  label: "Continue config directory",
@@ -127,7 +136,7 @@ function backupTargets() {
127
136
  },
128
137
  ];
129
138
  const deduped = new Map();
130
- for (const target of [...targets, ...shellRcTargets(), ...editorSettingsTargets()]) {
139
+ for (const target of [...targets, ...codexTargets(), ...shellRcTargets(), ...editorSettingsTargets()]) {
131
140
  deduped.set(target.targetPath, target);
132
141
  }
133
142
  return [...deduped.values()];
@@ -174,7 +183,13 @@ function formatBackupTimestamp(isoString) {
174
183
  }
175
184
  async function createBackup(reason) {
176
185
  const now = new Date();
177
- const id = backupIdFromDate(now);
186
+ const baseId = backupIdFromDate(now);
187
+ let id = baseId;
188
+ let suffix = 2;
189
+ while (await pathExists(node_path_1.default.join(paths_1.theclawbayBackupDir, id))) {
190
+ id = `${baseId}-${suffix}`;
191
+ suffix += 1;
192
+ }
178
193
  const backupDirectory = node_path_1.default.join(paths_1.theclawbayBackupDir, id);
179
194
  const snapshotRoot = node_path_1.default.join(backupDirectory, "files");
180
195
  await ensureCleanDirectory(snapshotRoot);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "theclawbay",
3
- "version": "0.3.79",
3
+ "version": "0.3.81",
4
4
  "description": "CLI for connecting Codex, Hermes Agent, Gemini-compatible apps, 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": {