theclawbay 0.3.56 → 0.3.57

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.
@@ -17,6 +17,7 @@ const errors_1 = require("../lib/managed/errors");
17
17
  const supported_models_1 = require("../lib/supported-models");
18
18
  const paths_1 = require("../lib/config/paths");
19
19
  const OPENAI_PROVIDER_ID = "openai";
20
+ const ANTHROPIC_PROVIDER_ID = "anthropic";
20
21
  const DEFAULT_PROVIDER_ID = "theclawbay";
21
22
  const WAN_PROVIDER_ID = "theclawbay-wan";
22
23
  const MANAGED_START = "# theclawbay-managed:start";
@@ -49,6 +50,7 @@ const KILO_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayState
49
50
  const ROO_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "roo-settings.restore.json");
50
51
  const EDITOR_TERMINAL_ENV_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "editor-terminal-env.restore.json");
51
52
  const CLAUDE_EDITOR_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "claude-editor-settings.restore.json");
53
+ const CLAUDE_DESKTOP_3P_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "claude-desktop-3p.restore.json");
52
54
  const CODEX_FEATURE_FLAGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "codex-features.restore.json");
53
55
  const ROO_IMPORT_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "roo-code-settings.json");
54
56
  const MIGRATION_STATE_FILE = node_path_1.default.join(paths_1.codexDir, "theclawbay.migration.json");
@@ -319,6 +321,22 @@ function isTheClawBayOpenAiCompatibleBaseUrl(value) {
319
321
  return false;
320
322
  }
321
323
  }
324
+ function isTheClawBayAnthropicCompatibleBaseUrl(value) {
325
+ if (typeof value !== "string")
326
+ return false;
327
+ const normalized = value.trim();
328
+ if (!normalized)
329
+ return false;
330
+ try {
331
+ const parsed = new URL(normalized);
332
+ const hostname = parsed.hostname.toLowerCase();
333
+ const pathname = parsed.pathname.replace(/\/+$/g, "");
334
+ return hostname === THECLAWBAY_CANONICAL_API_HOST && pathname === "/anthropic/v1";
335
+ }
336
+ catch {
337
+ return false;
338
+ }
339
+ }
322
340
  function uniqueStrings(values) {
323
341
  const output = [];
324
342
  const seen = new Set();
@@ -348,6 +366,11 @@ function configDirCandidates(appName) {
348
366
  }
349
367
  return uniqueStrings(dirs);
350
368
  }
369
+ function claudeDesktop3pConfigPath() {
370
+ if (node_os_1.default.platform() !== "darwin")
371
+ return null;
372
+ return node_path_1.default.join(node_os_1.default.homedir(), "Library", "Application Support", "Claude-3p", "claude_desktop_config.json");
373
+ }
351
374
  function openCodeConfigCleanupTargets() {
352
375
  const home = node_os_1.default.homedir();
353
376
  const dirs = configDirCandidates("opencode");
@@ -399,6 +422,11 @@ function isManagedOpenCodeProvider(value) {
399
422
  const options = objectRecordOr(provider.options, {});
400
423
  return isTheClawBayOpenAiCompatibleBaseUrl(options.baseURL);
401
424
  }
425
+ function isManagedOpenCodeAnthropicProvider(value) {
426
+ const provider = objectRecordOr(value, {});
427
+ const options = objectRecordOr(provider.options, {});
428
+ return isTheClawBayAnthropicCompatibleBaseUrl(options.baseURL);
429
+ }
402
430
  function roamingAppDataDir() {
403
431
  if (process.env.APPDATA?.trim())
404
432
  return process.env.APPDATA;
@@ -888,6 +916,37 @@ async function cleanupClaudeEditorSettings() {
888
916
  await removeFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
889
917
  return updated;
890
918
  }
919
+ async function cleanupClaudeDesktop3pConfig() {
920
+ const snapshotRaw = await readFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
921
+ if (snapshotRaw === null || !snapshotRaw.trim())
922
+ return false;
923
+ let snapshot = null;
924
+ try {
925
+ snapshot = JSON.parse(snapshotRaw);
926
+ }
927
+ catch {
928
+ snapshot = null;
929
+ }
930
+ if (!snapshot?.configPath) {
931
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
932
+ return false;
933
+ }
934
+ if (!snapshot.existed) {
935
+ const removed = await removeFileIfExists(snapshot.configPath);
936
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
937
+ return removed;
938
+ }
939
+ const previousRaw = snapshot.previousRaw ?? "";
940
+ await promises_1.default.mkdir(node_path_1.default.dirname(snapshot.configPath), { recursive: true });
941
+ const existingRaw = await readFileIfExists(snapshot.configPath);
942
+ if (existingRaw === previousRaw) {
943
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
944
+ return false;
945
+ }
946
+ await promises_1.default.writeFile(snapshot.configPath, previousRaw, "utf8");
947
+ await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
948
+ return true;
949
+ }
891
950
  async function cleanupCodexFeatureFlags() {
892
951
  const snapshotRaw = await readFileIfExists(CODEX_FEATURE_FLAGS_STATE_PATH);
893
952
  if (snapshotRaw === null || !snapshotRaw.trim())
@@ -966,7 +1025,7 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
966
1025
  if (typeof snapshot !== "object" || snapshot === null || Array.isArray(snapshot))
967
1026
  return null;
968
1027
  const obj = snapshot;
969
- if (obj.version === 2 && Array.isArray(obj.targets)) {
1028
+ if ((obj.version === 2 || obj.version === 3) && Array.isArray(obj.targets)) {
970
1029
  const targets = [];
971
1030
  for (const entry of obj.targets) {
972
1031
  if (typeof entry !== "object" || entry === null || Array.isArray(entry))
@@ -983,6 +1042,11 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
983
1042
  !Array.isArray(candidate.openAiProvider)
984
1043
  ? candidate.openAiProvider
985
1044
  : null,
1045
+ anthropicProvider: typeof candidate.anthropicProvider === "object" &&
1046
+ candidate.anthropicProvider !== null &&
1047
+ !Array.isArray(candidate.anthropicProvider)
1048
+ ? candidate.anthropicProvider
1049
+ : null,
986
1050
  model: typeof candidate.model === "string" ? candidate.model : null,
987
1051
  schema: typeof candidate.schema === "string" ? candidate.schema : null,
988
1052
  plugin: "plugin" in candidate
@@ -992,13 +1056,13 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
992
1056
  : undefined,
993
1057
  });
994
1058
  }
995
- return { version: 2, targets };
1059
+ return { version: 3, targets };
996
1060
  }
997
1061
  const looksLikeV1 = "openAiProvider" in obj || "model" in obj || "schema" in obj;
998
1062
  if (!looksLikeV1)
999
1063
  return null;
1000
1064
  return {
1001
- version: 2,
1065
+ version: 3,
1002
1066
  targets: [
1003
1067
  {
1004
1068
  configPath: fallbackConfigPath,
@@ -1067,6 +1131,15 @@ async function cleanupOpenCodeFamilyConfigs(params) {
1067
1131
  }
1068
1132
  else if (OPENAI_PROVIDER_ID in providerRoot) {
1069
1133
  delete providerRoot[OPENAI_PROVIDER_ID];
1134
+ changed = true;
1135
+ }
1136
+ if (target.anthropicProvider) {
1137
+ providerRoot[ANTHROPIC_PROVIDER_ID] = target.anthropicProvider;
1138
+ changed = true;
1139
+ }
1140
+ else if (ANTHROPIC_PROVIDER_ID in providerRoot) {
1141
+ delete providerRoot[ANTHROPIC_PROVIDER_ID];
1142
+ changed = true;
1070
1143
  }
1071
1144
  doc.provider = providerRoot;
1072
1145
  if (target.model === null) {
@@ -1125,6 +1198,10 @@ async function cleanupOpenCodeFamilyConfigs(params) {
1125
1198
  delete providerRoot[OPENAI_PROVIDER_ID];
1126
1199
  fileChanged = true;
1127
1200
  }
1201
+ if (isManagedOpenCodeAnthropicProvider(providerRoot[ANTHROPIC_PROVIDER_ID])) {
1202
+ delete providerRoot[ANTHROPIC_PROVIDER_ID];
1203
+ fileChanged = true;
1204
+ }
1128
1205
  if (!fileChanged)
1129
1206
  continue;
1130
1207
  changed = true;
@@ -1187,6 +1264,7 @@ class LogoutCommand extends base_command_1.BaseCommand {
1187
1264
  let updatedVsCodeHooks = [];
1188
1265
  let updatedEditorTerminalSettings = [];
1189
1266
  let updatedClaudeEditorSettings = [];
1267
+ let updatedClaudeDesktopConfig = false;
1190
1268
  let updatedCodexConfig = false;
1191
1269
  let updatedCodexFeatures = false;
1192
1270
  let updatedContinueConfig = false;
@@ -1239,6 +1317,7 @@ class LogoutCommand extends base_command_1.BaseCommand {
1239
1317
  updatedVsCodeHooks = await cleanupVsCodeHooks();
1240
1318
  updatedEditorTerminalSettings = await cleanupEditorTerminalEnvSettings();
1241
1319
  updatedClaudeEditorSettings = await cleanupClaudeEditorSettings();
1320
+ updatedClaudeDesktopConfig = await cleanupClaudeDesktop3pConfig();
1242
1321
  progress.update("Reverting Codex");
1243
1322
  updatedCodexConfig = await cleanupCodexConfig();
1244
1323
  updatedCodexFeatures = await cleanupCodexFeatureFlags();
@@ -1294,11 +1373,12 @@ class LogoutCommand extends base_command_1.BaseCommand {
1294
1373
  this.log("Logout complete");
1295
1374
  }
1296
1375
  const revertedTargets = [];
1376
+ const claudeChanged = updatedClaudeEditorSettings.length > 0 ||
1377
+ updatedClaudeDesktopConfig;
1297
1378
  const codexChanged = updatedCodexConfig ||
1298
1379
  updatedCodexFeatures ||
1299
1380
  updatedVsCodeHooks.length > 0 ||
1300
1381
  updatedEditorTerminalSettings.length > 0 ||
1301
- updatedClaudeEditorSettings.length > 0 ||
1302
1382
  sessionMigration.rewritten > 0 ||
1303
1383
  sessionMigration.retimed > 0 ||
1304
1384
  stateDbMigration.updated > 0 ||
@@ -1307,6 +1387,8 @@ class LogoutCommand extends base_command_1.BaseCommand {
1307
1387
  modelCacheCleanup.action === "removed";
1308
1388
  if (codexChanged)
1309
1389
  revertedTargets.push("Codex");
1390
+ if (claudeChanged)
1391
+ revertedTargets.push("Claude");
1310
1392
  if (updatedContinueConfig)
1311
1393
  revertedTargets.push("Continue");
1312
1394
  if (updatedClineConfig)
@@ -1348,7 +1430,8 @@ class LogoutCommand extends base_command_1.BaseCommand {
1348
1430
  updatedPowerShellProfiles.length > 0 ||
1349
1431
  updatedVsCodeHooks.length > 0 ||
1350
1432
  updatedEditorTerminalSettings.length > 0 ||
1351
- updatedClaudeEditorSettings.length > 0,
1433
+ updatedClaudeEditorSettings.length > 0 ||
1434
+ updatedClaudeDesktopConfig,
1352
1435
  notes: Array.from(summaryNotes),
1353
1436
  });
1354
1437
  return;
@@ -1366,6 +1449,7 @@ class LogoutCommand extends base_command_1.BaseCommand {
1366
1449
  this.log(`- VS Code env hooks updated: ${updatedVsCodeHooks.length ? updatedVsCodeHooks.join(", ") : "none"}`);
1367
1450
  this.log(`- Editor terminal env updated: ${updatedEditorTerminalSettings.length ? updatedEditorTerminalSettings.join(", ") : "none"}`);
1368
1451
  this.log(`- Claude editor settings updated: ${updatedClaudeEditorSettings.length ? updatedClaudeEditorSettings.join(", ") : "none"}`);
1452
+ this.log(`- Claude Desktop 3P config cleaned: ${updatedClaudeDesktopConfig ? "yes" : "no"}`);
1369
1453
  this.log(`- Codex config cleaned: ${updatedCodexConfig ? "yes" : "no"}`);
1370
1454
  this.log(`- Codex Apps feature flags restored: ${updatedCodexFeatures ? "yes" : "no"}`);
1371
1455
  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;
@@ -1976,6 +2047,110 @@ async function persistClaudeEditorSettings(params) {
1976
2047
  await writeJsonObjectFile(CLAUDE_EDITOR_SETTINGS_STATE_PATH, Array.from(snapshotByPath.values()), 0o600);
1977
2048
  return managedPaths;
1978
2049
  }
2050
+ function expandClaudeDesktopInferenceModels(modelIds) {
2051
+ const expanded = [];
2052
+ const add = (value) => {
2053
+ const normalized = value.trim();
2054
+ if (!normalized || expanded.includes(normalized))
2055
+ return;
2056
+ expanded.push(normalized);
2057
+ };
2058
+ for (const modelId of modelIds) {
2059
+ add(modelId);
2060
+ const datedMatch = modelId.match(/^(claude-[a-z]+-\d+(?:-\d+)?)-(\d{8})$/);
2061
+ if (datedMatch?.[1])
2062
+ add(datedMatch[1]);
2063
+ const base = datedMatch?.[1] ?? modelId;
2064
+ const dottedMatch = base.match(/^(claude-[a-z]+-\d+)-(\d+)$/);
2065
+ if (dottedMatch?.[1] && dottedMatch?.[2]) {
2066
+ add(`${dottedMatch[1]}.${dottedMatch[2]}`);
2067
+ }
2068
+ }
2069
+ return expanded;
2070
+ }
2071
+ async function discoverClaudeDesktopDeploymentOrganizationUuid(existing) {
2072
+ const enterpriseConfig = objectRecordOr(existing.enterpriseConfig, {});
2073
+ const existingValue = enterpriseConfig.deploymentOrganizationUuid;
2074
+ if (typeof existingValue === "string" && existingValue.trim()) {
2075
+ return existingValue.trim();
2076
+ }
2077
+ 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;
2078
+ const logPattern = /(deploymentOrganizationUuid|organizationUuid|organization uuid|org uuid)/i;
2079
+ for (const logPath of claudeDesktopLogCandidates()) {
2080
+ const logRaw = await readFileIfExists(logPath);
2081
+ if (!logRaw)
2082
+ continue;
2083
+ const tail = logRaw.slice(-200000);
2084
+ for (const line of tail.split(/\r?\n/).reverse()) {
2085
+ if (!logPattern.test(line))
2086
+ continue;
2087
+ const match = line.match(uuidPattern);
2088
+ if (match?.[0])
2089
+ return match[0];
2090
+ }
2091
+ }
2092
+ return null;
2093
+ }
2094
+ async function writeClaudeDesktop3pConfig(params) {
2095
+ const configPath = claudeDesktop3pConfigPath();
2096
+ if (!configPath || params.claudeModels.length === 0)
2097
+ return null;
2098
+ const desktopPresent = claudeDesktopAppCandidatePaths().some((candidate) => (0, node_fs_1.existsSync)(candidate)) ||
2099
+ (await pathExists(configPath)) ||
2100
+ (await pathExists(node_path_1.default.dirname(configPath))) ||
2101
+ claudeDesktopLogCandidates().some((candidate) => (0, node_fs_1.existsSync)(candidate));
2102
+ if (!desktopPresent)
2103
+ return null;
2104
+ let existed = true;
2105
+ let existingRaw = "";
2106
+ try {
2107
+ existingRaw = await promises_1.default.readFile(configPath, "utf8");
2108
+ }
2109
+ catch (error) {
2110
+ const err = error;
2111
+ if (err.code !== "ENOENT")
2112
+ throw error;
2113
+ existed = false;
2114
+ }
2115
+ const existingSnapshotRaw = await readFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2116
+ if (!existingSnapshotRaw?.trim()) {
2117
+ const snapshot = {
2118
+ version: 1,
2119
+ configPath,
2120
+ existed,
2121
+ previousRaw: existed ? existingRaw : null,
2122
+ };
2123
+ await writeJsonObjectFile(CLAUDE_DESKTOP_3P_STATE_PATH, snapshot, 0o600);
2124
+ }
2125
+ let doc = {};
2126
+ if (existingRaw.trim()) {
2127
+ try {
2128
+ doc = objectRecordOr(JSON.parse(existingRaw), {});
2129
+ }
2130
+ catch {
2131
+ throw new Error(`invalid JSON in Claude Desktop config: ${configPath}`);
2132
+ }
2133
+ }
2134
+ const enterpriseConfig = objectRecordOr(doc.enterpriseConfig, {});
2135
+ enterpriseConfig.inferenceProvider = "gateway";
2136
+ enterpriseConfig.inferenceGatewayBaseUrl = anthropicCompatibleProxyUrl(params.backendUrl);
2137
+ enterpriseConfig.inferenceGatewayApiKey = params.apiKey;
2138
+ enterpriseConfig.inferenceModels = expandClaudeDesktopInferenceModels(params.claudeModels);
2139
+ enterpriseConfig.isDesktopExtensionEnabled = true;
2140
+ enterpriseConfig.isDesktopExtensionDirectoryEnabled = true;
2141
+ enterpriseConfig.isDesktopExtensionSignatureRequired = false;
2142
+ enterpriseConfig.isLocalDevMcpEnabled = true;
2143
+ enterpriseConfig.isClaudeCodeForDesktopEnabled = true;
2144
+ const deploymentOrganizationUuid = await discoverClaudeDesktopDeploymentOrganizationUuid(doc);
2145
+ if (deploymentOrganizationUuid) {
2146
+ enterpriseConfig.deploymentOrganizationUuid = deploymentOrganizationUuid;
2147
+ }
2148
+ doc.enterpriseConfig = enterpriseConfig;
2149
+ doc.isUsingBuiltInNodeForMcp = true;
2150
+ doc.isDxtAutoUpdatesEnabled = true;
2151
+ await writeJsonObjectFile(configPath, doc);
2152
+ return configPath;
2153
+ }
1979
2154
  function runOpenClawConfigCommand(args) {
1980
2155
  const run = (0, node_child_process_1.spawnSync)("openclaw", args, {
1981
2156
  encoding: "utf8",
@@ -2117,7 +2292,7 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
2117
2292
  if (typeof snapshot !== "object" || snapshot === null || Array.isArray(snapshot))
2118
2293
  return null;
2119
2294
  const obj = snapshot;
2120
- if (obj.version === 2 && Array.isArray(obj.targets)) {
2295
+ if ((obj.version === 2 || obj.version === 3) && Array.isArray(obj.targets)) {
2121
2296
  const targets = [];
2122
2297
  for (const entry of obj.targets) {
2123
2298
  if (typeof entry !== "object" || entry === null || Array.isArray(entry))
@@ -2134,6 +2309,11 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
2134
2309
  !Array.isArray(candidate.openAiProvider)
2135
2310
  ? candidate.openAiProvider
2136
2311
  : null,
2312
+ anthropicProvider: typeof candidate.anthropicProvider === "object" &&
2313
+ candidate.anthropicProvider !== null &&
2314
+ !Array.isArray(candidate.anthropicProvider)
2315
+ ? candidate.anthropicProvider
2316
+ : null,
2137
2317
  model: typeof candidate.model === "string" ? candidate.model : null,
2138
2318
  schema: typeof candidate.schema === "string" ? candidate.schema : null,
2139
2319
  plugin: "plugin" in candidate
@@ -2143,13 +2323,13 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
2143
2323
  : undefined,
2144
2324
  });
2145
2325
  }
2146
- return { version: 2, targets };
2326
+ return { version: 3, targets };
2147
2327
  }
2148
2328
  const looksLikeV1 = "openAiProvider" in obj || "model" in obj || "schema" in obj;
2149
2329
  if (!looksLikeV1)
2150
2330
  return null;
2151
2331
  return {
2152
- version: 2,
2332
+ version: 3,
2153
2333
  targets: [
2154
2334
  {
2155
2335
  configPath: fallbackConfigPath,
@@ -2240,6 +2420,10 @@ function isManagedOpenCodeProvider(provider) {
2240
2420
  const options = objectRecordOr(provider.options, {});
2241
2421
  return isTheClawBayOpenAiCompatibleBaseUrl(options.baseURL);
2242
2422
  }
2423
+ function isManagedOpenCodeAnthropicProvider(provider) {
2424
+ const options = objectRecordOr(provider.options, {});
2425
+ return isTheClawBayAnthropicCompatibleBaseUrl(options.baseURL);
2426
+ }
2243
2427
  function isManagedOpenCodeModel(value, supportedModelIds) {
2244
2428
  if (typeof value !== "string")
2245
2429
  return false;
@@ -2252,7 +2436,7 @@ async function writeOpenCodeFamilyConfig(params) {
2252
2436
  const fallbackConfigPath = normalizedConfigPaths[0] ?? node_path_1.default.join(xdgConfigHomeDir(), "opencode", "opencode.json");
2253
2437
  const restoreState = (await readOpenCodeRestoreSnapshot(params.restoreStatePath, fallbackConfigPath)) ??
2254
2438
  {
2255
- version: 2,
2439
+ version: 3,
2256
2440
  targets: [],
2257
2441
  };
2258
2442
  const supportedModelIds = new Set(params.models.map((entry) => entry.id).filter(Boolean));
@@ -2291,12 +2475,14 @@ async function writeOpenCodeFamilyConfig(params) {
2291
2475
  }
2292
2476
  const providerRoot = objectRecordOr(doc.provider, {});
2293
2477
  const openAiProvider = objectRecordOr(providerRoot[OPENAI_PROVIDER_ID], {});
2478
+ const anthropicProvider = objectRecordOr(providerRoot[ANTHROPIC_PROVIDER_ID], {});
2294
2479
  const alreadyManaged = isManagedOpenCodeProvider(openAiProvider) && isManagedOpenCodeModel(doc.model, supportedModelIds);
2295
2480
  if (!alreadyManaged && !restoreHasTarget(configPath)) {
2296
2481
  restoreState.targets.push({
2297
2482
  configPath,
2298
2483
  existed,
2299
2484
  openAiProvider: Object.keys(openAiProvider).length > 0 ? openAiProvider : null,
2485
+ anthropicProvider: Object.keys(anthropicProvider).length > 0 ? anthropicProvider : null,
2300
2486
  model: typeof doc.model === "string" && !doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`)
2301
2487
  ? doc.model
2302
2488
  : null,
@@ -2324,6 +2510,18 @@ async function writeOpenCodeFamilyConfig(params) {
2324
2510
  delete managedOpenAiProvider.blacklist;
2325
2511
  }
2326
2512
  providerRoot[OPENAI_PROVIDER_ID] = managedOpenAiProvider;
2513
+ if ((params.claudeModels?.length ?? 0) > 0) {
2514
+ const managedAnthropicProvider = objectRecordOr(providerRoot[ANTHROPIC_PROVIDER_ID], {});
2515
+ const anthropicOptions = objectRecordOr(managedAnthropicProvider.options, {});
2516
+ anthropicOptions.baseURL = anthropicCompatibleProxyV1Url(params.backendUrl);
2517
+ anthropicOptions.apiKey = params.apiKey;
2518
+ managedAnthropicProvider.options = anthropicOptions;
2519
+ managedAnthropicProvider.whitelist = uniqueStrings(params.claudeModels ?? []);
2520
+ if ("blacklist" in managedAnthropicProvider) {
2521
+ delete managedAnthropicProvider.blacklist;
2522
+ }
2523
+ providerRoot[ANTHROPIC_PROVIDER_ID] = managedAnthropicProvider;
2524
+ }
2327
2525
  if (DEFAULT_PROVIDER_ID in providerRoot) {
2328
2526
  delete providerRoot[DEFAULT_PROVIDER_ID];
2329
2527
  }
@@ -2429,12 +2627,13 @@ class SetupCommand extends base_command_1.BaseCommand {
2429
2627
  managed?.backendUrl ??
2430
2628
  DEFAULT_BACKEND_URL;
2431
2629
  const backendUrl = normalizeUrl(backendRaw, "--backend");
2432
- const [codexDetected, claudeDetected, continueDetected, clineDetected, gsdDetected, kiloDetected, rooDetected, traeDetected, aiderDetected, zoDetected] = await Promise.all([
2630
+ const [codexDetected, claudeDetected, continueDetected, clineDetected, gsdDetected, openCodeDetected, kiloDetected, rooDetected, traeDetected, aiderDetected, zoDetected] = await Promise.all([
2433
2631
  detectCodexClient(),
2434
- detectClaudeCodeClient(),
2632
+ detectClaudeClient(),
2435
2633
  detectContinueClient(),
2436
2634
  detectClineClient(),
2437
2635
  detectGsdClient(),
2636
+ detectOpenCodeClient(),
2438
2637
  detectKiloClient(),
2439
2638
  detectRooClient(),
2440
2639
  detectTraeClient(),
@@ -2453,8 +2652,8 @@ class SetupCommand extends base_command_1.BaseCommand {
2453
2652
  },
2454
2653
  {
2455
2654
  id: "claude",
2456
- label: "Claude Code CLI",
2457
- summaryLabel: "Claude Code",
2655
+ label: "Claude Code / Claude Desktop",
2656
+ summaryLabel: "Claude",
2458
2657
  detected: claudeDetected,
2459
2658
  recommended: true,
2460
2659
  icon: "✳",
@@ -2500,7 +2699,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2500
2699
  id: "opencode",
2501
2700
  label: "OpenCode",
2502
2701
  summaryLabel: "OpenCode",
2503
- detected: hasCommand("opencode"),
2702
+ detected: openCodeDetected,
2504
2703
  recommended: true,
2505
2704
  icon: "⌘",
2506
2705
  siteUrl: "https://opencode.ai",
@@ -2601,6 +2800,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2601
2800
  let updatedVsCodeEnvFiles = [];
2602
2801
  let updatedEditorTerminalSettings = [];
2603
2802
  let updatedClaudeEditorSettings = [];
2803
+ let claudeDesktop3pConfigPathManaged = null;
2604
2804
  let sessionMigration = null;
2605
2805
  let authSeed = null;
2606
2806
  let authSeedCleanup = null;
@@ -2668,6 +2868,11 @@ class SetupCommand extends base_command_1.BaseCommand {
2668
2868
  backendUrl,
2669
2869
  claudeEnabled: claudeEnvEnabled,
2670
2870
  });
2871
+ claudeDesktop3pConfigPathManaged = await writeClaudeDesktop3pConfig({
2872
+ backendUrl,
2873
+ apiKey: authCredential,
2874
+ claudeModels: claudeAccess?.enabled ? claudeAccess.models : [],
2875
+ });
2671
2876
  }
2672
2877
  }
2673
2878
  if (selectedSetupClients.has("codex")) {
@@ -2744,6 +2949,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2744
2949
  backendUrl,
2745
2950
  model: resolved?.model ?? DEFAULT_CODEX_MODEL,
2746
2951
  models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
2952
+ claudeModels: claudeAccess?.enabled ? claudeAccess.models : [],
2747
2953
  apiKey: authCredential,
2748
2954
  });
2749
2955
  }
@@ -2805,6 +3011,9 @@ class SetupCommand extends base_command_1.BaseCommand {
2805
3011
  summaryNotes.add(resolved.note);
2806
3012
  if (selectedSetupClients.has("claude") && claudeAccess?.enabled) {
2807
3013
  summaryNotes.add("Claude Code: exported ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY, CLAUDE_CODE_SIMPLE=1, disabled nonessential traffic, and disabled telemetry.");
3014
+ if (claudeDesktop3pConfigPathManaged) {
3015
+ summaryNotes.add("Claude Desktop: configured hidden Claude-3p gateway mode with the /anthropic base URL for The Claw Bay.");
3016
+ }
2808
3017
  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
3018
  }
2810
3019
  else if (selectedSetupClients.has("claude") && claudeAccess?.note) {
@@ -2861,6 +3070,9 @@ class SetupCommand extends base_command_1.BaseCommand {
2861
3070
  this.log(resolved.note);
2862
3071
  if (selectedSetupClients.has("claude") && claudeAccess?.enabled) {
2863
3072
  this.log(`- Claude Code base URL: ${anthropicCompatibleProxyUrl(backendUrl)}`);
3073
+ if (claudeDesktop3pConfigPathManaged) {
3074
+ this.log(`- Claude Desktop 3P config: ${claudeDesktop3pConfigPathManaged}`);
3075
+ }
2864
3076
  }
2865
3077
  else if (selectedSetupClients.has("claude") && claudeAccess?.note) {
2866
3078
  this.log(`- Claude Code: ${claudeAccess.note}`);
@@ -2976,9 +3188,9 @@ class SetupCommand extends base_command_1.BaseCommand {
2976
3188
  else {
2977
3189
  this.log("- OpenClaw: not detected (skipped)");
2978
3190
  }
2979
- const openCodeDetected = setupClients.find((client) => client.id === "opencode")?.detected ?? false;
2980
3191
  if (selectedSetupClients.has("opencode")) {
2981
3192
  this.log(`- OpenCode: configured (${openCodeConfigPaths.join(", ")})`);
3193
+ this.log("- OpenCode note: configured the OpenAI-compatible /v1 route and, when Claude access is available, the Anthropic-compatible /anthropic/v1 route.");
2982
3194
  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
3195
  const openCodeProjectConfig = findOpenCodeProjectConfigFile();
2984
3196
  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.57",
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": {