theclawbay 0.3.87 → 0.4.0

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.
Files changed (86) hide show
  1. package/README.md +3 -2
  2. package/dist/commands/logout.js +82 -1466
  3. package/dist/commands/setup.js +143 -3604
  4. package/dist/lib/base-command.js +7 -5
  5. package/dist/lib/clients/aider-cleanup.d.ts +1 -0
  6. package/dist/lib/clients/aider-cleanup.js +15 -0
  7. package/dist/lib/clients/aider.d.ts +14 -0
  8. package/dist/lib/clients/aider.js +57 -0
  9. package/dist/lib/clients/claude-cleanup.d.ts +2 -0
  10. package/dist/lib/clients/claude-cleanup.js +88 -0
  11. package/dist/lib/clients/claude.d.ts +49 -0
  12. package/dist/lib/clients/claude.js +400 -0
  13. package/dist/lib/clients/cline-cleanup.d.ts +1 -0
  14. package/dist/lib/clients/cline-cleanup.js +80 -0
  15. package/dist/lib/clients/cline.d.ts +15 -0
  16. package/dist/lib/clients/cline.js +73 -0
  17. package/dist/lib/clients/codex-cleanup.d.ts +7 -0
  18. package/dist/lib/clients/codex-cleanup.js +145 -0
  19. package/dist/lib/clients/codex.d.ts +28 -0
  20. package/dist/lib/clients/codex.js +176 -0
  21. package/dist/lib/clients/continue-cleanup.d.ts +1 -0
  22. package/dist/lib/clients/continue-cleanup.js +40 -0
  23. package/dist/lib/clients/continue.d.ts +22 -0
  24. package/dist/lib/clients/continue.js +81 -0
  25. package/dist/lib/clients/gsd-cleanup.d.ts +4 -0
  26. package/dist/lib/clients/gsd-cleanup.js +168 -0
  27. package/dist/lib/clients/gsd.d.ts +30 -0
  28. package/dist/lib/clients/gsd.js +136 -0
  29. package/dist/lib/clients/hermes-cleanup.d.ts +2 -0
  30. package/dist/lib/clients/hermes-cleanup.js +112 -0
  31. package/dist/lib/clients/hermes.d.ts +18 -0
  32. package/dist/lib/clients/hermes.js +145 -0
  33. package/dist/lib/clients/openclaw-cleanup.d.ts +1 -0
  34. package/dist/lib/clients/openclaw-cleanup.js +52 -0
  35. package/dist/lib/clients/openclaw.d.ts +34 -0
  36. package/dist/lib/clients/openclaw.js +100 -0
  37. package/dist/lib/clients/opencode-family-cleanup.d.ts +12 -0
  38. package/dist/lib/clients/opencode-family-cleanup.js +201 -0
  39. package/dist/lib/clients/opencode-family.d.ts +86 -0
  40. package/dist/lib/clients/opencode-family.js +427 -0
  41. package/dist/lib/clients/roo-cleanup.d.ts +1 -0
  42. package/dist/lib/clients/roo-cleanup.js +48 -0
  43. package/dist/lib/clients/roo.d.ts +21 -0
  44. package/dist/lib/clients/roo.js +91 -0
  45. package/dist/lib/clients/trae-cleanup.d.ts +2 -0
  46. package/dist/lib/clients/trae-cleanup.js +40 -0
  47. package/dist/lib/clients/trae.d.ts +27 -0
  48. package/dist/lib/clients/trae.js +118 -0
  49. package/dist/lib/clients/zo.d.ts +39 -0
  50. package/dist/lib/clients/zo.js +210 -0
  51. package/dist/lib/shared/backend.d.ts +31 -0
  52. package/dist/lib/shared/backend.js +273 -0
  53. package/dist/lib/shared/client-registry.d.ts +23 -0
  54. package/dist/lib/shared/client-registry.js +68 -0
  55. package/dist/lib/shared/editor-env-cleanup.d.ts +2 -0
  56. package/dist/lib/shared/editor-env-cleanup.js +86 -0
  57. package/dist/lib/shared/editor-env.d.ts +27 -0
  58. package/dist/lib/shared/editor-env.js +224 -0
  59. package/dist/lib/shared/env-persist-cleanup.d.ts +4 -0
  60. package/dist/lib/shared/env-persist-cleanup.js +77 -0
  61. package/dist/lib/shared/env-persist.d.ts +26 -0
  62. package/dist/lib/shared/env-persist.js +179 -0
  63. package/dist/lib/shared/format.d.ts +5 -0
  64. package/dist/lib/shared/format.js +48 -0
  65. package/dist/lib/shared/fsx-cleanup.d.ts +3 -0
  66. package/dist/lib/shared/fsx-cleanup.js +36 -0
  67. package/dist/lib/shared/fsx.d.ts +9 -0
  68. package/dist/lib/shared/fsx.js +103 -0
  69. package/dist/lib/shared/models.d.ts +60 -0
  70. package/dist/lib/shared/models.js +124 -0
  71. package/dist/lib/shared/platform.d.ts +16 -0
  72. package/dist/lib/shared/platform.js +116 -0
  73. package/dist/lib/shared/prompts.d.ts +29 -0
  74. package/dist/lib/shared/prompts.js +504 -0
  75. package/dist/lib/shared/providers.d.ts +3 -0
  76. package/dist/lib/shared/providers.js +8 -0
  77. package/dist/lib/shared/toml.d.ts +7 -0
  78. package/dist/lib/shared/toml.js +157 -0
  79. package/dist/lib/shared/tui.d.ts +42 -0
  80. package/dist/lib/shared/tui.js +173 -0
  81. package/dist/lib/shared/urls.d.ts +18 -0
  82. package/dist/lib/shared/urls.js +149 -0
  83. package/dist/lib/supported-models.d.ts +1 -1
  84. package/dist/lib/supported-models.js +1 -1
  85. package/package.json +1 -6
  86. package/theclawbay-supported-models.json +202 -5
@@ -3,194 +3,41 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const node_fs_1 = require("node:fs");
7
- const promises_1 = __importDefault(require("node:fs/promises"));
8
- const node_os_1 = __importDefault(require("node:os"));
9
- const node_path_1 = __importDefault(require("node:path"));
10
- const node_readline_1 = require("node:readline");
11
- const promises_2 = require("node:readline/promises");
12
- const node_child_process_1 = require("node:child_process");
13
- const core_1 = require("@oclif/core");
14
- const yaml_1 = require("yaml");
6
+ const client_registry_1 = require("../lib/shared/client-registry");
7
+ const format_1 = require("../lib/shared/format");
15
8
  const base_command_1 = require("../lib/base-command");
9
+ const core_1 = require("@oclif/core");
10
+ const config_1 = require("../lib/managed/config");
11
+ const errors_1 = require("../lib/managed/errors");
12
+ const api_key_1 = require("../lib/managed/api-key");
13
+ const urls_1 = require("../lib/shared/urls");
14
+ const codex_1 = require("../lib/clients/codex");
15
+ const claude_1 = require("../lib/clients/claude");
16
+ const continue_1 = require("../lib/clients/continue");
17
+ const cline_1 = require("../lib/clients/cline");
18
+ const gsd_1 = require("../lib/clients/gsd");
19
+ const opencode_family_1 = require("../lib/clients/opencode-family");
20
+ const roo_1 = require("../lib/clients/roo");
21
+ const trae_1 = require("../lib/clients/trae");
22
+ const aider_1 = require("../lib/clients/aider");
23
+ const zo_1 = require("../lib/clients/zo");
24
+ const hermes_1 = require("../lib/clients/hermes");
25
+ const platform_1 = require("../lib/shared/platform");
26
+ const prompts_1 = require("../lib/shared/prompts");
27
+ const backend_1 = require("../lib/shared/backend");
28
+ const device_session_auth_1 = require("../lib/device-session-auth");
29
+ const models_1 = require("../lib/shared/models");
16
30
  const backups_1 = require("../lib/backups");
17
- const codex_auth_seeding_1 = require("../lib/codex-auth-seeding");
18
31
  const codex_history_migration_1 = require("../lib/codex-history-migration");
32
+ const codex_auth_seeding_1 = require("../lib/codex-auth-seeding");
19
33
  const codex_model_cache_migration_1 = require("../lib/codex-model-cache-migration");
34
+ const env_persist_1 = require("../lib/shared/env-persist");
35
+ const editor_env_1 = require("../lib/shared/editor-env");
20
36
  const paths_1 = require("../lib/config/paths");
21
- const api_key_1 = require("../lib/managed/api-key");
22
- const config_1 = require("../lib/managed/config");
23
- const errors_1 = require("../lib/managed/errors");
24
- const supported_models_1 = require("../lib/supported-models");
25
- const device_session_auth_1 = require("../lib/device-session-auth");
26
- const DEFAULT_BACKEND_URL = "https://theclawbay.com";
27
- const DEFAULT_PROVIDER_ID = "theclawbay";
28
- const CLI_HTTP_USER_AGENT = "theclawbay-cli";
29
- const SUPPORTED_MODEL_IDS = (0, supported_models_1.getSupportedModelIds)();
30
- const MODEL_DISPLAY_NAMES = (0, supported_models_1.getSupportedModelDisplayNames)();
31
- const DEFAULT_CODEX_MODEL = SUPPORTED_MODEL_IDS[0] ?? "gpt-5.5";
32
- const DEFAULT_CONTINUE_MODEL = DEFAULT_CODEX_MODEL;
33
- const DEFAULT_CLINE_MODEL = DEFAULT_CODEX_MODEL;
34
- const DEFAULT_OPENCLAW_MODEL = DEFAULT_CODEX_MODEL;
35
- const DEFAULT_KILO_MODEL = DEFAULT_CODEX_MODEL;
36
- const DEFAULT_ROO_MODEL = DEFAULT_CODEX_MODEL;
37
- const DEFAULT_TRAE_MODEL = DEFAULT_CODEX_MODEL;
38
- const DEFAULT_AIDER_MODEL = DEFAULT_CODEX_MODEL;
39
- const DEFAULT_ZO_MODEL = DEFAULT_CODEX_MODEL;
40
- const DEFAULT_HERMES_MODEL = DEFAULT_CODEX_MODEL;
41
- const DEFAULT_MODELS = [...SUPPORTED_MODEL_IDS];
42
- const PREFERRED_MODELS = [...SUPPORTED_MODEL_IDS];
43
- const ENV_KEY_NAME = "THECLAWBAY_API_KEY";
44
- const GEMINI_ENV_API_KEY_NAME = "GEMINI_API_KEY";
45
- const GOOGLE_ENV_API_KEY_NAME = "GOOGLE_API_KEY";
46
- const GOOGLE_GEMINI_BASE_URL_NAME = "GOOGLE_GEMINI_BASE_URL";
47
- const CLAUDE_ENV_API_KEY_NAME = "ANTHROPIC_API_KEY";
48
- const CLAUDE_ENV_BASE_URL_NAME = "ANTHROPIC_BASE_URL";
49
- const CLAUDE_ENV_SIMPLE_MODE_NAME = "CLAUDE_CODE_SIMPLE";
50
- const CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME = "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC";
51
- const CLAUDE_ENV_ENABLE_TELEMETRY_NAME = "CLAUDE_CODE_ENABLE_TELEMETRY";
52
- const CLAUDE_CODE_EDITOR_ENV_SETTING = "claudeCode.environmentVariables";
53
- const CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING = "claudeCode.disableLoginPrompt";
54
- const ANTHROPIC_PROVIDER_ID = "anthropic";
55
- const MANAGED_EDITOR_TERMINAL_ENV_NAMES = [
56
- ENV_KEY_NAME,
57
- GEMINI_ENV_API_KEY_NAME,
58
- GOOGLE_ENV_API_KEY_NAME,
59
- GOOGLE_GEMINI_BASE_URL_NAME,
60
- ];
61
- const MANAGED_CLAUDE_ENV_NAMES = [
62
- CLAUDE_ENV_API_KEY_NAME,
63
- CLAUDE_ENV_BASE_URL_NAME,
64
- CLAUDE_ENV_SIMPLE_MODE_NAME,
65
- CLAUDE_ENV_DISABLE_NONESSENTIAL_TRAFFIC_NAME,
66
- CLAUDE_ENV_ENABLE_TELEMETRY_NAME,
67
- ];
68
- const ENV_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "env");
69
- const CONTINUE_CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".continue", "config.yaml");
70
- const CLINE_GLOBAL_STATE_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".cline", "data", "globalState.json");
71
- const CLINE_SECRETS_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".cline", "data", "secrets.json");
72
- const AIDER_CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".aider.conf.yml");
73
- const GSD_AGENT_DIR = node_path_1.default.join(node_os_1.default.homedir(), ".gsd", "agent");
74
- const GSD_MODELS_PATH = node_path_1.default.join(GSD_AGENT_DIR, "models.json");
75
- const GSD_AUTH_PATH = node_path_1.default.join(GSD_AGENT_DIR, "auth.json");
76
- const GSD_SETTINGS_PATH = node_path_1.default.join(GSD_AGENT_DIR, "settings.json");
77
- const CLINE_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "cline.restore.json");
78
- const GSD_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "gsd.restore.json");
79
- const OPENCODE_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "opencode.restore.json");
80
- const KILO_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "kilo.restore.json");
81
- const ROO_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "roo-settings.restore.json");
82
- const EDITOR_TERMINAL_ENV_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "editor-terminal-env.restore.json");
83
- const CLAUDE_EDITOR_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "claude-editor-settings.restore.json");
84
- const CLAUDE_DESKTOP_3P_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "claude-desktop-3p.restore.json");
85
- const CODEX_FEATURE_FLAGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "codex-features.restore.json");
86
- const ROO_IMPORT_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "roo-code-settings.json");
87
- const MIGRATION_STATE_FILE = node_path_1.default.join(paths_1.codexDir, "theclawbay.migration.json");
88
- const MANAGED_START = "# theclawbay-managed:start";
89
- const MANAGED_END = "# theclawbay-managed:end";
90
- const AIDER_MANAGED_START = "# theclawbay-aider-managed:start";
91
- const AIDER_MANAGED_END = "# theclawbay-aider-managed:end";
92
- const SHELL_START = "# theclawbay-shell-managed:start";
93
- const SHELL_END = "# theclawbay-shell-managed:end";
94
- const OPENCLAW_PROVIDER_ID = DEFAULT_PROVIDER_ID;
95
- const HISTORY_PROVIDER_NEUTRALIZE_SOURCES = new Set(["openai", "theclawbay-wan", DEFAULT_PROVIDER_ID]);
96
- const HISTORY_PROVIDER_DB_MIGRATE_SOURCES = ["openai", DEFAULT_PROVIDER_ID, "theclawbay-wan"];
97
- const SETUP_CLIENT_IDS = ["codex", "claude", "continue", "cline", "gsd", "openclaw", "opencode", "kilo", "roo", "trae", "aider", "zo", "hermes"];
98
- const LEGACY_THECLAWBAY_OPENAI_PROXY_SUFFIX = "/api/codex-auth/v1/proxy/v1";
99
- const CANONICAL_THECLAWBAY_OPENAI_PROXY_SUFFIX = "/v1";
100
- const CANONICAL_CODEX_NATIVE_PROXY_SUFFIX = "/backend-api/codex";
101
- const THECLAWBAY_CANONICAL_API_HOST = "api.theclawbay.com";
102
- const THECLAWBAY_WEBSITE_HOSTS = new Set(["theclawbay.com", "www.theclawbay.com"]);
103
- const CONTINUE_MODEL_NAME = "The Claw Bay";
104
- const ROO_PROFILE_NAME = "The Claw Bay";
105
- const ROO_PROFILE_ID = "theclawbay-openai-compatible";
106
- const ROO_MODE_SLUGS = ["architect", "code", "ask", "debug", "orchestrator"];
107
- const TRAE_PATCH_MARKER = "theclawbay-trae-patch";
108
- const TRAE_BUNDLE_BACKUP_SUFFIX = ".theclawbay-managed-backup";
109
- const TRAE_TARGET_FUNCTION_START = "async setOriginModelListMapAndCache(e,t=!0){";
110
- const TRAE_TARGET_FUNCTION_END = "}async refreshModelListConfig";
111
- const OPENAI_PROVIDER_ID = "openai";
112
- const OPENCODE_CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
113
- const KILO_CONFIG_SCHEMA_URL = "https://kilo.ai/config.json";
114
- const ZO_CONFIG_NAME_PREFIX = "The Claw Bay";
115
- const ZO_COOKIE_HOST = ".zo.computer";
116
- const ZO_ACCESS_TOKEN_COOKIE = "access_token";
117
- const ZO_API_BASE_URL = "https://api.zo.computer";
118
- const HERMES_PROVIDER_NAME = "The Claw Bay";
119
- const HERMES_KEY_ENV_NAME = "HERMES_THECLAWBAY_API_KEY";
120
- function trimTrailingSlash(value) {
121
- return value.replace(/\/+$/g, "");
122
- }
123
- function normalizeUrl(raw, label) {
124
- try {
125
- const parsed = new URL(raw.trim());
126
- return trimTrailingSlash(parsed.toString());
127
- }
128
- catch {
129
- throw new Error(`invalid ${label} URL: ${raw}`);
130
- }
131
- }
132
- function hasCommand(name) {
133
- const lookupCommand = node_os_1.default.platform() === "win32" ? "where.exe" : "which";
134
- const result = (0, node_child_process_1.spawnSync)(lookupCommand, [name], { stdio: "ignore" });
135
- return result.status === 0;
136
- }
137
- function resolveCommandPath(name) {
138
- const lookupCommand = node_os_1.default.platform() === "win32" ? "where.exe" : "which";
139
- const result = (0, node_child_process_1.spawnSync)(lookupCommand, [name], { encoding: "utf8" });
140
- if (result.status !== 0)
141
- return null;
142
- const match = result.stdout
143
- .split(/\r?\n/)
144
- .map((line) => line.trim())
145
- .find(Boolean);
146
- return match ?? null;
147
- }
148
- function supportsTerminalHyperlinks() {
149
- if (!process.stdout.isTTY)
150
- return false;
151
- if (process.env.FORCE_HYPERLINK === "1")
152
- return true;
153
- if (process.env.NO_HYPERLINK === "1")
154
- return false;
155
- const term = (process.env.TERM ?? "").toLowerCase();
156
- if (!term || term === "dumb")
157
- return false;
158
- const termProgram = (process.env.TERM_PROGRAM ?? "").toLowerCase();
159
- if (termProgram === "iterm.app" || termProgram === "wezterm" || termProgram === "vscode") {
160
- return true;
161
- }
162
- if (process.env.WT_SESSION ||
163
- process.env.KITTY_WINDOW_ID ||
164
- process.env.KONSOLE_VERSION ||
165
- process.env.VTE_VERSION ||
166
- process.env.DOMTERM ||
167
- process.env.TERMINAL_EMULATOR === "JetBrains-JediTerm") {
168
- return true;
169
- }
170
- return term.includes("xterm-kitty") || term.includes("foot") || term.includes("wezterm");
171
- }
172
- function formatTerminalHyperlink(label, url) {
173
- return `\u001B]8;;${url}\u0007${label}\u001B]8;;\u0007`;
174
- }
175
- function formatDim(value) {
176
- return `\u001B[2m${value}\u001B[0m`;
177
- }
178
- function officialSiteLabel(siteUrl) {
179
- const parsed = new URL(siteUrl);
180
- const hostname = parsed.hostname.replace(/^www\./, "");
181
- const pathname = parsed.pathname.replace(/\/+$/g, "");
182
- return pathname && pathname !== "/" ? `${hostname}${pathname}` : hostname;
183
- }
184
- function formatSetupClientLabel(client, hyperlinksEnabled) {
185
- const iconAndLabel = `${client.icon} ${client.label}`;
186
- if (hyperlinksEnabled) {
187
- return `${formatTerminalHyperlink(iconAndLabel, client.siteUrl)} ${formatDim(`(${officialSiteLabel(client.siteUrl)})`)}`;
188
- }
189
- return `${iconAndLabel} - ${client.siteUrl}`;
190
- }
191
- function formatSummaryList(items) {
192
- return items.join(", ");
193
- }
37
+ const providers_1 = require("../lib/shared/providers");
38
+ const openclaw_1 = require("../lib/clients/openclaw");
39
+ const node_path_1 = __importDefault(require("node:path"));
40
+ const tui_1 = require("../lib/shared/tui");
194
41
  function logSetupCompactSummary(params) {
195
42
  const configured = params.setupClients
196
43
  .filter((client) => params.selectedSetupClients.has(client.id))
@@ -198,3362 +45,49 @@ function logSetupCompactSummary(params) {
198
45
  const restartTargets = params.setupClients
199
46
  .filter((client) => params.selectedSetupClients.has(client.id) && ["continue", "cline", "gsd", "roo", "trae", "zo"].includes(client.id))
200
47
  .map((client) => client.summaryLabel);
201
- if (configured.length > 0) {
202
- params.log(`Configured: ${formatSummaryList(configured)}`);
203
- }
204
- else {
205
- params.log("Configured: no local tools selected.");
206
- }
207
- params.log("Shared config: saved your The Claw Bay credential and managed machine setup.");
208
- if (restartTargets.length > 0) {
209
- params.log(`Restart if already open: ${formatSummaryList(restartTargets)}`);
210
- }
211
- for (const note of params.notes) {
212
- params.log(`Note: ${note}`);
213
- }
214
- }
215
- function localAppDataDir() {
216
- if (process.env.LOCALAPPDATA?.trim())
217
- return process.env.LOCALAPPDATA;
218
- return node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Local");
219
- }
220
- function roamingAppDataDir() {
221
- if (process.env.APPDATA?.trim())
222
- return process.env.APPDATA;
223
- return node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Roaming");
224
- }
225
- function zoCookieDbCandidates() {
226
- const home = node_os_1.default.homedir();
227
- const candidates = new Set();
228
- switch (node_os_1.default.platform()) {
229
- case "win32":
230
- candidates.add(node_path_1.default.join(roamingAppDataDir(), "Zo", "Network", "Cookies"));
231
- break;
232
- case "darwin":
233
- candidates.add(node_path_1.default.join(home, "Library", "Application Support", "Zo", "Network", "Cookies"));
234
- break;
235
- default:
236
- candidates.add(node_path_1.default.join(home, ".config", "Zo", "Network", "Cookies"));
237
- break;
238
- }
239
- return Array.from(candidates);
240
- }
241
- function zoProgramCandidates() {
242
- const candidates = new Set();
243
- if (process.env.THECLAWBAY_ZO_PATH?.trim()) {
244
- candidates.add(process.env.THECLAWBAY_ZO_PATH.trim());
245
- }
246
- if (node_os_1.default.platform() === "win32") {
247
- candidates.add(node_path_1.default.join(localAppDataDir(), "Programs", "Zo"));
248
- for (const envKey of ["ProgramFiles", "ProgramFiles(x86)"]) {
249
- const root = process.env[envKey]?.trim();
250
- if (root)
251
- candidates.add(node_path_1.default.join(root, "Zo"));
252
- }
253
- }
254
- else if (node_os_1.default.platform() === "darwin") {
255
- candidates.add("/Applications/Zo.app");
256
- }
257
- return Array.from(candidates);
258
- }
259
- function pythonCommandCandidates() {
260
- if (node_os_1.default.platform() === "win32") {
261
- return [
262
- { command: "py", baseArgs: ["-3"] },
263
- { command: "python", baseArgs: [] },
264
- { command: "python3", baseArgs: [] },
265
- ];
266
- }
267
- return [
268
- { command: "python3", baseArgs: [] },
269
- { command: "python", baseArgs: [] },
270
- ];
271
- }
272
- function readZoCookieValue(cookieName) {
273
- const dbPath = zoCookieDbCandidates().find((candidate) => (0, node_fs_1.existsSync)(candidate));
274
- if (!dbPath)
275
- return null;
276
- const pythonScript = [
277
- "import json, sqlite3, sys",
278
- "db_path, host_key, cookie_name = sys.argv[1:4]",
279
- 'conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)',
280
- "row = conn.execute(",
281
- ' "select value from cookies where host_key=? and name=? order by length(value) desc limit 1",',
282
- " (host_key, cookie_name),",
283
- ").fetchone()",
284
- 'print(json.dumps({"value": row[0] if row else None}))',
285
- ].join("\n");
286
- for (const candidate of pythonCommandCandidates()) {
287
- const result = (0, node_child_process_1.spawnSync)(candidate.command, [...candidate.baseArgs, "-c", pythonScript, dbPath, ZO_COOKIE_HOST, cookieName], {
288
- encoding: "utf8",
289
- stdio: ["ignore", "pipe", "pipe"],
290
- });
291
- if (result.status !== 0 || !result.stdout.trim())
292
- continue;
293
- try {
294
- const parsed = JSON.parse(result.stdout);
295
- if (typeof parsed.value === "string" && parsed.value.trim()) {
296
- return parsed.value.trim();
297
- }
298
- }
299
- catch {
300
- continue;
301
- }
302
- }
303
- if (hasCommand("sqlite3")) {
304
- const result = (0, node_child_process_1.spawnSync)("sqlite3", [
305
- dbPath,
306
- `select value from cookies where host_key='${ZO_COOKIE_HOST}' and name='${cookieName}' order by length(value) desc limit 1;`,
307
- ], {
308
- encoding: "utf8",
309
- stdio: ["ignore", "pipe", "pipe"],
310
- });
311
- if (result.status === 0 && result.stdout.trim()) {
312
- return result.stdout.trim();
313
- }
314
- }
315
- return null;
316
- }
317
- function parseZoByokConfig(value) {
318
- if (!value || typeof value !== "object")
319
- return null;
320
- return value;
321
- }
322
- async function zoApiRequest(params) {
323
- const response = await fetch(`${ZO_API_BASE_URL}${params.pathname}`, {
324
- method: params.method ?? "GET",
325
- headers: {
326
- Authorization: `Bearer ${params.token}`,
327
- Accept: "application/json",
328
- "User-Agent": CLI_HTTP_USER_AGENT,
329
- ...(params.body ? { "Content-Type": "application/json" } : {}),
330
- },
331
- body: params.body ? JSON.stringify(params.body) : undefined,
332
- signal: AbortSignal.timeout(10000),
333
- });
334
- const responseText = await response.text();
335
- if (!response.ok) {
336
- const detail = responseText ? ` ${responseText.slice(0, 400)}` : "";
337
- throw new Error(`Zo API ${params.method ?? "GET"} ${params.pathname} failed with ${response.status}.${detail}`.trim());
338
- }
339
- return (responseText ? JSON.parse(responseText) : null);
340
- }
341
- async function configureZoByok(params) {
342
- const accessToken = readZoCookieValue(ZO_ACCESS_TOKEN_COOKIE);
343
- if (!accessToken) {
344
- throw new Error("Zo was detected, but no active Zo desktop session token could be read. Open Zo, sign in, and rerun setup.");
345
- }
346
- const desiredName = `${ZO_CONFIG_NAME_PREFIX} ${modelDisplayName(params.model)}`;
347
- const desiredBaseUrl = openAiCompatibleProxyUrl(params.backendUrl);
348
- const body = {
349
- provider: "openai-style",
350
- format: "openai",
351
- name: desiredName,
352
- base_url: desiredBaseUrl,
353
- api_key: params.apiKey,
354
- model_id: params.model,
355
- supports_images: false,
356
- };
357
- const existing = await zoApiRequest({
358
- token: accessToken,
359
- pathname: "/byok/",
360
- });
361
- const existingConfigs = (Array.isArray(existing) ? existing : []).map(parseZoByokConfig).filter(Boolean);
362
- const matched = existingConfigs.find((entry) => {
363
- const name = typeof entry.name === "string" ? entry.name : "";
364
- const baseUrl = typeof entry.base_url === "string" ? entry.base_url : "";
365
- const modelId = typeof entry.model_id === "string" ? entry.model_id : "";
366
- return name === desiredName || (baseUrl === desiredBaseUrl && modelId === params.model);
367
- });
368
- if (matched && typeof matched.id === "string" && matched.id) {
369
- await zoApiRequest({
370
- token: accessToken,
371
- pathname: `/byok/${matched.id}`,
372
- method: "PATCH",
373
- body,
374
- });
375
- return desiredName;
376
- }
377
- await zoApiRequest({
378
- token: accessToken,
379
- pathname: "/byok/",
380
- method: "POST",
381
- body,
382
- });
383
- return desiredName;
384
- }
385
- const TRAE_BUNDLE_RELATIVE_PATH = node_path_1.default.join("resources", "app", "node_modules", "@byted-icube", "ai-modules-chat", "dist", "index.js");
386
- function appendTraeBundlePath(basePath) {
387
- if (basePath.toLowerCase().endsWith(node_path_1.default.normalize(TRAE_BUNDLE_RELATIVE_PATH).toLowerCase())) {
388
- return basePath;
389
- }
390
- return node_path_1.default.join(basePath, TRAE_BUNDLE_RELATIVE_PATH);
391
- }
392
- function traeBundleCandidateRoots() {
393
- const candidates = new Set();
394
- if (process.env.THECLAWBAY_TRAE_PATH?.trim()) {
395
- candidates.add(process.env.THECLAWBAY_TRAE_PATH.trim());
396
- }
397
- const localPrograms = node_path_1.default.join(localAppDataDir(), "Programs");
398
- candidates.add(node_path_1.default.join(localPrograms, "Trae"));
399
- for (const envKey of ["ProgramFiles", "ProgramFiles(x86)"]) {
400
- const root = process.env[envKey]?.trim();
401
- if (root)
402
- candidates.add(node_path_1.default.join(root, "Trae"));
403
- }
404
- if ((0, node_fs_1.existsSync)(localPrograms)) {
405
- for (const entry of (0, node_fs_1.readdirSync)(localPrograms, { withFileTypes: true })) {
406
- if (!entry.isDirectory())
407
- continue;
408
- if (!entry.name.toLowerCase().includes("trae"))
409
- continue;
410
- candidates.add(node_path_1.default.join(localPrograms, entry.name));
411
- }
412
- }
413
- return Array.from(candidates);
414
- }
415
- function traeBundlePath() {
416
- if (node_os_1.default.platform() !== "win32")
417
- return null;
418
- for (const candidateRoot of traeBundleCandidateRoots()) {
419
- const bundlePath = appendTraeBundlePath(candidateRoot);
420
- if ((0, node_fs_1.existsSync)(bundlePath))
421
- return bundlePath;
422
- }
423
- return appendTraeBundlePath(node_path_1.default.join(localAppDataDir(), "Programs", "Trae"));
424
- }
425
- function traeBundleBackupPath(bundlePath) {
426
- return `${bundlePath}${TRAE_BUNDLE_BACKUP_SUFFIX}`;
427
- }
428
- async function pathExists(filePath) {
429
- try {
430
- await promises_1.default.access(filePath);
431
- return true;
432
- }
433
- catch (error) {
434
- const err = error;
435
- if (err.code === "ENOENT")
436
- return false;
437
- throw error;
438
- }
439
- }
440
- function removeManagedBlock(source, start, end) {
441
- const markerStart = source.indexOf(start);
442
- if (markerStart < 0)
443
- return source;
444
- const markerEnd = source.indexOf(end, markerStart);
445
- if (markerEnd < 0)
446
- return source.slice(0, markerStart).trimEnd() + "\n";
447
- return (source.slice(0, markerStart) + source.slice(markerEnd + end.length)).trimEnd() + "\n";
448
- }
449
- function appendManagedBlock(source, lines) {
450
- const body = lines.join("\n").trimEnd() + "\n";
451
- if (!source.trim())
452
- return body;
453
- return `${source.trimEnd()}\n\n${body}`;
454
- }
455
- function removeProviderTable(source, providerId) {
456
- const header = `[model_providers.${providerId}]`;
457
- const lines = source.split(/\r?\n/);
458
- const output = [];
459
- for (let i = 0; i < lines.length; i++) {
460
- if ((lines[i] ?? "").trim() !== header) {
461
- output.push(lines[i] ?? "");
462
- continue;
463
- }
464
- i++;
465
- while (i < lines.length && !/^\s*\[[^\]]+\]\s*$/.test(lines[i] ?? "")) {
466
- i++;
467
- }
468
- i--;
469
- }
470
- return `${output.join("\n").trimEnd()}\n`;
471
- }
472
- function upsertFirstKeyLine(source, key, tomlValue) {
473
- const lines = source.split(/\r?\n/);
474
- for (let i = 0; i < lines.length; i++) {
475
- const line = lines[i] ?? "";
476
- if (/^\s*#/.test(line))
477
- continue;
478
- if (/^\s*\[/.test(line))
479
- break;
480
- if (new RegExp(`^\\s*${key}\\s*=`).test(line)) {
481
- lines[i] = `${key} = ${tomlValue}`;
482
- return `${lines.join("\n").trimEnd()}\n`;
483
- }
484
- }
485
- return `${`${key} = ${tomlValue}\n${source}`.trimEnd()}\n`;
486
- }
487
- function parseTomlScalarValue(line) {
488
- const match = line.match(/^\s*[A-Za-z0-9_.-]+\s*=\s*(.+?)\s*$/);
489
- return match?.[1]?.trim() ?? null;
490
- }
491
- function upsertTopLevelTableKey(source, tableName, key, tomlValue) {
492
- const header = `[${tableName}]`;
493
- const lines = source.split(/\r?\n/);
494
- let tableStart = -1;
495
- for (let i = 0; i < lines.length; i++) {
496
- if ((lines[i] ?? "").trim() === header) {
497
- tableStart = i;
498
- break;
499
- }
500
- }
501
- if (tableStart < 0) {
502
- const block = `${header}\n${key} = ${tomlValue}\n`;
503
- if (!source.trim())
504
- return block;
505
- return `${source.trimEnd()}\n\n${block}`;
506
- }
507
- let tableEnd = lines.length;
508
- for (let i = tableStart + 1; i < lines.length; i++) {
509
- if (/^\s*\[[^\]]+\]\s*$/.test(lines[i] ?? "")) {
510
- tableEnd = i;
511
- break;
512
- }
513
- }
514
- for (let i = tableStart + 1; i < tableEnd; i++) {
515
- const line = lines[i] ?? "";
516
- if (/^\s*#/.test(line))
517
- continue;
518
- if (new RegExp(`^\\s*${key}\\s*=`).test(line)) {
519
- lines[i] = `${key} = ${tomlValue}`;
520
- return `${lines.join("\n").trimEnd()}\n`;
521
- }
522
- }
523
- lines.splice(tableEnd, 0, `${key} = ${tomlValue}`);
524
- return `${lines.join("\n").trimEnd()}\n`;
525
- }
526
- function setTopLevelTableKey(source, tableName, key, tomlValue) {
527
- return upsertTopLevelTableKey(source, tableName, key, tomlValue);
528
- }
529
- function getTopLevelTableKeyValue(source, tableName, key) {
530
- const header = `[${tableName}]`;
531
- const lines = source.split(/\r?\n/);
532
- let tableStart = -1;
533
- for (let i = 0; i < lines.length; i++) {
534
- if ((lines[i] ?? "").trim() === header) {
535
- tableStart = i;
536
- break;
537
- }
538
- }
539
- if (tableStart < 0)
540
- return null;
541
- let tableEnd = lines.length;
542
- for (let i = tableStart + 1; i < lines.length; i++) {
543
- if (/^\s*\[[^\]]+\]\s*$/.test(lines[i] ?? "")) {
544
- tableEnd = i;
545
- break;
546
- }
547
- }
548
- for (let i = tableStart + 1; i < tableEnd; i++) {
549
- const line = lines[i] ?? "";
550
- if (/^\s*#/.test(line))
551
- continue;
552
- if (!new RegExp(`^\\s*${key}\\s*=`).test(line))
553
- continue;
554
- return parseTomlScalarValue(line);
555
- }
556
- return null;
557
- }
558
- function removeTopLevelTableKey(source, tableName, key) {
559
- const header = `[${tableName}]`;
560
- const lines = source.split(/\r?\n/);
561
- let tableStart = -1;
562
- for (let i = 0; i < lines.length; i++) {
563
- if ((lines[i] ?? "").trim() === header) {
564
- tableStart = i;
565
- break;
566
- }
567
- }
568
- if (tableStart < 0)
569
- return source;
570
- let tableEnd = lines.length;
571
- for (let i = tableStart + 1; i < lines.length; i++) {
572
- if (/^\s*\[[^\]]+\]\s*$/.test(lines[i] ?? "")) {
573
- tableEnd = i;
574
- break;
575
- }
576
- }
577
- const nextLines = [...lines];
578
- for (let i = tableStart + 1; i < tableEnd; i++) {
579
- const line = nextLines[i] ?? "";
580
- if (/^\s*#/.test(line))
581
- continue;
582
- if (new RegExp(`^\\s*${key}\\s*=`).test(line)) {
583
- nextLines.splice(i, 1);
584
- return `${nextLines.join("\n").trimEnd()}\n`;
585
- }
586
- }
587
- return source;
588
- }
589
- async function restoreManagedCodexFeatureFlags(source) {
590
- const snapshotRaw = await readFileIfExists(CODEX_FEATURE_FLAGS_STATE_PATH);
591
- if (snapshotRaw === null || !snapshotRaw.trim())
592
- return source;
593
- let snapshot = null;
594
- try {
595
- snapshot = JSON.parse(snapshotRaw);
596
- }
597
- catch {
598
- snapshot = null;
599
- }
600
- await removeFileIfExists(CODEX_FEATURE_FLAGS_STATE_PATH);
601
- if (!snapshot)
602
- return source;
603
- let next = source;
604
- if (snapshot.appsHadKey && snapshot.appsPreviousValue !== null) {
605
- next = setTopLevelTableKey(next, "features", "apps", snapshot.appsPreviousValue);
606
- }
607
- else {
608
- next = removeTopLevelTableKey(next, "features", "apps");
609
- }
610
- if (snapshot.appsMcpGatewayHadKey && snapshot.appsMcpGatewayPreviousValue !== null) {
611
- next = setTopLevelTableKey(next, "features", "apps_mcp_gateway", snapshot.appsMcpGatewayPreviousValue);
612
- }
613
- else {
614
- next = removeTopLevelTableKey(next, "features", "apps_mcp_gateway");
615
- }
616
- return next;
617
- }
618
- async function ensureManagedCodexFeatureFlags(source) {
619
- const existingSnapshotRaw = await readFileIfExists(CODEX_FEATURE_FLAGS_STATE_PATH);
620
- if (existingSnapshotRaw === null || !existingSnapshotRaw.trim()) {
621
- const featuresHeader = "[features]";
622
- const lines = source.split(/\r?\n/);
623
- const hadFeaturesTable = lines.some((line) => (line ?? "").trim() === featuresHeader);
624
- const appsPreviousValue = getTopLevelTableKeyValue(source, "features", "apps");
625
- const appsMcpGatewayPreviousValue = getTopLevelTableKeyValue(source, "features", "apps_mcp_gateway");
626
- const snapshot = {
627
- hadFeaturesTable,
628
- appsHadKey: appsPreviousValue !== null,
629
- appsPreviousValue,
630
- appsMcpGatewayHadKey: appsMcpGatewayPreviousValue !== null,
631
- appsMcpGatewayPreviousValue,
632
- };
633
- await writeJsonObjectFile(CODEX_FEATURE_FLAGS_STATE_PATH, snapshot, 0o600);
634
- }
635
- let next = source;
636
- next = setTopLevelTableKey(next, "features", "apps", "true");
637
- next = setTopLevelTableKey(next, "features", "apps_mcp_gateway", "true");
638
- return next;
639
- }
640
- function removeTopLevelKeyLineIf(source, key, shouldRemove) {
641
- const lines = source.split(/\r?\n/);
642
- const filtered = [];
643
- let removed = false;
644
- for (let i = 0; i < lines.length; i++) {
645
- const line = lines[i] ?? "";
646
- if (/^\s*\[/.test(line)) {
647
- filtered.push(...lines.slice(i));
648
- break;
48
+ if (!process.stdout.isTTY) {
49
+ if (configured.length > 0) {
50
+ params.log(`Configured: ${(0, format_1.formatSummaryList)(configured)}`);
649
51
  }
650
- if (/^\s*#/.test(line) || !line.trim()) {
651
- filtered.push(line);
652
- continue;
52
+ else {
53
+ params.log("Configured: no local tools selected.");
653
54
  }
654
- const match = line.match(new RegExp(`^\\s*${key}\\s*=\\s*\"([^\"]*)\"\\s*$`));
655
- if (!match) {
656
- filtered.push(line);
657
- continue;
55
+ params.log("Shared config: saved your The Claw Bay credential and managed machine setup.");
56
+ if (restartTargets.length > 0) {
57
+ params.log(`Restart if already open: ${(0, format_1.formatSummaryList)(restartTargets)}`);
658
58
  }
659
- if (shouldRemove(match[1] ?? "")) {
660
- removed = true;
661
- continue;
59
+ for (const note of params.notes) {
60
+ params.log(`Note: ${note}`);
662
61
  }
663
- filtered.push(line);
664
- }
665
- if (!removed)
666
- return source;
667
- return `${filtered.join("\n").trimEnd()}\n`;
668
- }
669
- function shellQuote(value) {
670
- return `'${value.replace(/'/g, `'\\''`)}'`;
671
- }
672
- function powerShellQuote(value) {
673
- return `'${value.replace(/'/g, "''")}'`;
674
- }
675
- function shellCommandForCurrentPlatform(command) {
676
- if (node_os_1.default.platform() === "win32") {
677
- return { executable: "cmd.exe", args: ["/c", command] };
678
- }
679
- return { executable: "bash", args: ["-lc", command] };
680
- }
681
- function installPlanForSetupClient(clientId) {
682
- if (clientId === "codex") {
683
- return {
684
- summary: "Install Codex CLI",
685
- command: "npm install -g @openai/codex",
686
- };
687
- }
688
- if (clientId === "claude") {
689
- return {
690
- summary: "Install Claude Code CLI",
691
- command: "npm install -g @anthropic-ai/claude-code",
692
- };
693
- }
694
- if (clientId === "opencode") {
695
- return {
696
- summary: "Install OpenCode CLI",
697
- command: "npm install -g opencode-ai",
698
- };
699
- }
700
- if (clientId === "aider") {
701
- return {
702
- summary: "Install Aider",
703
- command: node_os_1.default.platform() === "win32"
704
- ? "py -m pip install --user --upgrade aider-chat"
705
- : "python3 -m pip install --user --upgrade aider-chat",
706
- };
707
- }
708
- return null;
709
- }
710
- function isSetupClientInstallable(clientId) {
711
- return installPlanForSetupClient(clientId) !== null;
712
- }
713
- function runInstallPlan(plan) {
714
- const { executable, args } = shellCommandForCurrentPlatform(plan.command);
715
- const run = (0, node_child_process_1.spawnSync)(executable, args, {
716
- stdio: "inherit",
717
- env: process.env,
718
- });
719
- if (run.status === 0)
720
62
  return;
721
- throw new Error(`${plan.summary} failed.`);
722
- }
723
- function modelDisplayName(modelId) {
724
- return MODEL_DISPLAY_NAMES[modelId] ?? prettyModelId(modelId);
725
- }
726
- function normalizeClaudeCatalogModelId(modelId) {
727
- const trimmed = modelId.trim();
728
- if (!trimmed.startsWith("claude-"))
729
- return trimmed;
730
- return trimmed
731
- .replace(/\./g, "-")
732
- .replace(/-+/g, "-")
733
- .replace(/-(20\d{6})$/, "");
734
- }
735
- function dedupeClaudeCatalogModelIds(modelIds) {
736
- const seen = new Set();
737
- const deduped = [];
738
- for (const modelId of modelIds) {
739
- const normalized = normalizeClaudeCatalogModelId(modelId);
740
- if (!normalized || seen.has(normalized))
741
- continue;
742
- seen.add(normalized);
743
- deduped.push(normalized);
744
- }
745
- return deduped;
746
- }
747
- function supportedModelProviderMap() {
748
- return new Map((0, supported_models_1.getSupportedModels)().map((model) => [model.id, model.provider]));
749
- }
750
- function groupForModelId(modelId) {
751
- const knownProvider = supportedModelProviderMap().get(modelId);
752
- if (knownProvider === "openai") {
753
- return { id: "openai", label: "OpenAI / Codex", icon: "◎", order: 0 };
754
- }
755
- if (knownProvider === "google") {
756
- return { id: "google", label: "Google Gemini", icon: "◈", order: 1 };
757
- }
758
- if (knownProvider === "deepseek") {
759
- return { id: "deepseek", label: "DeepSeek", icon: "◉", order: 2 };
760
- }
761
- if (modelId.startsWith("claude-")) {
762
- return { id: "anthropic", label: "Anthropic Claude", icon: "✳", order: 3 };
763
- }
764
- if (modelId.startsWith("gemini-")) {
765
- return { id: "google", label: "Google Gemini", icon: "◈", order: 1 };
766
- }
767
- if (modelId.startsWith("deepseek-")) {
768
- return { id: "deepseek", label: "DeepSeek", icon: "◉", order: 2 };
769
- }
770
- if (modelId.startsWith("gpt-") || modelId.startsWith("codex-")) {
771
- return { id: "openai", label: "OpenAI / Codex", icon: "◎", order: 0 };
772
- }
773
- return { id: "other", label: "Other models", icon: "•", order: 9 };
774
- }
775
- function groupModelOptions(models) {
776
- return models.map((model) => ({
777
- ...model,
778
- provider: groupForModelId(model.id),
779
- }));
780
- }
781
- function groupModelSections(models) {
782
- const sections = new Map();
783
- for (const model of groupModelOptions(models)) {
784
- const existing = sections.get(model.provider.id);
785
- if (existing) {
786
- existing.models.push(model);
787
- continue;
788
- }
789
- sections.set(model.provider.id, { provider: model.provider, models: [model] });
790
- }
791
- return [...sections.values()]
792
- .sort((left, right) => left.provider.order - right.provider.order || left.provider.label.localeCompare(right.provider.label))
793
- .map((section) => ({
794
- provider: section.provider,
795
- models: [...section.models].sort((left, right) => left.name.localeCompare(right.name)),
796
- }));
797
- }
798
- function prettyModelId(modelId) {
799
- return modelId
800
- .replace(/^claude-/, "Claude ")
801
- .replace(/^gemini-/, "Gemini ")
802
- .replace(/^deepseek-/, "DeepSeek ")
803
- .replace(/-/g, " ")
804
- .replace(/\b\w/g, (match) => match.toUpperCase());
805
- }
806
- function publicApiOriginForBackendUrl(backendUrl) {
807
- const trimmed = trimTrailingSlash(backendUrl);
808
- try {
809
- const parsed = new URL(trimmed);
810
- parsed.hash = "";
811
- parsed.search = "";
812
- parsed.pathname = "";
813
- if (THECLAWBAY_WEBSITE_HOSTS.has(parsed.hostname.toLowerCase())) {
814
- parsed.hostname = THECLAWBAY_CANONICAL_API_HOST;
815
- }
816
- return trimTrailingSlash(parsed.toString());
817
- }
818
- catch {
819
- return trimmed;
820
- }
821
- }
822
- function isPrivateHostname(hostname) {
823
- const normalized = hostname.trim().toLowerCase();
824
- if (!normalized)
825
- return false;
826
- if (normalized === "localhost" ||
827
- normalized === "127.0.0.1" ||
828
- normalized === "::1" ||
829
- normalized === "[::1]" ||
830
- normalized === "0.0.0.0") {
831
- return true;
832
- }
833
- if (normalized.startsWith("10.") || normalized.startsWith("192.168.") || normalized.startsWith("127.")) {
834
- return true;
835
- }
836
- const match = normalized.match(/^172\.(\d{1,3})\./);
837
- if (!match)
838
- return false;
839
- const secondOctet = Number.parseInt(match[1] ?? "", 10);
840
- return Number.isInteger(secondOctet) && secondOctet >= 16 && secondOctet <= 31;
841
- }
842
- function shouldPreferPublicSetupBackend(backendUrl) {
843
- const trimmed = backendUrl?.trim();
844
- if (!trimmed)
845
- return false;
846
- try {
847
- const parsed = new URL(trimmed);
848
- const hostname = parsed.hostname.toLowerCase();
849
- return hostname !== THECLAWBAY_CANONICAL_API_HOST && !THECLAWBAY_WEBSITE_HOSTS.has(hostname) && isPrivateHostname(hostname);
850
- }
851
- catch {
852
- return false;
853
- }
854
- }
855
- function isTheClawBayChatgptBaseUrl(value) {
856
- const trimmed = trimTrailingSlash(value.trim());
857
- if (!trimmed)
858
- return false;
859
- try {
860
- const parsed = new URL(trimmed);
861
- const hostname = parsed.hostname.toLowerCase();
862
- return hostname === THECLAWBAY_CANONICAL_API_HOST || THECLAWBAY_WEBSITE_HOSTS.has(hostname);
863
- }
864
- catch {
865
- return false;
866
- }
867
- }
868
- function openAiCompatibleProxyUrl(backendUrl) {
869
- return `${publicApiOriginForBackendUrl(backendUrl)}${CANONICAL_THECLAWBAY_OPENAI_PROXY_SUFFIX}`;
870
- }
871
- function geminiCompatibleProxyBaseUrl(backendUrl) {
872
- return publicApiOriginForBackendUrl(backendUrl);
873
- }
874
- function anthropicCompatibleProxyUrl(backendUrl) {
875
- return `${publicApiOriginForBackendUrl(backendUrl)}/anthropic`;
876
- }
877
- function anthropicCompatibleProxyV1Url(backendUrl) {
878
- return `${anthropicCompatibleProxyUrl(backendUrl)}/v1`;
879
- }
880
- function isTheClawBayOpenAiCompatibleBaseUrl(value) {
881
- if (typeof value !== "string")
882
- return false;
883
- const normalized = value.trim();
884
- if (!normalized)
885
- return false;
886
- try {
887
- const parsed = new URL(normalized);
888
- const hostname = parsed.hostname.toLowerCase();
889
- const pathname = trimTrailingSlash(parsed.pathname);
890
- if (pathname === LEGACY_THECLAWBAY_OPENAI_PROXY_SUFFIX &&
891
- (THECLAWBAY_WEBSITE_HOSTS.has(hostname) ||
892
- hostname === "localhost" ||
893
- hostname === "127.0.0.1" ||
894
- hostname === "::1")) {
895
- return true;
896
- }
897
- return (hostname === THECLAWBAY_CANONICAL_API_HOST &&
898
- pathname === CANONICAL_THECLAWBAY_OPENAI_PROXY_SUFFIX);
899
- }
900
- catch {
901
- return false;
902
- }
903
- }
904
- function isTheClawBayAnthropicCompatibleBaseUrl(value) {
905
- if (typeof value !== "string")
906
- return false;
907
- const normalized = value.trim();
908
- if (!normalized)
909
- return false;
910
- try {
911
- const parsed = new URL(normalized);
912
- const hostname = parsed.hostname.toLowerCase();
913
- const pathname = trimTrailingSlash(parsed.pathname);
914
- return hostname === THECLAWBAY_CANONICAL_API_HOST && pathname === "/anthropic/v1";
915
- }
916
- catch {
917
- return false;
918
- }
919
- }
920
- function uniqueStrings(values) {
921
- const output = [];
922
- const seen = new Set();
923
- for (const value of values) {
924
- const normalized = value.trim();
925
- if (!normalized || seen.has(normalized))
926
- continue;
927
- seen.add(normalized);
928
- output.push(normalized);
929
- }
930
- return output;
931
- }
932
- function xdgConfigHomeDir() {
933
- const raw = process.env.XDG_CONFIG_HOME?.trim();
934
- if (raw)
935
- return raw;
936
- return node_path_1.default.join(node_os_1.default.homedir(), ".config");
937
- }
938
- function configFileCandidatesForDir(dir, basenames) {
939
- return basenames.map((basename) => node_path_1.default.join(dir, basename));
940
- }
941
- function findProjectConfigFile(params) {
942
- let current = node_path_1.default.resolve(params.startDir);
943
- const root = node_path_1.default.parse(current).root;
944
- while (true) {
945
- for (const candidate of configFileCandidatesForDir(current, params.basenames)) {
946
- if ((0, node_fs_1.existsSync)(candidate))
947
- return candidate;
948
- }
949
- if ((0, node_fs_1.existsSync)(node_path_1.default.join(current, ".git")))
950
- return null;
951
- if (current === root)
952
- return null;
953
- const parent = node_path_1.default.dirname(current);
954
- if (parent === current)
955
- return null;
956
- current = parent;
957
- }
958
- }
959
- function configDirCandidates(appName) {
960
- const home = node_os_1.default.homedir();
961
- const dirs = [node_path_1.default.join(xdgConfigHomeDir(), appName)];
962
- if (node_os_1.default.platform() === "darwin") {
963
- dirs.push(node_path_1.default.join(home, "Library", "Application Support", appName));
964
- }
965
- else if (node_os_1.default.platform() === "win32") {
966
- dirs.push(node_path_1.default.join(roamingAppDataDir(), appName));
967
- }
968
- return uniqueStrings(dirs);
969
- }
970
- function claudeDesktop3pConfigPath() {
971
- if (node_os_1.default.platform() !== "darwin")
972
- return null;
973
- return node_path_1.default.join(node_os_1.default.homedir(), "Library", "Application Support", "Claude-3p", "claude_desktop_config.json");
974
- }
975
- function claudeDesktopLogCandidates() {
976
- if (node_os_1.default.platform() !== "darwin")
977
- return [];
978
- const home = node_os_1.default.homedir();
979
- return [
980
- node_path_1.default.join(home, "Library", "Logs", "Claude-3p", "main.log"),
981
- node_path_1.default.join(home, "Library", "Logs", "Claude", "main.log"),
982
- ];
983
- }
984
- function claudeDesktopAppCandidatePaths() {
985
- if (node_os_1.default.platform() !== "darwin")
986
- return [];
987
- const home = node_os_1.default.homedir();
988
- return uniqueStrings([
989
- "/Applications/Claude.app",
990
- node_path_1.default.join(home, "Applications", "Claude.app"),
991
- ]);
992
- }
993
- async function readFileIfExists(filePath) {
994
- try {
995
- return await promises_1.default.readFile(filePath, "utf8");
996
- }
997
- catch (error) {
998
- const err = error;
999
- if (err.code === "ENOENT")
1000
- return null;
1001
- throw error;
1002
- }
1003
- }
1004
- async function removeFileIfExists(filePath) {
1005
- try {
1006
- await promises_1.default.unlink(filePath);
1007
- return true;
1008
- }
1009
- catch (error) {
1010
- const err = error;
1011
- if (err.code === "ENOENT")
1012
- return false;
1013
- throw error;
1014
- }
1015
- }
1016
- async function readJsonObjectFile(filePath) {
1017
- const existingRaw = await readFileIfExists(filePath);
1018
- if (existingRaw === null || !existingRaw.trim())
1019
- return {};
1020
- try {
1021
- return objectRecordOr(JSON.parse(existingRaw), {});
1022
63
  }
1023
- catch {
1024
- throw new Error(`invalid JSON in ${filePath}`);
1025
- }
1026
- }
1027
- async function writeJsonObjectFile(filePath, doc, mode) {
1028
- await promises_1.default.mkdir(node_path_1.default.dirname(filePath), { recursive: true });
1029
- await promises_1.default.writeFile(filePath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
1030
- if (mode !== undefined)
1031
- await promises_1.default.chmod(filePath, mode);
1032
- }
1033
- function editorHosts() {
1034
- const home = node_os_1.default.homedir();
1035
- if (node_os_1.default.platform() === "darwin") {
1036
- const appSupport = node_path_1.default.join(home, "Library", "Application Support");
1037
- return [
1038
- {
1039
- id: "vscode",
1040
- label: "VS Code",
1041
- extensionDir: node_path_1.default.join(home, ".vscode", "extensions"),
1042
- userSettingsPath: node_path_1.default.join(appSupport, "Code", "User", "settings.json"),
1043
- },
1044
- {
1045
- id: "vscode-insiders",
1046
- label: "VS Code Insiders",
1047
- extensionDir: node_path_1.default.join(home, ".vscode-insiders", "extensions"),
1048
- userSettingsPath: node_path_1.default.join(appSupport, "Code - Insiders", "User", "settings.json"),
1049
- },
1050
- {
1051
- id: "cursor",
1052
- label: "Cursor",
1053
- extensionDir: node_path_1.default.join(home, ".cursor", "extensions"),
1054
- userSettingsPath: node_path_1.default.join(appSupport, "Cursor", "User", "settings.json"),
1055
- },
1056
- {
1057
- id: "windsurf",
1058
- label: "Windsurf",
1059
- extensionDir: node_path_1.default.join(home, ".windsurf", "extensions"),
1060
- userSettingsPath: node_path_1.default.join(appSupport, "Windsurf", "User", "settings.json"),
1061
- },
1062
- {
1063
- id: "vscodium",
1064
- label: "VSCodium",
1065
- extensionDir: node_path_1.default.join(home, ".vscode-oss", "extensions"),
1066
- userSettingsPath: node_path_1.default.join(appSupport, "VSCodium", "User", "settings.json"),
1067
- },
1068
- ];
64
+ const lines = [];
65
+ if (configured.length > 0) {
66
+ lines.push((0, tui_1.statusLine)("ok", `Configured ${tui_1.tint.bold((0, format_1.formatSummaryList)(configured))}`));
1069
67
  }
1070
- if (node_os_1.default.platform() === "win32") {
1071
- const homeDir = process.env.USERPROFILE?.trim() || home;
1072
- const appData = roamingAppDataDir();
1073
- return [
1074
- {
1075
- id: "vscode",
1076
- label: "VS Code",
1077
- extensionDir: node_path_1.default.join(homeDir, ".vscode", "extensions"),
1078
- userSettingsPath: node_path_1.default.join(appData, "Code", "User", "settings.json"),
1079
- },
1080
- {
1081
- id: "vscode-insiders",
1082
- label: "VS Code Insiders",
1083
- extensionDir: node_path_1.default.join(homeDir, ".vscode-insiders", "extensions"),
1084
- userSettingsPath: node_path_1.default.join(appData, "Code - Insiders", "User", "settings.json"),
1085
- },
1086
- {
1087
- id: "cursor",
1088
- label: "Cursor",
1089
- extensionDir: node_path_1.default.join(homeDir, ".cursor", "extensions"),
1090
- userSettingsPath: node_path_1.default.join(appData, "Cursor", "User", "settings.json"),
1091
- },
1092
- {
1093
- id: "windsurf",
1094
- label: "Windsurf",
1095
- extensionDir: node_path_1.default.join(homeDir, ".windsurf", "extensions"),
1096
- userSettingsPath: node_path_1.default.join(appData, "Windsurf", "User", "settings.json"),
1097
- },
1098
- {
1099
- id: "vscodium",
1100
- label: "VSCodium",
1101
- extensionDir: node_path_1.default.join(homeDir, ".vscode-oss", "extensions"),
1102
- userSettingsPath: node_path_1.default.join(appData, "VSCodium", "User", "settings.json"),
1103
- },
1104
- ];
68
+ else {
69
+ lines.push((0, tui_1.statusLine)("info", "No local tools selected."));
1105
70
  }
1106
- return [
1107
- {
1108
- id: "vscode",
1109
- label: "VS Code",
1110
- extensionDir: node_path_1.default.join(home, ".vscode", "extensions"),
1111
- userSettingsPath: node_path_1.default.join(home, ".config", "Code", "User", "settings.json"),
1112
- },
1113
- {
1114
- id: "vscode-insiders",
1115
- label: "VS Code Insiders",
1116
- extensionDir: node_path_1.default.join(home, ".vscode-insiders", "extensions"),
1117
- userSettingsPath: node_path_1.default.join(home, ".config", "Code - Insiders", "User", "settings.json"),
1118
- },
1119
- {
1120
- id: "cursor",
1121
- label: "Cursor",
1122
- extensionDir: node_path_1.default.join(home, ".cursor", "extensions"),
1123
- userSettingsPath: node_path_1.default.join(home, ".config", "Cursor", "User", "settings.json"),
1124
- },
1125
- {
1126
- id: "windsurf",
1127
- label: "Windsurf",
1128
- extensionDir: node_path_1.default.join(home, ".windsurf", "extensions"),
1129
- userSettingsPath: node_path_1.default.join(home, ".config", "Windsurf", "User", "settings.json"),
1130
- },
1131
- {
1132
- id: "vscodium",
1133
- label: "VSCodium",
1134
- extensionDir: node_path_1.default.join(home, ".vscode-oss", "extensions"),
1135
- userSettingsPath: node_path_1.default.join(home, ".config", "VSCodium", "User", "settings.json"),
1136
- },
1137
- ];
1138
- }
1139
- async function detectExtensionHosts(prefixes) {
1140
- const matches = [];
1141
- for (const host of editorHosts()) {
1142
- if (!(await pathExists(host.extensionDir)))
1143
- continue;
1144
- const entries = (0, node_fs_1.readdirSync)(host.extensionDir, { withFileTypes: true });
1145
- const found = entries.some((entry) => {
1146
- if (!entry.isDirectory())
1147
- return false;
1148
- const normalized = entry.name.toLowerCase();
1149
- return prefixes.some((prefix) => normalized.startsWith(prefix));
1150
- });
1151
- if (found)
1152
- matches.push(host);
71
+ lines.push((0, tui_1.statusLine)("ok", "Saved your The Claw Bay credential and managed machine setup."));
72
+ if (restartTargets.length > 0) {
73
+ lines.push((0, tui_1.statusLine)("warn", `Restart if already open: ${(0, format_1.formatSummaryList)(restartTargets)}`));
1153
74
  }
1154
- return matches;
1155
- }
1156
- function parseSetupClientFlags(raw) {
1157
- if (!raw)
1158
- return null;
1159
- const parts = raw
1160
- .split(",")
1161
- .map((part) => part.trim().toLowerCase())
1162
- .filter(Boolean);
1163
- const selected = new Set();
1164
- for (const part of parts) {
1165
- if (SETUP_CLIENT_IDS.includes(part)) {
1166
- selected.add(part);
1167
- continue;
1168
- }
1169
- throw new Error(`unknown client "${part}". Supported values: ${SETUP_CLIENT_IDS.join(", ")}.`);
75
+ for (const note of params.notes) {
76
+ lines.push((0, tui_1.statusLine)("info", note));
1170
77
  }
1171
- return selected;
1172
- }
1173
- async function promptForSetupClients(clients) {
1174
- const selected = new Set(clients.filter((client) => client.detected && client.recommended).map((client) => client.id));
1175
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
1176
- return selected;
78
+ for (const line of (0, tui_1.renderBox)({ title: "Setup complete", lines })) {
79
+ params.log(line);
1177
80
  }
1178
- const options = clients.map((client) => ({
1179
- ...client,
1180
- checked: client.detected && selected.has(client.id),
1181
- }));
1182
- return new Promise((resolve) => {
1183
- let cursor = 0;
1184
- let settled = false;
1185
- let hint = clients.some((client) => client.detected)
1186
- ? "Choose the tools you want to configure."
1187
- : "No supported local clients are detected yet. Apply setup will just save your managed config and local credential env.";
1188
- const stdin = process.stdin;
1189
- const stdout = process.stdout;
1190
- const wasRaw = stdin.isRaw;
1191
- const applyIndex = options.length;
1192
- const hyperlinksEnabled = supportsTerminalHyperlinks();
1193
- const ansi = {
1194
- reset: "\x1b[0m",
1195
- bold: "\x1b[1m",
1196
- dim: "\x1b[2m",
1197
- inverse: "\x1b[7m",
1198
- red: "\x1b[31m",
1199
- green: "\x1b[32m",
1200
- yellow: "\x1b[33m",
1201
- gray: "\x1b[90m",
1202
- };
1203
- const paint = (text, ...codes) => `${codes.join("")}${text}${ansi.reset}`;
1204
- const clearScreen = () => {
1205
- stdout.write("\x1b[2J\x1b[H");
1206
- };
1207
- const selectedCount = () => options.filter((option) => option.checked).length;
1208
- const directToggleHint = options.length <= 9
1209
- ? `press 1-${options.length} to toggle directly`
1210
- : options.length === 10
1211
- ? "press 1-9 or 0 to toggle directly"
1212
- : "press a number key to toggle directly";
1213
- const render = () => {
1214
- clearScreen();
1215
- stdout.write(`${paint("Choose local clients to configure", ansi.bold)}\n`);
1216
- stdout.write(`${paint(`Use ↑/↓ to move, Enter to toggle the highlighted integration, or ${directToggleHint}.`, ansi.dim, ansi.gray)}\n`);
1217
- stdout.write(`${paint("Each tool name links to its official site when your terminal supports it.", ansi.dim, ansi.gray)}\n`);
1218
- stdout.write(`${paint("Move to Apply setup and press Enter when you're ready.", ansi.dim, ansi.gray)}\n\n`);
1219
- for (const [index, option] of options.entries()) {
1220
- const pointer = index === cursor ? paint(">", ansi.bold) : " ";
1221
- const selectable = option.detected || option.installable;
1222
- const mark = selectable
1223
- ? option.checked
1224
- ? paint("[x]", ansi.green, ansi.bold)
1225
- : paint("[ ]", ansi.gray)
1226
- : paint("[-]", ansi.red, ansi.bold);
1227
- const badge = option.detected
1228
- ? paint("found", ansi.green)
1229
- : option.installable
1230
- ? paint(option.checked ? "will install" : "available to install", ansi.yellow)
1231
- : paint("not found", ansi.red);
1232
- const label = selectable
1233
- ? formatSetupClientLabel(option, hyperlinksEnabled)
1234
- : paint(formatSetupClientLabel(option, hyperlinksEnabled), ansi.dim, ansi.gray);
1235
- stdout.write(`${pointer} ${index + 1}. ${mark} ${label} ${badge}\n`);
1236
- }
1237
- const applyPointer = cursor === applyIndex ? ">" : " ";
1238
- const applyLabel = `Apply setup (${selectedCount()} selected)`;
1239
- const applyStyled = cursor === applyIndex
1240
- ? paint(` ${applyLabel} `, ansi.inverse, ansi.bold, ansi.green)
1241
- : paint(applyLabel, ansi.bold, ansi.green);
1242
- stdout.write(`\n${applyPointer} ${applyStyled}\n`);
1243
- stdout.write(`\n${paint(hint, ansi.dim, ansi.gray)}\n`);
1244
- };
1245
- const finish = () => {
1246
- if (settled)
1247
- return;
1248
- settled = true;
1249
- stdin.off("keypress", onKeypress);
1250
- if (stdin.isTTY)
1251
- stdin.setRawMode(Boolean(wasRaw));
1252
- stdout.write("\x1b[?25h");
1253
- clearScreen();
1254
- const finalSelection = new Set(options.filter((option) => option.checked).map((option) => option.id));
1255
- resolve(finalSelection);
1256
- };
1257
- const toggleOption = (index) => {
1258
- const option = options[index];
1259
- if (!option)
1260
- return;
1261
- cursor = index;
1262
- if (!option.detected && !option.installable) {
1263
- hint = `${option.label} is not detected on this machine yet, so it cannot be selected.`;
1264
- render();
1265
- return;
1266
- }
1267
- option.checked = !option.checked;
1268
- if (option.checked && !option.detected && option.installable) {
1269
- hint = `${option.label} selected. Setup will install it first.`;
1270
- }
1271
- else {
1272
- hint = `${option.label} ${option.checked ? "selected" : "cleared"}.`;
1273
- }
1274
- render();
1275
- };
1276
- const onKeypress = (_str, key) => {
1277
- if (key.ctrl && key.name === "c") {
1278
- stdout.write("\x1b[?25h");
1279
- process.exit(130);
1280
- }
1281
- if (key.name === "up" || key.name === "k") {
1282
- cursor = (cursor - 1 + options.length + 1) % (options.length + 1);
1283
- render();
1284
- return;
1285
- }
1286
- if (key.name === "down" || key.name === "j") {
1287
- cursor = (cursor + 1) % (options.length + 1);
1288
- render();
1289
- return;
1290
- }
1291
- if (key.name === "space") {
1292
- if (cursor === applyIndex) {
1293
- finish();
1294
- return;
1295
- }
1296
- toggleOption(cursor);
1297
- return;
1298
- }
1299
- const directSelection = key.sequence === "0" && options.length >= 10
1300
- ? 10
1301
- : /^[1-9]$/.test(key.sequence ?? "")
1302
- ? Number.parseInt(key.sequence ?? "", 10)
1303
- : Number.NaN;
1304
- if (Number.isInteger(directSelection) && directSelection >= 1 && directSelection <= options.length) {
1305
- toggleOption(directSelection - 1);
1306
- return;
1307
- }
1308
- if (key.name === "a") {
1309
- const detectedOptions = options.filter((option) => option.detected);
1310
- const nextValue = detectedOptions.some((option) => !option.checked);
1311
- for (const option of detectedOptions)
1312
- option.checked = nextValue;
1313
- hint = `${nextValue ? "Selected" : "Cleared"} all detected integrations.`;
1314
- render();
1315
- return;
1316
- }
1317
- if (key.name === "return" || key.name === "enter") {
1318
- if (cursor === applyIndex) {
1319
- finish();
1320
- return;
1321
- }
1322
- toggleOption(cursor);
1323
- return;
1324
- }
1325
- if (key.name === "c") {
1326
- finish();
1327
- }
1328
- };
1329
- (0, node_readline_1.emitKeypressEvents)(stdin);
1330
- stdin.resume();
1331
- if (stdin.isTTY)
1332
- stdin.setRawMode(true);
1333
- stdout.write("\x1b[?25l");
1334
- stdin.on("keypress", onKeypress);
1335
- render();
1336
- });
1337
81
  }
1338
- function resolveSetupClientSelection(params) {
1339
- const { setupClients, flagSelection, skipPrompt } = params;
1340
- if (flagSelection) {
1341
- for (const client of setupClients) {
1342
- if (!flagSelection.has(client.id))
1343
- continue;
1344
- if (!client.detected && !client.installable) {
1345
- throw new Error(`${client.label} is not detected on this machine, so it cannot be selected yet.`);
82
+ class SetupCommand extends base_command_1.BaseCommand {
83
+ async run() {
84
+ await this.runSafe(async () => {
85
+ const { flags } = await this.parse(SetupCommand);
86
+ const debugOutput = flags["debug-output"];
87
+ if (!debugOutput && process.stdout.isTTY) {
88
+ this.log((0, tui_1.banner)("Connect your coding tools to The Claw Bay relay"));
89
+ this.log("");
1346
90
  }
1347
- }
1348
- return Promise.resolve(flagSelection);
1349
- }
1350
- const defaults = new Set(setupClients.filter((client) => client.detected && client.recommended).map((client) => client.id));
1351
- if (skipPrompt)
1352
- return Promise.resolve(defaults);
1353
- return promptForSetupClients(setupClients);
1354
- }
1355
- function installSelectedMissingClients(params) {
1356
- const installed = [];
1357
- for (const client of params.setupClients) {
1358
- if (!params.selectedClientIds.has(client.id) || client.detected || !client.installable)
1359
- continue;
1360
- const plan = installPlanForSetupClient(client.id);
1361
- if (!plan)
1362
- continue;
1363
- params.log(`Installing ${client.summaryLabel}...`);
1364
- runInstallPlan(plan);
1365
- installed.push(client.summaryLabel);
1366
- }
1367
- return installed;
1368
- }
1369
- async function promptForCodexConversationMigration() {
1370
- if (!process.stdin.isTTY || !process.stdout.isTTY)
1371
- return false;
1372
- const rl = (0, promises_2.createInterface)({
1373
- input: process.stdin,
1374
- output: process.stdout,
1375
- });
1376
- try {
1377
- const answer = await rl.question("\nConvert old Codex conversations so they stay visible under The Claw Bay? [y/N] ");
1378
- const normalized = answer.trim().toLowerCase();
1379
- return normalized === "y" || normalized === "yes";
1380
- }
1381
- finally {
1382
- rl.close();
1383
- process.stdin.resume();
1384
- }
1385
- }
1386
- async function resolveCodexConversationMigrationSelection(params) {
1387
- if (!params.codexSelected)
1388
- return false;
1389
- if (params.flagValue !== undefined)
1390
- return params.flagValue;
1391
- if (params.skipPrompt || !process.stdin.isTTY || !process.stdout.isTTY)
1392
- return false;
1393
- return promptForCodexConversationMigration();
1394
- }
1395
- async function resolveDeviceLabel(params) {
1396
- const fallback = node_os_1.default.hostname().trim() || "This device";
1397
- const preferred = (params.flagValue ?? params.managedValue ?? "").trim() || fallback;
1398
- if (params.flagValue?.trim())
1399
- return preferred;
1400
- if (params.skipPrompt || !process.stdin.isTTY || !process.stdout.isTTY) {
1401
- return preferred;
1402
- }
1403
- const rl = (0, promises_2.createInterface)({
1404
- input: process.stdin,
1405
- output: process.stdout,
1406
- });
1407
- try {
1408
- const answer = await rl.question(`\nName this device [${preferred}] `);
1409
- return answer.trim() || preferred;
1410
- }
1411
- finally {
1412
- rl.close();
1413
- process.stdin.resume();
1414
- }
1415
- }
1416
- async function promptForGroupedModelSelection(params) {
1417
- if (!process.stdin.isTTY || !process.stdout.isTTY || params.models.length <= 1) {
1418
- return uniqueStrings(params.defaultSelectedIds);
1419
- }
1420
- const sections = groupModelSections(params.models);
1421
- const knownIds = new Set(params.models.map((model) => model.id));
1422
- const selected = new Set(uniqueStrings(params.defaultSelectedIds).filter((modelId) => knownIds.has(modelId)));
1423
- const rows = [];
1424
- for (const [sectionIndex, section] of sections.entries()) {
1425
- rows.push({ kind: "group", sectionIndex });
1426
- for (const [modelIndex] of section.models.entries()) {
1427
- rows.push({ kind: "model", sectionIndex, modelIndex });
1428
- }
1429
- }
1430
- rows.push({ kind: "apply" });
1431
- const stdin = process.stdin;
1432
- const stdout = process.stdout;
1433
- const wasRaw = stdin.isRaw;
1434
- const ansi = {
1435
- reset: "\x1b[0m",
1436
- bold: "\x1b[1m",
1437
- dim: "\x1b[2m",
1438
- inverse: "\x1b[7m",
1439
- green: "\x1b[32m",
1440
- yellow: "\x1b[33m",
1441
- gray: "\x1b[90m",
1442
- };
1443
- const paint = (text, ...codes) => `${codes.join("")}${text}${ansi.reset}`;
1444
- const clearScreen = () => {
1445
- stdout.write("\x1b[2J\x1b[H");
1446
- };
1447
- const rowForModel = (sectionIndex, modelIndex) => rows.findIndex((row) => row.kind === "model" && row.sectionIndex === sectionIndex && row.modelIndex === modelIndex);
1448
- let settled = false;
1449
- let hint = "Use ↑/↓ to move. Press Enter or Space to toggle a model or a provider header.";
1450
- let cursor = Math.max(0, rows.findIndex((row) => {
1451
- if (row.kind !== "model")
1452
- return false;
1453
- const model = sections[row.sectionIndex]?.models[row.modelIndex];
1454
- return model?.id === params.defaultSelectedIds[0];
1455
- }));
1456
- const groupState = (sectionIndex) => {
1457
- const models = sections[sectionIndex]?.models ?? [];
1458
- const selectedCount = models.filter((model) => selected.has(model.id)).length;
1459
- if (selectedCount === 0)
1460
- return "[ ]";
1461
- if (selectedCount === models.length)
1462
- return "[x]";
1463
- return "[-]";
1464
- };
1465
- const setGroupSelection = (sectionIndex, nextValue) => {
1466
- for (const model of sections[sectionIndex]?.models ?? []) {
1467
- if (nextValue)
1468
- selected.add(model.id);
1469
- else
1470
- selected.delete(model.id);
1471
- }
1472
- };
1473
- const toggleCurrent = () => {
1474
- const current = rows[cursor];
1475
- if (!current)
1476
- return;
1477
- if (current.kind === "apply") {
1478
- finish();
1479
- return;
1480
- }
1481
- if (current.kind === "group") {
1482
- const state = groupState(current.sectionIndex);
1483
- const nextValue = state !== "[x]";
1484
- setGroupSelection(current.sectionIndex, nextValue);
1485
- hint = `${sections[current.sectionIndex]?.provider.label ?? "Group"} ${nextValue ? "selected" : "cleared"}.`;
1486
- render();
1487
- return;
1488
- }
1489
- const model = sections[current.sectionIndex]?.models[current.modelIndex];
1490
- if (!model)
1491
- return;
1492
- if (selected.has(model.id))
1493
- selected.delete(model.id);
1494
- else
1495
- selected.add(model.id);
1496
- hint = `${model.name} ${selected.has(model.id) ? "selected" : "cleared"}.`;
1497
- render();
1498
- };
1499
- const finish = () => {
1500
- if (settled)
1501
- return;
1502
- settled = true;
1503
- stdin.off("keypress", onKeypress);
1504
- if (stdin.isTTY)
1505
- stdin.setRawMode(Boolean(wasRaw));
1506
- stdout.write("\x1b[?25h");
1507
- clearScreen();
1508
- resolvePromise([...selected].filter((modelId) => knownIds.has(modelId)));
1509
- };
1510
- const render = () => {
1511
- clearScreen();
1512
- stdout.write(`${paint(params.title, ansi.bold)}\n`);
1513
- stdout.write(`${paint("Each provider header toggles that provider on or off. Press A to toggle all models.", ansi.dim, ansi.gray)}\n`);
1514
- stdout.write(`${paint("Move to Apply and press Enter when you're ready.", ansi.dim, ansi.gray)}\n\n`);
1515
- for (const [sectionIndex, section] of sections.entries()) {
1516
- const currentRow = rows[cursor];
1517
- const groupCursor = currentRow?.kind === "group" && currentRow.sectionIndex === sectionIndex;
1518
- const groupPointer = groupCursor ? paint("→", ansi.bold, ansi.yellow) : " ";
1519
- const groupMark = groupState(sectionIndex);
1520
- stdout.write(`${groupPointer} ${paint(groupMark, groupMark === "[x]" ? ansi.green : ansi.gray)} ${section.provider.icon} ${paint(`${section.provider.label} — Select all`, ansi.bold)}\n`);
1521
- for (const [modelIndex, model] of section.models.entries()) {
1522
- const activeRow = rows[cursor];
1523
- const isActive = activeRow?.kind === "model" &&
1524
- activeRow.sectionIndex === sectionIndex &&
1525
- activeRow.modelIndex === modelIndex;
1526
- const pointer = isActive ? paint("→", ansi.bold, ansi.yellow) : " ";
1527
- const mark = selected.has(model.id) ? paint("[x]", ansi.green) : paint("[ ]", ansi.gray);
1528
- stdout.write(`${pointer} ${mark} ${model.name} ${paint(`(${model.id})`, ansi.dim, ansi.gray)}\n`);
1529
- }
1530
- stdout.write("\n");
1531
- }
1532
- const applyActive = rows[cursor]?.kind === "apply";
1533
- const applyStyled = applyActive
1534
- ? paint(` ${params.applyLabel} (${selected.size} selected) `, ansi.inverse, ansi.bold, ansi.green)
1535
- : paint(`${params.applyLabel} (${selected.size} selected)`, ansi.bold, ansi.green);
1536
- stdout.write(`${applyActive ? "→" : " "} ${applyStyled}\n`);
1537
- stdout.write(`\n${paint(hint, ansi.dim, ansi.gray)}\n`);
1538
- };
1539
- const onKeypress = (_str, key) => {
1540
- if (key.ctrl && key.name === "c") {
1541
- stdout.write("\x1b[?25h");
1542
- process.exit(130);
1543
- }
1544
- if (key.name === "up" || key.name === "k") {
1545
- cursor = (cursor - 1 + rows.length) % rows.length;
1546
- render();
1547
- return;
1548
- }
1549
- if (key.name === "down" || key.name === "j") {
1550
- cursor = (cursor + 1) % rows.length;
1551
- render();
1552
- return;
1553
- }
1554
- if (key.name === "space" || key.name === "return" || key.name === "enter") {
1555
- toggleCurrent();
1556
- return;
1557
- }
1558
- if (key.name === "a") {
1559
- const nextValue = sections.some((section) => section.models.some((model) => !selected.has(model.id)));
1560
- for (const [sectionIndex] of sections.entries())
1561
- setGroupSelection(sectionIndex, nextValue);
1562
- hint = `${nextValue ? "Selected" : "Cleared"} all available models.`;
1563
- render();
1564
- return;
1565
- }
1566
- if (key.name === "c") {
1567
- finish();
1568
- }
1569
- };
1570
- let resolvePromise = () => { };
1571
- const result = new Promise((resolve) => {
1572
- resolvePromise = resolve;
1573
- });
1574
- (0, node_readline_1.emitKeypressEvents)(stdin);
1575
- stdin.resume();
1576
- if (stdin.isTTY)
1577
- stdin.setRawMode(true);
1578
- stdout.write("\x1b[?25l");
1579
- stdin.on("keypress", onKeypress);
1580
- render();
1581
- return result;
1582
- }
1583
- async function promptForPrimaryModel(models, defaultModel) {
1584
- if (!process.stdin.isTTY || !process.stdout.isTTY || models.length <= 1) {
1585
- return defaultModel;
1586
- }
1587
- const sections = groupModelSections(models);
1588
- const flattened = sections.flatMap((section) => section.models);
1589
- const stdin = process.stdin;
1590
- const stdout = process.stdout;
1591
- const wasRaw = stdin.isRaw;
1592
- const ansi = {
1593
- reset: "\x1b[0m",
1594
- bold: "\x1b[1m",
1595
- dim: "\x1b[2m",
1596
- green: "\x1b[32m",
1597
- yellow: "\x1b[33m",
1598
- gray: "\x1b[90m",
1599
- };
1600
- const paint = (text, ...codes) => `${codes.join("")}${text}${ansi.reset}`;
1601
- const clearScreen = () => {
1602
- stdout.write("\x1b[2J\x1b[H");
1603
- };
1604
- let cursor = Math.max(0, flattened.findIndex((model) => model.id === defaultModel));
1605
- let settled = false;
1606
- let resolvePromise = () => { };
1607
- const finish = (modelId) => {
1608
- if (settled)
1609
- return;
1610
- settled = true;
1611
- stdin.off("keypress", onKeypress);
1612
- if (stdin.isTTY)
1613
- stdin.setRawMode(Boolean(wasRaw));
1614
- stdout.write("\x1b[?25h");
1615
- clearScreen();
1616
- resolvePromise(modelId);
1617
- };
1618
- const render = () => {
1619
- clearScreen();
1620
- stdout.write(`${paint("Choose the default model for local setup", ansi.bold)}\n`);
1621
- stdout.write(`${paint("Use ↑/↓ to move and Enter to select the default model.", ansi.dim, ansi.gray)}\n\n`);
1622
- let index = 0;
1623
- for (const section of sections) {
1624
- stdout.write(`${paint(`${section.provider.icon} ${section.provider.label}`, ansi.bold)}\n`);
1625
- for (const model of section.models) {
1626
- const active = index === cursor;
1627
- const pointer = active ? paint("→", ansi.bold, ansi.yellow) : " ";
1628
- const defaultSuffix = model.id === defaultModel ? paint(" default", ansi.dim, ansi.gray) : "";
1629
- stdout.write(`${pointer} ${model.name} ${paint(`(${model.id})`, ansi.dim, ansi.gray)}${defaultSuffix}\n`);
1630
- index += 1;
1631
- }
1632
- stdout.write("\n");
1633
- }
1634
- };
1635
- const onKeypress = (_str, key) => {
1636
- if (key.ctrl && key.name === "c") {
1637
- stdout.write("\x1b[?25h");
1638
- process.exit(130);
1639
- }
1640
- if (key.name === "up" || key.name === "k") {
1641
- cursor = (cursor - 1 + flattened.length) % flattened.length;
1642
- render();
1643
- return;
1644
- }
1645
- if (key.name === "down" || key.name === "j") {
1646
- cursor = (cursor + 1) % flattened.length;
1647
- render();
1648
- return;
1649
- }
1650
- if (key.name === "return" || key.name === "enter" || key.name === "space") {
1651
- finish(flattened[cursor]?.id ?? defaultModel);
1652
- }
1653
- };
1654
- const result = new Promise((resolve) => {
1655
- resolvePromise = resolve;
1656
- });
1657
- (0, node_readline_1.emitKeypressEvents)(stdin);
1658
- stdin.resume();
1659
- if (stdin.isTTY)
1660
- stdin.setRawMode(true);
1661
- stdout.write("\x1b[?25l");
1662
- stdin.on("keypress", onKeypress);
1663
- render();
1664
- return result;
1665
- }
1666
- async function resolveConfiguredModelSelection(params) {
1667
- const requestedModel = params.requestedModel?.trim();
1668
- const requestedModels = params.requestedModels
1669
- ?.split(",")
1670
- .map((entry) => entry.trim())
1671
- .filter(Boolean);
1672
- const availableIds = new Set(params.resolved.models.map((entry) => entry.id));
1673
- if (requestedModel && availableIds.size > 0 && !availableIds.has(requestedModel)) {
1674
- throw new Error(`Requested model "${requestedModel}" is not advertised by the live backend.`);
1675
- }
1676
- if (requestedModels?.length) {
1677
- for (const modelId of requestedModels) {
1678
- if (availableIds.size > 0 && !availableIds.has(modelId)) {
1679
- throw new Error(`Requested model "${modelId}" is not advertised by the live backend.`);
1680
- }
1681
- }
1682
- }
1683
- let selectedModels;
1684
- if (requestedModels?.length) {
1685
- selectedModels = params.resolved.models.filter((entry) => requestedModels.includes(entry.id));
1686
- }
1687
- else if (params.skipPrompt || !process.stdin.isTTY || !process.stdout.isTTY) {
1688
- selectedModels = params.resolved.models;
1689
- }
1690
- else if (params.resolved.models.length > 1) {
1691
- const selectedIds = new Set(await promptForGroupedModelSelection({
1692
- title: "Choose the OpenAI-compatible models to save into local tools",
1693
- models: params.resolved.models,
1694
- defaultSelectedIds: params.resolved.models.map((entry) => entry.id),
1695
- applyLabel: "Apply model selection",
1696
- }));
1697
- selectedModels = params.resolved.models.filter((entry) => selectedIds.has(entry.id));
1698
- }
1699
- else {
1700
- selectedModels = params.resolved.models;
1701
- }
1702
- const fallbackModels = selectedModels.length > 0 ? selectedModels : params.resolved.models;
1703
- const selectedModelId = requestedModel && fallbackModels.some((entry) => entry.id === requestedModel)
1704
- ? requestedModel
1705
- : params.skipPrompt || !process.stdin.isTTY || !process.stdout.isTTY
1706
- ? fallbackModels.some((entry) => entry.id === params.resolved.model)
1707
- ? params.resolved.model
1708
- : fallbackModels[0]?.id ?? params.resolved.model
1709
- : await promptForPrimaryModel(fallbackModels, fallbackModels.some((entry) => entry.id === params.resolved.model)
1710
- ? params.resolved.model
1711
- : (fallbackModels[0]?.id ?? params.resolved.model));
1712
- const noteParts = [];
1713
- if (requestedModels?.length) {
1714
- noteParts.push(`Configured ${requestedModels.length} OpenAI-compatible models.`);
1715
- }
1716
- else if (selectedModels.length > 0 && selectedModels.length < params.resolved.models.length) {
1717
- noteParts.push(`Configured ${selectedModels.length} OpenAI-compatible models for local tools.`);
1718
- }
1719
- if (selectedModelId !== params.resolved.model) {
1720
- noteParts.push(`Using ${selectedModelId} as the default configured model for local tools.`);
1721
- }
1722
- if (availableIds.size === 0 && requestedModel && params.resolved.failure) {
1723
- noteParts.push(`Could not verify requested model ${requestedModel} against the live backend (${params.resolved.failure}).`);
1724
- }
1725
- return {
1726
- model: selectedModelId,
1727
- models: fallbackModels,
1728
- note: noteParts.join(" ").trim() || undefined,
1729
- };
1730
- }
1731
- function summarizeModelFetchFailure(detail) {
1732
- const normalized = detail.replace(/\s+/g, " ").trim();
1733
- if (!normalized)
1734
- return "unknown error";
1735
- if (normalized.length <= 140)
1736
- return normalized;
1737
- return `${normalized.slice(0, 137).trimEnd()}...`;
1738
- }
1739
- function isCredentialRejectedFailure(detail) {
1740
- if (!detail)
1741
- return false;
1742
- const normalized = detail.toLowerCase();
1743
- return (normalized.includes("http 401") ||
1744
- normalized.includes("invalid api key") ||
1745
- normalized.includes("missing bearer token") ||
1746
- normalized.includes("device session not found") ||
1747
- normalized.includes("billing access required"));
1748
- }
1749
- function describeCredentialRejection(authType, failure) {
1750
- const detail = failure?.trim() || "credential rejected by backend";
1751
- if (authType === "device-session") {
1752
- return `The saved device-session credential was rejected by the backend (${detail}). Re-link this machine in the browser or run \`theclawbay logout\` before rerunning setup.`;
1753
- }
1754
- return `The provided API key was rejected by the backend (${detail}). Update the key and rerun setup.`;
1755
- }
1756
- async function fetchBackendModelIds(backendUrl, apiKey) {
1757
- const url = `${openAiCompatibleProxyUrl(backendUrl)}/models`;
1758
- try {
1759
- const response = await fetch(url, {
1760
- headers: {
1761
- Authorization: `Bearer ${apiKey}`,
1762
- Accept: "application/json",
1763
- "User-Agent": CLI_HTTP_USER_AGENT,
1764
- },
1765
- signal: AbortSignal.timeout(10000),
1766
- });
1767
- if (!response.ok) {
1768
- const responseText = await response.text().catch(() => "");
1769
- let detail = responseText.trim();
1770
- try {
1771
- const parsed = JSON.parse(responseText);
1772
- if (typeof parsed.error === "string" && parsed.error.trim()) {
1773
- detail = parsed.error.trim();
1774
- }
1775
- else if (typeof parsed.code === "string" && parsed.code.trim()) {
1776
- detail = parsed.code.trim();
1777
- }
1778
- }
1779
- catch {
1780
- // Keep the raw response text when the body is not JSON.
1781
- }
1782
- return {
1783
- ids: null,
1784
- failure: `HTTP ${response.status}${detail ? `: ${summarizeModelFetchFailure(detail)}` : ""}`,
1785
- };
1786
- }
1787
- const body = (await response.json());
1788
- const ids = dedupeClaudeCatalogModelIds((body.data ?? [])
1789
- .map((entry) => (typeof entry.id === "string" ? entry.id.trim() : ""))
1790
- .filter((id) => id.length > 0));
1791
- if (!ids.length) {
1792
- return {
1793
- ids: null,
1794
- failure: "backend returned an empty model list",
1795
- };
1796
- }
1797
- return { ids };
1798
- }
1799
- catch (error) {
1800
- return {
1801
- ids: null,
1802
- failure: summarizeModelFetchFailure(error instanceof Error ? error.message : String(error)),
1803
- };
1804
- }
1805
- }
1806
- async function fetchClaudeModelIds(backendUrl, apiKey) {
1807
- const url = `${anthropicCompatibleProxyUrl(backendUrl)}/v1/models`;
1808
- try {
1809
- const response = await fetch(url, {
1810
- headers: {
1811
- "x-api-key": apiKey,
1812
- Accept: "application/json",
1813
- "User-Agent": CLI_HTTP_USER_AGENT,
1814
- },
1815
- signal: AbortSignal.timeout(10000),
1816
- });
1817
- if (!response.ok) {
1818
- const responseText = await response.text().catch(() => "");
1819
- let detail = responseText.trim();
1820
- try {
1821
- const parsed = JSON.parse(responseText);
1822
- if (typeof parsed.error === "string" && parsed.error.trim()) {
1823
- detail = parsed.error.trim();
1824
- }
1825
- else if (typeof parsed.code === "string" && parsed.code.trim()) {
1826
- detail = parsed.code.trim();
1827
- }
1828
- }
1829
- catch {
1830
- // Keep the raw response text when the body is not JSON.
1831
- }
1832
- return {
1833
- ids: null,
1834
- failure: `HTTP ${response.status}${detail ? `: ${summarizeModelFetchFailure(detail)}` : ""}`,
1835
- };
1836
- }
1837
- const body = (await response.json());
1838
- const ids = (body.data ?? [])
1839
- .map((entry) => (typeof entry.id === "string" ? entry.id.trim() : ""))
1840
- .filter((id) => id.length > 0);
1841
- if (!ids.length) {
1842
- return {
1843
- ids: null,
1844
- failure: "backend returned an empty Claude model list",
1845
- };
1846
- }
1847
- return { ids };
1848
- }
1849
- catch (error) {
1850
- return {
1851
- ids: null,
1852
- failure: summarizeModelFetchFailure(error instanceof Error ? error.message : String(error)),
1853
- };
1854
- }
1855
- }
1856
- function kiloStorageCandidates() {
1857
- const home = node_os_1.default.homedir();
1858
- const candidates = [node_path_1.default.join(home, ".config", "kilo"), node_path_1.default.join(home, ".kilo")];
1859
- if (node_os_1.default.platform() === "darwin") {
1860
- candidates.push(node_path_1.default.join(home, "Library", "Application Support", "Code", "User", "globalStorage", "kilocode.kilo-code"));
1861
- }
1862
- else if (node_os_1.default.platform() === "win32") {
1863
- const appData = process.env.APPDATA;
1864
- if (appData) {
1865
- candidates.push(node_path_1.default.join(appData, "Code", "User", "globalStorage", "kilocode.kilo-code"));
1866
- }
1867
- }
1868
- else {
1869
- candidates.push(node_path_1.default.join(home, ".config", "Code", "User", "globalStorage", "kilocode.kilo-code"));
1870
- }
1871
- return candidates;
1872
- }
1873
- async function detectCodexClient() {
1874
- if (hasCommand("codex"))
1875
- return true;
1876
- const candidates = [
1877
- node_path_1.default.join(paths_1.codexDir, "config.toml"),
1878
- node_path_1.default.join(paths_1.codexDir, "auth.json"),
1879
- node_path_1.default.join(paths_1.codexDir, "history.jsonl"),
1880
- node_path_1.default.join(paths_1.codexDir, "sessions"),
1881
- ];
1882
- for (const candidate of candidates) {
1883
- if (await pathExists(candidate))
1884
- return true;
1885
- }
1886
- return false;
1887
- }
1888
- async function detectGsdClient() {
1889
- if (hasCommand("gsd"))
1890
- return true;
1891
- const candidates = [
1892
- GSD_AGENT_DIR,
1893
- GSD_MODELS_PATH,
1894
- GSD_AUTH_PATH,
1895
- GSD_SETTINGS_PATH,
1896
- ];
1897
- for (const candidate of candidates) {
1898
- if (await pathExists(candidate))
1899
- return true;
1900
- }
1901
- return false;
1902
- }
1903
- async function detectContinueClient() {
1904
- if (hasCommand("cn") || hasCommand("continue"))
1905
- return true;
1906
- const candidates = [
1907
- CONTINUE_CONFIG_PATH,
1908
- node_path_1.default.join(node_os_1.default.homedir(), ".continue", "config.json"),
1909
- node_path_1.default.join(node_os_1.default.homedir(), ".continue", "sessions"),
1910
- ];
1911
- for (const candidate of candidates) {
1912
- if (await pathExists(candidate))
1913
- return true;
1914
- }
1915
- return (await detectExtensionHosts(["continue.continue-"])).length > 0;
1916
- }
1917
- async function detectClaudeCodeClient() {
1918
- if (hasCommand("claude"))
1919
- return true;
1920
- const candidates = [
1921
- node_path_1.default.join(node_os_1.default.homedir(), ".claude"),
1922
- node_path_1.default.join(node_os_1.default.homedir(), ".claude.json"),
1923
- node_path_1.default.join(node_os_1.default.homedir(), ".config", "claude"),
1924
- ];
1925
- for (const candidate of candidates) {
1926
- if (await pathExists(candidate))
1927
- return true;
1928
- }
1929
- return false;
1930
- }
1931
- async function detectClaudeClient() {
1932
- if (await detectClaudeCodeClient())
1933
- return true;
1934
- if (claudeDesktopAppCandidatePaths().some((candidate) => (0, node_fs_1.existsSync)(candidate)))
1935
- return true;
1936
- const configPath = claudeDesktop3pConfigPath();
1937
- if (!configPath)
1938
- return false;
1939
- return (await pathExists(configPath)) || (await pathExists(node_path_1.default.dirname(configPath)));
1940
- }
1941
- async function detectOpenCodeClient() {
1942
- if (hasCommand("opencode"))
1943
- return true;
1944
- const candidates = [
1945
- ...resolveOpenCodeConfigTargets(),
1946
- node_path_1.default.join(node_os_1.default.homedir(), ".opencode.json"),
1947
- ];
1948
- if (node_os_1.default.platform() === "darwin") {
1949
- candidates.push("/Applications/OpenCode.app");
1950
- candidates.push(node_path_1.default.join(node_os_1.default.homedir(), "Applications", "OpenCode.app"));
1951
- }
1952
- for (const candidate of candidates) {
1953
- if (await pathExists(candidate))
1954
- return true;
1955
- }
1956
- return false;
1957
- }
1958
- async function detectClineClient() {
1959
- if (hasCommand("cline"))
1960
- return true;
1961
- const candidates = [
1962
- CLINE_GLOBAL_STATE_PATH,
1963
- CLINE_SECRETS_PATH,
1964
- node_path_1.default.join(node_os_1.default.homedir(), ".cline", "state", "taskHistory.json"),
1965
- node_path_1.default.join(node_os_1.default.homedir(), ".cline", "tasks"),
1966
- ];
1967
- for (const candidate of candidates) {
1968
- if (await pathExists(candidate))
1969
- return true;
1970
- }
1971
- return (await detectExtensionHosts(["saoudrizwan.claude-dev-"])).length > 0;
1972
- }
1973
- async function detectKiloClient() {
1974
- if (hasCommand("kilo"))
1975
- return true;
1976
- for (const candidate of kiloStorageCandidates()) {
1977
- if (await pathExists(candidate))
1978
- return true;
1979
- }
1980
- return false;
1981
- }
1982
- async function detectRooClient() {
1983
- return (await detectExtensionHosts(["rooveterinaryinc.roo-cline-"])).length > 0;
1984
- }
1985
- async function detectTraeClient() {
1986
- const bundlePath = traeBundlePath();
1987
- if (!bundlePath)
1988
- return false;
1989
- return pathExists(bundlePath);
1990
- }
1991
- async function detectAiderClient() {
1992
- if (hasCommand("aider"))
1993
- return true;
1994
- const candidates = [
1995
- AIDER_CONFIG_PATH,
1996
- node_path_1.default.join(node_os_1.default.homedir(), ".aider.conf.yaml"),
1997
- node_path_1.default.join(process.cwd(), ".aider.conf.yml"),
1998
- node_path_1.default.join(process.cwd(), ".aider.chat.history.md"),
1999
- ];
2000
- for (const candidate of candidates) {
2001
- if (await pathExists(candidate))
2002
- return true;
2003
- }
2004
- return false;
2005
- }
2006
- async function detectZoClient() {
2007
- for (const candidate of zoProgramCandidates()) {
2008
- if (await pathExists(candidate))
2009
- return true;
2010
- }
2011
- for (const candidate of zoCookieDbCandidates()) {
2012
- if (await pathExists(candidate))
2013
- return true;
2014
- }
2015
- return false;
2016
- }
2017
- function hermesHomeDir() {
2018
- const raw = process.env.HERMES_HOME?.trim();
2019
- if (raw)
2020
- return raw;
2021
- return node_path_1.default.join(node_os_1.default.homedir(), ".hermes");
2022
- }
2023
- function hermesConfigPath() {
2024
- return node_path_1.default.join(hermesHomeDir(), "config.yaml");
2025
- }
2026
- function hermesEnvPath() {
2027
- return node_path_1.default.join(hermesHomeDir(), ".env");
2028
- }
2029
- async function detectHermesClient() {
2030
- if (hasCommand("hermes"))
2031
- return true;
2032
- const candidates = [
2033
- hermesHomeDir(),
2034
- hermesConfigPath(),
2035
- hermesEnvPath(),
2036
- ];
2037
- for (const candidate of candidates) {
2038
- if (await pathExists(candidate))
2039
- return true;
2040
- }
2041
- return false;
2042
- }
2043
- function isOpenAiCompatibleSetupModelId(modelId) {
2044
- const trimmed = modelId.trim();
2045
- if (!trimmed)
2046
- return false;
2047
- return !trimmed.startsWith("claude-") && SUPPORTED_MODEL_IDS.includes(trimmed);
2048
- }
2049
- function formatDotEnvValue(value) {
2050
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
2051
- }
2052
- async function upsertDotEnvValue(filePath, key, value) {
2053
- const existing = (await readFileIfExists(filePath)) ?? "";
2054
- const lines = existing ? existing.split(/\r?\n/) : [];
2055
- const assignment = `${key}=${formatDotEnvValue(value)}`;
2056
- let changed = false;
2057
- let replaced = false;
2058
- const nextLines = lines.map((line) => {
2059
- if (new RegExp(`^\\s*${key}\\s*=`).test(line)) {
2060
- replaced = true;
2061
- if (line === assignment)
2062
- return line;
2063
- changed = true;
2064
- return assignment;
2065
- }
2066
- return line;
2067
- });
2068
- if (!replaced) {
2069
- if (nextLines.length > 0 && nextLines[nextLines.length - 1]?.trim()) {
2070
- nextLines.push("");
2071
- }
2072
- nextLines.push(assignment);
2073
- changed = true;
2074
- }
2075
- if (!changed)
2076
- return false;
2077
- await promises_1.default.mkdir(node_path_1.default.dirname(filePath), { recursive: true });
2078
- await promises_1.default.writeFile(filePath, `${nextLines.join("\n").trimEnd()}\n`, "utf8");
2079
- return true;
2080
- }
2081
- async function writeHermesConfig(params) {
2082
- const configPath = hermesConfigPath();
2083
- const envPath = hermesEnvPath();
2084
- const baseUrl = openAiCompatibleProxyUrl(params.backendUrl);
2085
- const selectedModel = params.model.trim() || DEFAULT_HERMES_MODEL;
2086
- const existingRaw = await readFileIfExists(configPath);
2087
- const existingConfig = existingRaw?.trim()
2088
- ? objectRecordOr((0, yaml_1.parseDocument)(existingRaw).toJS(), {})
2089
- : {};
2090
- const nextConfig = { ...existingConfig };
2091
- const modelConfig = objectRecordOr(existingConfig.model, {});
2092
- modelConfig.provider = "custom";
2093
- modelConfig.default = selectedModel;
2094
- modelConfig.base_url = baseUrl;
2095
- modelConfig.api_mode = "codex_responses";
2096
- delete modelConfig.api_key;
2097
- delete modelConfig.context_length;
2098
- nextConfig.model = modelConfig;
2099
- const rawCustomProviders = Array.isArray(existingConfig.custom_providers)
2100
- ? existingConfig.custom_providers
2101
- : [];
2102
- const nextCustomProviders = rawCustomProviders.filter((entry) => {
2103
- if (typeof entry !== "object" || entry === null || Array.isArray(entry))
2104
- return true;
2105
- const record = entry;
2106
- const name = typeof record.name === "string" ? record.name.trim() : "";
2107
- const providerBaseUrl = typeof record.base_url === "string" ? record.base_url.trim() : "";
2108
- const keyEnv = typeof record.key_env === "string" ? record.key_env.trim() : "";
2109
- if (keyEnv === HERMES_KEY_ENV_NAME)
2110
- return false;
2111
- if (name === HERMES_PROVIDER_NAME && providerBaseUrl === baseUrl)
2112
- return false;
2113
- return true;
2114
- });
2115
- nextCustomProviders.push({
2116
- name: HERMES_PROVIDER_NAME,
2117
- base_url: baseUrl,
2118
- key_env: HERMES_KEY_ENV_NAME,
2119
- model: selectedModel,
2120
- api_mode: "codex_responses",
2121
- });
2122
- nextConfig.custom_providers = nextCustomProviders;
2123
- await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
2124
- await promises_1.default.writeFile(configPath, (0, yaml_1.stringify)(nextConfig), "utf8");
2125
- await upsertDotEnvValue(envPath, HERMES_KEY_ENV_NAME, params.apiKey);
2126
- return [configPath, envPath];
2127
- }
2128
- async function resolveModels(backendUrl, apiKey) {
2129
- const { ids, failure } = await fetchBackendModelIds(backendUrl, apiKey);
2130
- const filteredIds = uniqueStrings((ids ?? []).filter(isOpenAiCompatibleSetupModelId));
2131
- const available = new Set(filteredIds);
2132
- let selected = DEFAULT_CODEX_MODEL;
2133
- let note;
2134
- const authRejected = isCredentialRejectedFailure(failure);
2135
- for (const preferred of PREFERRED_MODELS) {
2136
- if (available.has(preferred)) {
2137
- selected = preferred;
2138
- break;
2139
- }
2140
- }
2141
- if (filteredIds.length && !available.has(selected)) {
2142
- selected = filteredIds[0] ?? DEFAULT_CODEX_MODEL;
2143
- note = "No preferred Codex model advertised by backend; selected first available model.";
2144
- }
2145
- else if (!filteredIds.length) {
2146
- note = failure
2147
- ? `Unable to query backend model list (${failure}); defaulted to ${DEFAULT_CODEX_MODEL}.`
2148
- : `Unable to query backend model list; defaulted to ${DEFAULT_CODEX_MODEL}.`;
2149
- }
2150
- const unique = [];
2151
- const pushUnique = (modelId) => {
2152
- if (!modelId || !isOpenAiCompatibleSetupModelId(modelId) || unique.includes(modelId))
2153
- return;
2154
- unique.push(modelId);
2155
- };
2156
- pushUnique(selected);
2157
- if (filteredIds.length > 0) {
2158
- for (const modelId of filteredIds)
2159
- pushUnique(modelId);
2160
- }
2161
- else {
2162
- for (const modelId of DEFAULT_MODELS)
2163
- pushUnique(modelId);
2164
- }
2165
- return {
2166
- model: selected,
2167
- models: unique.map((modelId) => ({ id: modelId, name: modelDisplayName(modelId) || prettyModelId(modelId) })),
2168
- note,
2169
- failure,
2170
- authRejected,
2171
- };
2172
- }
2173
- async function resolveClaudeAccess(backendUrl, apiKey) {
2174
- const { ids, failure } = await fetchClaudeModelIds(backendUrl, apiKey);
2175
- const authRejected = isCredentialRejectedFailure(failure);
2176
- if (!ids?.length) {
2177
- return {
2178
- enabled: false,
2179
- models: [],
2180
- note: failure ? `Claude Code auto-setup skipped (${failure}).` : undefined,
2181
- failure,
2182
- authRejected,
2183
- };
2184
- }
2185
- return {
2186
- enabled: true,
2187
- models: ids,
2188
- failure,
2189
- authRejected,
2190
- };
2191
- }
2192
- async function resolveConfiguredClaudeModels(params) {
2193
- const requestedModels = params.requestedModels
2194
- ?.split(",")
2195
- .map((entry) => entry.trim())
2196
- .filter(Boolean);
2197
- if (!requestedModels?.length || !params.access.enabled) {
2198
- const selectedModels = !requestedModels?.length &&
2199
- params.access.enabled &&
2200
- !params.skipPrompt &&
2201
- process.stdin.isTTY &&
2202
- process.stdout.isTTY &&
2203
- params.access.models.length > 1
2204
- ? await promptForGroupedModelSelection({
2205
- title: "Choose the Claude models to save into Claude-compatible tools",
2206
- models: params.access.models.map((modelId) => ({
2207
- id: modelId,
2208
- name: prettyModelId(modelId),
2209
- })),
2210
- defaultSelectedIds: params.access.models,
2211
- applyLabel: "Apply Claude selection",
2212
- })
2213
- : params.access.models;
2214
- return {
2215
- ...params.access,
2216
- selectedModels,
2217
- };
2218
- }
2219
- const available = new Set(params.access.models);
2220
- for (const modelId of requestedModels) {
2221
- if (!available.has(modelId)) {
2222
- throw new Error(`Requested Claude model "${modelId}" is not advertised by the live Claude endpoint.`);
2223
- }
2224
- }
2225
- return {
2226
- ...params.access,
2227
- selectedModels: requestedModels,
2228
- note: `Configured Claude-compatible clients with ${requestedModels.join(", ")}.`,
2229
- };
2230
- }
2231
- async function writeCodexConfig(params) {
2232
- const configPath = node_path_1.default.join(paths_1.codexDir, "config.toml");
2233
- await promises_1.default.mkdir(paths_1.codexDir, { recursive: true });
2234
- let existing = "";
2235
- try {
2236
- existing = await promises_1.default.readFile(configPath, "utf8");
2237
- }
2238
- catch (error) {
2239
- const err = error;
2240
- if (err.code !== "ENOENT")
2241
- throw error;
2242
- }
2243
- const proxyRoot = publicApiOriginForBackendUrl(params.backendUrl);
2244
- const nativeCodexBaseUrl = `${proxyRoot}${CANONICAL_CODEX_NATIVE_PROXY_SUFFIX}`;
2245
- let next = await restoreManagedCodexFeatureFlags(existing);
2246
- next = await ensureManagedCodexFeatureFlags(next);
2247
- next = removeManagedBlock(next, MANAGED_START, MANAGED_END);
2248
- next = removeProviderTable(next, DEFAULT_PROVIDER_ID);
2249
- next = upsertFirstKeyLine(next, "model_provider", `"${DEFAULT_PROVIDER_ID}"`);
2250
- next = upsertFirstKeyLine(next, "model", `"${params.model}"`);
2251
- next = upsertFirstKeyLine(next, "openai_base_url", `"${nativeCodexBaseUrl}"`);
2252
- next = removeTopLevelKeyLineIf(next, "chatgpt_base_url", isTheClawBayChatgptBaseUrl);
2253
- const managedBlock = appendManagedBlock("", [
2254
- MANAGED_START,
2255
- `[model_providers.${DEFAULT_PROVIDER_ID}]`,
2256
- 'name = "The Claw Bay (OpenAI compatible)"',
2257
- `base_url = "${nativeCodexBaseUrl}"`,
2258
- 'wire_api = "responses"',
2259
- "requires_openai_auth = false",
2260
- "supports_websockets = false",
2261
- `experimental_bearer_token = "${params.apiKey}"`,
2262
- MANAGED_END,
2263
- ]);
2264
- next = appendManagedBlock(next, managedBlock.trimEnd().split("\n"));
2265
- await promises_1.default.writeFile(configPath, next, "utf8");
2266
- return configPath;
2267
- }
2268
- function isManagedContinueModel(model, proxyUrl) {
2269
- if (typeof model !== "object" || model === null || Array.isArray(model))
2270
- return false;
2271
- const candidate = model;
2272
- return (candidate.provider === "openai" &&
2273
- candidate.name === CONTINUE_MODEL_NAME &&
2274
- candidate.apiBase === proxyUrl);
2275
- }
2276
- async function writeContinueConfig(params) {
2277
- const configPath = CONTINUE_CONFIG_PATH;
2278
- await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
2279
- const existingRaw = (await readFileIfExists(configPath)) ?? "";
2280
- const proxyUrl = openAiCompatibleProxyUrl(params.backendUrl);
2281
- const doc = (0, yaml_1.parseDocument)(existingRaw);
2282
- const docRoot = objectRecordOr(doc.toJS(), {});
2283
- const models = Array.isArray(docRoot.models)
2284
- ? docRoot.models.filter((entry) => !isManagedContinueModel(entry, proxyUrl))
2285
- : [];
2286
- models.unshift({
2287
- name: CONTINUE_MODEL_NAME,
2288
- provider: "openai",
2289
- model: params.model.trim() || DEFAULT_CONTINUE_MODEL,
2290
- apiKey: params.apiKey,
2291
- apiBase: proxyUrl,
2292
- roles: ["chat", "edit", "apply"],
2293
- });
2294
- if (typeof docRoot.name !== "string" || !docRoot.name.trim()) {
2295
- doc.set("name", "Local Config");
2296
- }
2297
- if (typeof docRoot.version !== "string" || !docRoot.version.trim()) {
2298
- doc.set("version", "1.0.0");
2299
- }
2300
- doc.set("models", models);
2301
- await promises_1.default.writeFile(configPath, doc.toString(), "utf8");
2302
- return configPath;
2303
- }
2304
- function isManagedClineState(doc) {
2305
- return (doc.planModeApiProvider === "openai" &&
2306
- doc.actModeApiProvider === "openai" &&
2307
- isTheClawBayOpenAiCompatibleBaseUrl(doc.openAiBaseUrl));
2308
- }
2309
- async function writeClineConfig(params) {
2310
- const proxyUrl = openAiCompatibleProxyUrl(params.backendUrl);
2311
- const globalState = await readJsonObjectFile(CLINE_GLOBAL_STATE_PATH);
2312
- const secrets = await readJsonObjectFile(CLINE_SECRETS_PATH);
2313
- const alreadyManaged = isManagedClineState(globalState) && typeof secrets.openAiApiKey === "string";
2314
- if (!alreadyManaged) {
2315
- const snapshot = {
2316
- globalState: {
2317
- planModeApiProvider: "planModeApiProvider" in globalState ? globalState.planModeApiProvider : null,
2318
- actModeApiProvider: "actModeApiProvider" in globalState ? globalState.actModeApiProvider : null,
2319
- openAiBaseUrl: "openAiBaseUrl" in globalState ? globalState.openAiBaseUrl : null,
2320
- planModeOpenAiModelId: "planModeOpenAiModelId" in globalState ? globalState.planModeOpenAiModelId : null,
2321
- actModeOpenAiModelId: "actModeOpenAiModelId" in globalState ? globalState.actModeOpenAiModelId : null,
2322
- },
2323
- secrets: {
2324
- openAiApiKey: typeof secrets.openAiApiKey === "string" ? secrets.openAiApiKey : null,
2325
- },
2326
- };
2327
- await writeJsonObjectFile(CLINE_RESTORE_STATE_PATH, snapshot, 0o600);
2328
- }
2329
- globalState.planModeApiProvider = "openai";
2330
- globalState.actModeApiProvider = "openai";
2331
- globalState.openAiBaseUrl = proxyUrl;
2332
- globalState.planModeOpenAiModelId = params.model.trim() || DEFAULT_CLINE_MODEL;
2333
- globalState.actModeOpenAiModelId = params.model.trim() || DEFAULT_CLINE_MODEL;
2334
- secrets.openAiApiKey = params.apiKey;
2335
- await writeJsonObjectFile(CLINE_GLOBAL_STATE_PATH, globalState);
2336
- await writeJsonObjectFile(CLINE_SECRETS_PATH, secrets, 0o600);
2337
- return [CLINE_GLOBAL_STATE_PATH, CLINE_SECRETS_PATH];
2338
- }
2339
- function buildGsdModels(models) {
2340
- const supportedModelMap = new Map((0, supported_models_1.getSupportedModels)().map((model) => [model.id, model]));
2341
- return models
2342
- .filter((model) => Boolean(model.id))
2343
- .map((model) => {
2344
- const pricing = supportedModelMap.get(model.id);
2345
- return {
2346
- id: model.id,
2347
- name: model.name || model.id,
2348
- reasoning: true,
2349
- input: ["text", "image"],
2350
- cost: {
2351
- input: pricing?.inputPer1M ?? 0,
2352
- output: pricing?.outputPer1M ?? 0,
2353
- cacheRead: pricing?.cachedInputPer1M ?? 0,
2354
- cacheWrite: pricing?.inputPer1M ?? 0,
2355
- },
2356
- contextWindow: modelContextLimit(model.id),
2357
- maxTokens: modelOutputLimit(model.id),
2358
- };
2359
- });
2360
- }
2361
- function normalizeGsdCredential(value) {
2362
- if (typeof value !== "object" || value === null || Array.isArray(value))
2363
- return null;
2364
- return { ...value };
2365
- }
2366
- async function writeGsdConfig(params) {
2367
- const existingModelsRaw = await readFileIfExists(GSD_MODELS_PATH);
2368
- const existingAuthRaw = await readFileIfExists(GSD_AUTH_PATH);
2369
- const existingSettingsRaw = await readFileIfExists(GSD_SETTINGS_PATH);
2370
- const existingSnapshotRaw = await readFileIfExists(GSD_RESTORE_STATE_PATH);
2371
- let modelsDoc = {};
2372
- if (existingModelsRaw?.trim()) {
2373
- try {
2374
- modelsDoc = objectRecordOr(JSON.parse(existingModelsRaw), {});
2375
- }
2376
- catch {
2377
- throw new Error(`invalid JSON in GSD config: ${GSD_MODELS_PATH}`);
2378
- }
2379
- }
2380
- let authDoc = {};
2381
- if (existingAuthRaw?.trim()) {
2382
- try {
2383
- authDoc = objectRecordOr(JSON.parse(existingAuthRaw), {});
2384
- }
2385
- catch {
2386
- throw new Error(`invalid JSON in GSD auth: ${GSD_AUTH_PATH}`);
2387
- }
2388
- }
2389
- let settingsDoc = {};
2390
- if (existingSettingsRaw?.trim()) {
2391
- try {
2392
- settingsDoc = objectRecordOr(JSON.parse(existingSettingsRaw), {});
2393
- }
2394
- catch {
2395
- throw new Error(`invalid JSON in GSD settings: ${GSD_SETTINGS_PATH}`);
2396
- }
2397
- }
2398
- if (!existingSnapshotRaw?.trim()) {
2399
- const providersRoot = objectRecordOr(modelsDoc.providers, {});
2400
- const snapshot = {
2401
- version: 1,
2402
- modelsExisted: existingModelsRaw !== null,
2403
- providerConfig: objectRecordOr(providersRoot[DEFAULT_PROVIDER_ID], {}),
2404
- authExisted: existingAuthRaw !== null,
2405
- authEntry: normalizeGsdCredential(authDoc[DEFAULT_PROVIDER_ID]),
2406
- settingsExisted: existingSettingsRaw !== null,
2407
- defaultProvider: typeof settingsDoc.defaultProvider === "string" ? settingsDoc.defaultProvider : null,
2408
- defaultModel: typeof settingsDoc.defaultModel === "string" ? settingsDoc.defaultModel : null,
2409
- };
2410
- if (Object.keys(snapshot.providerConfig ?? {}).length === 0)
2411
- snapshot.providerConfig = null;
2412
- await writeJsonObjectFile(GSD_RESTORE_STATE_PATH, snapshot, 0o600);
2413
- }
2414
- const providersRoot = objectRecordOr(modelsDoc.providers, {});
2415
- providersRoot[DEFAULT_PROVIDER_ID] = {
2416
- baseUrl: openAiCompatibleProxyUrl(params.backendUrl),
2417
- apiKey: ENV_KEY_NAME,
2418
- api: "openai-responses",
2419
- models: buildGsdModels(params.models),
2420
- };
2421
- modelsDoc.providers = providersRoot;
2422
- authDoc[DEFAULT_PROVIDER_ID] = {
2423
- type: "api_key",
2424
- key: params.apiKey,
2425
- };
2426
- settingsDoc.defaultProvider = DEFAULT_PROVIDER_ID;
2427
- settingsDoc.defaultModel = params.model.trim() || DEFAULT_CODEX_MODEL;
2428
- await writeJsonObjectFile(GSD_MODELS_PATH, modelsDoc);
2429
- await writeJsonObjectFile(GSD_AUTH_PATH, authDoc, 0o600);
2430
- await writeJsonObjectFile(GSD_SETTINGS_PATH, settingsDoc);
2431
- return [GSD_MODELS_PATH, GSD_AUTH_PATH, GSD_SETTINGS_PATH];
2432
- }
2433
- async function writeRooConfig(params) {
2434
- const hosts = await detectExtensionHosts(["rooveterinaryinc.roo-cline-"]);
2435
- if (hosts.length === 0) {
2436
- throw new Error("Roo Code was not detected in a supported editor profile.");
2437
- }
2438
- const existingSnapshotRaw = await readFileIfExists(ROO_SETTINGS_STATE_PATH);
2439
- let existingSnapshot = [];
2440
- if (existingSnapshotRaw?.trim()) {
2441
- try {
2442
- existingSnapshot = JSON.parse(existingSnapshotRaw);
2443
- }
2444
- catch {
2445
- existingSnapshot = [];
2446
- }
2447
- }
2448
- const snapshotByPath = new Map(existingSnapshot.map((entry) => [entry.settingsPath, entry]));
2449
- const managedPaths = [];
2450
- for (const host of hosts) {
2451
- const existed = await pathExists(host.userSettingsPath);
2452
- const settings = await readJsonObjectFile(host.userSettingsPath);
2453
- const currentValue = typeof settings["roo-cline.autoImportSettingsPath"] === "string"
2454
- ? settings["roo-cline.autoImportSettingsPath"]
2455
- : null;
2456
- if (currentValue !== ROO_IMPORT_FILE) {
2457
- snapshotByPath.set(host.userSettingsPath, {
2458
- settingsPath: host.userSettingsPath,
2459
- existed,
2460
- previousValue: currentValue,
2461
- });
2462
- }
2463
- settings["roo-cline.autoImportSettingsPath"] = ROO_IMPORT_FILE;
2464
- await writeJsonObjectFile(host.userSettingsPath, settings);
2465
- managedPaths.push(host.userSettingsPath);
2466
- }
2467
- const providerConfigId = ROO_PROFILE_ID;
2468
- const rooImport = {
2469
- providerProfiles: {
2470
- currentApiConfigName: ROO_PROFILE_NAME,
2471
- apiConfigs: {
2472
- [ROO_PROFILE_NAME]: {
2473
- id: providerConfigId,
2474
- apiProvider: "openai",
2475
- openAiBaseUrl: openAiCompatibleProxyUrl(params.backendUrl),
2476
- openAiApiKey: params.apiKey,
2477
- openAiModelId: params.model.trim() || DEFAULT_ROO_MODEL,
2478
- },
2479
- },
2480
- modeApiConfigs: Object.fromEntries(ROO_MODE_SLUGS.map((slug) => [slug, providerConfigId])),
2481
- },
2482
- };
2483
- await writeJsonObjectFile(ROO_IMPORT_FILE, rooImport, 0o600);
2484
- await writeJsonObjectFile(ROO_SETTINGS_STATE_PATH, Array.from(snapshotByPath.values()), 0o600);
2485
- return [ROO_IMPORT_FILE, ...managedPaths];
2486
- }
2487
- async function writeAiderConfig(params) {
2488
- const existing = (await readFileIfExists(AIDER_CONFIG_PATH)) ?? "";
2489
- const cleaned = removeManagedBlock(existing, AIDER_MANAGED_START, AIDER_MANAGED_END);
2490
- const next = appendManagedBlock(cleaned, [
2491
- AIDER_MANAGED_START,
2492
- `model: ${JSON.stringify(`openai/${params.model.trim() || DEFAULT_AIDER_MODEL}`)}`,
2493
- `openai-api-key: ${JSON.stringify(params.apiKey)}`,
2494
- `openai-api-base: ${JSON.stringify(openAiCompatibleProxyUrl(params.backendUrl))}`,
2495
- "restore-chat-history: true",
2496
- AIDER_MANAGED_END,
2497
- ]);
2498
- await promises_1.default.mkdir(node_path_1.default.dirname(AIDER_CONFIG_PATH), { recursive: true });
2499
- await promises_1.default.writeFile(AIDER_CONFIG_PATH, next, "utf8");
2500
- return AIDER_CONFIG_PATH;
2501
- }
2502
- async function persistApiKeyEnv(params) {
2503
- const envDir = node_path_1.default.dirname(ENV_FILE);
2504
- await promises_1.default.mkdir(envDir, { recursive: true });
2505
- const envLines = [
2506
- "# Generated by theclawbay setup",
2507
- `export ${ENV_KEY_NAME}=${shellQuote(params.apiKey)}`,
2508
- "",
2509
- "# Gemini-compatible applications",
2510
- `export ${GEMINI_ENV_API_KEY_NAME}=${shellQuote(params.apiKey)}`,
2511
- `export ${GOOGLE_ENV_API_KEY_NAME}=${shellQuote(params.apiKey)}`,
2512
- `export ${GOOGLE_GEMINI_BASE_URL_NAME}=${shellQuote(geminiCompatibleProxyBaseUrl(params.backendUrl))}`,
2513
- ];
2514
- if (params.claudeEnabled) {
2515
- 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))}`);
2516
- }
2517
- const envContents = `${envLines.join("\n")}\n`;
2518
- await promises_1.default.writeFile(ENV_FILE, envContents, "utf8");
2519
- await promises_1.default.chmod(ENV_FILE, 0o600);
2520
- const sourceLine = `[ -f "$HOME/.config/theclawbay/env" ] && . "$HOME/.config/theclawbay/env"`;
2521
- const shellRcPaths = [
2522
- ".bashrc",
2523
- ".bash_profile",
2524
- ".bash_login",
2525
- ".zshrc",
2526
- ".zprofile",
2527
- ".zlogin",
2528
- ".profile",
2529
- ].map((name) => node_path_1.default.join(node_os_1.default.homedir(), name));
2530
- const updated = [];
2531
- for (const rcPath of shellRcPaths) {
2532
- let existing = "";
2533
- try {
2534
- existing = await promises_1.default.readFile(rcPath, "utf8");
2535
- }
2536
- catch (error) {
2537
- const err = error;
2538
- if (err.code !== "ENOENT")
2539
- throw error;
2540
- }
2541
- const cleaned = removeManagedBlock(existing, SHELL_START, SHELL_END);
2542
- const withBlock = appendManagedBlock(cleaned, [SHELL_START, sourceLine, SHELL_END]);
2543
- await promises_1.default.writeFile(rcPath, withBlock, "utf8");
2544
- updated.push(rcPath);
2545
- }
2546
- process.env[ENV_KEY_NAME] = params.apiKey;
2547
- process.env[GEMINI_ENV_API_KEY_NAME] = params.apiKey;
2548
- process.env[GOOGLE_ENV_API_KEY_NAME] = params.apiKey;
2549
- process.env[GOOGLE_GEMINI_BASE_URL_NAME] = geminiCompatibleProxyBaseUrl(params.backendUrl);
2550
- if (params.claudeEnabled) {
2551
- process.env[CLAUDE_ENV_API_KEY_NAME] = params.apiKey;
2552
- process.env[CLAUDE_ENV_BASE_URL_NAME] = anthropicCompatibleProxyUrl(params.backendUrl);
2553
- }
2554
- else {
2555
- delete process.env[CLAUDE_ENV_API_KEY_NAME];
2556
- delete process.env[CLAUDE_ENV_BASE_URL_NAME];
2557
- }
2558
- return updated;
2559
- }
2560
- async function persistFishEnv(params) {
2561
- const fishConfPath = node_path_1.default.join(node_os_1.default.homedir(), ".config", "fish", "conf.d", "theclawbay.fish");
2562
- await promises_1.default.mkdir(node_path_1.default.dirname(fishConfPath), { recursive: true });
2563
- const lines = [
2564
- "# Generated by theclawbay setup",
2565
- `set -gx ${ENV_KEY_NAME} ${shellQuote(params.apiKey)}`,
2566
- "",
2567
- "# Gemini-compatible applications",
2568
- `set -gx ${GEMINI_ENV_API_KEY_NAME} ${shellQuote(params.apiKey)}`,
2569
- `set -gx ${GOOGLE_ENV_API_KEY_NAME} ${shellQuote(params.apiKey)}`,
2570
- `set -gx ${GOOGLE_GEMINI_BASE_URL_NAME} ${shellQuote(geminiCompatibleProxyBaseUrl(params.backendUrl))}`,
2571
- ];
2572
- if (params.claudeEnabled) {
2573
- 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))}`);
2574
- }
2575
- await promises_1.default.writeFile(fishConfPath, `${lines.join("\n")}\n`, "utf8");
2576
- return [fishConfPath];
2577
- }
2578
- function powerShellProfilePaths() {
2579
- const candidates = new Set();
2580
- const addDocumentsProfiles = (documentsDir) => {
2581
- if (!documentsDir)
2582
- return;
2583
- candidates.add(node_path_1.default.join(documentsDir, "PowerShell", "profile.ps1"));
2584
- candidates.add(node_path_1.default.join(documentsDir, "PowerShell", "Microsoft.PowerShell_profile.ps1"));
2585
- candidates.add(node_path_1.default.join(documentsDir, "WindowsPowerShell", "profile.ps1"));
2586
- candidates.add(node_path_1.default.join(documentsDir, "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1"));
2587
- };
2588
- if (node_os_1.default.platform() === "win32") {
2589
- addDocumentsProfiles(node_path_1.default.join(node_os_1.default.homedir(), "Documents"));
2590
- addDocumentsProfiles(resolveWindowsDocumentsDirForHost());
2591
- return [...candidates];
2592
- }
2593
- if (!isWslInteropRuntime())
2594
- return [];
2595
- if (!shouldManageWindowsHostProfilesFromWsl())
2596
- return [];
2597
- addDocumentsProfiles(resolveWindowsDocumentsDirForHost());
2598
- addDocumentsProfiles(resolveWindowsPathForHost(node_path_1.default.join(resolveWindowsHomeDirForHost() ?? "", "Documents")));
2599
- return [...candidates];
2600
- }
2601
- function isWslInteropRuntime() {
2602
- return node_os_1.default.platform() === "linux" && Boolean(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);
2603
- }
2604
- function shouldManageWindowsHostProfilesFromWsl() {
2605
- const mode = (process.env.THECLAWBAY_WINDOWS_PROFILE_MODE ?? "").trim().toLowerCase();
2606
- if (mode === "always")
2607
- return true;
2608
- if (mode === "never")
2609
- return false;
2610
- const home = process.env.HOME?.trim() ?? "";
2611
- const user = process.env.USER?.trim() ?? "";
2612
- if (!home || !user)
2613
- return true;
2614
- return home === node_path_1.default.posix.join("/home", user);
2615
- }
2616
- function readWindowsCommandStdout(command) {
2617
- const shell = node_os_1.default.platform() === "win32" ? "powershell.exe" : isWslInteropRuntime() ? "powershell.exe" : null;
2618
- if (!shell)
2619
- return null;
2620
- const result = (0, node_child_process_1.spawnSync)(shell, ["-NoProfile", "-Command", command], {
2621
- encoding: "utf8",
2622
- stdio: ["ignore", "pipe", "ignore"],
2623
- });
2624
- if (result.status !== 0)
2625
- return null;
2626
- const next = result.stdout.replace(/\r/g, "").trim();
2627
- return next ? next : null;
2628
- }
2629
- function resolveWindowsPathForHost(input) {
2630
- if (!input)
2631
- return null;
2632
- const trimmed = input.trim();
2633
- if (!trimmed)
2634
- return null;
2635
- if (node_os_1.default.platform() === "win32")
2636
- return trimmed;
2637
- const match = /^([A-Za-z]):\\(.*)$/.exec(trimmed);
2638
- if (!match)
2639
- return null;
2640
- const drive = match[1].toLowerCase();
2641
- const rest = match[2].replace(/\\/g, "/");
2642
- return node_path_1.default.posix.join("/mnt", drive, rest);
2643
- }
2644
- function resolveWindowsHomeDirForHost() {
2645
- const reported = readWindowsCommandStdout("$env:USERPROFILE");
2646
- if (reported)
2647
- return resolveWindowsPathForHost(reported);
2648
- if (node_os_1.default.platform() === "win32")
2649
- return node_os_1.default.homedir();
2650
- return null;
2651
- }
2652
- function resolveWindowsDocumentsDirForHost() {
2653
- const reported = readWindowsCommandStdout("[Environment]::GetFolderPath('MyDocuments')");
2654
- if (reported)
2655
- return resolveWindowsPathForHost(reported);
2656
- const home = resolveWindowsHomeDirForHost();
2657
- if (!home)
2658
- return null;
2659
- return node_path_1.default.join(home, "Documents");
2660
- }
2661
- async function persistPowerShellEnv(params) {
2662
- const updated = [];
2663
- for (const profilePath of powerShellProfilePaths()) {
2664
- await promises_1.default.mkdir(node_path_1.default.dirname(profilePath), { recursive: true });
2665
- const existing = (await readFileIfExists(profilePath)) ?? "";
2666
- const cleaned = removeManagedBlock(existing, SHELL_START, SHELL_END);
2667
- const lines = [
2668
- SHELL_START,
2669
- `$env:${ENV_KEY_NAME} = ${powerShellQuote(params.apiKey)}`,
2670
- `$env:${GEMINI_ENV_API_KEY_NAME} = ${powerShellQuote(params.apiKey)}`,
2671
- `$env:${GOOGLE_ENV_API_KEY_NAME} = ${powerShellQuote(params.apiKey)}`,
2672
- `$env:${GOOGLE_GEMINI_BASE_URL_NAME} = ${powerShellQuote(geminiCompatibleProxyBaseUrl(params.backendUrl))}`,
2673
- ];
2674
- if (params.claudeEnabled) {
2675
- lines.push(`$env:${CLAUDE_ENV_API_KEY_NAME} = ${powerShellQuote(params.apiKey)}`, `$env:${CLAUDE_ENV_BASE_URL_NAME} = ${powerShellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`);
2676
- }
2677
- lines.push(SHELL_END);
2678
- const next = appendManagedBlock(cleaned, lines);
2679
- await promises_1.default.writeFile(profilePath, next, "utf8");
2680
- updated.push(profilePath);
2681
- }
2682
- return updated;
2683
- }
2684
- async function persistVsCodeServerEnvSource() {
2685
- const homes = [".vscode-server", ".vscode-server-insiders"];
2686
- const sourceLine = `[ -f "$HOME/.config/theclawbay/env" ] && . "$HOME/.config/theclawbay/env"`;
2687
- const updated = [];
2688
- for (const home of homes) {
2689
- const setupPath = node_path_1.default.join(node_os_1.default.homedir(), home, "server-env-setup");
2690
- await promises_1.default.mkdir(node_path_1.default.dirname(setupPath), { recursive: true });
2691
- let existing = "";
2692
- try {
2693
- existing = await promises_1.default.readFile(setupPath, "utf8");
2694
- }
2695
- catch (error) {
2696
- const err = error;
2697
- if (err.code !== "ENOENT")
2698
- throw error;
2699
- }
2700
- let next = existing.trimEnd();
2701
- if (!next.startsWith("#!")) {
2702
- next = `#!/usr/bin/env sh\n${next}`.trimEnd();
2703
- }
2704
- next = `${removeManagedBlock(next + "\n", SHELL_START, SHELL_END).trimEnd()}\n`;
2705
- next = appendManagedBlock(next, [SHELL_START, sourceLine, SHELL_END]);
2706
- await promises_1.default.writeFile(setupPath, next, "utf8");
2707
- await promises_1.default.chmod(setupPath, 0o700);
2708
- updated.push(setupPath);
2709
- }
2710
- return updated;
2711
- }
2712
- function terminalIntegratedEnvSettingsKey() {
2713
- if (node_os_1.default.platform() === "darwin")
2714
- return "terminal.integrated.env.osx";
2715
- if (node_os_1.default.platform() === "win32")
2716
- return "terminal.integrated.env.windows";
2717
- return "terminal.integrated.env.linux";
2718
- }
2719
- function buildManagedEditorTerminalEnv(params) {
2720
- return {
2721
- [ENV_KEY_NAME]: params.apiKey,
2722
- [GEMINI_ENV_API_KEY_NAME]: params.apiKey,
2723
- [GOOGLE_ENV_API_KEY_NAME]: params.apiKey,
2724
- [GOOGLE_GEMINI_BASE_URL_NAME]: geminiCompatibleProxyBaseUrl(params.backendUrl),
2725
- };
2726
- }
2727
- function buildManagedClaudeEditorEnv(params) {
2728
- return {
2729
- [CLAUDE_ENV_API_KEY_NAME]: params.apiKey,
2730
- [CLAUDE_ENV_BASE_URL_NAME]: anthropicCompatibleProxyUrl(params.backendUrl),
2731
- };
2732
- }
2733
- async function persistEditorTerminalEnvSettings(params) {
2734
- const settingsKey = terminalIntegratedEnvSettingsKey();
2735
- const desiredEnv = buildManagedEditorTerminalEnv(params);
2736
- const existingSnapshotRaw = await readFileIfExists(EDITOR_TERMINAL_ENV_STATE_PATH);
2737
- let existingSnapshot = [];
2738
- if (existingSnapshotRaw?.trim()) {
2739
- try {
2740
- existingSnapshot = JSON.parse(existingSnapshotRaw);
2741
- }
2742
- catch {
2743
- existingSnapshot = [];
2744
- }
2745
- }
2746
- const snapshotByPath = new Map(existingSnapshot.map((entry) => [entry.settingsPath, entry]));
2747
- const managedPaths = [];
2748
- for (const host of editorHosts()) {
2749
- const settingsPath = host.userSettingsPath;
2750
- const hostPresent = (await pathExists(host.extensionDir)) ||
2751
- (await pathExists(settingsPath)) ||
2752
- (await pathExists(node_path_1.default.dirname(settingsPath)));
2753
- if (!hostPresent)
2754
- continue;
2755
- const existed = await pathExists(settingsPath);
2756
- const settings = await readJsonObjectFile(settingsPath);
2757
- const currentValue = settings[settingsKey];
2758
- const currentEnv = typeof currentValue === "object" && currentValue !== null && !Array.isArray(currentValue)
2759
- ? { ...currentValue }
2760
- : {};
2761
- let changed = false;
2762
- for (const [name, value] of Object.entries(desiredEnv)) {
2763
- if (currentEnv[name] === value)
2764
- continue;
2765
- currentEnv[name] = value;
2766
- changed = true;
2767
- }
2768
- for (const name of MANAGED_EDITOR_TERMINAL_ENV_NAMES) {
2769
- if (name in desiredEnv || !(name in currentEnv))
2770
- continue;
2771
- delete currentEnv[name];
2772
- changed = true;
2773
- }
2774
- if (!changed)
2775
- continue;
2776
- if (!snapshotByPath.has(settingsPath)) {
2777
- snapshotByPath.set(settingsPath, {
2778
- settingsPath,
2779
- existed,
2780
- hadKey: Object.prototype.hasOwnProperty.call(settings, settingsKey),
2781
- previousValue: currentValue ?? null,
2782
- });
2783
- }
2784
- settings[settingsKey] = currentEnv;
2785
- await writeJsonObjectFile(settingsPath, settings);
2786
- managedPaths.push(settingsPath);
2787
- }
2788
- await writeJsonObjectFile(EDITOR_TERMINAL_ENV_STATE_PATH, Array.from(snapshotByPath.values()), 0o600);
2789
- return managedPaths;
2790
- }
2791
- async function persistClaudeEditorSettings(params) {
2792
- const existingSnapshotRaw = await readFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
2793
- let existingSnapshot = [];
2794
- if (existingSnapshotRaw?.trim()) {
2795
- try {
2796
- existingSnapshot = JSON.parse(existingSnapshotRaw);
2797
- }
2798
- catch {
2799
- existingSnapshot = [];
2800
- }
2801
- }
2802
- const snapshotByPath = new Map(existingSnapshot.map((entry) => [entry.settingsPath, entry]));
2803
- const managedPaths = [];
2804
- const desiredEnv = params.claudeEnabled
2805
- ? buildManagedClaudeEditorEnv({ apiKey: params.apiKey, backendUrl: params.backendUrl })
2806
- : null;
2807
- for (const host of editorHosts()) {
2808
- const settingsPath = host.userSettingsPath;
2809
- const hostPresent = (await pathExists(host.extensionDir)) ||
2810
- (await pathExists(settingsPath)) ||
2811
- (await pathExists(node_path_1.default.dirname(settingsPath)));
2812
- if (!hostPresent)
2813
- continue;
2814
- const existed = await pathExists(settingsPath);
2815
- const settings = await readJsonObjectFile(settingsPath);
2816
- const currentEnvValue = settings[CLAUDE_CODE_EDITOR_ENV_SETTING];
2817
- const currentEnv = typeof currentEnvValue === "object" && currentEnvValue !== null && !Array.isArray(currentEnvValue)
2818
- ? { ...currentEnvValue }
2819
- : {};
2820
- let changed = false;
2821
- if (!snapshotByPath.has(settingsPath)) {
2822
- snapshotByPath.set(settingsPath, {
2823
- settingsPath,
2824
- existed,
2825
- envHadKey: Object.prototype.hasOwnProperty.call(settings, CLAUDE_CODE_EDITOR_ENV_SETTING),
2826
- envPreviousValue: currentEnvValue ?? null,
2827
- disableLoginPromptHadKey: Object.prototype.hasOwnProperty.call(settings, CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING),
2828
- disableLoginPromptPreviousValue: settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] ?? null,
2829
- });
2830
- }
2831
- if (desiredEnv) {
2832
- const nextEnv = { ...currentEnv };
2833
- for (const [name, value] of Object.entries(desiredEnv)) {
2834
- if (nextEnv[name] === value)
2835
- continue;
2836
- nextEnv[name] = value;
2837
- changed = true;
2838
- }
2839
- for (const name of MANAGED_CLAUDE_ENV_NAMES) {
2840
- if (name in desiredEnv || !(name in nextEnv))
2841
- continue;
2842
- delete nextEnv[name];
2843
- changed = true;
2844
- }
2845
- settings[CLAUDE_CODE_EDITOR_ENV_SETTING] = nextEnv;
2846
- if (settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] !== true) {
2847
- settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] = true;
2848
- changed = true;
2849
- }
2850
- }
2851
- else {
2852
- if (Object.prototype.hasOwnProperty.call(settings, CLAUDE_CODE_EDITOR_ENV_SETTING)) {
2853
- delete settings[CLAUDE_CODE_EDITOR_ENV_SETTING];
2854
- changed = true;
2855
- }
2856
- if (Object.prototype.hasOwnProperty.call(settings, CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING)) {
2857
- delete settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING];
2858
- changed = true;
2859
- }
2860
- }
2861
- if (!changed)
2862
- continue;
2863
- await writeJsonObjectFile(settingsPath, settings);
2864
- managedPaths.push(settingsPath);
2865
- }
2866
- await writeJsonObjectFile(CLAUDE_EDITOR_SETTINGS_STATE_PATH, Array.from(snapshotByPath.values()), 0o600);
2867
- return managedPaths;
2868
- }
2869
- async function cleanupClaudeEditorSettings() {
2870
- const snapshotRaw = await readFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
2871
- if (snapshotRaw === null || !snapshotRaw.trim())
2872
- return [];
2873
- let entries = [];
2874
- try {
2875
- entries = JSON.parse(snapshotRaw);
2876
- }
2877
- catch {
2878
- return [];
2879
- }
2880
- const updated = [];
2881
- for (const entry of entries) {
2882
- const settings = await readJsonObjectFile(entry.settingsPath);
2883
- if (!entry.envHadKey) {
2884
- delete settings[CLAUDE_CODE_EDITOR_ENV_SETTING];
2885
- }
2886
- else {
2887
- settings[CLAUDE_CODE_EDITOR_ENV_SETTING] = entry.envPreviousValue;
2888
- }
2889
- if (!entry.disableLoginPromptHadKey) {
2890
- delete settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING];
2891
- }
2892
- else {
2893
- settings[CLAUDE_CODE_EDITOR_DISABLE_LOGIN_PROMPT_SETTING] = entry.disableLoginPromptPreviousValue;
2894
- }
2895
- if (entry.existed || Object.keys(settings).length > 0) {
2896
- await writeJsonObjectFile(entry.settingsPath, settings);
2897
- updated.push(entry.settingsPath);
2898
- continue;
2899
- }
2900
- if (await removeFileIfExists(entry.settingsPath)) {
2901
- updated.push(entry.settingsPath);
2902
- }
2903
- }
2904
- await removeFileIfExists(CLAUDE_EDITOR_SETTINGS_STATE_PATH);
2905
- return updated;
2906
- }
2907
- async function cleanupClaudeDesktop3pConfig() {
2908
- const snapshotRaw = await readFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2909
- if (snapshotRaw === null || !snapshotRaw.trim())
2910
- return false;
2911
- let snapshot = null;
2912
- try {
2913
- snapshot = JSON.parse(snapshotRaw);
2914
- }
2915
- catch {
2916
- snapshot = null;
2917
- }
2918
- if (!snapshot?.configPath) {
2919
- await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2920
- return false;
2921
- }
2922
- if (!snapshot.existed) {
2923
- const removed = await removeFileIfExists(snapshot.configPath);
2924
- await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2925
- return removed;
2926
- }
2927
- const previousRaw = snapshot.previousRaw ?? "";
2928
- const existingRaw = await readFileIfExists(snapshot.configPath);
2929
- if (existingRaw === previousRaw) {
2930
- await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2931
- return false;
2932
- }
2933
- await promises_1.default.mkdir(node_path_1.default.dirname(snapshot.configPath), { recursive: true });
2934
- await promises_1.default.writeFile(snapshot.configPath, previousRaw, "utf8");
2935
- await removeFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
2936
- return true;
2937
- }
2938
- async function cleanupLegacyClaudeEditorTerminalEnvSettings(params) {
2939
- const snapshotRaw = await readFileIfExists(EDITOR_TERMINAL_ENV_STATE_PATH);
2940
- if (snapshotRaw === null || !snapshotRaw.trim())
2941
- return [];
2942
- let entries = [];
2943
- try {
2944
- entries = JSON.parse(snapshotRaw);
2945
- }
2946
- catch {
2947
- return [];
2948
- }
2949
- const settingsKey = terminalIntegratedEnvSettingsKey();
2950
- const managedClaudeEnv = {
2951
- [CLAUDE_ENV_API_KEY_NAME]: params.apiKey,
2952
- [CLAUDE_ENV_BASE_URL_NAME]: anthropicCompatibleProxyUrl(params.backendUrl),
2953
- };
2954
- const updated = [];
2955
- for (const entry of entries) {
2956
- const settings = await readJsonObjectFile(entry.settingsPath);
2957
- const currentValue = settings[settingsKey];
2958
- if (typeof currentValue !== "object" || currentValue === null || Array.isArray(currentValue))
2959
- continue;
2960
- const currentEnv = { ...currentValue };
2961
- const previousEnv = typeof entry.previousValue === "object" &&
2962
- entry.previousValue !== null &&
2963
- !Array.isArray(entry.previousValue)
2964
- ? { ...entry.previousValue }
2965
- : {};
2966
- let changed = false;
2967
- for (const name of MANAGED_CLAUDE_ENV_NAMES) {
2968
- const expectedManagedValue = managedClaudeEnv[name];
2969
- const currentManagedValue = currentEnv[name];
2970
- const currentMatchesManaged = typeof expectedManagedValue === "string" ? currentManagedValue === expectedManagedValue : false;
2971
- if (!currentMatchesManaged)
2972
- continue;
2973
- if (Object.prototype.hasOwnProperty.call(previousEnv, name)) {
2974
- currentEnv[name] = previousEnv[name];
2975
- }
2976
- else {
2977
- delete currentEnv[name];
2978
- }
2979
- changed = true;
2980
- }
2981
- if (!changed)
2982
- continue;
2983
- settings[settingsKey] = currentEnv;
2984
- await writeJsonObjectFile(entry.settingsPath, settings);
2985
- updated.push(entry.settingsPath);
2986
- }
2987
- return updated;
2988
- }
2989
- function expandClaudeDesktopInferenceModels(modelIds) {
2990
- const expanded = [];
2991
- const add = (value) => {
2992
- const normalized = value.trim();
2993
- if (!normalized || expanded.includes(normalized))
2994
- return;
2995
- expanded.push(normalized);
2996
- };
2997
- for (const modelId of modelIds) {
2998
- add(modelId);
2999
- const datedMatch = modelId.match(/^(claude-[a-z]+-\d+(?:-\d+)?)-(\d{8})$/);
3000
- if (datedMatch?.[1])
3001
- add(datedMatch[1]);
3002
- const base = datedMatch?.[1] ?? modelId;
3003
- const dottedMatch = base.match(/^(claude-[a-z]+-\d+)-(\d+)$/);
3004
- if (dottedMatch?.[1] && dottedMatch?.[2]) {
3005
- add(`${dottedMatch[1]}.${dottedMatch[2]}`);
3006
- }
3007
- }
3008
- return expanded;
3009
- }
3010
- async function discoverClaudeDesktopDeploymentOrganizationUuid(existing) {
3011
- const enterpriseConfig = objectRecordOr(existing.enterpriseConfig, {});
3012
- const existingValue = enterpriseConfig.deploymentOrganizationUuid;
3013
- if (typeof existingValue === "string" && existingValue.trim()) {
3014
- return existingValue.trim();
3015
- }
3016
- 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;
3017
- const logPattern = /(deploymentOrganizationUuid|organizationUuid|organization uuid|org uuid)/i;
3018
- for (const logPath of claudeDesktopLogCandidates()) {
3019
- const logRaw = await readFileIfExists(logPath);
3020
- if (!logRaw)
3021
- continue;
3022
- const tail = logRaw.slice(-200000);
3023
- for (const line of tail.split(/\r?\n/).reverse()) {
3024
- if (!logPattern.test(line))
3025
- continue;
3026
- const match = line.match(uuidPattern);
3027
- if (match?.[0])
3028
- return match[0];
3029
- }
3030
- }
3031
- return null;
3032
- }
3033
- async function writeClaudeDesktop3pConfig(params) {
3034
- const configPath = claudeDesktop3pConfigPath();
3035
- if (!configPath || params.claudeModels.length === 0)
3036
- return null;
3037
- const desktopPresent = claudeDesktopAppCandidatePaths().some((candidate) => (0, node_fs_1.existsSync)(candidate)) ||
3038
- (await pathExists(configPath)) ||
3039
- (await pathExists(node_path_1.default.dirname(configPath))) ||
3040
- claudeDesktopLogCandidates().some((candidate) => (0, node_fs_1.existsSync)(candidate));
3041
- if (!desktopPresent)
3042
- return null;
3043
- let existed = true;
3044
- let existingRaw = "";
3045
- try {
3046
- existingRaw = await promises_1.default.readFile(configPath, "utf8");
3047
- }
3048
- catch (error) {
3049
- const err = error;
3050
- if (err.code !== "ENOENT")
3051
- throw error;
3052
- existed = false;
3053
- }
3054
- const existingSnapshotRaw = await readFileIfExists(CLAUDE_DESKTOP_3P_STATE_PATH);
3055
- if (!existingSnapshotRaw?.trim()) {
3056
- const snapshot = {
3057
- version: 1,
3058
- configPath,
3059
- existed,
3060
- previousRaw: existed ? existingRaw : null,
3061
- };
3062
- await writeJsonObjectFile(CLAUDE_DESKTOP_3P_STATE_PATH, snapshot, 0o600);
3063
- }
3064
- let doc = {};
3065
- if (existingRaw.trim()) {
3066
- try {
3067
- doc = objectRecordOr(JSON.parse(existingRaw), {});
3068
- }
3069
- catch {
3070
- throw new Error(`invalid JSON in Claude Desktop config: ${configPath}`);
3071
- }
3072
- }
3073
- const enterpriseConfig = objectRecordOr(doc.enterpriseConfig, {});
3074
- enterpriseConfig.inferenceProvider = "gateway";
3075
- enterpriseConfig.inferenceGatewayBaseUrl = anthropicCompatibleProxyUrl(params.backendUrl);
3076
- enterpriseConfig.inferenceGatewayApiKey = params.apiKey;
3077
- enterpriseConfig.inferenceModels = expandClaudeDesktopInferenceModels(params.claudeModels);
3078
- enterpriseConfig.isDesktopExtensionEnabled = true;
3079
- enterpriseConfig.isDesktopExtensionDirectoryEnabled = true;
3080
- enterpriseConfig.isDesktopExtensionSignatureRequired = false;
3081
- enterpriseConfig.isLocalDevMcpEnabled = true;
3082
- enterpriseConfig.isClaudeCodeForDesktopEnabled = true;
3083
- const deploymentOrganizationUuid = await discoverClaudeDesktopDeploymentOrganizationUuid(doc);
3084
- if (deploymentOrganizationUuid) {
3085
- enterpriseConfig.deploymentOrganizationUuid = deploymentOrganizationUuid;
3086
- }
3087
- doc.enterpriseConfig = enterpriseConfig;
3088
- doc.isUsingBuiltInNodeForMcp = true;
3089
- doc.isDxtAutoUpdatesEnabled = true;
3090
- await writeJsonObjectFile(configPath, doc);
3091
- return configPath;
3092
- }
3093
- function runOpenClawConfigCommand(args) {
3094
- const run = (0, node_child_process_1.spawnSync)("openclaw", args, {
3095
- encoding: "utf8",
3096
- stdio: ["ignore", "pipe", "pipe"],
3097
- });
3098
- if (run.status === 0)
3099
- return;
3100
- const stderr = (run.stderr ?? "").trim();
3101
- const stdout = (run.stdout ?? "").trim();
3102
- const details = stderr || stdout || "openclaw command returned a non-zero exit status";
3103
- throw new Error(`failed to update OpenClaw config: ${details}`);
3104
- }
3105
- function isOpenClawMissingApiKeyEnvError(error) {
3106
- if (!(error instanceof Error))
3107
- return false;
3108
- return (error.message.includes(`Missing env var "${ENV_KEY_NAME}"`) &&
3109
- error.message.includes(`models.providers.${OPENCLAW_PROVIDER_ID}.apiKey`));
3110
- }
3111
- function resolveOpenClawPrimaryModel(params) {
3112
- const hasStableDefault = params.models.some((entry) => entry.id === DEFAULT_OPENCLAW_MODEL);
3113
- const modelId = hasStableDefault ? DEFAULT_OPENCLAW_MODEL : params.model.trim() || DEFAULT_CODEX_MODEL;
3114
- return `${OPENCLAW_PROVIDER_ID}/${modelId}`;
3115
- }
3116
- async function patchOpenClawConfigFile(params) {
3117
- const provider = {
3118
- baseUrl: openAiCompatibleProxyUrl(params.backendUrl),
3119
- apiKey: params.apiKey,
3120
- api: "openai-responses",
3121
- models: buildOpenClawModels(params.models),
3122
- };
3123
- const configPath = node_path_1.default.join(node_os_1.default.homedir(), ".openclaw", "openclaw.json");
3124
- await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
3125
- let existingRaw = "";
3126
- try {
3127
- existingRaw = await promises_1.default.readFile(configPath, "utf8");
3128
- }
3129
- catch (error) {
3130
- const err = error;
3131
- if (err.code !== "ENOENT")
3132
- throw error;
3133
- }
3134
- let doc = {};
3135
- if (existingRaw.trim()) {
3136
- try {
3137
- doc = objectRecordOr(JSON.parse(existingRaw), {});
3138
- }
3139
- catch {
3140
- throw new Error(`invalid JSON in OpenClaw config: ${configPath}`);
3141
- }
3142
- }
3143
- const modelsRoot = objectRecordOr(doc.models, {});
3144
- modelsRoot.mode = "merge";
3145
- const providersRoot = objectRecordOr(modelsRoot.providers, {});
3146
- providersRoot[OPENCLAW_PROVIDER_ID] = provider;
3147
- modelsRoot.providers = providersRoot;
3148
- doc.models = modelsRoot;
3149
- const agentsRoot = objectRecordOr(doc.agents, {});
3150
- const defaultsRoot = objectRecordOr(agentsRoot.defaults, {});
3151
- const modelRoot = objectRecordOr(defaultsRoot.model, {});
3152
- modelRoot.primary = resolveOpenClawPrimaryModel(params);
3153
- if (typeof defaultsRoot.thinkingDefault !== "string") {
3154
- defaultsRoot.thinkingDefault = "medium";
3155
- }
3156
- defaultsRoot.model = modelRoot;
3157
- agentsRoot.defaults = defaultsRoot;
3158
- doc.agents = agentsRoot;
3159
- await promises_1.default.writeFile(configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
3160
- return configPath;
3161
- }
3162
- async function setupOpenClaw(params) {
3163
- const configPath = await patchOpenClawConfigFile(params);
3164
- return { configPath };
3165
- }
3166
- function objectRecordOr(value, fallback) {
3167
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3168
- return { ...value };
3169
- }
3170
- return fallback;
3171
- }
3172
- function modelContextLimit(modelId) {
3173
- if (modelId === "gpt-5.4")
3174
- return 1050000;
3175
- return 272000;
3176
- }
3177
- function modelOutputLimit(modelId) {
3178
- if (modelId === "gpt-5.4")
3179
- return 128000;
3180
- return 65536;
3181
- }
3182
- function buildOpenClawModels(models) {
3183
- const supportedModelMap = new Map((0, supported_models_1.getSupportedModels)().map((model) => [model.id, model]));
3184
- return models
3185
- .filter((model) => Boolean(model.id))
3186
- .map((model) => {
3187
- const pricing = supportedModelMap.get(model.id);
3188
- return {
3189
- id: model.id,
3190
- name: model.name || model.id,
3191
- api: "openai-responses",
3192
- reasoning: true,
3193
- input: ["text", "image"],
3194
- cost: {
3195
- input: pricing?.inputPer1M ?? 0,
3196
- output: pricing?.outputPer1M ?? 0,
3197
- cacheRead: pricing?.cachedInputPer1M ?? 0,
3198
- cacheWrite: pricing?.inputPer1M ?? 0,
3199
- },
3200
- contextWindow: modelContextLimit(model.id),
3201
- maxTokens: modelOutputLimit(model.id),
3202
- };
3203
- });
3204
- }
3205
- function buildOpenCodeModelConfig(model) {
3206
- return {
3207
- name: model.name || model.id,
3208
- reasoning: true,
3209
- tool_call: true,
3210
- attachment: true,
3211
- modalities: {
3212
- input: ["text", "image"],
3213
- output: ["text"],
3214
- },
3215
- limit: {
3216
- context: modelContextLimit(model.id),
3217
- output: modelOutputLimit(model.id),
3218
- },
3219
- };
3220
- }
3221
- function buildOpenCodeModelsObject(models) {
3222
- const result = {};
3223
- for (const model of models) {
3224
- if (!model.id)
3225
- continue;
3226
- result[model.id] = buildOpenCodeModelConfig(model);
3227
- }
3228
- return result;
3229
- }
3230
- function buildClaudeModelSelections(modelIds) {
3231
- return uniqueStrings(modelIds)
3232
- .filter(Boolean)
3233
- .map((modelId) => ({
3234
- id: modelId,
3235
- name: prettyModelId(modelId),
3236
- }));
3237
- }
3238
- function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
3239
- if (typeof snapshot !== "object" || snapshot === null || Array.isArray(snapshot))
3240
- return null;
3241
- const obj = snapshot;
3242
- if ((obj.version === 2 || obj.version === 3) && Array.isArray(obj.targets)) {
3243
- const targets = [];
3244
- for (const entry of obj.targets) {
3245
- if (typeof entry !== "object" || entry === null || Array.isArray(entry))
3246
- continue;
3247
- const candidate = entry;
3248
- const configPath = typeof candidate.configPath === "string" ? candidate.configPath.trim() : "";
3249
- if (!configPath)
3250
- continue;
3251
- targets.push({
3252
- configPath,
3253
- existed: candidate.existed === true,
3254
- openAiProvider: typeof candidate.openAiProvider === "object" &&
3255
- candidate.openAiProvider !== null &&
3256
- !Array.isArray(candidate.openAiProvider)
3257
- ? candidate.openAiProvider
3258
- : null,
3259
- anthropicProvider: typeof candidate.anthropicProvider === "object" &&
3260
- candidate.anthropicProvider !== null &&
3261
- !Array.isArray(candidate.anthropicProvider)
3262
- ? candidate.anthropicProvider
3263
- : null,
3264
- model: typeof candidate.model === "string" ? candidate.model : null,
3265
- schema: typeof candidate.schema === "string" ? candidate.schema : null,
3266
- plugin: "plugin" in candidate
3267
- ? Array.isArray(candidate.plugin)
3268
- ? candidate.plugin
3269
- : null
3270
- : undefined,
3271
- });
3272
- }
3273
- return { version: 3, targets };
3274
- }
3275
- const looksLikeV1 = "openAiProvider" in obj || "model" in obj || "schema" in obj;
3276
- if (!looksLikeV1)
3277
- return null;
3278
- return {
3279
- version: 3,
3280
- targets: [
3281
- {
3282
- configPath: fallbackConfigPath,
3283
- existed: true,
3284
- openAiProvider: typeof obj.openAiProvider === "object" && obj.openAiProvider !== null && !Array.isArray(obj.openAiProvider)
3285
- ? obj.openAiProvider
3286
- : null,
3287
- model: typeof obj.model === "string" ? obj.model : null,
3288
- schema: typeof obj.schema === "string" ? obj.schema : null,
3289
- },
3290
- ],
3291
- };
3292
- }
3293
- async function readOpenCodeRestoreSnapshot(restoreStatePath, fallbackConfigPath) {
3294
- const existingRaw = await readFileIfExists(restoreStatePath);
3295
- if (existingRaw === null || !existingRaw.trim())
3296
- return null;
3297
- try {
3298
- return normalizeOpenCodeRestoreSnapshot(JSON.parse(existingRaw), fallbackConfigPath);
3299
- }
3300
- catch {
3301
- return null;
3302
- }
3303
- }
3304
- function openCodeConfigFileCandidates() {
3305
- const home = node_os_1.default.homedir();
3306
- const dirs = configDirCandidates("opencode");
3307
- const candidates = [];
3308
- for (const dir of dirs) {
3309
- candidates.push(node_path_1.default.join(dir, "opencode.json"));
3310
- candidates.push(node_path_1.default.join(dir, ".opencode.json"));
3311
- candidates.push(node_path_1.default.join(dir, "config.json"));
3312
- }
3313
- candidates.push(node_path_1.default.join(home, ".opencode.json"));
3314
- return uniqueStrings(candidates);
3315
- }
3316
- function findOpenCodeProjectConfigFile() {
3317
- return findProjectConfigFile({
3318
- startDir: process.cwd(),
3319
- basenames: ["opencode.json", "opencode.jsonc", ".opencode.json", ".opencode.jsonc"],
3320
- });
3321
- }
3322
- function isOpenCodeAuthPlugin(value) {
3323
- if (typeof value !== "string")
3324
- return false;
3325
- const normalized = value.trim();
3326
- if (!normalized)
3327
- return false;
3328
- return normalized.startsWith("opencode-openai-codex-auth");
3329
- }
3330
- function filteredOpenCodePlugins(value) {
3331
- if (!Array.isArray(value))
3332
- return { next: value, changed: false };
3333
- const filtered = value.filter((entry) => !isOpenCodeAuthPlugin(entry));
3334
- if (filtered.length === value.length)
3335
- return { next: value, changed: false };
3336
- return { next: filtered, changed: true };
3337
- }
3338
- function kiloConfigFileCandidates() {
3339
- const home = node_os_1.default.homedir();
3340
- const dirs = uniqueStrings([...configDirCandidates("kilo"), node_path_1.default.join(home, ".kilo")]);
3341
- const candidates = [];
3342
- for (const dir of dirs) {
3343
- candidates.push(node_path_1.default.join(dir, "opencode.json"));
3344
- candidates.push(node_path_1.default.join(dir, "config.json"));
3345
- }
3346
- return uniqueStrings(candidates);
3347
- }
3348
- function resolveOpenCodeConfigTargets() {
3349
- const candidates = openCodeConfigFileCandidates();
3350
- const existing = candidates.filter((candidate) => (0, node_fs_1.existsSync)(candidate));
3351
- if (existing.length)
3352
- return existing;
3353
- const primaryDir = configDirCandidates("opencode")[0] ?? node_path_1.default.join(xdgConfigHomeDir(), "opencode");
3354
- return uniqueStrings([node_path_1.default.join(primaryDir, "opencode.json"), node_path_1.default.join(primaryDir, ".opencode.json")]);
3355
- }
3356
- function resolveKiloConfigTargets() {
3357
- const candidates = kiloConfigFileCandidates();
3358
- const existing = candidates.filter((candidate) => (0, node_fs_1.existsSync)(candidate));
3359
- if (existing.length)
3360
- return existing;
3361
- const home = node_os_1.default.homedir();
3362
- const primaryDir = uniqueStrings([...configDirCandidates("kilo"), node_path_1.default.join(home, ".kilo")])[0] ??
3363
- node_path_1.default.join(xdgConfigHomeDir(), "kilo");
3364
- return uniqueStrings([node_path_1.default.join(primaryDir, "opencode.json")]);
3365
- }
3366
- function isManagedOpenCodeProvider(provider, backendUrl) {
3367
- const options = objectRecordOr(provider.options, {});
3368
- if (isTheClawBayOpenAiCompatibleBaseUrl(options.baseURL))
3369
- return true;
3370
- return typeof options.baseURL === "string" && trimTrailingSlash(options.baseURL) === openAiCompatibleProxyUrl(backendUrl);
3371
- }
3372
- function isManagedOpenCodeAnthropicProvider(provider, backendUrl) {
3373
- const options = objectRecordOr(provider.options, {});
3374
- if (isTheClawBayAnthropicCompatibleBaseUrl(options.baseURL))
3375
- return true;
3376
- return typeof options.baseURL === "string" && trimTrailingSlash(options.baseURL) === anthropicCompatibleProxyV1Url(backendUrl);
3377
- }
3378
- function isManagedOpenCodeModel(value, supportedModelIds) {
3379
- if (typeof value !== "string")
3380
- return false;
3381
- if (!value.startsWith(`${OPENAI_PROVIDER_ID}/`))
3382
- return false;
3383
- return supportedModelIds.has(value.slice(`${OPENAI_PROVIDER_ID}/`.length));
3384
- }
3385
- async function writeOpenCodeFamilyConfig(params) {
3386
- const normalizedConfigPaths = uniqueStrings(params.configPaths);
3387
- const fallbackConfigPath = normalizedConfigPaths[0] ?? node_path_1.default.join(xdgConfigHomeDir(), "opencode", "opencode.json");
3388
- const restoreState = (await readOpenCodeRestoreSnapshot(params.restoreStatePath, fallbackConfigPath)) ??
3389
- {
3390
- version: 3,
3391
- targets: [],
3392
- };
3393
- const supportedModelIds = new Set(params.models.map((entry) => entry.id).filter(Boolean));
3394
- const restoreHasTarget = (configPath) => restoreState.targets.some((entry) => entry.configPath === configPath);
3395
- let restoreStateChanged = false;
3396
- const ensureRestorePluginSnapshot = (configPath, plugin) => {
3397
- const target = restoreState.targets.find((entry) => entry.configPath === configPath);
3398
- if (!target)
3399
- return;
3400
- if (Object.prototype.hasOwnProperty.call(target, "plugin"))
3401
- return;
3402
- target.plugin = Array.isArray(plugin) ? plugin : null;
3403
- restoreStateChanged = true;
3404
- };
3405
- for (const configPath of normalizedConfigPaths) {
3406
- await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
3407
- let existed = true;
3408
- let existingRaw = "";
3409
- try {
3410
- existingRaw = await promises_1.default.readFile(configPath, "utf8");
3411
- }
3412
- catch (error) {
3413
- const err = error;
3414
- if (err.code !== "ENOENT")
3415
- throw error;
3416
- existed = false;
3417
- }
3418
- let doc = {};
3419
- if (existingRaw.trim()) {
3420
- try {
3421
- doc = objectRecordOr(JSON.parse(existingRaw), {});
3422
- }
3423
- catch {
3424
- throw new Error(`invalid JSON in config: ${configPath}`);
3425
- }
3426
- }
3427
- const providerRoot = objectRecordOr(doc.provider, {});
3428
- const openAiProvider = objectRecordOr(providerRoot[OPENAI_PROVIDER_ID], {});
3429
- const anthropicProvider = objectRecordOr(providerRoot[ANTHROPIC_PROVIDER_ID], {});
3430
- const alreadyManaged = isManagedOpenCodeProvider(openAiProvider, params.backendUrl) && isManagedOpenCodeModel(doc.model, supportedModelIds);
3431
- if (!alreadyManaged && !restoreHasTarget(configPath)) {
3432
- restoreState.targets.push({
3433
- configPath,
3434
- existed,
3435
- openAiProvider: Object.keys(openAiProvider).length > 0 ? openAiProvider : null,
3436
- anthropicProvider: Object.keys(anthropicProvider).length > 0 ? anthropicProvider : null,
3437
- model: typeof doc.model === "string" && !doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`)
3438
- ? doc.model
3439
- : null,
3440
- schema: typeof doc.$schema === "string" ? doc.$schema : null,
3441
- plugin: Array.isArray(doc.plugin) ? doc.plugin : null,
3442
- });
3443
- restoreStateChanged = true;
3444
- }
3445
- else if (!alreadyManaged) {
3446
- ensureRestorePluginSnapshot(configPath, doc.plugin);
3447
- }
3448
- const pluginFilter = filteredOpenCodePlugins(doc.plugin);
3449
- if (pluginFilter.changed) {
3450
- doc.plugin = pluginFilter.next;
3451
- }
3452
- const managedOpenAiProvider = objectRecordOr(providerRoot[OPENAI_PROVIDER_ID], {});
3453
- const optionsRoot = objectRecordOr(managedOpenAiProvider.options, {});
3454
- optionsRoot.baseURL = openAiCompatibleProxyUrl(params.backendUrl);
3455
- optionsRoot.apiKey = params.apiKey;
3456
- optionsRoot.setCacheKey = true;
3457
- managedOpenAiProvider.options = optionsRoot;
3458
- managedOpenAiProvider.models = buildOpenCodeModelsObject(params.models);
3459
- managedOpenAiProvider.whitelist = params.models.map((entry) => entry.id).filter(Boolean);
3460
- if ("blacklist" in managedOpenAiProvider) {
3461
- delete managedOpenAiProvider.blacklist;
3462
- }
3463
- providerRoot[OPENAI_PROVIDER_ID] = managedOpenAiProvider;
3464
- if ((params.claudeModels?.length ?? 0) > 0) {
3465
- const claudeSelections = buildClaudeModelSelections(params.claudeModels ?? []);
3466
- const managedAnthropicProvider = objectRecordOr(providerRoot[ANTHROPIC_PROVIDER_ID], {});
3467
- const anthropicOptions = objectRecordOr(managedAnthropicProvider.options, {});
3468
- anthropicOptions.baseURL = anthropicCompatibleProxyV1Url(params.backendUrl);
3469
- anthropicOptions.apiKey = params.apiKey;
3470
- anthropicOptions.setCacheKey = true;
3471
- managedAnthropicProvider.options = anthropicOptions;
3472
- managedAnthropicProvider.models = buildOpenCodeModelsObject(claudeSelections);
3473
- managedAnthropicProvider.whitelist = claudeSelections.map((entry) => entry.id);
3474
- if ("blacklist" in managedAnthropicProvider) {
3475
- delete managedAnthropicProvider.blacklist;
3476
- }
3477
- providerRoot[ANTHROPIC_PROVIDER_ID] = managedAnthropicProvider;
3478
- }
3479
- else if (isManagedOpenCodeAnthropicProvider(anthropicProvider, params.backendUrl)) {
3480
- delete providerRoot[ANTHROPIC_PROVIDER_ID];
3481
- }
3482
- if (DEFAULT_PROVIDER_ID in providerRoot) {
3483
- delete providerRoot[DEFAULT_PROVIDER_ID];
3484
- }
3485
- doc.$schema = params.schemaUrl;
3486
- doc.provider = providerRoot;
3487
- doc.model = `${OPENAI_PROVIDER_ID}/${params.model}`;
3488
- await promises_1.default.writeFile(configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
3489
- }
3490
- if (restoreState.targets.length > 0 && restoreStateChanged) {
3491
- await writeJsonObjectFile(params.restoreStatePath, restoreState, 0o600);
3492
- }
3493
- return normalizedConfigPaths;
3494
- }
3495
- async function writeOpenCodeConfig(params) {
3496
- return writeOpenCodeFamilyConfig({
3497
- configPaths: resolveOpenCodeConfigTargets(),
3498
- restoreStatePath: OPENCODE_RESTORE_STATE_PATH,
3499
- schemaUrl: OPENCODE_CONFIG_SCHEMA_URL,
3500
- ...params,
3501
- });
3502
- }
3503
- async function writeKiloConfig(params) {
3504
- return writeOpenCodeFamilyConfig({
3505
- configPaths: resolveKiloConfigTargets(),
3506
- restoreStatePath: KILO_RESTORE_STATE_PATH,
3507
- schemaUrl: KILO_CONFIG_SCHEMA_URL,
3508
- ...params,
3509
- });
3510
- }
3511
- function traePatchSnippet(params) {
3512
- const patchedModels = params.models.length
3513
- ? params.models
3514
- : [{ id: params.model.trim() || DEFAULT_TRAE_MODEL, name: modelDisplayName(params.model.trim() || DEFAULT_TRAE_MODEL) }];
3515
- const proxyBaseUrl = openAiCompatibleProxyUrl(params.backendUrl);
3516
- return `async setOriginModelListMapAndCache(e,t=!0){let{userProfile:r}=this.credentialStore.getState(),i=r?.scope!==v9.BYTEDANCE,n={},o=${JSON.stringify(patchedModels)};Object.keys(e).forEach(t=>{let r=[...(e[t]||[])],s=r[0]&&"object"==typeof r[0]?r[0]:null;if([Mh.Builder,Mh.SoloCoder].includes(t)&&s){let a=o.map((e,l)=>{let o={...s};return o.id=${JSON.stringify(TRAE_PATCH_MARKER)}+"-"+t+"-"+e.id,o.name=e.id,o.display_name=e.name,o.provider="theclawbay",o.icon={dark:"https://theclawbay.com/favicon.ico",light:"https://theclawbay.com/favicon.ico"},o.ak=${JSON.stringify(params.apiKey)},o.base_url=${JSON.stringify(proxyBaseUrl)},o.is_custom_base_url=!0,o.custom_model_id=${JSON.stringify(TRAE_PATCH_MARKER)}+"-"+e.id,o.selectable=!0,o.status=!0,o.is_default=0===l,o.config_source=MK.Personal,o.model_type=s.model_type??"chat_model",o.builder=s.builder??null,o.client_connect=!0,o.multimodal=!1!==s.multimodal,o.tags=Array.isArray(s.tags)?s.tags:[],o.auth_type="number"==typeof s.auth_type?s.auth_type:0,o.region=s.region??null,o.max_turns=s.max_turns??{default:50,max:50},o.context_window_size=s.context_window_size??{max:[128000],default:64000},o.features=s.features??{memory:{enable:!1},cost:{enable:!1,data:{manual_usage:0}},multimodal:{enable:!0},context_windows:{enable:!0,data:{dev_context:64000,max_context:128000,max_context_list:[128000],dev_turns:50,max_turns:50}}},o.commercial_info=s.commercial_info??{manual_usage:0,info:""},o.saas_usage=s.saas_usage??{max:0,default:null},o});n[t]=a;return}!i&&[Mh.SoloCoder,Mh.UIBuilder].includes(t)?n[t]=r?.filter(e=>e.config_source!==MK.Personal)||[]:n[t]=r}),this._modelStore.actions.setOriginModelListMap(n),t&&this._modelStorageService.storeModelListMap(n),this._logService.info("[ModelService.setOriginModelListMapAndCache] officialModels",Object.entries(n).map(([e,t])=>({scene:e,models:(t||[]).map(e=>e.display_name||e.name)})))}`;
3517
- }
3518
- async function patchTraeBundle(params) {
3519
- const bundlePath = traeBundlePath();
3520
- if (!bundlePath)
3521
- throw new Error("Trae experimental support is currently Windows-only.");
3522
- const existing = await promises_1.default.readFile(bundlePath, "utf8");
3523
- const backupPath = traeBundleBackupPath(bundlePath);
3524
- const backupExists = await pathExists(backupPath);
3525
- const isPatched = existing.includes(TRAE_PATCH_MARKER);
3526
- let baseSource = existing;
3527
- if (!isPatched) {
3528
- await promises_1.default.writeFile(backupPath, existing, "utf8");
3529
- }
3530
- else if (backupExists) {
3531
- baseSource = await promises_1.default.readFile(backupPath, "utf8");
3532
- }
3533
- else {
3534
- await promises_1.default.writeFile(backupPath, existing, "utf8");
3535
- }
3536
- const functionStart = baseSource.indexOf(TRAE_TARGET_FUNCTION_START);
3537
- const functionEnd = functionStart >= 0 ? baseSource.indexOf(TRAE_TARGET_FUNCTION_END, functionStart) : -1;
3538
- if (functionStart < 0 || functionEnd < 0) {
3539
- throw new Error("Unsupported Trae build for experimental The Claw Bay patching.");
3540
- }
3541
- const next = baseSource.slice(0, functionStart) +
3542
- traePatchSnippet({
3543
- backendUrl: params.backendUrl,
3544
- apiKey: params.apiKey,
3545
- model: params.model,
3546
- models: params.models,
3547
- }) +
3548
- baseSource.slice(functionEnd + 1);
3549
- await promises_1.default.writeFile(bundlePath, next, "utf8");
3550
- return bundlePath;
3551
- }
3552
- class SetupCommand extends base_command_1.BaseCommand {
3553
- async run() {
3554
- await this.runSafe(async () => {
3555
- const { flags } = await this.parse(SetupCommand);
3556
- const debugOutput = flags["debug-output"];
3557
91
  let managed = null;
3558
92
  try {
3559
93
  managed = await (0, config_1.readManagedConfig)();
@@ -3583,12 +117,12 @@ class SetupCommand extends base_command_1.BaseCommand {
3583
117
  const managedBackendUrl = managed?.backendUrl ?? null;
3584
118
  const backendRaw = flags.backend ??
3585
119
  inferredBackend ??
3586
- (shouldPreferPublicSetupBackend(managedBackendUrl) ? DEFAULT_BACKEND_URL : managedBackendUrl) ??
3587
- DEFAULT_BACKEND_URL;
3588
- const backendUrl = normalizeUrl(backendRaw, "--backend");
120
+ ((0, urls_1.shouldPreferPublicSetupBackend)(managedBackendUrl) ? urls_1.DEFAULT_BACKEND_URL : managedBackendUrl) ??
121
+ urls_1.DEFAULT_BACKEND_URL;
122
+ const backendUrl = (0, urls_1.normalizeUrl)(backendRaw, "--backend");
3589
123
  const reusingLocalManagedCredential = !explicitApiKey &&
3590
124
  !flags.backend &&
3591
- shouldPreferPublicSetupBackend(managedBackendUrl) &&
125
+ (0, urls_1.shouldPreferPublicSetupBackend)(managedBackendUrl) &&
3592
126
  authCredential.length > 0;
3593
127
  if (reusingLocalManagedCredential) {
3594
128
  authType = "device-session";
@@ -3597,18 +131,18 @@ class SetupCommand extends base_command_1.BaseCommand {
3597
131
  deviceLabel = null;
3598
132
  }
3599
133
  const [codexDetected, claudeDetected, continueDetected, clineDetected, gsdDetected, openCodeDetected, kiloDetected, rooDetected, traeDetected, aiderDetected, zoDetected, hermesDetected] = await Promise.all([
3600
- detectCodexClient(),
3601
- detectClaudeClient(),
3602
- detectContinueClient(),
3603
- detectClineClient(),
3604
- detectGsdClient(),
3605
- detectOpenCodeClient(),
3606
- detectKiloClient(),
3607
- detectRooClient(),
3608
- detectTraeClient(),
3609
- detectAiderClient(),
3610
- detectZoClient(),
3611
- detectHermesClient(),
134
+ (0, codex_1.detectCodexClient)(),
135
+ (0, claude_1.detectClaudeClient)(),
136
+ (0, continue_1.detectContinueClient)(),
137
+ (0, cline_1.detectClineClient)(),
138
+ (0, gsd_1.detectGsdClient)(),
139
+ (0, opencode_family_1.detectOpenCodeClient)(),
140
+ (0, opencode_family_1.detectKiloClient)(),
141
+ (0, roo_1.detectRooClient)(),
142
+ (0, trae_1.detectTraeClient)(),
143
+ (0, aider_1.detectAiderClient)(),
144
+ (0, zo_1.detectZoClient)(),
145
+ (0, hermes_1.detectHermesClient)(),
3612
146
  ]);
3613
147
  const setupClients = [
3614
148
  {
@@ -3665,7 +199,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3665
199
  id: "openclaw",
3666
200
  label: "OpenClaw",
3667
201
  summaryLabel: "OpenClaw",
3668
- detected: hasCommand("openclaw"),
202
+ detected: (0, platform_1.hasCommand)("openclaw"),
3669
203
  recommended: true,
3670
204
  installable: false,
3671
205
  icon: "🦞",
@@ -3742,17 +276,17 @@ class SetupCommand extends base_command_1.BaseCommand {
3742
276
  siteUrl: "https://hermes-agent.nousresearch.com/docs/",
3743
277
  },
3744
278
  ];
3745
- const selectedSetupClients = await resolveSetupClientSelection({
279
+ const selectedSetupClients = await (0, prompts_1.resolveSetupClientSelection)({
3746
280
  setupClients,
3747
- flagSelection: parseSetupClientFlags(flags.clients),
281
+ flagSelection: (0, prompts_1.parseSetupClientFlags)(flags.clients),
3748
282
  skipPrompt: flags.yes,
3749
283
  });
3750
- const installedMissingClients = installSelectedMissingClients({
284
+ const installedMissingClients = (0, client_registry_1.installSelectedMissingClients)({
3751
285
  setupClients,
3752
286
  selectedClientIds: selectedSetupClients,
3753
287
  log: (message) => this.log(message),
3754
288
  });
3755
- const migrateCodexConversations = await resolveCodexConversationMigrationSelection({
289
+ const migrateCodexConversations = await (0, codex_1.resolveCodexConversationMigrationSelection)({
3756
290
  codexSelected: selectedSetupClients.has("codex"),
3757
291
  flagValue: flags["migrate-conversations"],
3758
292
  skipPrompt: flags.yes,
@@ -3761,7 +295,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3761
295
  throw new Error("--migrate-conversations requires Codex to be selected for setup.");
3762
296
  }
3763
297
  const linkFreshDeviceSession = async () => {
3764
- deviceLabel = await resolveDeviceLabel({
298
+ deviceLabel = await (0, backend_1.resolveDeviceLabel)({
3765
299
  flagValue: flags["device-name"],
3766
300
  managedValue: managed?.deviceLabel,
3767
301
  skipPrompt: flags.yes,
@@ -3781,8 +315,8 @@ class SetupCommand extends base_command_1.BaseCommand {
3781
315
  await linkFreshDeviceSession();
3782
316
  }
3783
317
  else if (!explicitApiKey && managedAuthType === "device-session") {
3784
- const managedProbe = await fetchBackendModelIds(backendUrl, authCredential);
3785
- if (isCredentialRejectedFailure(managedProbe.failure)) {
318
+ const managedProbe = await (0, backend_1.fetchBackendModelIds)(backendUrl, authCredential);
319
+ if ((0, backend_1.isCredentialRejectedFailure)(managedProbe.failure)) {
3786
320
  this.log(`Saved device-session credential was rejected by the backend (${managedProbe.failure}). Re-linking this machine now.`);
3787
321
  await linkFreshDeviceSession();
3788
322
  }
@@ -3820,12 +354,12 @@ class SetupCommand extends base_command_1.BaseCommand {
3820
354
  try {
3821
355
  if (selectedSetupClients.size > 0) {
3822
356
  progress.update("Resolving supported models");
3823
- resolved = await resolveModels(backendUrl, authCredential);
357
+ resolved = await (0, backend_1.resolveModels)(backendUrl, authCredential);
3824
358
  if (resolved.authRejected) {
3825
- throw new Error(describeCredentialRejection(authType, resolved.failure));
359
+ throw new Error((0, backend_1.describeCredentialRejection)(authType, resolved.failure));
3826
360
  }
3827
361
  progress.stop();
3828
- const configuredModel = await resolveConfiguredModelSelection({
362
+ const configuredModel = await (0, prompts_1.resolveConfiguredModelSelection)({
3829
363
  resolved,
3830
364
  requestedModel: flags.model,
3831
365
  requestedModels: flags.models,
@@ -3839,12 +373,12 @@ class SetupCommand extends base_command_1.BaseCommand {
3839
373
  };
3840
374
  }
3841
375
  progress.update("Checking Claude access");
3842
- claudeAccess = await resolveClaudeAccess(backendUrl, authCredential);
376
+ claudeAccess = await (0, backend_1.resolveClaudeAccess)(backendUrl, authCredential);
3843
377
  if (!resolved?.authRejected && claudeAccess.authRejected) {
3844
- throw new Error(describeCredentialRejection(authType, claudeAccess.failure));
378
+ throw new Error((0, backend_1.describeCredentialRejection)(authType, claudeAccess.failure));
3845
379
  }
3846
380
  progress.stop();
3847
- claudeAccess = await resolveConfiguredClaudeModels({
381
+ claudeAccess = await (0, backend_1.resolveConfiguredClaudeModels)({
3848
382
  access: claudeAccess,
3849
383
  requestedModels: flags["claude-models"],
3850
384
  skipPrompt: flags.yes,
@@ -3852,7 +386,7 @@ class SetupCommand extends base_command_1.BaseCommand {
3852
386
  if (flags["list-models"]) {
3853
387
  if (resolved) {
3854
388
  this.log("\nOpenAI-compatible models:");
3855
- for (const section of groupModelSections(resolved.models)) {
389
+ for (const section of (0, models_1.groupModelSections)(resolved.models)) {
3856
390
  this.log(`${section.provider.icon} ${section.provider.label}`);
3857
391
  for (const model of section.models) {
3858
392
  const marker = model.id === resolved.model ? " (default)" : "";
@@ -3883,69 +417,69 @@ class SetupCommand extends base_command_1.BaseCommand {
3883
417
  deviceSessionId,
3884
418
  deviceLabel,
3885
419
  });
3886
- updatedShellFiles = await persistApiKeyEnv({
420
+ updatedShellFiles = await (0, env_persist_1.persistApiKeyEnv)({
3887
421
  apiKey: authCredential,
3888
422
  backendUrl,
3889
423
  claudeEnabled: claudeEnvEnabled,
3890
424
  });
3891
- updatedFishFiles = await persistFishEnv({
425
+ updatedFishFiles = await (0, env_persist_1.persistFishEnv)({
3892
426
  apiKey: authCredential,
3893
427
  backendUrl,
3894
428
  claudeEnabled: claudeEnvEnabled,
3895
429
  });
3896
- updatedPowerShellProfiles = await persistPowerShellEnv({
430
+ updatedPowerShellProfiles = await (0, env_persist_1.persistPowerShellEnv)({
3897
431
  apiKey: authCredential,
3898
432
  backendUrl,
3899
433
  claudeEnabled: claudeEnvEnabled,
3900
434
  });
3901
435
  if (selectedSetupClients.has("codex") || selectedSetupClients.has("claude")) {
3902
- updatedVsCodeEnvFiles = await persistVsCodeServerEnvSource();
3903
- updatedEditorTerminalSettings = await persistEditorTerminalEnvSettings({
436
+ updatedVsCodeEnvFiles = await (0, env_persist_1.persistVsCodeServerEnvSource)();
437
+ updatedEditorTerminalSettings = await (0, editor_env_1.persistEditorTerminalEnvSettings)({
3904
438
  apiKey: authCredential,
3905
439
  backendUrl,
3906
440
  claudeEnabled: claudeEnvEnabled,
3907
441
  });
3908
442
  }
3909
443
  if (selectedSetupClients.has("claude")) {
3910
- updatedClaudeEditorSettings = await persistClaudeEditorSettings({
444
+ updatedClaudeEditorSettings = await (0, claude_1.persistClaudeEditorSettings)({
3911
445
  apiKey: authCredential,
3912
446
  backendUrl,
3913
447
  claudeEnabled: claudeEnvEnabled,
3914
448
  });
3915
449
  if (claudeEnvEnabled) {
3916
- claudeDesktop3pConfigPathManaged = await writeClaudeDesktop3pConfig({
450
+ claudeDesktop3pConfigPathManaged = await (0, claude_1.writeClaudeDesktop3pConfig)({
3917
451
  backendUrl,
3918
452
  apiKey: authCredential,
3919
453
  claudeModels: claudeAccess?.enabled ? (claudeAccess.selectedModels ?? claudeAccess.models) : [],
3920
454
  });
3921
455
  }
3922
456
  else {
3923
- restoredClaudeDesktop3pConfig = await cleanupClaudeDesktop3pConfig();
457
+ restoredClaudeDesktop3pConfig = await (0, claude_1.cleanupClaudeDesktop3pConfig)();
3924
458
  }
3925
459
  }
3926
460
  else {
3927
461
  updatedEditorTerminalSettings = [
3928
462
  ...updatedEditorTerminalSettings,
3929
- ...await cleanupLegacyClaudeEditorTerminalEnvSettings({
463
+ ...await (0, claude_1.cleanupLegacyClaudeEditorTerminalEnvSettings)({
3930
464
  apiKey: authCredential,
3931
465
  backendUrl,
3932
466
  }),
3933
467
  ];
3934
- updatedClaudeEditorSettings = await cleanupClaudeEditorSettings();
3935
- restoredClaudeDesktop3pConfig = await cleanupClaudeDesktop3pConfig();
468
+ updatedClaudeEditorSettings = await (0, claude_1.cleanupClaudeEditorSettings)();
469
+ restoredClaudeDesktop3pConfig = await (0, claude_1.cleanupClaudeDesktop3pConfig)();
3936
470
  }
3937
471
  if (selectedSetupClients.has("codex")) {
3938
472
  progress.update("Configuring Codex");
3939
- codexConfigPath = await writeCodexConfig({
473
+ codexConfigPath = await (0, codex_1.writeCodexConfig)({
3940
474
  backendUrl,
3941
- model: resolved?.model ?? DEFAULT_CODEX_MODEL,
475
+ model: resolved?.model ?? models_1.DEFAULT_CODEX_MODEL,
3942
476
  apiKey: authCredential,
3943
477
  });
3944
478
  if (migrateCodexConversations) {
3945
479
  sessionMigration = await (0, codex_history_migration_1.migrateSessionProviders)({
3946
480
  codexHome: paths_1.codexDir,
3947
- migrationStateFile: MIGRATION_STATE_FILE,
3948
- neutralizeSources: HISTORY_PROVIDER_NEUTRALIZE_SOURCES,
481
+ migrationStateFile: codex_1.MIGRATION_STATE_FILE,
482
+ neutralizeSources: codex_1.HISTORY_PROVIDER_NEUTRALIZE_SOURCES,
3949
483
  });
3950
484
  }
3951
485
  authSeedCleanup = await (0, codex_auth_seeding_1.cleanupSeededCodexAuth)({
@@ -3954,8 +488,8 @@ class SetupCommand extends base_command_1.BaseCommand {
3954
488
  if (migrateCodexConversations) {
3955
489
  stateDbMigration = await (0, codex_history_migration_1.migrateStateDbProviders)({
3956
490
  codexHome: paths_1.codexDir,
3957
- targetProvider: DEFAULT_PROVIDER_ID,
3958
- sourceProviders: HISTORY_PROVIDER_DB_MIGRATE_SOURCES,
491
+ targetProvider: providers_1.DEFAULT_PROVIDER_ID,
492
+ sourceProviders: codex_1.HISTORY_PROVIDER_DB_MIGRATE_SOURCES,
3959
493
  });
3960
494
  }
3961
495
  modelCacheMigration = await (0, codex_model_cache_migration_1.ensureCodexModelCacheHasGpt54)({
@@ -3964,35 +498,36 @@ class SetupCommand extends base_command_1.BaseCommand {
3964
498
  }
3965
499
  if (selectedSetupClients.has("continue")) {
3966
500
  progress.update("Configuring Continue");
3967
- continueConfigPath = await writeContinueConfig({
501
+ continueConfigPath = await (0, continue_1.writeContinueConfig)({
3968
502
  backendUrl,
3969
- model: resolved?.model ?? DEFAULT_CONTINUE_MODEL,
503
+ model: resolved?.model ?? continue_1.DEFAULT_CONTINUE_MODEL,
504
+ models: resolved?.models ?? [],
3970
505
  apiKey: authCredential,
3971
506
  });
3972
507
  }
3973
508
  if (selectedSetupClients.has("cline")) {
3974
509
  progress.update("Configuring Cline");
3975
- clineConfigPaths = await writeClineConfig({
510
+ clineConfigPaths = await (0, cline_1.writeClineConfig)({
3976
511
  backendUrl,
3977
- model: resolved?.model ?? DEFAULT_CLINE_MODEL,
512
+ model: resolved?.model ?? cline_1.DEFAULT_CLINE_MODEL,
3978
513
  apiKey: authCredential,
3979
514
  });
3980
515
  }
3981
516
  if (selectedSetupClients.has("gsd")) {
3982
517
  progress.update("Configuring GSD");
3983
- gsdConfigPaths = await writeGsdConfig({
518
+ gsdConfigPaths = await (0, gsd_1.writeGsdConfig)({
3984
519
  backendUrl,
3985
- model: resolved?.model ?? DEFAULT_CODEX_MODEL,
3986
- models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
520
+ model: resolved?.model ?? models_1.DEFAULT_CODEX_MODEL,
521
+ models: resolved?.models ?? [{ id: models_1.DEFAULT_CODEX_MODEL, name: (0, models_1.modelDisplayName)(models_1.DEFAULT_CODEX_MODEL) }],
3987
522
  apiKey: authCredential,
3988
523
  });
3989
524
  }
3990
525
  if (selectedSetupClients.has("openclaw")) {
3991
526
  progress.update("Configuring OpenClaw");
3992
- const openClawResult = await setupOpenClaw({
527
+ const openClawResult = await (0, openclaw_1.setupOpenClaw)({
3993
528
  backendUrl,
3994
- model: resolved?.model ?? DEFAULT_CODEX_MODEL,
3995
- models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
529
+ model: resolved?.model ?? models_1.DEFAULT_CODEX_MODEL,
530
+ models: resolved?.models ?? [{ id: models_1.DEFAULT_CODEX_MODEL, name: (0, models_1.modelDisplayName)(models_1.DEFAULT_CODEX_MODEL) }],
3996
531
  apiKey: authCredential,
3997
532
  });
3998
533
  openClawConfigPath = openClawResult.configPath;
@@ -4000,62 +535,66 @@ class SetupCommand extends base_command_1.BaseCommand {
4000
535
  }
4001
536
  if (selectedSetupClients.has("opencode")) {
4002
537
  progress.update("Configuring OpenCode");
4003
- openCodeConfigPaths = await writeOpenCodeConfig({
538
+ openCodeConfigPaths = await (0, opencode_family_1.writeOpenCodeConfig)({
4004
539
  backendUrl,
4005
- model: resolved?.model ?? DEFAULT_CODEX_MODEL,
4006
- models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
540
+ model: resolved?.model ?? models_1.DEFAULT_CODEX_MODEL,
541
+ models: resolved?.models ?? [{ id: models_1.DEFAULT_CODEX_MODEL, name: (0, models_1.modelDisplayName)(models_1.DEFAULT_CODEX_MODEL) }],
4007
542
  claudeModels: claudeAccess?.enabled ? (claudeAccess.selectedModels ?? claudeAccess.models) : [],
4008
543
  apiKey: authCredential,
4009
544
  });
4010
545
  }
4011
546
  if (selectedSetupClients.has("kilo")) {
4012
547
  progress.update("Configuring Kilo Code");
4013
- kiloConfigPaths = await writeKiloConfig({
548
+ kiloConfigPaths = await (0, opencode_family_1.writeKiloConfig)({
4014
549
  backendUrl,
4015
- model: resolved?.model ?? DEFAULT_CODEX_MODEL,
4016
- models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
550
+ model: resolved?.model ?? models_1.DEFAULT_CODEX_MODEL,
551
+ models: resolved?.models ?? [{ id: models_1.DEFAULT_CODEX_MODEL, name: (0, models_1.modelDisplayName)(models_1.DEFAULT_CODEX_MODEL) }],
4017
552
  claudeModels: claudeAccess?.enabled ? (claudeAccess.selectedModels ?? claudeAccess.models) : [],
4018
553
  apiKey: authCredential,
4019
554
  });
4020
555
  }
4021
556
  if (selectedSetupClients.has("roo")) {
4022
557
  progress.update("Configuring Roo Code");
4023
- rooConfigPaths = await writeRooConfig({
558
+ rooConfigPaths = await (0, roo_1.writeRooConfig)({
4024
559
  backendUrl,
4025
- model: resolved?.model ?? DEFAULT_ROO_MODEL,
560
+ model: resolved?.model ?? roo_1.DEFAULT_ROO_MODEL,
561
+ models: resolved?.models ?? [],
4026
562
  apiKey: authCredential,
4027
563
  });
4028
564
  }
4029
565
  if (selectedSetupClients.has("trae")) {
4030
566
  progress.update("Configuring Trae");
4031
- traeBundlePathPatched = await patchTraeBundle({
567
+ traeBundlePathPatched = await (0, trae_1.patchTraeBundle)({
4032
568
  backendUrl,
4033
- model: resolved?.model ?? DEFAULT_CODEX_MODEL,
569
+ model: resolved?.model ?? models_1.DEFAULT_CODEX_MODEL,
4034
570
  apiKey: authCredential,
4035
- models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
571
+ models: resolved?.models ?? [{ id: models_1.DEFAULT_CODEX_MODEL, name: (0, models_1.modelDisplayName)(models_1.DEFAULT_CODEX_MODEL) }],
4036
572
  });
4037
573
  }
4038
574
  if (selectedSetupClients.has("aider")) {
4039
575
  progress.update("Configuring Aider");
4040
- aiderConfigPath = await writeAiderConfig({
576
+ aiderConfigPath = await (0, aider_1.writeAiderConfig)({
4041
577
  backendUrl,
4042
- model: resolved?.model ?? DEFAULT_AIDER_MODEL,
578
+ model: resolved?.model ?? aider_1.DEFAULT_AIDER_MODEL,
579
+ models: resolved?.models ?? [],
4043
580
  apiKey: authCredential,
4044
581
  });
4045
582
  }
4046
583
  if (selectedSetupClients.has("zo")) {
4047
584
  progress.update("Configuring Zo");
4048
- zoConfigName = await configureZoByok({
585
+ zoConfigName = await (0, zo_1.configureZoByok)({
4049
586
  backendUrl,
4050
- model: resolved?.model ?? DEFAULT_ZO_MODEL,
587
+ model: resolved?.model ?? zo_1.DEFAULT_ZO_MODEL,
588
+ models: resolved?.models ?? [],
4051
589
  apiKey: authCredential,
4052
590
  });
4053
591
  }
4054
592
  if (selectedSetupClients.has("hermes")) {
4055
593
  progress.update("Configuring Hermes Agent");
4056
- hermesConfigPaths = await writeHermesConfig({
594
+ hermesConfigPaths = await (0, hermes_1.writeHermesConfig)({
4057
595
  backendUrl,
4058
- model: resolved?.model ?? DEFAULT_HERMES_MODEL,
596
+ model: resolved?.model ?? hermes_1.DEFAULT_HERMES_MODEL,
597
+ models: resolved?.models ?? [],
4059
598
  apiKey: authCredential,
4060
599
  });
4061
600
  }
@@ -4149,7 +688,7 @@ class SetupCommand extends base_command_1.BaseCommand {
4149
688
  if (resolved?.note)
4150
689
  this.log(resolved.note);
4151
690
  if (selectedSetupClients.has("claude") && claudeAccess?.enabled) {
4152
- this.log(`- Claude Code base URL: ${anthropicCompatibleProxyUrl(backendUrl)}`);
691
+ this.log(`- Claude Code base URL: ${(0, urls_1.anthropicCompatibleProxyUrl)(backendUrl)}`);
4153
692
  if (claudeDesktop3pConfigPathManaged) {
4154
693
  this.log(`- Claude Desktop 3P config: ${claudeDesktop3pConfigPathManaged}`);
4155
694
  }
@@ -4157,7 +696,7 @@ class SetupCommand extends base_command_1.BaseCommand {
4157
696
  else if (selectedSetupClients.has("claude") && claudeAccess?.note) {
4158
697
  this.log(`- Claude Code: ${claudeAccess.note}`);
4159
698
  }
4160
- this.log(`- Local credential env: ${ENV_FILE}`);
699
+ this.log(`- Local credential env: ${env_persist_1.ENV_FILE}`);
4161
700
  this.log(`- Shell profiles updated: ${updatedShellFiles.join(", ")}`);
4162
701
  if (updatedFishFiles.length > 0) {
4163
702
  this.log(`- Fish profiles updated: ${updatedFishFiles.join(", ")}`);
@@ -4287,7 +826,7 @@ class SetupCommand extends base_command_1.BaseCommand {
4287
826
  this.log(`- OpenCode: configured (${openCodeConfigPaths.join(", ")})`);
4288
827
  this.log("- OpenCode note: configured the OpenAI-compatible /v1 route and, when Claude access is available, the Anthropic-compatible /anthropic/v1 route.");
4289
828
  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.");
4290
- const openCodeProjectConfig = findOpenCodeProjectConfigFile();
829
+ const openCodeProjectConfig = (0, opencode_family_1.findOpenCodeProjectConfigFile)();
4291
830
  const openCodeConfigEnv = process.env.OPENCODE_CONFIG?.trim() || "";
4292
831
  if (openCodeConfigEnv) {
4293
832
  this.log(`- OpenCode note: OPENCODE_CONFIG is set (${openCodeConfigEnv}). OpenCode may load that file and override your global config.`);