theclawbay 0.3.55 → 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";
@@ -27,6 +28,13 @@ const SHELL_START = "# theclawbay-shell-managed:start";
27
28
  const SHELL_END = "# theclawbay-shell-managed:end";
28
29
  const ENV_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "env");
29
30
  const ENV_KEY_NAME = "THECLAWBAY_API_KEY";
31
+ const CLAUDE_ENV_API_KEY_NAME = "ANTHROPIC_API_KEY";
32
+ const CLAUDE_ENV_BASE_URL_NAME = "ANTHROPIC_BASE_URL";
33
+ const CLAUDE_ENV_SIMPLE_MODE_NAME = "CLAUDE_CODE_SIMPLE";
34
+ const CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME = "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC";
35
+ const CLAUDE_ENV_ENABLE_TELEMETRY_NAME = "CLAUDE_CODE_ENABLE_TELEMETRY";
36
+ const CLAUDE_CODE_EDITOR_ENV_SETTING = "claudeCode.environmentVariables";
37
+ const CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING = "claudeCode.disableLoginPrompt";
30
38
  const CONTINUE_CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".continue", "config.yaml");
31
39
  const CLINE_GLOBAL_STATE_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".cline", "data", "globalState.json");
32
40
  const CLINE_SECRETS_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".cline", "data", "secrets.json");
@@ -40,6 +48,10 @@ const GSD_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateD
40
48
  const OPENCODE_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "opencode.restore.json");
41
49
  const KILO_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "kilo.restore.json");
42
50
  const ROO_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "roo-settings.restore.json");
51
+ const EDITOR_TERMINAL_ENV_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "editor-terminal-env.restore.json");
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");
54
+ const CODEX_FEATURE_FLAGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "codex-features.restore.json");
43
55
  const ROO_IMPORT_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "roo-code-settings.json");
44
56
  const MIGRATION_STATE_FILE = node_path_1.default.join(paths_1.codexDir, "theclawbay.migration.json");
45
57
  const HISTORY_PROVIDER_NEUTRALIZE_SOURCES = new Set([
@@ -126,6 +138,118 @@ function removeTopLevelProviderSelection(source) {
126
138
  }
127
139
  return `${filtered.join("\n").trimEnd()}\n`;
128
140
  }
141
+ function removeTopLevelKeyLineIf(source, key, shouldRemove) {
142
+ const lines = source.split(/\r?\n/);
143
+ const filtered = [];
144
+ let removed = false;
145
+ for (let i = 0; i < lines.length; i++) {
146
+ const line = lines[i] ?? "";
147
+ if (/^\s*\[/.test(line)) {
148
+ filtered.push(...lines.slice(i));
149
+ break;
150
+ }
151
+ if (/^\s*#/.test(line) || !line.trim()) {
152
+ filtered.push(line);
153
+ continue;
154
+ }
155
+ const match = line.match(new RegExp(`^\\s*${key}\\s*=\\s*\"([^\"]*)\"\\s*$`));
156
+ if (!match) {
157
+ filtered.push(line);
158
+ continue;
159
+ }
160
+ if (shouldRemove(match[1] ?? "")) {
161
+ removed = true;
162
+ continue;
163
+ }
164
+ filtered.push(line);
165
+ }
166
+ if (!removed)
167
+ return source;
168
+ return `${filtered.join("\n").trimEnd()}\n`;
169
+ }
170
+ function setTopLevelTableKey(source, tableName, key, tomlValue) {
171
+ const header = `[${tableName}]`;
172
+ const lines = source.split(/\r?\n/);
173
+ let tableStart = -1;
174
+ for (let i = 0; i < lines.length; i++) {
175
+ if ((lines[i] ?? "").trim() === header) {
176
+ tableStart = i;
177
+ break;
178
+ }
179
+ }
180
+ if (tableStart < 0) {
181
+ const block = `${header}\n${key} = ${tomlValue}\n`;
182
+ if (!source.trim())
183
+ return block;
184
+ return `${source.trimEnd()}\n\n${block}`;
185
+ }
186
+ let tableEnd = lines.length;
187
+ for (let i = tableStart + 1; i < lines.length; i++) {
188
+ if (/^\s*\[[^\]]+\]\s*$/.test(lines[i] ?? "")) {
189
+ tableEnd = i;
190
+ break;
191
+ }
192
+ }
193
+ for (let i = tableStart + 1; i < tableEnd; i++) {
194
+ const line = lines[i] ?? "";
195
+ if (/^\s*#/.test(line))
196
+ continue;
197
+ if (new RegExp(`^\\s*${key}\\s*=`).test(line)) {
198
+ lines[i] = `${key} = ${tomlValue}`;
199
+ return `${lines.join("\n").trimEnd()}\n`;
200
+ }
201
+ }
202
+ lines.splice(tableEnd, 0, `${key} = ${tomlValue}`);
203
+ return `${lines.join("\n").trimEnd()}\n`;
204
+ }
205
+ function removeTopLevelTableKey(source, tableName, key) {
206
+ const header = `[${tableName}]`;
207
+ const lines = source.split(/\r?\n/);
208
+ let tableStart = -1;
209
+ for (let i = 0; i < lines.length; i++) {
210
+ if ((lines[i] ?? "").trim() === header) {
211
+ tableStart = i;
212
+ break;
213
+ }
214
+ }
215
+ if (tableStart < 0)
216
+ return source;
217
+ let tableEnd = lines.length;
218
+ for (let i = tableStart + 1; i < lines.length; i++) {
219
+ if (/^\s*\[[^\]]+\]\s*$/.test(lines[i] ?? "")) {
220
+ tableEnd = i;
221
+ break;
222
+ }
223
+ }
224
+ let removed = false;
225
+ const nextLines = [...lines];
226
+ for (let i = tableStart + 1; i < tableEnd; i++) {
227
+ const line = nextLines[i] ?? "";
228
+ if (/^\s*#/.test(line))
229
+ continue;
230
+ if (new RegExp(`^\\s*${key}\\s*=`).test(line)) {
231
+ nextLines.splice(i, 1);
232
+ removed = true;
233
+ break;
234
+ }
235
+ }
236
+ if (!removed)
237
+ return source;
238
+ return `${nextLines.join("\n").trimEnd()}\n`;
239
+ }
240
+ function isTheClawBayChatgptBaseUrl(value) {
241
+ const trimmed = value.trim().replace(/\/+$/g, "");
242
+ if (!trimmed)
243
+ return false;
244
+ try {
245
+ const parsed = new URL(trimmed);
246
+ const hostname = parsed.hostname.toLowerCase();
247
+ return hostname === THECLAWBAY_CANONICAL_API_HOST || THECLAWBAY_WEBSITE_HOSTS.has(hostname);
248
+ }
249
+ catch {
250
+ return false;
251
+ }
252
+ }
129
253
  async function readFileIfExists(filePath) {
130
254
  try {
131
255
  return await promises_1.default.readFile(filePath, "utf8");
@@ -197,6 +321,22 @@ function isTheClawBayOpenAiCompatibleBaseUrl(value) {
197
321
  return false;
198
322
  }
199
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
+ }
200
340
  function uniqueStrings(values) {
201
341
  const output = [];
202
342
  const seen = new Set();
@@ -226,6 +366,11 @@ function configDirCandidates(appName) {
226
366
  }
227
367
  return uniqueStrings(dirs);
228
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
+ }
229
374
  function openCodeConfigCleanupTargets() {
230
375
  const home = node_os_1.default.homedir();
231
376
  const dirs = configDirCandidates("opencode");
@@ -277,6 +422,11 @@ function isManagedOpenCodeProvider(value) {
277
422
  const options = objectRecordOr(provider.options, {});
278
423
  return isTheClawBayOpenAiCompatibleBaseUrl(options.baseURL);
279
424
  }
425
+ function isManagedOpenCodeAnthropicProvider(value) {
426
+ const provider = objectRecordOr(value, {});
427
+ const options = objectRecordOr(provider.options, {});
428
+ return isTheClawBayAnthropicCompatibleBaseUrl(options.baseURL);
429
+ }
280
430
  function roamingAppDataDir() {
281
431
  if (process.env.APPDATA?.trim())
282
432
  return process.env.APPDATA;
@@ -321,6 +471,7 @@ async function cleanupCodexConfig() {
321
471
  next = removeManagedBlock(next, MANAGED_START, MANAGED_END);
322
472
  next = removeProviderTable(next, DEFAULT_PROVIDER_ID);
323
473
  next = removeTopLevelProviderSelection(next);
474
+ next = removeTopLevelKeyLineIf(next, "chatgpt_base_url", isTheClawBayChatgptBaseUrl);
324
475
  return writeIfChanged(configPath, next, existing);
325
476
  }
326
477
  async function cleanupContinueConfig() {
@@ -675,6 +826,162 @@ async function cleanupVsCodeHooks() {
675
826
  }
676
827
  return updated;
677
828
  }
829
+ function terminalIntegratedEnvSettingsKey() {
830
+ if (node_os_1.default.platform() === "darwin")
831
+ return "terminal.integrated.env.osx";
832
+ if (node_os_1.default.platform() === "win32")
833
+ return "terminal.integrated.env.windows";
834
+ return "terminal.integrated.env.linux";
835
+ }
836
+ async function cleanupEditorTerminalEnvSettings() {
837
+ const snapshotRaw = await readFileIfExists(EDITOR_TERMINAL_ENV_STATE_PATH);
838
+ if (snapshotRaw === null || !snapshotRaw.trim())
839
+ return [];
840
+ let entries = [];
841
+ try {
842
+ entries = JSON.parse(snapshotRaw);
843
+ }
844
+ catch {
845
+ return [];
846
+ }
847
+ const settingsKey = terminalIntegratedEnvSettingsKey();
848
+ const updated = [];
849
+ for (const entry of entries) {
850
+ const settings = await readJsonObjectFile(entry.settingsPath);
851
+ if (!entry.hadKey) {
852
+ if (Object.prototype.hasOwnProperty.call(settings, settingsKey)) {
853
+ delete settings[settingsKey];
854
+ }
855
+ }
856
+ else {
857
+ settings[settingsKey] = entry.previousValue;
858
+ }
859
+ if (entry.existed || Object.keys(settings).length > 0) {
860
+ const next = `${JSON.stringify(settings, null, 2)}\n`;
861
+ const existing = await readFileIfExists(entry.settingsPath);
862
+ if (existing !== next) {
863
+ await promises_1.default.mkdir(node_path_1.default.dirname(entry.settingsPath), { recursive: true });
864
+ await promises_1.default.writeFile(entry.settingsPath, next, "utf8");
865
+ updated.push(entry.settingsPath);
866
+ }
867
+ continue;
868
+ }
869
+ if (await removeFileIfExists(entry.settingsPath)) {
870
+ updated.push(entry.settingsPath);
871
+ }
872
+ }
873
+ await removeFileIfExists(EDITOR_TERMINAL_ENV_STATE_PATH);
874
+ return updated;
875
+ }
876
+ async function cleanupClaudeEditorSettings() {
877
+ const snapshotRaw = await readFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
878
+ if (snapshotRaw === null || !snapshotRaw.trim())
879
+ return [];
880
+ let entries = [];
881
+ try {
882
+ entries = JSON.parse(snapshotRaw);
883
+ }
884
+ catch {
885
+ return [];
886
+ }
887
+ const updated = [];
888
+ for (const entry of entries) {
889
+ const settings = await readJsonObjectFile(entry.settingsPath);
890
+ if (!entry.envHadKey) {
891
+ delete settings[CLAUDE_CODE_EDITOR_ENV_SETTING];
892
+ }
893
+ else {
894
+ settings[CLAUDE_CODE_EDITOR_ENV_SETTING] = entry.envPreviousValue;
895
+ }
896
+ if (!entry.disableLoginPromptHadKey) {
897
+ delete settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING];
898
+ }
899
+ else {
900
+ settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] = entry.disableLoginPromptPreviousValue;
901
+ }
902
+ if (entry.existed || Object.keys(settings).length > 0) {
903
+ const next = `${JSON.stringify(settings, null, 2)}\n`;
904
+ const existing = await readFileIfExists(entry.settingsPath);
905
+ if (existing !== next) {
906
+ await promises_1.default.mkdir(node_path_1.default.dirname(entry.settingsPath), { recursive: true });
907
+ await promises_1.default.writeFile(entry.settingsPath, next, "utf8");
908
+ updated.push(entry.settingsPath);
909
+ }
910
+ continue;
911
+ }
912
+ if (await removeFileIfExists(entry.settingsPath)) {
913
+ updated.push(entry.settingsPath);
914
+ }
915
+ }
916
+ await removeFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
917
+ return updated;
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
+ }
950
+ async function cleanupCodexFeatureFlags() {
951
+ const snapshotRaw = await readFileIfExists(CODEX_FEATURE_FLAGS_STATE_PATH);
952
+ if (snapshotRaw === null || !snapshotRaw.trim())
953
+ return false;
954
+ let snapshot = null;
955
+ try {
956
+ snapshot = JSON.parse(snapshotRaw);
957
+ }
958
+ catch {
959
+ snapshot = null;
960
+ }
961
+ if (!snapshot)
962
+ return false;
963
+ const configPath = node_path_1.default.join(paths_1.codexDir, "config.toml");
964
+ const existing = await readFileIfExists(configPath);
965
+ if (existing === null) {
966
+ await removeFileIfExists(CODEX_FEATURE_FLAGS_STATE_PATH);
967
+ return false;
968
+ }
969
+ let next = existing;
970
+ if (snapshot.appsHadKey && snapshot.appsPreviousValue !== null) {
971
+ next = setTopLevelTableKey(next, "features", "apps", snapshot.appsPreviousValue);
972
+ }
973
+ else {
974
+ next = removeTopLevelTableKey(next, "features", "apps");
975
+ }
976
+ if (snapshot.appsMcpGatewayHadKey && snapshot.appsMcpGatewayPreviousValue !== null) {
977
+ next = setTopLevelTableKey(next, "features", "apps_mcp_gateway", snapshot.appsMcpGatewayPreviousValue);
978
+ }
979
+ else {
980
+ next = removeTopLevelTableKey(next, "features", "apps_mcp_gateway");
981
+ }
982
+ await removeFileIfExists(CODEX_FEATURE_FLAGS_STATE_PATH);
983
+ return writeIfChanged(configPath, next, existing);
984
+ }
678
985
  async function cleanupOpenClawConfig() {
679
986
  const configPath = node_path_1.default.join(node_os_1.default.homedir(), ".openclaw", "openclaw.json");
680
987
  const existingRaw = await readFileIfExists(configPath);
@@ -718,7 +1025,7 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
718
1025
  if (typeof snapshot !== "object" || snapshot === null || Array.isArray(snapshot))
719
1026
  return null;
720
1027
  const obj = snapshot;
721
- if (obj.version === 2 && Array.isArray(obj.targets)) {
1028
+ if ((obj.version === 2 || obj.version === 3) && Array.isArray(obj.targets)) {
722
1029
  const targets = [];
723
1030
  for (const entry of obj.targets) {
724
1031
  if (typeof entry !== "object" || entry === null || Array.isArray(entry))
@@ -735,6 +1042,11 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
735
1042
  !Array.isArray(candidate.openAiProvider)
736
1043
  ? candidate.openAiProvider
737
1044
  : null,
1045
+ anthropicProvider: typeof candidate.anthropicProvider === "object" &&
1046
+ candidate.anthropicProvider !== null &&
1047
+ !Array.isArray(candidate.anthropicProvider)
1048
+ ? candidate.anthropicProvider
1049
+ : null,
738
1050
  model: typeof candidate.model === "string" ? candidate.model : null,
739
1051
  schema: typeof candidate.schema === "string" ? candidate.schema : null,
740
1052
  plugin: "plugin" in candidate
@@ -744,13 +1056,13 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
744
1056
  : undefined,
745
1057
  });
746
1058
  }
747
- return { version: 2, targets };
1059
+ return { version: 3, targets };
748
1060
  }
749
1061
  const looksLikeV1 = "openAiProvider" in obj || "model" in obj || "schema" in obj;
750
1062
  if (!looksLikeV1)
751
1063
  return null;
752
1064
  return {
753
- version: 2,
1065
+ version: 3,
754
1066
  targets: [
755
1067
  {
756
1068
  configPath: fallbackConfigPath,
@@ -819,6 +1131,15 @@ async function cleanupOpenCodeFamilyConfigs(params) {
819
1131
  }
820
1132
  else if (OPENAI_PROVIDER_ID in providerRoot) {
821
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;
822
1143
  }
823
1144
  doc.provider = providerRoot;
824
1145
  if (target.model === null) {
@@ -877,6 +1198,10 @@ async function cleanupOpenCodeFamilyConfigs(params) {
877
1198
  delete providerRoot[OPENAI_PROVIDER_ID];
878
1199
  fileChanged = true;
879
1200
  }
1201
+ if (isManagedOpenCodeAnthropicProvider(providerRoot[ANTHROPIC_PROVIDER_ID])) {
1202
+ delete providerRoot[ANTHROPIC_PROVIDER_ID];
1203
+ fileChanged = true;
1204
+ }
880
1205
  if (!fileChanged)
881
1206
  continue;
882
1207
  changed = true;
@@ -937,7 +1262,11 @@ class LogoutCommand extends base_command_1.BaseCommand {
937
1262
  let updatedShellFiles = [];
938
1263
  let updatedPowerShellProfiles = [];
939
1264
  let updatedVsCodeHooks = [];
1265
+ let updatedEditorTerminalSettings = [];
1266
+ let updatedClaudeEditorSettings = [];
1267
+ let updatedClaudeDesktopConfig = false;
940
1268
  let updatedCodexConfig = false;
1269
+ let updatedCodexFeatures = false;
941
1270
  let updatedContinueConfig = false;
942
1271
  let updatedClineConfig = false;
943
1272
  let updatedGsdConfig = false;
@@ -986,8 +1315,12 @@ class LogoutCommand extends base_command_1.BaseCommand {
986
1315
  updatedShellFiles = await cleanupShellFiles();
987
1316
  updatedPowerShellProfiles = await cleanupPowerShellProfiles();
988
1317
  updatedVsCodeHooks = await cleanupVsCodeHooks();
1318
+ updatedEditorTerminalSettings = await cleanupEditorTerminalEnvSettings();
1319
+ updatedClaudeEditorSettings = await cleanupClaudeEditorSettings();
1320
+ updatedClaudeDesktopConfig = await cleanupClaudeDesktop3pConfig();
989
1321
  progress.update("Reverting Codex");
990
1322
  updatedCodexConfig = await cleanupCodexConfig();
1323
+ updatedCodexFeatures = await cleanupCodexFeatureFlags();
991
1324
  sessionMigration = await (0, codex_history_migration_1.migrateSessionProviders)({
992
1325
  codexHome: paths_1.codexDir,
993
1326
  migrationStateFile: MIGRATION_STATE_FILE,
@@ -1028,8 +1361,11 @@ class LogoutCommand extends base_command_1.BaseCommand {
1028
1361
  throw error;
1029
1362
  }
1030
1363
  delete process.env[ENV_KEY_NAME];
1031
- delete process.env.ANTHROPIC_API_KEY;
1032
- delete process.env.ANTHROPIC_BASE_URL;
1364
+ delete process.env[CLAUDE_ENV_API_KEY_NAME];
1365
+ delete process.env[CLAUDE_ENV_BASE_URL_NAME];
1366
+ delete process.env[CLAUDE_ENV_SIMPLE_MODE_NAME];
1367
+ delete process.env[CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME];
1368
+ delete process.env[CLAUDE_ENV_ENABLE_TELEMETRY_NAME];
1033
1369
  if (!debugOutput && process.stdout.isTTY) {
1034
1370
  progress.succeed("Logout complete");
1035
1371
  }
@@ -1037,8 +1373,12 @@ class LogoutCommand extends base_command_1.BaseCommand {
1037
1373
  this.log("Logout complete");
1038
1374
  }
1039
1375
  const revertedTargets = [];
1376
+ const claudeChanged = updatedClaudeEditorSettings.length > 0 ||
1377
+ updatedClaudeDesktopConfig;
1040
1378
  const codexChanged = updatedCodexConfig ||
1379
+ updatedCodexFeatures ||
1041
1380
  updatedVsCodeHooks.length > 0 ||
1381
+ updatedEditorTerminalSettings.length > 0 ||
1042
1382
  sessionMigration.rewritten > 0 ||
1043
1383
  sessionMigration.retimed > 0 ||
1044
1384
  stateDbMigration.updated > 0 ||
@@ -1047,6 +1387,8 @@ class LogoutCommand extends base_command_1.BaseCommand {
1047
1387
  modelCacheCleanup.action === "removed";
1048
1388
  if (codexChanged)
1049
1389
  revertedTargets.push("Codex");
1390
+ if (claudeChanged)
1391
+ revertedTargets.push("Claude");
1050
1392
  if (updatedContinueConfig)
1051
1393
  revertedTargets.push("Continue");
1052
1394
  if (updatedClineConfig)
@@ -1086,7 +1428,10 @@ class LogoutCommand extends base_command_1.BaseCommand {
1086
1428
  removedEnvFile ||
1087
1429
  updatedShellFiles.length > 0 ||
1088
1430
  updatedPowerShellProfiles.length > 0 ||
1089
- updatedVsCodeHooks.length > 0,
1431
+ updatedVsCodeHooks.length > 0 ||
1432
+ updatedEditorTerminalSettings.length > 0 ||
1433
+ updatedClaudeEditorSettings.length > 0 ||
1434
+ updatedClaudeDesktopConfig,
1090
1435
  notes: Array.from(summaryNotes),
1091
1436
  });
1092
1437
  return;
@@ -1102,7 +1447,11 @@ class LogoutCommand extends base_command_1.BaseCommand {
1102
1447
  this.log(`- Shell profiles updated: ${updatedShellFiles.length ? updatedShellFiles.join(", ") : "none"}`);
1103
1448
  this.log(`- PowerShell profiles updated: ${updatedPowerShellProfiles.length ? updatedPowerShellProfiles.join(", ") : "none"}`);
1104
1449
  this.log(`- VS Code env hooks updated: ${updatedVsCodeHooks.length ? updatedVsCodeHooks.join(", ") : "none"}`);
1450
+ this.log(`- Editor terminal env updated: ${updatedEditorTerminalSettings.length ? updatedEditorTerminalSettings.join(", ") : "none"}`);
1451
+ this.log(`- Claude editor settings updated: ${updatedClaudeEditorSettings.length ? updatedClaudeEditorSettings.join(", ") : "none"}`);
1452
+ this.log(`- Claude Desktop 3P config cleaned: ${updatedClaudeDesktopConfig ? "yes" : "no"}`);
1105
1453
  this.log(`- Codex config cleaned: ${updatedCodexConfig ? "yes" : "no"}`);
1454
+ this.log(`- Codex Apps feature flags restored: ${updatedCodexFeatures ? "yes" : "no"}`);
1106
1455
  if (sessionMigration.rewritten > 0) {
1107
1456
  this.log(`- Conversations: updated ${sessionMigration.rewritten}/${sessionMigration.scanned} local sessions for cross-provider visibility.`);
1108
1457
  }
@@ -42,6 +42,19 @@ const ENV_KEY_NAME = "THECLAWBAY_API_KEY";
42
42
  const CLAUDE_ENV_API_KEY_NAME = "ANTHROPIC_API_KEY";
43
43
  const CLAUDE_ENV_BASE_URL_NAME = "ANTHROPIC_BASE_URL";
44
44
  const CLAUDE_ENV_SIMPLE_MODE_NAME = "CLAUDE_CODE_SIMPLE";
45
+ const CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME = "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC";
46
+ const CLAUDE_ENV_ENABLE_TELEMETRY_NAME = "CLAUDE_CODE_ENABLE_TELEMETRY";
47
+ const CLAUDE_CODE_EDITOR_ENV_SETTING = "claudeCode.environmentVariables";
48
+ const CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING = "claudeCode.disableLoginPrompt";
49
+ const ANTHROPIC_PROVIDER_ID = "anthropic";
50
+ const MANAGED_EDITOR_TERMINAL_ENV_NAMES = [
51
+ ENV_KEY_NAME,
52
+ CLAUDE_ENV_API_KEY_NAME,
53
+ CLAUDE_ENV_BASE_URL_NAME,
54
+ CLAUDE_ENV_SIMPLE_MODE_NAME,
55
+ CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME,
56
+ CLAUDE_ENV_ENABLE_TELEMETRY_NAME,
57
+ ];
45
58
  const ENV_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "env");
46
59
  const CONTINUE_CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".continue", "config.yaml");
47
60
  const CLINE_GLOBAL_STATE_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".cline", "data", "globalState.json");
@@ -56,6 +69,10 @@ const GSD_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateD
56
69
  const OPENCODE_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "opencode.restore.json");
57
70
  const KILO_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "kilo.restore.json");
58
71
  const ROO_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "roo-settings.restore.json");
72
+ const EDITOR_TERMINAL_ENV_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "editor-terminal-env.restore.json");
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");
75
+ const CODEX_FEATURE_FLAGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "codex-features.restore.json");
59
76
  const ROO_IMPORT_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "roo-code-settings.json");
60
77
  const MIGRATION_STATE_FILE = node_path_1.default.join(paths_1.codexDir, "theclawbay.migration.json");
61
78
  const MANAGED_START = "# theclawbay-managed:start";
@@ -455,6 +472,99 @@ function upsertFirstKeyLine(source, key, tomlValue) {
455
472
  }
456
473
  return `${`${key} = ${tomlValue}\n${source}`.trimEnd()}\n`;
457
474
  }
475
+ function parseTomlScalarValue(line) {
476
+ const match = line.match(/^\s*[A-Za-z0-9_.-]+\s*=\s*(.+?)\s*$/);
477
+ return match?.[1]?.trim() ?? null;
478
+ }
479
+ function upsertTopLevelTableKey(source, tableName, key, tomlValue) {
480
+ const header = `[${tableName}]`;
481
+ const lines = source.split(/\r?\n/);
482
+ let tableStart = -1;
483
+ for (let i = 0; i < lines.length; i++) {
484
+ if ((lines[i] ?? "").trim() === header) {
485
+ tableStart = i;
486
+ break;
487
+ }
488
+ }
489
+ if (tableStart < 0) {
490
+ const block = `${header}\n${key} = ${tomlValue}\n`;
491
+ if (!source.trim())
492
+ return block;
493
+ return `${source.trimEnd()}\n\n${block}`;
494
+ }
495
+ let tableEnd = lines.length;
496
+ for (let i = tableStart + 1; i < lines.length; i++) {
497
+ if (/^\s*\[[^\]]+\]\s*$/.test(lines[i] ?? "")) {
498
+ tableEnd = i;
499
+ break;
500
+ }
501
+ }
502
+ for (let i = tableStart + 1; i < tableEnd; i++) {
503
+ const line = lines[i] ?? "";
504
+ if (/^\s*#/.test(line))
505
+ continue;
506
+ if (new RegExp(`^\\s*${key}\\s*=`).test(line)) {
507
+ lines[i] = `${key} = ${tomlValue}`;
508
+ return `${lines.join("\n").trimEnd()}\n`;
509
+ }
510
+ }
511
+ lines.splice(tableEnd, 0, `${key} = ${tomlValue}`);
512
+ return `${lines.join("\n").trimEnd()}\n`;
513
+ }
514
+ function readTopLevelTableKeyValue(source, tableName, key) {
515
+ const header = `[${tableName}]`;
516
+ const lines = source.split(/\r?\n/);
517
+ let tableStart = -1;
518
+ for (let i = 0; i < lines.length; i++) {
519
+ if ((lines[i] ?? "").trim() === header) {
520
+ tableStart = i;
521
+ break;
522
+ }
523
+ }
524
+ if (tableStart < 0) {
525
+ return { hadTable: false, hadKey: false, value: null };
526
+ }
527
+ for (let i = tableStart + 1; i < lines.length; i++) {
528
+ const line = lines[i] ?? "";
529
+ if (/^\s*\[[^\]]+\]\s*$/.test(line))
530
+ break;
531
+ if (/^\s*#/.test(line) || !line.trim())
532
+ continue;
533
+ if (new RegExp(`^\\s*${key}\\s*=`).test(line)) {
534
+ return { hadTable: true, hadKey: true, value: parseTomlScalarValue(line) };
535
+ }
536
+ }
537
+ return { hadTable: true, hadKey: false, value: null };
538
+ }
539
+ function removeTopLevelKeyLineIf(source, key, shouldRemove) {
540
+ const lines = source.split(/\r?\n/);
541
+ const filtered = [];
542
+ let removed = false;
543
+ for (let i = 0; i < lines.length; i++) {
544
+ const line = lines[i] ?? "";
545
+ if (/^\s*\[/.test(line)) {
546
+ filtered.push(...lines.slice(i));
547
+ break;
548
+ }
549
+ if (/^\s*#/.test(line) || !line.trim()) {
550
+ filtered.push(line);
551
+ continue;
552
+ }
553
+ const match = line.match(new RegExp(`^\\s*${key}\\s*=\\s*\"([^\"]*)\"\\s*$`));
554
+ if (!match) {
555
+ filtered.push(line);
556
+ continue;
557
+ }
558
+ if (shouldRemove(match[1] ?? "")) {
559
+ removed = true;
560
+ continue;
561
+ }
562
+ filtered.push(line);
563
+ }
564
+ if (!removed)
565
+ return source;
566
+ return `${filtered.join("\n").trimEnd()}\n`;
567
+ }
458
568
  function shellQuote(value) {
459
569
  return `'${value.replace(/'/g, `'\\''`)}'`;
460
570
  }
@@ -480,12 +590,28 @@ function publicApiOriginForBackendUrl(backendUrl) {
480
590
  return trimmed;
481
591
  }
482
592
  }
593
+ function isTheClawBayChatgptBaseUrl(value) {
594
+ const trimmed = trimTrailingSlash(value.trim());
595
+ if (!trimmed)
596
+ return false;
597
+ try {
598
+ const parsed = new URL(trimmed);
599
+ const hostname = parsed.hostname.toLowerCase();
600
+ return hostname === THECLAWBAY_CANONICAL_API_HOST || THECLAWBAY_WEBSITE_HOSTS.has(hostname);
601
+ }
602
+ catch {
603
+ return false;
604
+ }
605
+ }
483
606
  function openAiCompatibleProxyUrl(backendUrl) {
484
607
  return `${publicApiOriginForBackendUrl(backendUrl)}${CANONICAL_THECLAWBAY_OPENAI_PROXY_SUFFIX}`;
485
608
  }
486
609
  function anthropicCompatibleProxyUrl(backendUrl) {
487
610
  return `${publicApiOriginForBackendUrl(backendUrl)}/anthropic`;
488
611
  }
612
+ function anthropicCompatibleProxyV1Url(backendUrl) {
613
+ return `${anthropicCompatibleProxyUrl(backendUrl)}/v1`;
614
+ }
489
615
  function isTheClawBayOpenAiCompatibleBaseUrl(value) {
490
616
  if (typeof value !== "string")
491
617
  return false;
@@ -510,6 +636,22 @@ function isTheClawBayOpenAiCompatibleBaseUrl(value) {
510
636
  return false;
511
637
  }
512
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
+ }
513
655
  function uniqueStrings(values) {
514
656
  const output = [];
515
657
  const seen = new Set();
@@ -560,6 +702,29 @@ function configDirCandidates(appName) {
560
702
  }
561
703
  return uniqueStrings(dirs);
562
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
+ }
563
728
  async function readFileIfExists(filePath) {
564
729
  try {
565
730
  return await promises_1.default.readFile(filePath, "utf8");
@@ -1168,6 +1333,33 @@ async function detectClaudeCodeClient() {
1168
1333
  }
1169
1334
  return false;
1170
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
+ }
1171
1363
  async function detectClineClient() {
1172
1364
  if (hasCommand("cline"))
1173
1365
  return true;
@@ -1305,7 +1497,22 @@ async function writeCodexConfig(params) {
1305
1497
  next = removeProviderTable(next, DEFAULT_PROVIDER_ID);
1306
1498
  next = upsertFirstKeyLine(next, "model_provider", `"${DEFAULT_PROVIDER_ID}"`);
1307
1499
  next = upsertFirstKeyLine(next, "model", `"${params.model}"`);
1308
- next = upsertFirstKeyLine(next, "chatgpt_base_url", `"${proxyRoot}"`);
1500
+ next = removeTopLevelKeyLineIf(next, "chatgpt_base_url", isTheClawBayChatgptBaseUrl);
1501
+ const existingFeatureSnapshot = await readFileIfExists(CODEX_FEATURE_FLAGS_STATE_PATH);
1502
+ if (!existingFeatureSnapshot?.trim()) {
1503
+ const appsState = readTopLevelTableKeyValue(next, "features", "apps");
1504
+ const appsMcpGatewayState = readTopLevelTableKeyValue(next, "features", "apps_mcp_gateway");
1505
+ const snapshot = {
1506
+ hadFeaturesTable: appsState.hadTable || appsMcpGatewayState.hadTable,
1507
+ appsHadKey: appsState.hadKey,
1508
+ appsPreviousValue: appsState.value,
1509
+ appsMcpGatewayHadKey: appsMcpGatewayState.hadKey,
1510
+ appsMcpGatewayPreviousValue: appsMcpGatewayState.value,
1511
+ };
1512
+ await writeJsonObjectFile(CODEX_FEATURE_FLAGS_STATE_PATH, snapshot, 0o600);
1513
+ }
1514
+ next = upsertTopLevelTableKey(next, "features", "apps", "false");
1515
+ next = upsertTopLevelTableKey(next, "features", "apps_mcp_gateway", "false");
1309
1516
  const managedBlock = appendManagedBlock("", [
1310
1517
  MANAGED_START,
1311
1518
  `[model_providers.${DEFAULT_PROVIDER_ID}]`,
@@ -1563,7 +1770,7 @@ async function persistApiKeyEnv(params) {
1563
1770
  `export ${ENV_KEY_NAME}=${shellQuote(params.apiKey)}`,
1564
1771
  ];
1565
1772
  if (params.claudeEnabled) {
1566
- 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`);
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))}`, `export ${CLAUDE_ENV_SIMPLE_MODE_NAME}=1`, `export ${CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME}=1`, `export ${CLAUDE_ENV_ENABLE_TELEMETRY_NAME}=0`);
1567
1774
  }
1568
1775
  const envContents = `${envLines.join("\n")}\n`;
1569
1776
  await promises_1.default.writeFile(ENV_FILE, envContents, "utf8");
@@ -1599,11 +1806,15 @@ async function persistApiKeyEnv(params) {
1599
1806
  process.env[CLAUDE_ENV_API_KEY_NAME] = params.apiKey;
1600
1807
  process.env[CLAUDE_ENV_BASE_URL_NAME] = anthropicCompatibleProxyUrl(params.backendUrl);
1601
1808
  process.env[CLAUDE_ENV_SIMPLE_MODE_NAME] = "1";
1809
+ process.env[CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME] = "1";
1810
+ process.env[CLAUDE_ENV_ENABLE_TELEMETRY_NAME] = "0";
1602
1811
  }
1603
1812
  else {
1604
1813
  delete process.env[CLAUDE_ENV_API_KEY_NAME];
1605
1814
  delete process.env[CLAUDE_ENV_BASE_URL_NAME];
1606
1815
  delete process.env[CLAUDE_ENV_SIMPLE_MODE_NAME];
1816
+ delete process.env[CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME];
1817
+ delete process.env[CLAUDE_ENV_ENABLE_TELEMETRY_NAME];
1607
1818
  }
1608
1819
  return updated;
1609
1820
  }
@@ -1615,7 +1826,7 @@ async function persistFishEnv(params) {
1615
1826
  `set -gx ${ENV_KEY_NAME} ${shellQuote(params.apiKey)}`,
1616
1827
  ];
1617
1828
  if (params.claudeEnabled) {
1618
- 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`);
1829
+ 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`);
1619
1830
  }
1620
1831
  await promises_1.default.writeFile(fishConfPath, `${lines.join("\n")}\n`, "utf8");
1621
1832
  return [fishConfPath];
@@ -1640,7 +1851,7 @@ async function persistPowerShellEnv(params) {
1640
1851
  `$env:${ENV_KEY_NAME} = ${powerShellQuote(params.apiKey)}`,
1641
1852
  ];
1642
1853
  if (params.claudeEnabled) {
1643
- 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"`);
1854
+ 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"`);
1644
1855
  }
1645
1856
  lines.push(SHELL_END);
1646
1857
  const next = appendManagedBlock(cleaned, lines);
@@ -1677,6 +1888,269 @@ async function persistVsCodeServerEnvSource() {
1677
1888
  }
1678
1889
  return updated;
1679
1890
  }
1891
+ function terminalIntegratedEnvSettingsKey() {
1892
+ if (node_os_1.default.platform() === "darwin")
1893
+ return "terminal.integrated.env.osx";
1894
+ if (node_os_1.default.platform() === "win32")
1895
+ return "terminal.integrated.env.windows";
1896
+ return "terminal.integrated.env.linux";
1897
+ }
1898
+ function buildManagedEditorTerminalEnv(params) {
1899
+ const env = {
1900
+ [ENV_KEY_NAME]: params.apiKey,
1901
+ };
1902
+ if (params.claudeEnabled) {
1903
+ env[CLAUDE_ENV_API_KEY_NAME] = params.apiKey;
1904
+ env[CLAUDE_ENV_BASE_URL_NAME] = anthropicCompatibleProxyUrl(params.backendUrl);
1905
+ env[CLAUDE_ENV_SIMPLE_MODE_NAME] = "1";
1906
+ env[CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME] = "1";
1907
+ env[CLAUDE_ENV_ENABLE_TELEMETRY_NAME] = "0";
1908
+ }
1909
+ return env;
1910
+ }
1911
+ function buildManagedClaudeEditorEnv(params) {
1912
+ return {
1913
+ [CLAUDE_ENV_API_KEY_NAME]: params.apiKey,
1914
+ [CLAUDE_ENV_BASE_URL_NAME]: anthropicCompatibleProxyUrl(params.backendUrl),
1915
+ [CLAUDE_ENV_SIMPLE_MODE_NAME]: "1",
1916
+ [CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME]: "1",
1917
+ [CLAUDE_ENV_ENABLE_TELEMETRY_NAME]: "0",
1918
+ };
1919
+ }
1920
+ async function persistEditorTerminalEnvSettings(params) {
1921
+ const settingsKey = terminalIntegratedEnvSettingsKey();
1922
+ const desiredEnv = buildManagedEditorTerminalEnv(params);
1923
+ const existingSnapshotRaw = await readFileIfExists(EDITOR_TERMINAL_ENV_STATE_PATH);
1924
+ let existingSnapshot = [];
1925
+ if (existingSnapshotRaw?.trim()) {
1926
+ try {
1927
+ existingSnapshot = JSON.parse(existingSnapshotRaw);
1928
+ }
1929
+ catch {
1930
+ existingSnapshot = [];
1931
+ }
1932
+ }
1933
+ const snapshotByPath = new Map(existingSnapshot.map((entry) => [entry.settingsPath, entry]));
1934
+ const managedPaths = [];
1935
+ for (const host of editorHosts()) {
1936
+ const settingsPath = host.userSettingsPath;
1937
+ const hostPresent = (await pathExists(host.extensionDir)) ||
1938
+ (await pathExists(settingsPath)) ||
1939
+ (await pathExists(node_path_1.default.dirname(settingsPath)));
1940
+ if (!hostPresent)
1941
+ continue;
1942
+ const existed = await pathExists(settingsPath);
1943
+ const settings = await readJsonObjectFile(settingsPath);
1944
+ const currentValue = settings[settingsKey];
1945
+ const currentEnv = typeof currentValue === "object" && currentValue !== null && !Array.isArray(currentValue)
1946
+ ? { ...currentValue }
1947
+ : {};
1948
+ let changed = false;
1949
+ for (const [name, value] of Object.entries(desiredEnv)) {
1950
+ if (currentEnv[name] === value)
1951
+ continue;
1952
+ currentEnv[name] = value;
1953
+ changed = true;
1954
+ }
1955
+ for (const name of MANAGED_EDITOR_TERMINAL_ENV_NAMES) {
1956
+ if (name in desiredEnv || !(name in currentEnv))
1957
+ continue;
1958
+ delete currentEnv[name];
1959
+ changed = true;
1960
+ }
1961
+ if (!changed)
1962
+ continue;
1963
+ if (!snapshotByPath.has(settingsPath)) {
1964
+ snapshotByPath.set(settingsPath, {
1965
+ settingsPath,
1966
+ existed,
1967
+ hadKey: Object.prototype.hasOwnProperty.call(settings, settingsKey),
1968
+ previousValue: currentValue ?? null,
1969
+ });
1970
+ }
1971
+ settings[settingsKey] = currentEnv;
1972
+ await writeJsonObjectFile(settingsPath, settings);
1973
+ managedPaths.push(settingsPath);
1974
+ }
1975
+ await writeJsonObjectFile(EDITOR_TERMINAL_ENV_STATE_PATH, Array.from(snapshotByPath.values()), 0o600);
1976
+ return managedPaths;
1977
+ }
1978
+ async function persistClaudeEditorSettings(params) {
1979
+ const existingSnapshotRaw = await readFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
1980
+ let existingSnapshot = [];
1981
+ if (existingSnapshotRaw?.trim()) {
1982
+ try {
1983
+ existingSnapshot = JSON.parse(existingSnapshotRaw);
1984
+ }
1985
+ catch {
1986
+ existingSnapshot = [];
1987
+ }
1988
+ }
1989
+ const snapshotByPath = new Map(existingSnapshot.map((entry) => [entry.settingsPath, entry]));
1990
+ const managedPaths = [];
1991
+ const desiredEnv = params.claudeEnabled
1992
+ ? buildManagedClaudeEditorEnv({ apiKey: params.apiKey, backendUrl: params.backendUrl })
1993
+ : null;
1994
+ for (const host of editorHosts()) {
1995
+ const settingsPath = host.userSettingsPath;
1996
+ const hostPresent = (await pathExists(host.extensionDir)) ||
1997
+ (await pathExists(settingsPath)) ||
1998
+ (await pathExists(node_path_1.default.dirname(settingsPath)));
1999
+ if (!hostPresent)
2000
+ continue;
2001
+ const existed = await pathExists(settingsPath);
2002
+ const settings = await readJsonObjectFile(settingsPath);
2003
+ const currentEnvValue = settings[CLAUDE_CODE_EDITOR_ENV_SETTING];
2004
+ const currentEnv = typeof currentEnvValue === "object" && currentEnvValue !== null && !Array.isArray(currentEnvValue)
2005
+ ? { ...currentEnvValue }
2006
+ : {};
2007
+ let changed = false;
2008
+ if (!snapshotByPath.has(settingsPath)) {
2009
+ snapshotByPath.set(settingsPath, {
2010
+ settingsPath,
2011
+ existed,
2012
+ envHadKey: Object.prototype.hasOwnProperty.call(settings, CLAUDE_CODE_EDITOR_ENV_SETTING),
2013
+ envPreviousValue: currentEnvValue ?? null,
2014
+ disableLoginPromptHadKey: Object.prototype.hasOwnProperty.call(settings, CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING),
2015
+ disableLoginPromptPreviousValue: settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] ?? null,
2016
+ });
2017
+ }
2018
+ if (desiredEnv) {
2019
+ const nextEnv = { ...currentEnv };
2020
+ for (const [name, value] of Object.entries(desiredEnv)) {
2021
+ if (nextEnv[name] === value)
2022
+ continue;
2023
+ nextEnv[name] = value;
2024
+ changed = true;
2025
+ }
2026
+ settings[CLAUDE_CODE_EDITOR_ENV_SETTING] = nextEnv;
2027
+ if (settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] !== true) {
2028
+ settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] = true;
2029
+ changed = true;
2030
+ }
2031
+ }
2032
+ else {
2033
+ if (Object.prototype.hasOwnProperty.call(settings, CLAUDE_CODE_EDITOR_ENV_SETTING)) {
2034
+ delete settings[CLAUDE_CODE_EDITOR_ENV_SETTING];
2035
+ changed = true;
2036
+ }
2037
+ if (Object.prototype.hasOwnProperty.call(settings, CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING)) {
2038
+ delete settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING];
2039
+ changed = true;
2040
+ }
2041
+ }
2042
+ if (!changed)
2043
+ continue;
2044
+ await writeJsonObjectFile(settingsPath, settings);
2045
+ managedPaths.push(settingsPath);
2046
+ }
2047
+ await writeJsonObjectFile(CLAUDE_EDITOR_SETTINGS_STATE_PATH, Array.from(snapshotByPath.values()), 0o600);
2048
+ return managedPaths;
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
+ }
1680
2154
  function runOpenClawConfigCommand(args) {
1681
2155
  const run = (0, node_child_process_1.spawnSync)("openclaw", args, {
1682
2156
  encoding: "utf8",
@@ -1818,7 +2292,7 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
1818
2292
  if (typeof snapshot !== "object" || snapshot === null || Array.isArray(snapshot))
1819
2293
  return null;
1820
2294
  const obj = snapshot;
1821
- if (obj.version === 2 && Array.isArray(obj.targets)) {
2295
+ if ((obj.version === 2 || obj.version === 3) && Array.isArray(obj.targets)) {
1822
2296
  const targets = [];
1823
2297
  for (const entry of obj.targets) {
1824
2298
  if (typeof entry !== "object" || entry === null || Array.isArray(entry))
@@ -1835,6 +2309,11 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
1835
2309
  !Array.isArray(candidate.openAiProvider)
1836
2310
  ? candidate.openAiProvider
1837
2311
  : null,
2312
+ anthropicProvider: typeof candidate.anthropicProvider === "object" &&
2313
+ candidate.anthropicProvider !== null &&
2314
+ !Array.isArray(candidate.anthropicProvider)
2315
+ ? candidate.anthropicProvider
2316
+ : null,
1838
2317
  model: typeof candidate.model === "string" ? candidate.model : null,
1839
2318
  schema: typeof candidate.schema === "string" ? candidate.schema : null,
1840
2319
  plugin: "plugin" in candidate
@@ -1844,13 +2323,13 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
1844
2323
  : undefined,
1845
2324
  });
1846
2325
  }
1847
- return { version: 2, targets };
2326
+ return { version: 3, targets };
1848
2327
  }
1849
2328
  const looksLikeV1 = "openAiProvider" in obj || "model" in obj || "schema" in obj;
1850
2329
  if (!looksLikeV1)
1851
2330
  return null;
1852
2331
  return {
1853
- version: 2,
2332
+ version: 3,
1854
2333
  targets: [
1855
2334
  {
1856
2335
  configPath: fallbackConfigPath,
@@ -1941,6 +2420,10 @@ function isManagedOpenCodeProvider(provider) {
1941
2420
  const options = objectRecordOr(provider.options, {});
1942
2421
  return isTheClawBayOpenAiCompatibleBaseUrl(options.baseURL);
1943
2422
  }
2423
+ function isManagedOpenCodeAnthropicProvider(provider) {
2424
+ const options = objectRecordOr(provider.options, {});
2425
+ return isTheClawBayAnthropicCompatibleBaseUrl(options.baseURL);
2426
+ }
1944
2427
  function isManagedOpenCodeModel(value, supportedModelIds) {
1945
2428
  if (typeof value !== "string")
1946
2429
  return false;
@@ -1953,7 +2436,7 @@ async function writeOpenCodeFamilyConfig(params) {
1953
2436
  const fallbackConfigPath = normalizedConfigPaths[0] ?? node_path_1.default.join(xdgConfigHomeDir(), "opencode", "opencode.json");
1954
2437
  const restoreState = (await readOpenCodeRestoreSnapshot(params.restoreStatePath, fallbackConfigPath)) ??
1955
2438
  {
1956
- version: 2,
2439
+ version: 3,
1957
2440
  targets: [],
1958
2441
  };
1959
2442
  const supportedModelIds = new Set(params.models.map((entry) => entry.id).filter(Boolean));
@@ -1992,12 +2475,14 @@ async function writeOpenCodeFamilyConfig(params) {
1992
2475
  }
1993
2476
  const providerRoot = objectRecordOr(doc.provider, {});
1994
2477
  const openAiProvider = objectRecordOr(providerRoot[OPENAI_PROVIDER_ID], {});
2478
+ const anthropicProvider = objectRecordOr(providerRoot[ANTHROPIC_PROVIDER_ID], {});
1995
2479
  const alreadyManaged = isManagedOpenCodeProvider(openAiProvider) && isManagedOpenCodeModel(doc.model, supportedModelIds);
1996
2480
  if (!alreadyManaged && !restoreHasTarget(configPath)) {
1997
2481
  restoreState.targets.push({
1998
2482
  configPath,
1999
2483
  existed,
2000
2484
  openAiProvider: Object.keys(openAiProvider).length > 0 ? openAiProvider : null,
2485
+ anthropicProvider: Object.keys(anthropicProvider).length > 0 ? anthropicProvider : null,
2001
2486
  model: typeof doc.model === "string" && !doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`)
2002
2487
  ? doc.model
2003
2488
  : null,
@@ -2017,6 +2502,7 @@ async function writeOpenCodeFamilyConfig(params) {
2017
2502
  const optionsRoot = objectRecordOr(managedOpenAiProvider.options, {});
2018
2503
  optionsRoot.baseURL = openAiCompatibleProxyUrl(params.backendUrl);
2019
2504
  optionsRoot.apiKey = params.apiKey;
2505
+ optionsRoot.setCacheKey = true;
2020
2506
  managedOpenAiProvider.options = optionsRoot;
2021
2507
  managedOpenAiProvider.models = buildOpenCodeModelsObject(params.models);
2022
2508
  managedOpenAiProvider.whitelist = params.models.map((entry) => entry.id).filter(Boolean);
@@ -2024,6 +2510,18 @@ async function writeOpenCodeFamilyConfig(params) {
2024
2510
  delete managedOpenAiProvider.blacklist;
2025
2511
  }
2026
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
+ }
2027
2525
  if (DEFAULT_PROVIDER_ID in providerRoot) {
2028
2526
  delete providerRoot[DEFAULT_PROVIDER_ID];
2029
2527
  }
@@ -2129,12 +2627,13 @@ class SetupCommand extends base_command_1.BaseCommand {
2129
2627
  managed?.backendUrl ??
2130
2628
  DEFAULT_BACKEND_URL;
2131
2629
  const backendUrl = normalizeUrl(backendRaw, "--backend");
2132
- 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([
2133
2631
  detectCodexClient(),
2134
- detectClaudeCodeClient(),
2632
+ detectClaudeClient(),
2135
2633
  detectContinueClient(),
2136
2634
  detectClineClient(),
2137
2635
  detectGsdClient(),
2636
+ detectOpenCodeClient(),
2138
2637
  detectKiloClient(),
2139
2638
  detectRooClient(),
2140
2639
  detectTraeClient(),
@@ -2144,7 +2643,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2144
2643
  const setupClients = [
2145
2644
  {
2146
2645
  id: "codex",
2147
- label: "Codex CLI, VS Code extension, and Codex app",
2646
+ label: "Codex CLI and VS Code extension",
2148
2647
  summaryLabel: "Codex",
2149
2648
  detected: codexDetected,
2150
2649
  recommended: true,
@@ -2153,8 +2652,8 @@ class SetupCommand extends base_command_1.BaseCommand {
2153
2652
  },
2154
2653
  {
2155
2654
  id: "claude",
2156
- label: "Claude Code CLI",
2157
- summaryLabel: "Claude Code",
2655
+ label: "Claude Code / Claude Desktop",
2656
+ summaryLabel: "Claude",
2158
2657
  detected: claudeDetected,
2159
2658
  recommended: true,
2160
2659
  icon: "✳",
@@ -2200,7 +2699,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2200
2699
  id: "opencode",
2201
2700
  label: "OpenCode",
2202
2701
  summaryLabel: "OpenCode",
2203
- detected: hasCommand("opencode"),
2702
+ detected: openCodeDetected,
2204
2703
  recommended: true,
2205
2704
  icon: "⌘",
2206
2705
  siteUrl: "https://opencode.ai",
@@ -2299,6 +2798,9 @@ class SetupCommand extends base_command_1.BaseCommand {
2299
2798
  let updatedPowerShellProfiles = [];
2300
2799
  let codexConfigPath = null;
2301
2800
  let updatedVsCodeEnvFiles = [];
2801
+ let updatedEditorTerminalSettings = [];
2802
+ let updatedClaudeEditorSettings = [];
2803
+ let claudeDesktop3pConfigPathManaged = null;
2302
2804
  let sessionMigration = null;
2303
2805
  let authSeed = null;
2304
2806
  let authSeedCleanup = null;
@@ -2355,6 +2857,23 @@ class SetupCommand extends base_command_1.BaseCommand {
2355
2857
  });
2356
2858
  if (selectedSetupClients.has("codex") || selectedSetupClients.has("claude")) {
2357
2859
  updatedVsCodeEnvFiles = await persistVsCodeServerEnvSource();
2860
+ updatedEditorTerminalSettings = await persistEditorTerminalEnvSettings({
2861
+ apiKey: authCredential,
2862
+ backendUrl,
2863
+ claudeEnabled: claudeEnvEnabled,
2864
+ });
2865
+ if (selectedSetupClients.has("claude")) {
2866
+ updatedClaudeEditorSettings = await persistClaudeEditorSettings({
2867
+ apiKey: authCredential,
2868
+ backendUrl,
2869
+ claudeEnabled: claudeEnvEnabled,
2870
+ });
2871
+ claudeDesktop3pConfigPathManaged = await writeClaudeDesktop3pConfig({
2872
+ backendUrl,
2873
+ apiKey: authCredential,
2874
+ claudeModels: claudeAccess?.enabled ? claudeAccess.models : [],
2875
+ });
2876
+ }
2358
2877
  }
2359
2878
  if (selectedSetupClients.has("codex")) {
2360
2879
  progress.update("Configuring Codex");
@@ -2430,6 +2949,7 @@ class SetupCommand extends base_command_1.BaseCommand {
2430
2949
  backendUrl,
2431
2950
  model: resolved?.model ?? DEFAULT_CODEX_MODEL,
2432
2951
  models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
2952
+ claudeModels: claudeAccess?.enabled ? claudeAccess.models : [],
2433
2953
  apiKey: authCredential,
2434
2954
  });
2435
2955
  }
@@ -2490,7 +3010,10 @@ class SetupCommand extends base_command_1.BaseCommand {
2490
3010
  if (resolved?.note)
2491
3011
  summaryNotes.add(resolved.note);
2492
3012
  if (selectedSetupClients.has("claude") && claudeAccess?.enabled) {
2493
- summaryNotes.add("Claude Code: exported ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY, and CLAUDE_CODE_SIMPLE=1 for the official client.");
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
+ }
2494
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.");
2495
3018
  }
2496
3019
  else if (selectedSetupClients.has("claude") && claudeAccess?.note) {
@@ -2547,6 +3070,9 @@ class SetupCommand extends base_command_1.BaseCommand {
2547
3070
  this.log(resolved.note);
2548
3071
  if (selectedSetupClients.has("claude") && claudeAccess?.enabled) {
2549
3072
  this.log(`- Claude Code base URL: ${anthropicCompatibleProxyUrl(backendUrl)}`);
3073
+ if (claudeDesktop3pConfigPathManaged) {
3074
+ this.log(`- Claude Desktop 3P config: ${claudeDesktop3pConfigPathManaged}`);
3075
+ }
2550
3076
  }
2551
3077
  else if (selectedSetupClients.has("claude") && claudeAccess?.note) {
2552
3078
  this.log(`- Claude Code: ${claudeAccess.note}`);
@@ -2559,6 +3085,12 @@ class SetupCommand extends base_command_1.BaseCommand {
2559
3085
  if (updatedPowerShellProfiles.length > 0) {
2560
3086
  this.log(`- PowerShell profiles updated: ${updatedPowerShellProfiles.join(", ")}`);
2561
3087
  }
3088
+ if (updatedEditorTerminalSettings.length > 0) {
3089
+ this.log(`- Editor terminal env updated: ${updatedEditorTerminalSettings.join(", ")}`);
3090
+ }
3091
+ if (updatedClaudeEditorSettings.length > 0) {
3092
+ this.log(`- Claude editor settings updated: ${updatedClaudeEditorSettings.join(", ")}`);
3093
+ }
2562
3094
  if (selectedSetupClients.has("codex")) {
2563
3095
  this.log(`- Codex: configured (${codexConfigPath})`);
2564
3096
  if (resolved)
@@ -2656,9 +3188,9 @@ class SetupCommand extends base_command_1.BaseCommand {
2656
3188
  else {
2657
3189
  this.log("- OpenClaw: not detected (skipped)");
2658
3190
  }
2659
- const openCodeDetected = setupClients.find((client) => client.id === "opencode")?.detected ?? false;
2660
3191
  if (selectedSetupClients.has("opencode")) {
2661
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.");
2662
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.");
2663
3195
  const openCodeProjectConfig = findOpenCodeProjectConfigFile();
2664
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.55",
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": {