theclawbay 0.3.56 → 0.3.58

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.
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const promises_1 = __importDefault(require("node:fs/promises"));
7
7
  const node_os_1 = __importDefault(require("node:os"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
+ const node_child_process_1 = require("node:child_process");
9
10
  const core_1 = require("@oclif/core");
10
11
  const yaml_1 = require("yaml");
11
12
  const base_command_1 = require("../lib/base-command");
@@ -17,6 +18,7 @@ const errors_1 = require("../lib/managed/errors");
17
18
  const supported_models_1 = require("../lib/supported-models");
18
19
  const paths_1 = require("../lib/config/paths");
19
20
  const OPENAI_PROVIDER_ID = "openai";
21
+ const ANTHROPIC_PROVIDER_ID = "anthropic";
20
22
  const DEFAULT_PROVIDER_ID = "theclawbay";
21
23
  const WAN_PROVIDER_ID = "theclawbay-wan";
22
24
  const MANAGED_START = "# theclawbay-managed:start";
@@ -49,6 +51,7 @@ const KILO_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayState
49
51
  const ROO_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "roo-settings.restore.json");
50
52
  const EDITOR_TERMINAL_ENV_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "editor-terminal-env.restore.json");
51
53
  const CLAUDE_EDITOR_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "claude-editor-settings.restore.json");
54
+ const CLAUDE_DESKTOP_3P_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "claude-desktop-3p.restore.json");
52
55
  const CODEX_FEATURE_FLAGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "codex-features.restore.json");
53
56
  const ROO_IMPORT_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "roo-code-settings.json");
54
57
  const MIGRATION_STATE_FILE = node_path_1.default.join(paths_1.codexDir, "theclawbay.migration.json");
@@ -319,6 +322,22 @@ function isTheClawBayOpenAiCompatibleBaseUrl(value) {
319
322
  return false;
320
323
  }
321
324
  }
325
+ function isTheClawBayAnthropicCompatibleBaseUrl(value) {
326
+ if (typeof value !== "string")
327
+ return false;
328
+ const normalized = value.trim();
329
+ if (!normalized)
330
+ return false;
331
+ try {
332
+ const parsed = new URL(normalized);
333
+ const hostname = parsed.hostname.toLowerCase();
334
+ const pathname = parsed.pathname.replace(/\/+$/g, "");
335
+ return hostname === THECLAWBAY_CANONICAL_API_HOST && pathname === "/anthropic/v1";
336
+ }
337
+ catch {
338
+ return false;
339
+ }
340
+ }
322
341
  function uniqueStrings(values) {
323
342
  const output = [];
324
343
  const seen = new Set();
@@ -348,6 +367,11 @@ function configDirCandidates(appName) {
348
367
  }
349
368
  return uniqueStrings(dirs);
350
369
  }
370
+ function claudeDesktop3pConfigPath() {
371
+ if (node_os_1.default.platform() !== "darwin")
372
+ return null;
373
+ return node_path_1.default.join(node_os_1.default.homedir(), "Library", "Application Support", "Claude-3p", "claude_desktop_config.json");
374
+ }
351
375
  function openCodeConfigCleanupTargets() {
352
376
  const home = node_os_1.default.homedir();
353
377
  const dirs = configDirCandidates("opencode");
@@ -399,6 +423,11 @@ function isManagedOpenCodeProvider(value) {
399
423
  const options = objectRecordOr(provider.options, {});
400
424
  return isTheClawBayOpenAiCompatibleBaseUrl(options.baseURL);
401
425
  }
426
+ function isManagedOpenCodeAnthropicProvider(value) {
427
+ const provider = objectRecordOr(value, {});
428
+ const options = objectRecordOr(provider.options, {});
429
+ return isTheClawBayAnthropicCompatibleBaseUrl(options.baseURL);
430
+ }
402
431
  function roamingAppDataDir() {
403
432
  if (process.env.APPDATA?.trim())
404
433
  return process.env.APPDATA;
@@ -764,13 +793,71 @@ async function cleanupShellFiles() {
764
793
  return updated;
765
794
  }
766
795
  function powerShellProfilePaths() {
767
- if (node_os_1.default.platform() !== "win32")
796
+ const candidates = new Set();
797
+ const addDocumentsProfiles = (documentsDir) => {
798
+ if (!documentsDir)
799
+ return;
800
+ candidates.add(node_path_1.default.join(documentsDir, "PowerShell", "Microsoft.PowerShell_profile.ps1"));
801
+ candidates.add(node_path_1.default.join(documentsDir, "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1"));
802
+ };
803
+ if (node_os_1.default.platform() === "win32") {
804
+ addDocumentsProfiles(node_path_1.default.join(node_os_1.default.homedir(), "Documents"));
805
+ addDocumentsProfiles(resolveWindowsDocumentsDirForHost());
806
+ return [...candidates];
807
+ }
808
+ if (!isWslInteropRuntime())
768
809
  return [];
769
- const home = node_os_1.default.homedir();
770
- return [
771
- node_path_1.default.join(home, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1"),
772
- node_path_1.default.join(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1"),
773
- ];
810
+ addDocumentsProfiles(resolveWindowsDocumentsDirForHost());
811
+ addDocumentsProfiles(resolveWindowsPathForHost(node_path_1.default.join(resolveWindowsHomeDirForHost() ?? "", "Documents")));
812
+ return [...candidates];
813
+ }
814
+ function isWslInteropRuntime() {
815
+ return node_os_1.default.platform() === "linux" && Boolean(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);
816
+ }
817
+ function readWindowsCommandStdout(command) {
818
+ const shell = node_os_1.default.platform() === "win32" ? "powershell.exe" : isWslInteropRuntime() ? "powershell.exe" : null;
819
+ if (!shell)
820
+ return null;
821
+ const result = (0, node_child_process_1.spawnSync)(shell, ["-NoProfile", "-Command", command], {
822
+ encoding: "utf8",
823
+ stdio: ["ignore", "pipe", "ignore"],
824
+ });
825
+ if (result.status !== 0)
826
+ return null;
827
+ const next = result.stdout.replace(/\r/g, "").trim();
828
+ return next ? next : null;
829
+ }
830
+ function resolveWindowsPathForHost(input) {
831
+ if (!input)
832
+ return null;
833
+ const trimmed = input.trim();
834
+ if (!trimmed)
835
+ return null;
836
+ if (node_os_1.default.platform() === "win32")
837
+ return trimmed;
838
+ const match = /^([A-Za-z]):\\(.*)$/.exec(trimmed);
839
+ if (!match)
840
+ return null;
841
+ const drive = match[1].toLowerCase();
842
+ const rest = match[2].replace(/\\/g, "/");
843
+ return node_path_1.default.posix.join("/mnt", drive, rest);
844
+ }
845
+ function resolveWindowsHomeDirForHost() {
846
+ const reported = readWindowsCommandStdout("$env:USERPROFILE");
847
+ if (reported)
848
+ return resolveWindowsPathForHost(reported);
849
+ if (node_os_1.default.platform() === "win32")
850
+ return node_os_1.default.homedir();
851
+ return null;
852
+ }
853
+ function resolveWindowsDocumentsDirForHost() {
854
+ const reported = readWindowsCommandStdout("[Environment]::GetFolderPath('MyDocuments')");
855
+ if (reported)
856
+ return resolveWindowsPathForHost(reported);
857
+ const home = resolveWindowsHomeDirForHost();
858
+ if (!home)
859
+ return null;
860
+ return node_path_1.default.join(home, "Documents");
774
861
  }
775
862
  async function cleanupPowerShellProfiles() {
776
863
  const updated = [];
@@ -888,6 +975,37 @@ async function cleanupClaudeEditorSettings() {
888
975
  await removeFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
889
976
  return updated;
890
977
  }
978
+ async function cleanupClaudeDesktop3pConfig() {
979
+ const snapshotRaw = await readFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
980
+ if (snapshotRaw === null || !snapshotRaw.trim())
981
+ return false;
982
+ let snapshot = null;
983
+ try {
984
+ snapshot = JSON.parse(snapshotRaw);
985
+ }
986
+ catch {
987
+ snapshot = null;
988
+ }
989
+ if (!snapshot?.configPath) {
990
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
991
+ return false;
992
+ }
993
+ if (!snapshot.existed) {
994
+ const removed = await removeFileIfExists(snapshot.configPath);
995
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
996
+ return removed;
997
+ }
998
+ const previousRaw = snapshot.previousRaw ?? "";
999
+ await promises_1.default.mkdir(node_path_1.default.dirname(snapshot.configPath), { recursive: true });
1000
+ const existingRaw = await readFileIfExists(snapshot.configPath);
1001
+ if (existingRaw === previousRaw) {
1002
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
1003
+ return false;
1004
+ }
1005
+ await promises_1.default.writeFile(snapshot.configPath, previousRaw, "utf8");
1006
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
1007
+ return true;
1008
+ }
891
1009
  async function cleanupCodexFeatureFlags() {
892
1010
  const snapshotRaw = await readFileIfExists(CODEX_FEATURE_FLAGS_STATE_PATH);
893
1011
  if (snapshotRaw === null || !snapshotRaw.trim())
@@ -966,7 +1084,7 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
966
1084
  if (typeof snapshot !== "object" || snapshot === null || Array.isArray(snapshot))
967
1085
  return null;
968
1086
  const obj = snapshot;
969
- if (obj.version === 2 && Array.isArray(obj.targets)) {
1087
+ if ((obj.version === 2 || obj.version === 3) && Array.isArray(obj.targets)) {
970
1088
  const targets = [];
971
1089
  for (const entry of obj.targets) {
972
1090
  if (typeof entry !== "object" || entry === null || Array.isArray(entry))
@@ -983,6 +1101,11 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
983
1101
  !Array.isArray(candidate.openAiProvider)
984
1102
  ? candidate.openAiProvider
985
1103
  : null,
1104
+ anthropicProvider: typeof candidate.anthropicProvider === "object" &&
1105
+ candidate.anthropicProvider !== null &&
1106
+ !Array.isArray(candidate.anthropicProvider)
1107
+ ? candidate.anthropicProvider
1108
+ : null,
986
1109
  model: typeof candidate.model === "string" ? candidate.model : null,
987
1110
  schema: typeof candidate.schema === "string" ? candidate.schema : null,
988
1111
  plugin: "plugin" in candidate
@@ -992,13 +1115,13 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
992
1115
  : undefined,
993
1116
  });
994
1117
  }
995
- return { version: 2, targets };
1118
+ return { version: 3, targets };
996
1119
  }
997
1120
  const looksLikeV1 = "openAiProvider" in obj || "model" in obj || "schema" in obj;
998
1121
  if (!looksLikeV1)
999
1122
  return null;
1000
1123
  return {
1001
- version: 2,
1124
+ version: 3,
1002
1125
  targets: [
1003
1126
  {
1004
1127
  configPath: fallbackConfigPath,
@@ -1067,6 +1190,15 @@ async function cleanupOpenCodeFamilyConfigs(params) {
1067
1190
  }
1068
1191
  else if (OPENAI_PROVIDER_ID in providerRoot) {
1069
1192
  delete providerRoot[OPENAI_PROVIDER_ID];
1193
+ changed = true;
1194
+ }
1195
+ if (target.anthropicProvider) {
1196
+ providerRoot[ANTHROPIC_PROVIDER_ID] = target.anthropicProvider;
1197
+ changed = true;
1198
+ }
1199
+ else if (ANTHROPIC_PROVIDER_ID in providerRoot) {
1200
+ delete providerRoot[ANTHROPIC_PROVIDER_ID];
1201
+ changed = true;
1070
1202
  }
1071
1203
  doc.provider = providerRoot;
1072
1204
  if (target.model === null) {
@@ -1125,6 +1257,10 @@ async function cleanupOpenCodeFamilyConfigs(params) {
1125
1257
  delete providerRoot[OPENAI_PROVIDER_ID];
1126
1258
  fileChanged = true;
1127
1259
  }
1260
+ if (isManagedOpenCodeAnthropicProvider(providerRoot[ANTHROPIC_PROVIDER_ID])) {
1261
+ delete providerRoot[ANTHROPIC_PROVIDER_ID];
1262
+ fileChanged = true;
1263
+ }
1128
1264
  if (!fileChanged)
1129
1265
  continue;
1130
1266
  changed = true;
@@ -1187,6 +1323,7 @@ class LogoutCommand extends base_command_1.BaseCommand {
1187
1323
  let updatedVsCodeHooks = [];
1188
1324
  let updatedEditorTerminalSettings = [];
1189
1325
  let updatedClaudeEditorSettings = [];
1326
+ let updatedClaudeDesktopConfig = false;
1190
1327
  let updatedCodexConfig = false;
1191
1328
  let updatedCodexFeatures = false;
1192
1329
  let updatedContinueConfig = false;
@@ -1239,6 +1376,7 @@ class LogoutCommand extends base_command_1.BaseCommand {
1239
1376
  updatedVsCodeHooks = await cleanupVsCodeHooks();
1240
1377
  updatedEditorTerminalSettings = await cleanupEditorTerminalEnvSettings();
1241
1378
  updatedClaudeEditorSettings = await cleanupClaudeEditorSettings();
1379
+ updatedClaudeDesktopConfig = await cleanupClaudeDesktop3pConfig();
1242
1380
  progress.update("Reverting Codex");
1243
1381
  updatedCodexConfig = await cleanupCodexConfig();
1244
1382
  updatedCodexFeatures = await cleanupCodexFeatureFlags();
@@ -1294,11 +1432,12 @@ class LogoutCommand extends base_command_1.BaseCommand {
1294
1432
  this.log("Logout complete");
1295
1433
  }
1296
1434
  const revertedTargets = [];
1435
+ const claudeChanged = updatedClaudeEditorSettings.length > 0 ||
1436
+ updatedClaudeDesktopConfig;
1297
1437
  const codexChanged = updatedCodexConfig ||
1298
1438
  updatedCodexFeatures ||
1299
1439
  updatedVsCodeHooks.length > 0 ||
1300
1440
  updatedEditorTerminalSettings.length > 0 ||
1301
- updatedClaudeEditorSettings.length > 0 ||
1302
1441
  sessionMigration.rewritten > 0 ||
1303
1442
  sessionMigration.retimed > 0 ||
1304
1443
  stateDbMigration.updated > 0 ||
@@ -1307,6 +1446,8 @@ class LogoutCommand extends base_command_1.BaseCommand {
1307
1446
  modelCacheCleanup.action === "removed";
1308
1447
  if (codexChanged)
1309
1448
  revertedTargets.push("Codex");
1449
+ if (claudeChanged)
1450
+ revertedTargets.push("Claude");
1310
1451
  if (updatedContinueConfig)
1311
1452
  revertedTargets.push("Continue");
1312
1453
  if (updatedClineConfig)
@@ -1348,7 +1489,8 @@ class LogoutCommand extends base_command_1.BaseCommand {
1348
1489
  updatedPowerShellProfiles.length > 0 ||
1349
1490
  updatedVsCodeHooks.length > 0 ||
1350
1491
  updatedEditorTerminalSettings.length > 0 ||
1351
- updatedClaudeEditorSettings.length > 0,
1492
+ updatedClaudeEditorSettings.length > 0 ||
1493
+ updatedClaudeDesktopConfig,
1352
1494
  notes: Array.from(summaryNotes),
1353
1495
  });
1354
1496
  return;
@@ -1366,6 +1508,7 @@ class LogoutCommand extends base_command_1.BaseCommand {
1366
1508
  this.log(`- VS Code env hooks updated: ${updatedVsCodeHooks.length ? updatedVsCodeHooks.join(", ") : "none"}`);
1367
1509
  this.log(`- Editor terminal env updated: ${updatedEditorTerminalSettings.length ? updatedEditorTerminalSettings.join(", ") : "none"}`);
1368
1510
  this.log(`- Claude editor settings updated: ${updatedClaudeEditorSettings.length ? updatedClaudeEditorSettings.join(", ") : "none"}`);
1511
+ this.log(`- Claude Desktop 3P config cleaned: ${updatedClaudeDesktopConfig ? "yes" : "no"}`);
1369
1512
  this.log(`- Codex config cleaned: ${updatedCodexConfig ? "yes" : "no"}`);
1370
1513
  this.log(`- Codex Apps feature flags restored: ${updatedCodexFeatures ? "yes" : "no"}`);
1371
1514
  if (sessionMigration.rewritten > 0) {
@@ -46,6 +46,7 @@ const CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME = "CLAUDE_CODE_DISABLE_NONESS
46
46
  const CLAUDE_ENV_ENABLE_TELEMETRY_NAME = "CLAUDE_CODE_ENABLE_TELEMETRY";
47
47
  const CLAUDE_CODE_EDITOR_ENV_SETTING = "claudeCode.environmentVariables";
48
48
  const CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING = "claudeCode.disableLoginPrompt";
49
+ const ANTHROPIC_PROVIDER_ID = "anthropic";
49
50
  const MANAGED_EDITOR_TERMINAL_ENV_NAMES = [
50
51
  ENV_KEY_NAME,
51
52
  CLAUDE_ENV_API_KEY_NAME,
@@ -70,6 +71,7 @@ const KILO_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayState
70
71
  const ROO_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "roo-settings.restore.json");
71
72
  const EDITOR_TERMINAL_ENV_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "editor-terminal-env.restore.json");
72
73
  const CLAUDE_EDITOR_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "claude-editor-settings.restore.json");
74
+ const CLAUDE_DESKTOP_3P_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "claude-desktop-3p.restore.json");
73
75
  const CODEX_FEATURE_FLAGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "codex-features.restore.json");
74
76
  const ROO_IMPORT_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "roo-code-settings.json");
75
77
  const MIGRATION_STATE_FILE = node_path_1.default.join(paths_1.codexDir, "theclawbay.migration.json");
@@ -607,6 +609,9 @@ function openAiCompatibleProxyUrl(backendUrl) {
607
609
  function anthropicCompatibleProxyUrl(backendUrl) {
608
610
  return `${publicApiOriginForBackendUrl(backendUrl)}/anthropic`;
609
611
  }
612
+ function anthropicCompatibleProxyV1Url(backendUrl) {
613
+ return `${anthropicCompatibleProxyUrl(backendUrl)}/v1`;
614
+ }
610
615
  function isTheClawBayOpenAiCompatibleBaseUrl(value) {
611
616
  if (typeof value !== "string")
612
617
  return false;
@@ -631,6 +636,22 @@ function isTheClawBayOpenAiCompatibleBaseUrl(value) {
631
636
  return false;
632
637
  }
633
638
  }
639
+ function isTheClawBayAnthropicCompatibleBaseUrl(value) {
640
+ if (typeof value !== "string")
641
+ return false;
642
+ const normalized = value.trim();
643
+ if (!normalized)
644
+ return false;
645
+ try {
646
+ const parsed = new URL(normalized);
647
+ const hostname = parsed.hostname.toLowerCase();
648
+ const pathname = trimTrailingSlash(parsed.pathname);
649
+ return hostname === THECLAWBAY_CANONICAL_API_HOST && pathname === "/anthropic/v1";
650
+ }
651
+ catch {
652
+ return false;
653
+ }
654
+ }
634
655
  function uniqueStrings(values) {
635
656
  const output = [];
636
657
  const seen = new Set();
@@ -681,6 +702,29 @@ function configDirCandidates(appName) {
681
702
  }
682
703
  return uniqueStrings(dirs);
683
704
  }
705
+ function claudeDesktop3pConfigPath() {
706
+ if (node_os_1.default.platform() !== "darwin")
707
+ return null;
708
+ return node_path_1.default.join(node_os_1.default.homedir(), "Library", "Application Support", "Claude-3p", "claude_desktop_config.json");
709
+ }
710
+ function claudeDesktopLogCandidates() {
711
+ if (node_os_1.default.platform() !== "darwin")
712
+ return [];
713
+ const home = node_os_1.default.homedir();
714
+ return [
715
+ node_path_1.default.join(home, "Library", "Logs", "Claude-3p", "main.log"),
716
+ node_path_1.default.join(home, "Library", "Logs", "Claude", "main.log"),
717
+ ];
718
+ }
719
+ function claudeDesktopAppCandidatePaths() {
720
+ if (node_os_1.default.platform() !== "darwin")
721
+ return [];
722
+ const home = node_os_1.default.homedir();
723
+ return uniqueStrings([
724
+ "/Applications/Claude.app",
725
+ node_path_1.default.join(home, "Applications", "Claude.app"),
726
+ ]);
727
+ }
684
728
  async function readFileIfExists(filePath) {
685
729
  try {
686
730
  return await promises_1.default.readFile(filePath, "utf8");
@@ -1289,6 +1333,33 @@ async function detectClaudeCodeClient() {
1289
1333
  }
1290
1334
  return false;
1291
1335
  }
1336
+ async function detectClaudeClient() {
1337
+ if (await detectClaudeCodeClient())
1338
+ return true;
1339
+ if (claudeDesktopAppCandidatePaths().some((candidate) => (0, node_fs_1.existsSync)(candidate)))
1340
+ return true;
1341
+ const configPath = claudeDesktop3pConfigPath();
1342
+ if (!configPath)
1343
+ return false;
1344
+ return (await pathExists(configPath)) || (await pathExists(node_path_1.default.dirname(configPath)));
1345
+ }
1346
+ async function detectOpenCodeClient() {
1347
+ if (hasCommand("opencode"))
1348
+ return true;
1349
+ const candidates = [
1350
+ ...resolveOpenCodeConfigTargets(),
1351
+ node_path_1.default.join(node_os_1.default.homedir(), ".opencode.json"),
1352
+ ];
1353
+ if (node_os_1.default.platform() === "darwin") {
1354
+ candidates.push("/Applications/OpenCode.app");
1355
+ candidates.push(node_path_1.default.join(node_os_1.default.homedir(), "Applications", "OpenCode.app"));
1356
+ }
1357
+ for (const candidate of candidates) {
1358
+ if (await pathExists(candidate))
1359
+ return true;
1360
+ }
1361
+ return false;
1362
+ }
1292
1363
  async function detectClineClient() {
1293
1364
  if (hasCommand("cline"))
1294
1365
  return true;
@@ -1699,7 +1770,7 @@ async function persistApiKeyEnv(params) {
1699
1770
  `export ${ENV_KEY_NAME}=${shellQuote(params.apiKey)}`,
1700
1771
  ];
1701
1772
  if (params.claudeEnabled) {
1702
- envLines.push("", "# Official Claude Code CLI", `export ${CLAUDE_ENV_API_KEY_NAME}=${shellQuote(params.apiKey)}`, `export ${CLAUDE_ENV_BASE_URL_NAME}=${shellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`, `export ${CLAUDE_ENV_SIMPLE_MODE_NAME}=1`, `export ${CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME}=1`, `export ${CLAUDE_ENV_ENABLE_TELEMETRY_NAME}=0`);
1773
+ envLines.push("", "# Official Claude Code CLI", `export ${CLAUDE_ENV_API_KEY_NAME}=${shellQuote(params.apiKey)}`, `export ${CLAUDE_ENV_BASE_URL_NAME}=${shellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`);
1703
1774
  }
1704
1775
  const envContents = `${envLines.join("\n")}\n`;
1705
1776
  await promises_1.default.writeFile(ENV_FILE, envContents, "utf8");
@@ -1734,16 +1805,10 @@ async function persistApiKeyEnv(params) {
1734
1805
  if (params.claudeEnabled) {
1735
1806
  process.env[CLAUDE_ENV_API_KEY_NAME] = params.apiKey;
1736
1807
  process.env[CLAUDE_ENV_BASE_URL_NAME] = anthropicCompatibleProxyUrl(params.backendUrl);
1737
- process.env[CLAUDE_ENV_SIMPLE_MODE_NAME] = "1";
1738
- process.env[CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME] = "1";
1739
- process.env[CLAUDE_ENV_ENABLE_TELEMETRY_NAME] = "0";
1740
1808
  }
1741
1809
  else {
1742
1810
  delete process.env[CLAUDE_ENV_API_KEY_NAME];
1743
1811
  delete process.env[CLAUDE_ENV_BASE_URL_NAME];
1744
- delete process.env[CLAUDE_ENV_SIMPLE_MODE_NAME];
1745
- delete process.env[CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME];
1746
- delete process.env[CLAUDE_ENV_ENABLE_TELEMETRY_NAME];
1747
1812
  }
1748
1813
  return updated;
1749
1814
  }
@@ -1755,19 +1820,77 @@ async function persistFishEnv(params) {
1755
1820
  `set -gx ${ENV_KEY_NAME} ${shellQuote(params.apiKey)}`,
1756
1821
  ];
1757
1822
  if (params.claudeEnabled) {
1758
- lines.push("", "# Official Claude Code CLI", `set -gx ${CLAUDE_ENV_API_KEY_NAME} ${shellQuote(params.apiKey)}`, `set -gx ${CLAUDE_ENV_BASE_URL_NAME} ${shellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`, `set -gx ${CLAUDE_ENV_SIMPLE_MODE_NAME} 1`, `set -gx ${CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME} 1`, `set -gx ${CLAUDE_ENV_ENABLE_TELEMETRY_NAME} 0`);
1823
+ lines.push("", "# Official Claude Code CLI", `set -gx ${CLAUDE_ENV_API_KEY_NAME} ${shellQuote(params.apiKey)}`, `set -gx ${CLAUDE_ENV_BASE_URL_NAME} ${shellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`);
1759
1824
  }
1760
1825
  await promises_1.default.writeFile(fishConfPath, `${lines.join("\n")}\n`, "utf8");
1761
1826
  return [fishConfPath];
1762
1827
  }
1763
1828
  function powerShellProfilePaths() {
1764
- if (node_os_1.default.platform() !== "win32")
1829
+ const candidates = new Set();
1830
+ const addDocumentsProfiles = (documentsDir) => {
1831
+ if (!documentsDir)
1832
+ return;
1833
+ candidates.add(node_path_1.default.join(documentsDir, "PowerShell", "Microsoft.PowerShell_profile.ps1"));
1834
+ candidates.add(node_path_1.default.join(documentsDir, "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1"));
1835
+ };
1836
+ if (node_os_1.default.platform() === "win32") {
1837
+ addDocumentsProfiles(node_path_1.default.join(node_os_1.default.homedir(), "Documents"));
1838
+ addDocumentsProfiles(resolveWindowsDocumentsDirForHost());
1839
+ return [...candidates];
1840
+ }
1841
+ if (!isWslInteropRuntime())
1765
1842
  return [];
1766
- const home = node_os_1.default.homedir();
1767
- return [
1768
- node_path_1.default.join(home, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1"),
1769
- node_path_1.default.join(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1"),
1770
- ];
1843
+ addDocumentsProfiles(resolveWindowsDocumentsDirForHost());
1844
+ addDocumentsProfiles(resolveWindowsPathForHost(node_path_1.default.join(resolveWindowsHomeDirForHost() ?? "", "Documents")));
1845
+ return [...candidates];
1846
+ }
1847
+ function isWslInteropRuntime() {
1848
+ return node_os_1.default.platform() === "linux" && Boolean(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);
1849
+ }
1850
+ function readWindowsCommandStdout(command) {
1851
+ const shell = node_os_1.default.platform() === "win32" ? "powershell.exe" : isWslInteropRuntime() ? "powershell.exe" : null;
1852
+ if (!shell)
1853
+ return null;
1854
+ const result = (0, node_child_process_1.spawnSync)(shell, ["-NoProfile", "-Command", command], {
1855
+ encoding: "utf8",
1856
+ stdio: ["ignore", "pipe", "ignore"],
1857
+ });
1858
+ if (result.status !== 0)
1859
+ return null;
1860
+ const next = result.stdout.replace(/\r/g, "").trim();
1861
+ return next ? next : null;
1862
+ }
1863
+ function resolveWindowsPathForHost(input) {
1864
+ if (!input)
1865
+ return null;
1866
+ const trimmed = input.trim();
1867
+ if (!trimmed)
1868
+ return null;
1869
+ if (node_os_1.default.platform() === "win32")
1870
+ return trimmed;
1871
+ const match = /^([A-Za-z]):\\(.*)$/.exec(trimmed);
1872
+ if (!match)
1873
+ return null;
1874
+ const drive = match[1].toLowerCase();
1875
+ const rest = match[2].replace(/\\/g, "/");
1876
+ return node_path_1.default.posix.join("/mnt", drive, rest);
1877
+ }
1878
+ function resolveWindowsHomeDirForHost() {
1879
+ const reported = readWindowsCommandStdout("$env:USERPROFILE");
1880
+ if (reported)
1881
+ return resolveWindowsPathForHost(reported);
1882
+ if (node_os_1.default.platform() === "win32")
1883
+ return node_os_1.default.homedir();
1884
+ return null;
1885
+ }
1886
+ function resolveWindowsDocumentsDirForHost() {
1887
+ const reported = readWindowsCommandStdout("[Environment]::GetFolderPath('MyDocuments')");
1888
+ if (reported)
1889
+ return resolveWindowsPathForHost(reported);
1890
+ const home = resolveWindowsHomeDirForHost();
1891
+ if (!home)
1892
+ return null;
1893
+ return node_path_1.default.join(home, "Documents");
1771
1894
  }
1772
1895
  async function persistPowerShellEnv(params) {
1773
1896
  const updated = [];
@@ -1780,7 +1903,7 @@ async function persistPowerShellEnv(params) {
1780
1903
  `$env:${ENV_KEY_NAME} = ${powerShellQuote(params.apiKey)}`,
1781
1904
  ];
1782
1905
  if (params.claudeEnabled) {
1783
- lines.push(`$env:${CLAUDE_ENV_API_KEY_NAME} = ${powerShellQuote(params.apiKey)}`, `$env:${CLAUDE_ENV_BASE_URL_NAME} = ${powerShellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`, `$env:${CLAUDE_ENV_SIMPLE_MODE_NAME} = "1"`, `$env:${CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME} = "1"`, `$env:${CLAUDE_ENV_ENABLE_TELEMETRY_NAME} = "0"`);
1906
+ lines.push(`$env:${CLAUDE_ENV_API_KEY_NAME} = ${powerShellQuote(params.apiKey)}`, `$env:${CLAUDE_ENV_BASE_URL_NAME} = ${powerShellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`);
1784
1907
  }
1785
1908
  lines.push(SHELL_END);
1786
1909
  const next = appendManagedBlock(cleaned, lines);
@@ -1831,9 +1954,6 @@ function buildManagedEditorTerminalEnv(params) {
1831
1954
  if (params.claudeEnabled) {
1832
1955
  env[CLAUDE_ENV_API_KEY_NAME] = params.apiKey;
1833
1956
  env[CLAUDE_ENV_BASE_URL_NAME] = anthropicCompatibleProxyUrl(params.backendUrl);
1834
- env[CLAUDE_ENV_SIMPLE_MODE_NAME] = "1";
1835
- env[CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME] = "1";
1836
- env[CLAUDE_ENV_ENABLE_TELEMETRY_NAME] = "0";
1837
1957
  }
1838
1958
  return env;
1839
1959
  }
@@ -1841,9 +1961,6 @@ function buildManagedClaudeEditorEnv(params) {
1841
1961
  return {
1842
1962
  [CLAUDE_ENV_API_KEY_NAME]: params.apiKey,
1843
1963
  [CLAUDE_ENV_BASE_URL_NAME]: anthropicCompatibleProxyUrl(params.backendUrl),
1844
- [CLAUDE_ENV_SIMPLE_MODE_NAME]: "1",
1845
- [CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME]: "1",
1846
- [CLAUDE_ENV_ENABLE_TELEMETRY_NAME]: "0",
1847
1964
  };
1848
1965
  }
1849
1966
  async function persistEditorTerminalEnvSettings(params) {
@@ -1976,6 +2093,110 @@ async function persistClaudeEditorSettings(params) {
1976
2093
  await writeJsonObjectFile(CLAUDE_EDITOR_SETTINGS_STATE_PATH, Array.from(snapshotByPath.values()), 0o600);
1977
2094
  return managedPaths;
1978
2095
  }
2096
+ function expandClaudeDesktopInferenceModels(modelIds) {
2097
+ const expanded = [];
2098
+ const add = (value) => {
2099
+ const normalized = value.trim();
2100
+ if (!normalized || expanded.includes(normalized))
2101
+ return;
2102
+ expanded.push(normalized);
2103
+ };
2104
+ for (const modelId of modelIds) {
2105
+ add(modelId);
2106
+ const datedMatch = modelId.match(/^(claude-[a-z]+-\d+(?:-\d+)?)-(\d{8})$/);
2107
+ if (datedMatch?.[1])
2108
+ add(datedMatch[1]);
2109
+ const base = datedMatch?.[1] ?? modelId;
2110
+ const dottedMatch = base.match(/^(claude-[a-z]+-\d+)-(\d+)$/);
2111
+ if (dottedMatch?.[1] && dottedMatch?.[2]) {
2112
+ add(`${dottedMatch[1]}.${dottedMatch[2]}`);
2113
+ }
2114
+ }
2115
+ return expanded;
2116
+ }
2117
+ async function discoverClaudeDesktopDeploymentOrganizationUuid(existing) {
2118
+ const enterpriseConfig = objectRecordOr(existing.enterpriseConfig, {});
2119
+ const existingValue = enterpriseConfig.deploymentOrganizationUuid;
2120
+ if (typeof existingValue === "string" && existingValue.trim()) {
2121
+ return existingValue.trim();
2122
+ }
2123
+ const uuidPattern = /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/i;
2124
+ const logPattern = /(deploymentOrganizationUuid|organizationUuid|organization uuid|org uuid)/i;
2125
+ for (const logPath of claudeDesktopLogCandidates()) {
2126
+ const logRaw = await readFileIfExists(logPath);
2127
+ if (!logRaw)
2128
+ continue;
2129
+ const tail = logRaw.slice(-200000);
2130
+ for (const line of tail.split(/\r?\n/).reverse()) {
2131
+ if (!logPattern.test(line))
2132
+ continue;
2133
+ const match = line.match(uuidPattern);
2134
+ if (match?.[0])
2135
+ return match[0];
2136
+ }
2137
+ }
2138
+ return null;
2139
+ }
2140
+ async function writeClaudeDesktop3pConfig(params) {
2141
+ const configPath = claudeDesktop3pConfigPath();
2142
+ if (!configPath || params.claudeModels.length === 0)
2143
+ return null;
2144
+ const desktopPresent = claudeDesktopAppCandidatePaths().some((candidate) => (0, node_fs_1.existsSync)(candidate)) ||
2145
+ (await pathExists(configPath)) ||
2146
+ (await pathExists(node_path_1.default.dirname(configPath))) ||
2147
+ claudeDesktopLogCandidates().some((candidate) => (0, node_fs_1.existsSync)(candidate));
2148
+ if (!desktopPresent)
2149
+ return null;
2150
+ let existed = true;
2151
+ let existingRaw = "";
2152
+ try {
2153
+ existingRaw = await promises_1.default.readFile(configPath, "utf8");
2154
+ }
2155
+ catch (error) {
2156
+ const err = error;
2157
+ if (err.code !== "ENOENT")
2158
+ throw error;
2159
+ existed = false;
2160
+ }
2161
+ const existingSnapshotRaw = await readFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2162
+ if (!existingSnapshotRaw?.trim()) {
2163
+ const snapshot = {
2164
+ version: 1,
2165
+ configPath,
2166
+ existed,
2167
+ previousRaw: existed ? existingRaw : null,
2168
+ };
2169
+ await writeJsonObjectFile(CLAUDE_DESKTOP_3P_STATE_PATH, snapshot, 0o600);
2170
+ }
2171
+ let doc = {};
2172
+ if (existingRaw.trim()) {
2173
+ try {
2174
+ doc = objectRecordOr(JSON.parse(existingRaw), {});
2175
+ }
2176
+ catch {
2177
+ throw new Error(`invalid JSON in Claude Desktop config: ${configPath}`);
2178
+ }
2179
+ }
2180
+ const enterpriseConfig = objectRecordOr(doc.enterpriseConfig, {});
2181
+ enterpriseConfig.inferenceProvider = "gateway";
2182
+ enterpriseConfig.inferenceGatewayBaseUrl = anthropicCompatibleProxyUrl(params.backendUrl);
2183
+ enterpriseConfig.inferenceGatewayApiKey = params.apiKey;
2184
+ enterpriseConfig.inferenceModels = expandClaudeDesktopInferenceModels(params.claudeModels);
2185
+ enterpriseConfig.isDesktopExtensionEnabled = true;
2186
+ enterpriseConfig.isDesktopExtensionDirectoryEnabled = true;
2187
+ enterpriseConfig.isDesktopExtensionSignatureRequired = false;
2188
+ enterpriseConfig.isLocalDevMcpEnabled = true;
2189
+ enterpriseConfig.isClaudeCodeForDesktopEnabled = true;
2190
+ const deploymentOrganizationUuid = await discoverClaudeDesktopDeploymentOrganizationUuid(doc);
2191
+ if (deploymentOrganizationUuid) {
2192
+ enterpriseConfig.deploymentOrganizationUuid = deploymentOrganizationUuid;
2193
+ }
2194
+ doc.enterpriseConfig = enterpriseConfig;
2195
+ doc.isUsingBuiltInNodeForMcp = true;
2196
+ doc.isDxtAutoUpdatesEnabled = true;
2197
+ await writeJsonObjectFile(configPath, doc);
2198
+ return configPath;
2199
+ }
1979
2200
  function runOpenClawConfigCommand(args) {
1980
2201
  const run = (0, node_child_process_1.spawnSync)("openclaw", args, {
1981
2202
  encoding: "utf8",
@@ -2117,7 +2338,7 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
2117
2338
  if (typeof snapshot !== "object" || snapshot === null || Array.isArray(snapshot))
2118
2339
  return null;
2119
2340
  const obj = snapshot;
2120
- if (obj.version === 2 && Array.isArray(obj.targets)) {
2341
+ if ((obj.version === 2 || obj.version === 3) && Array.isArray(obj.targets)) {
2121
2342
  const targets = [];
2122
2343
  for (const entry of obj.targets) {
2123
2344
  if (typeof entry !== "object" || entry === null || Array.isArray(entry))
@@ -2134,6 +2355,11 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
2134
2355
  !Array.isArray(candidate.openAiProvider)
2135
2356
  ? candidate.openAiProvider
2136
2357
  : null,
2358
+ anthropicProvider: typeof candidate.anthropicProvider === "object" &&
2359
+ candidate.anthropicProvider !== null &&
2360
+ !Array.isArray(candidate.anthropicProvider)
2361
+ ? candidate.anthropicProvider
2362
+ : null,
2137
2363
  model: typeof candidate.model === "string" ? candidate.model : null,
2138
2364
  schema: typeof candidate.schema === "string" ? candidate.schema : null,
2139
2365
  plugin: "plugin" in candidate
@@ -2143,13 +2369,13 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
2143
2369
  : undefined,
2144
2370
  });
2145
2371
  }
2146
- return { version: 2, targets };
2372
+ return { version: 3, targets };
2147
2373
  }
2148
2374
  const looksLikeV1 = "openAiProvider" in obj || "model" in obj || "schema" in obj;
2149
2375
  if (!looksLikeV1)
2150
2376
  return null;
2151
2377
  return {
2152
- version: 2,
2378
+ version: 3,
2153
2379
  targets: [
2154
2380
  {
2155
2381
  configPath: fallbackConfigPath,
@@ -2240,6 +2466,10 @@ function isManagedOpenCodeProvider(provider) {
2240
2466
  const options = objectRecordOr(provider.options, {});
2241
2467
  return isTheClawBayOpenAiCompatibleBaseUrl(options.baseURL);
2242
2468
  }
2469
+ function isManagedOpenCodeAnthropicProvider(provider) {
2470
+ const options = objectRecordOr(provider.options, {});
2471
+ return isTheClawBayAnthropicCompatibleBaseUrl(options.baseURL);
2472
+ }
2243
2473
  function isManagedOpenCodeModel(value, supportedModelIds) {
2244
2474
  if (typeof value !== "string")
2245
2475
  return false;
@@ -2252,7 +2482,7 @@ async function writeOpenCodeFamilyConfig(params) {
2252
2482
  const fallbackConfigPath = normalizedConfigPaths[0] ?? node_path_1.default.join(xdgConfigHomeDir(), "opencode", "opencode.json");
2253
2483
  const restoreState = (await readOpenCodeRestoreSnapshot(params.restoreStatePath, fallbackConfigPath)) ??
2254
2484
  {
2255
- version: 2,
2485
+ version: 3,
2256
2486
  targets: [],
2257
2487
  };
2258
2488
  const supportedModelIds = new Set(params.models.map((entry) => entry.id).filter(Boolean));
@@ -2291,12 +2521,14 @@ async function writeOpenCodeFamilyConfig(params) {
2291
2521
  }
2292
2522
  const providerRoot = objectRecordOr(doc.provider, {});
2293
2523
  const openAiProvider = objectRecordOr(providerRoot[OPENAI_PROVIDER_ID], {});
2524
+ const anthropicProvider = objectRecordOr(providerRoot[ANTHROPIC_PROVIDER_ID], {});
2294
2525
  const alreadyManaged = isManagedOpenCodeProvider(openAiProvider) && isManagedOpenCodeModel(doc.model, supportedModelIds);
2295
2526
  if (!alreadyManaged && !restoreHasTarget(configPath)) {
2296
2527
  restoreState.targets.push({
2297
2528
  configPath,
2298
2529
  existed,
2299
2530
  openAiProvider: Object.keys(openAiProvider).length > 0 ? openAiProvider : null,
2531
+ anthropicProvider: Object.keys(anthropicProvider).length > 0 ? anthropicProvider : null,
2300
2532
  model: typeof doc.model === "string" && !doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`)
2301
2533
  ? doc.model
2302
2534
  : null,
@@ -2324,6 +2556,18 @@ async function writeOpenCodeFamilyConfig(params) {
2324
2556
  delete managedOpenAiProvider.blacklist;
2325
2557
  }
2326
2558
  providerRoot[OPENAI_PROVIDER_ID] = managedOpenAiProvider;
2559
+ if ((params.claudeModels?.length ?? 0) > 0) {
2560
+ const managedAnthropicProvider = objectRecordOr(providerRoot[ANTHROPIC_PROVIDER_ID], {});
2561
+ const anthropicOptions = objectRecordOr(managedAnthropicProvider.options, {});
2562
+ anthropicOptions.baseURL = anthropicCompatibleProxyV1Url(params.backendUrl);
2563
+ anthropicOptions.apiKey = params.apiKey;
2564
+ managedAnthropicProvider.options = anthropicOptions;
2565
+ managedAnthropicProvider.whitelist = uniqueStrings(params.claudeModels ?? []);
2566
+ if ("blacklist" in managedAnthropicProvider) {
2567
+ delete managedAnthropicProvider.blacklist;
2568
+ }
2569
+ providerRoot[ANTHROPIC_PROVIDER_ID] = managedAnthropicProvider;
2570
+ }
2327
2571
  if (DEFAULT_PROVIDER_ID in providerRoot) {
2328
2572
  delete providerRoot[DEFAULT_PROVIDER_ID];
2329
2573
  }
@@ -2429,12 +2673,13 @@ class SetupCommand extends base_command_1.BaseCommand {
2429
2673
  managed?.backendUrl ??
2430
2674
  DEFAULT_BACKEND_URL;
2431
2675
  const backendUrl = normalizeUrl(backendRaw, "--backend");
2432
- const [codexDetected, claudeDetected, continueDetected, clineDetected, gsdDetected, kiloDetected, rooDetected, traeDetected, aiderDetected, zoDetected] = await Promise.all([
2676
+ const [codexDetected, claudeDetected, continueDetected, clineDetected, gsdDetected, openCodeDetected, kiloDetected, rooDetected, traeDetected, aiderDetected, zoDetected] = await Promise.all([
2433
2677
  detectCodexClient(),
2434
- detectClaudeCodeClient(),
2678
+ detectClaudeClient(),
2435
2679
  detectContinueClient(),
2436
2680
  detectClineClient(),
2437
2681
  detectGsdClient(),
2682
+ detectOpenCodeClient(),
2438
2683
  detectKiloClient(),
2439
2684
  detectRooClient(),
2440
2685
  detectTraeClient(),
@@ -2453,8 +2698,8 @@ class SetupCommand extends base_command_1.BaseCommand {
2453
2698
  },
2454
2699
  {
2455
2700
  id: "claude",
2456
- label: "Claude Code CLI",
2457
- summaryLabel: "Claude Code",
2701
+ label: "Claude Code / Claude Desktop",
2702
+ summaryLabel: "Claude",
2458
2703
  detected: claudeDetected,
2459
2704
  recommended: true,
2460
2705
  icon: "✳",
@@ -2500,7 +2745,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2500
2745
  id: "opencode",
2501
2746
  label: "OpenCode",
2502
2747
  summaryLabel: "OpenCode",
2503
- detected: hasCommand("opencode"),
2748
+ detected: openCodeDetected,
2504
2749
  recommended: true,
2505
2750
  icon: "⌘",
2506
2751
  siteUrl: "https://opencode.ai",
@@ -2601,6 +2846,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2601
2846
  let updatedVsCodeEnvFiles = [];
2602
2847
  let updatedEditorTerminalSettings = [];
2603
2848
  let updatedClaudeEditorSettings = [];
2849
+ let claudeDesktop3pConfigPathManaged = null;
2604
2850
  let sessionMigration = null;
2605
2851
  let authSeed = null;
2606
2852
  let authSeedCleanup = null;
@@ -2668,6 +2914,11 @@ class SetupCommand extends base_command_1.BaseCommand {
2668
2914
  backendUrl,
2669
2915
  claudeEnabled: claudeEnvEnabled,
2670
2916
  });
2917
+ claudeDesktop3pConfigPathManaged = await writeClaudeDesktop3pConfig({
2918
+ backendUrl,
2919
+ apiKey: authCredential,
2920
+ claudeModels: claudeAccess?.enabled ? claudeAccess.models : [],
2921
+ });
2671
2922
  }
2672
2923
  }
2673
2924
  if (selectedSetupClients.has("codex")) {
@@ -2744,6 +2995,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2744
2995
  backendUrl,
2745
2996
  model: resolved?.model ?? DEFAULT_CODEX_MODEL,
2746
2997
  models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
2998
+ claudeModels: claudeAccess?.enabled ? claudeAccess.models : [],
2747
2999
  apiKey: authCredential,
2748
3000
  });
2749
3001
  }
@@ -2804,7 +3056,10 @@ class SetupCommand extends base_command_1.BaseCommand {
2804
3056
  if (resolved?.note)
2805
3057
  summaryNotes.add(resolved.note);
2806
3058
  if (selectedSetupClients.has("claude") && claudeAccess?.enabled) {
2807
- summaryNotes.add("Claude Code: exported ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY, CLAUDE_CODE_SIMPLE=1, disabled nonessential traffic, and disabled telemetry.");
3059
+ summaryNotes.add("Claude Code: exported ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY.");
3060
+ if (claudeDesktop3pConfigPathManaged) {
3061
+ summaryNotes.add("Claude Desktop: configured hidden Claude-3p gateway mode with the /anthropic base URL for The Claw Bay.");
3062
+ }
2808
3063
  summaryNotes.add("If Claude Code is already open, restart it so the custom-key env takes effect cleanly. `claude auth status` should show `authMethod: api_key` once the new env is loaded.");
2809
3064
  }
2810
3065
  else if (selectedSetupClients.has("claude") && claudeAccess?.note) {
@@ -2861,6 +3116,9 @@ class SetupCommand extends base_command_1.BaseCommand {
2861
3116
  this.log(resolved.note);
2862
3117
  if (selectedSetupClients.has("claude") && claudeAccess?.enabled) {
2863
3118
  this.log(`- Claude Code base URL: ${anthropicCompatibleProxyUrl(backendUrl)}`);
3119
+ if (claudeDesktop3pConfigPathManaged) {
3120
+ this.log(`- Claude Desktop 3P config: ${claudeDesktop3pConfigPathManaged}`);
3121
+ }
2864
3122
  }
2865
3123
  else if (selectedSetupClients.has("claude") && claudeAccess?.note) {
2866
3124
  this.log(`- Claude Code: ${claudeAccess.note}`);
@@ -2976,9 +3234,9 @@ class SetupCommand extends base_command_1.BaseCommand {
2976
3234
  else {
2977
3235
  this.log("- OpenClaw: not detected (skipped)");
2978
3236
  }
2979
- const openCodeDetected = setupClients.find((client) => client.id === "opencode")?.detected ?? false;
2980
3237
  if (selectedSetupClients.has("opencode")) {
2981
3238
  this.log(`- OpenCode: configured (${openCodeConfigPaths.join(", ")})`);
3239
+ this.log("- OpenCode note: configured the OpenAI-compatible /v1 route and, when Claude access is available, the Anthropic-compatible /anthropic/v1 route.");
2982
3240
  this.log("- OpenCode note: plain OpenAI-compatible config is preferred. If you add a quota/auth plugin manually, use /api/codex-auth/v1/quota?format=legacy_codex for legacy Codex-style quota responses.");
2983
3241
  const openCodeProjectConfig = findOpenCodeProjectConfigFile();
2984
3242
  const openCodeConfigEnv = process.env.OPENCODE_CONFIG?.trim() || "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "theclawbay",
3
- "version": "0.3.56",
3
+ "version": "0.3.58",
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
  "bin": {