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.
- package/dist/commands/logout.js +355 -6
- package/dist/commands/setup.js +548 -16
- package/package.json +1 -1
package/dist/commands/logout.js
CHANGED
|
@@ -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:
|
|
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:
|
|
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
|
|
1032
|
-
delete process.env
|
|
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
|
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
2157
|
-
summaryLabel: "Claude
|
|
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:
|
|
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,
|
|
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.
|
|
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": {
|