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