theclawbay 0.3.36 → 0.3.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/commands/link.js +1 -1
- package/dist/commands/logout.js +5 -5
- package/dist/commands/setup.js +267 -43
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# theclawbay
|
|
2
2
|
|
|
3
|
-
CLI for connecting Codex, Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Aider, and experimental
|
|
3
|
+
CLI for connecting Codex, Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Aider, experimental Trae, and experimental Zo to The Claw Bay.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -16,8 +16,8 @@ Get your API key from `https://theclawbay.com/dashboard`.
|
|
|
16
16
|
theclawbay setup --api-key <apiKey>
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
In an interactive terminal, setup shows one picker for Codex, Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Aider,
|
|
20
|
-
Each row includes a terminal-friendly icon plus the tool's official site, and you can toggle items with arrow keys plus `Enter` or by pressing
|
|
19
|
+
In an interactive terminal, setup shows one picker for Codex, Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Aider, Windows Trae, and Zo.
|
|
20
|
+
Each row includes a terminal-friendly icon plus the tool's official site, and you can toggle items with arrow keys plus `Enter` or by pressing `1-9` and `0`.
|
|
21
21
|
Re-running setup also migrates legacy OpenCode and Kilo patches to the current reasoning-capable provider path and refreshes OpenClaw model metadata.
|
|
22
22
|
|
|
23
23
|
## Optional
|
package/dist/commands/link.js
CHANGED
|
@@ -20,7 +20,7 @@ class LinkCommand extends base_command_1.BaseCommand {
|
|
|
20
20
|
});
|
|
21
21
|
this.log(`Linked. Managed config written to ${paths_1.managedConfigPath}`);
|
|
22
22
|
this.log(`Backend: ${backendUrl}`);
|
|
23
|
-
this.log('Run "theclawbay setup" to configure Codex and any detected OpenClaw, OpenCode, Kilo, or experimental
|
|
23
|
+
this.log('Run "theclawbay setup" to configure Codex and any detected OpenClaw, OpenCode, Kilo, experimental Trae, or experimental Zo installs on this machine.');
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
}
|
package/dist/commands/logout.js
CHANGED
|
@@ -12,8 +12,8 @@ const base_command_1 = require("../lib/base-command");
|
|
|
12
12
|
const codex_auth_seeding_1 = require("../lib/codex-auth-seeding");
|
|
13
13
|
const codex_history_migration_1 = require("../lib/codex-history-migration");
|
|
14
14
|
const codex_model_cache_migration_1 = require("../lib/codex-model-cache-migration");
|
|
15
|
-
const paths_1 = require("../lib/config/paths");
|
|
16
15
|
const supported_models_1 = require("../lib/supported-models");
|
|
16
|
+
const paths_1 = require("../lib/config/paths");
|
|
17
17
|
const OPENAI_PROVIDER_ID = "openai";
|
|
18
18
|
const DEFAULT_PROVIDER_ID = "theclawbay";
|
|
19
19
|
const WAN_PROVIDER_ID = "theclawbay-wan";
|
|
@@ -516,7 +516,9 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
|
|
|
516
516
|
targets.push({
|
|
517
517
|
configPath,
|
|
518
518
|
existed: candidate.existed === true,
|
|
519
|
-
openAiProvider: typeof candidate.openAiProvider === "object" &&
|
|
519
|
+
openAiProvider: typeof candidate.openAiProvider === "object" &&
|
|
520
|
+
candidate.openAiProvider !== null &&
|
|
521
|
+
!Array.isArray(candidate.openAiProvider)
|
|
520
522
|
? candidate.openAiProvider
|
|
521
523
|
: null,
|
|
522
524
|
model: typeof candidate.model === "string" ? candidate.model : null,
|
|
@@ -544,7 +546,6 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
|
|
|
544
546
|
: null,
|
|
545
547
|
model: typeof obj.model === "string" ? obj.model : null,
|
|
546
548
|
schema: typeof obj.schema === "string" ? obj.schema : null,
|
|
547
|
-
plugin: undefined,
|
|
548
549
|
},
|
|
549
550
|
],
|
|
550
551
|
};
|
|
@@ -607,8 +608,7 @@ async function cleanupOpenCodeFamilyConfigs(params) {
|
|
|
607
608
|
}
|
|
608
609
|
doc.provider = providerRoot;
|
|
609
610
|
if (target.model === null) {
|
|
610
|
-
if (isManagedOpenCodeModel(doc.model) ||
|
|
611
|
-
(typeof doc.model === "string" && doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`))) {
|
|
611
|
+
if (isManagedOpenCodeModel(doc.model) || (typeof doc.model === "string" && doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`))) {
|
|
612
612
|
delete doc.model;
|
|
613
613
|
changed = true;
|
|
614
614
|
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -33,6 +33,7 @@ const DEFAULT_KILO_MODEL = DEFAULT_CODEX_MODEL;
|
|
|
33
33
|
const DEFAULT_ROO_MODEL = DEFAULT_CODEX_MODEL;
|
|
34
34
|
const DEFAULT_TRAE_MODEL = DEFAULT_CODEX_MODEL;
|
|
35
35
|
const DEFAULT_AIDER_MODEL = DEFAULT_CODEX_MODEL;
|
|
36
|
+
const DEFAULT_ZO_MODEL = DEFAULT_CODEX_MODEL;
|
|
36
37
|
const DEFAULT_MODELS = [...SUPPORTED_MODEL_IDS];
|
|
37
38
|
const PREFERRED_MODELS = [...SUPPORTED_MODEL_IDS];
|
|
38
39
|
const ENV_KEY_NAME = "THECLAWBAY_API_KEY";
|
|
@@ -56,11 +57,8 @@ const SHELL_END = "# theclawbay-shell-managed:end";
|
|
|
56
57
|
const OPENCLAW_PROVIDER_ID = DEFAULT_PROVIDER_ID;
|
|
57
58
|
const HISTORY_PROVIDER_NEUTRALIZE_SOURCES = new Set(["openai", "theclawbay-wan", DEFAULT_PROVIDER_ID]);
|
|
58
59
|
const HISTORY_PROVIDER_DB_MIGRATE_SOURCES = ["openai", "theclawbay-wan"];
|
|
59
|
-
const SETUP_CLIENT_IDS = ["codex", "continue", "cline", "openclaw", "opencode", "kilo", "roo", "trae", "aider"];
|
|
60
|
+
const SETUP_CLIENT_IDS = ["codex", "continue", "cline", "openclaw", "opencode", "kilo", "roo", "trae", "aider", "zo"];
|
|
60
61
|
const THECLAWBAY_OPENAI_PROXY_SUFFIX = "/api/codex-auth/v1/proxy/v1";
|
|
61
|
-
const OPENAI_PROVIDER_ID = "openai";
|
|
62
|
-
const OPENCODE_CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
|
|
63
|
-
const KILO_CONFIG_SCHEMA_URL = "https://kilo.ai/config.json";
|
|
64
62
|
const CONTINUE_MODEL_NAME = "The Claw Bay";
|
|
65
63
|
const ROO_PROFILE_NAME = "The Claw Bay";
|
|
66
64
|
const ROO_PROFILE_ID = "theclawbay-openai-compatible";
|
|
@@ -69,6 +67,13 @@ const TRAE_PATCH_MARKER = "theclawbay-trae-patch";
|
|
|
69
67
|
const TRAE_BUNDLE_BACKUP_SUFFIX = ".theclawbay-managed-backup";
|
|
70
68
|
const TRAE_TARGET_FUNCTION_START = "async setOriginModelListMapAndCache(e,t=!0){";
|
|
71
69
|
const TRAE_TARGET_FUNCTION_END = "}async refreshModelListConfig";
|
|
70
|
+
const OPENAI_PROVIDER_ID = "openai";
|
|
71
|
+
const OPENCODE_CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
|
|
72
|
+
const KILO_CONFIG_SCHEMA_URL = "https://kilo.ai/config.json";
|
|
73
|
+
const ZO_CONFIG_NAME_PREFIX = "The Claw Bay";
|
|
74
|
+
const ZO_COOKIE_HOST = ".zo.computer";
|
|
75
|
+
const ZO_ACCESS_TOKEN_COOKIE = "access_token";
|
|
76
|
+
const ZO_API_BASE_URL = "https://api.zo.computer";
|
|
72
77
|
function trimTrailingSlash(value) {
|
|
73
78
|
return value.replace(/\/+$/g, "");
|
|
74
79
|
}
|
|
@@ -137,7 +142,7 @@ function logSetupCompactSummary(params) {
|
|
|
137
142
|
.filter((client) => params.selectedSetupClients.has(client.id))
|
|
138
143
|
.map((client) => client.summaryLabel);
|
|
139
144
|
const restartTargets = params.setupClients
|
|
140
|
-
.filter((client) => params.selectedSetupClients.has(client.id) && ["continue", "cline", "roo", "trae"].includes(client.id))
|
|
145
|
+
.filter((client) => params.selectedSetupClients.has(client.id) && ["continue", "cline", "roo", "trae", "zo"].includes(client.id))
|
|
141
146
|
.map((client) => client.summaryLabel);
|
|
142
147
|
if (configured.length > 0) {
|
|
143
148
|
params.log(`Configured: ${formatSummaryList(configured)}`);
|
|
@@ -158,6 +163,171 @@ function localAppDataDir() {
|
|
|
158
163
|
return process.env.LOCALAPPDATA;
|
|
159
164
|
return node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Local");
|
|
160
165
|
}
|
|
166
|
+
function roamingAppDataDir() {
|
|
167
|
+
if (process.env.APPDATA?.trim())
|
|
168
|
+
return process.env.APPDATA;
|
|
169
|
+
return node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Roaming");
|
|
170
|
+
}
|
|
171
|
+
function zoCookieDbCandidates() {
|
|
172
|
+
const home = node_os_1.default.homedir();
|
|
173
|
+
const candidates = new Set();
|
|
174
|
+
switch (node_os_1.default.platform()) {
|
|
175
|
+
case "win32":
|
|
176
|
+
candidates.add(node_path_1.default.join(roamingAppDataDir(), "Zo", "Network", "Cookies"));
|
|
177
|
+
break;
|
|
178
|
+
case "darwin":
|
|
179
|
+
candidates.add(node_path_1.default.join(home, "Library", "Application Support", "Zo", "Network", "Cookies"));
|
|
180
|
+
break;
|
|
181
|
+
default:
|
|
182
|
+
candidates.add(node_path_1.default.join(home, ".config", "Zo", "Network", "Cookies"));
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
return Array.from(candidates);
|
|
186
|
+
}
|
|
187
|
+
function zoProgramCandidates() {
|
|
188
|
+
const candidates = new Set();
|
|
189
|
+
if (process.env.THECLAWBAY_ZO_PATH?.trim()) {
|
|
190
|
+
candidates.add(process.env.THECLAWBAY_ZO_PATH.trim());
|
|
191
|
+
}
|
|
192
|
+
if (node_os_1.default.platform() === "win32") {
|
|
193
|
+
candidates.add(node_path_1.default.join(localAppDataDir(), "Programs", "Zo"));
|
|
194
|
+
for (const envKey of ["ProgramFiles", "ProgramFiles(x86)"]) {
|
|
195
|
+
const root = process.env[envKey]?.trim();
|
|
196
|
+
if (root)
|
|
197
|
+
candidates.add(node_path_1.default.join(root, "Zo"));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else if (node_os_1.default.platform() === "darwin") {
|
|
201
|
+
candidates.add("/Applications/Zo.app");
|
|
202
|
+
}
|
|
203
|
+
return Array.from(candidates);
|
|
204
|
+
}
|
|
205
|
+
function pythonCommandCandidates() {
|
|
206
|
+
if (node_os_1.default.platform() === "win32") {
|
|
207
|
+
return [
|
|
208
|
+
{ command: "py", baseArgs: ["-3"] },
|
|
209
|
+
{ command: "python", baseArgs: [] },
|
|
210
|
+
{ command: "python3", baseArgs: [] },
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
return [
|
|
214
|
+
{ command: "python3", baseArgs: [] },
|
|
215
|
+
{ command: "python", baseArgs: [] },
|
|
216
|
+
];
|
|
217
|
+
}
|
|
218
|
+
function readZoCookieValue(cookieName) {
|
|
219
|
+
const dbPath = zoCookieDbCandidates().find((candidate) => (0, node_fs_1.existsSync)(candidate));
|
|
220
|
+
if (!dbPath)
|
|
221
|
+
return null;
|
|
222
|
+
const pythonScript = [
|
|
223
|
+
"import json, sqlite3, sys",
|
|
224
|
+
"db_path, host_key, cookie_name = sys.argv[1:4]",
|
|
225
|
+
'conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)',
|
|
226
|
+
"row = conn.execute(",
|
|
227
|
+
' "select value from cookies where host_key=? and name=? order by length(value) desc limit 1",',
|
|
228
|
+
" (host_key, cookie_name),",
|
|
229
|
+
").fetchone()",
|
|
230
|
+
'print(json.dumps({"value": row[0] if row else None}))',
|
|
231
|
+
].join("\n");
|
|
232
|
+
for (const candidate of pythonCommandCandidates()) {
|
|
233
|
+
const result = (0, node_child_process_1.spawnSync)(candidate.command, [...candidate.baseArgs, "-c", pythonScript, dbPath, ZO_COOKIE_HOST, cookieName], {
|
|
234
|
+
encoding: "utf8",
|
|
235
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
236
|
+
});
|
|
237
|
+
if (result.status !== 0 || !result.stdout.trim())
|
|
238
|
+
continue;
|
|
239
|
+
try {
|
|
240
|
+
const parsed = JSON.parse(result.stdout);
|
|
241
|
+
if (typeof parsed.value === "string" && parsed.value.trim()) {
|
|
242
|
+
return parsed.value.trim();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (hasCommand("sqlite3")) {
|
|
250
|
+
const result = (0, node_child_process_1.spawnSync)("sqlite3", [
|
|
251
|
+
dbPath,
|
|
252
|
+
`select value from cookies where host_key='${ZO_COOKIE_HOST}' and name='${cookieName}' order by length(value) desc limit 1;`,
|
|
253
|
+
], {
|
|
254
|
+
encoding: "utf8",
|
|
255
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
256
|
+
});
|
|
257
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
258
|
+
return result.stdout.trim();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
function parseZoByokConfig(value) {
|
|
264
|
+
if (!value || typeof value !== "object")
|
|
265
|
+
return null;
|
|
266
|
+
return value;
|
|
267
|
+
}
|
|
268
|
+
async function zoApiRequest(params) {
|
|
269
|
+
const response = await fetch(`${ZO_API_BASE_URL}${params.pathname}`, {
|
|
270
|
+
method: params.method ?? "GET",
|
|
271
|
+
headers: {
|
|
272
|
+
Authorization: `Bearer ${params.token}`,
|
|
273
|
+
Accept: "application/json",
|
|
274
|
+
"User-Agent": CLI_HTTP_USER_AGENT,
|
|
275
|
+
...(params.body ? { "Content-Type": "application/json" } : {}),
|
|
276
|
+
},
|
|
277
|
+
body: params.body ? JSON.stringify(params.body) : undefined,
|
|
278
|
+
signal: AbortSignal.timeout(10000),
|
|
279
|
+
});
|
|
280
|
+
const responseText = await response.text();
|
|
281
|
+
if (!response.ok) {
|
|
282
|
+
const detail = responseText ? ` ${responseText.slice(0, 400)}` : "";
|
|
283
|
+
throw new Error(`Zo API ${params.method ?? "GET"} ${params.pathname} failed with ${response.status}.${detail}`.trim());
|
|
284
|
+
}
|
|
285
|
+
return (responseText ? JSON.parse(responseText) : null);
|
|
286
|
+
}
|
|
287
|
+
async function configureZoByok(params) {
|
|
288
|
+
const accessToken = readZoCookieValue(ZO_ACCESS_TOKEN_COOKIE);
|
|
289
|
+
if (!accessToken) {
|
|
290
|
+
throw new Error("Zo was detected, but no active Zo desktop session token could be read. Open Zo, sign in, and rerun setup.");
|
|
291
|
+
}
|
|
292
|
+
const desiredName = `${ZO_CONFIG_NAME_PREFIX} ${modelDisplayName(params.model)}`;
|
|
293
|
+
const desiredBaseUrl = `${trimTrailingSlash(params.backendUrl)}${THECLAWBAY_OPENAI_PROXY_SUFFIX}`;
|
|
294
|
+
const body = {
|
|
295
|
+
provider: "openai-style",
|
|
296
|
+
format: "openai",
|
|
297
|
+
name: desiredName,
|
|
298
|
+
base_url: desiredBaseUrl,
|
|
299
|
+
api_key: params.apiKey,
|
|
300
|
+
model_id: params.model,
|
|
301
|
+
supports_images: false,
|
|
302
|
+
};
|
|
303
|
+
const existing = await zoApiRequest({
|
|
304
|
+
token: accessToken,
|
|
305
|
+
pathname: "/byok/",
|
|
306
|
+
});
|
|
307
|
+
const existingConfigs = (Array.isArray(existing) ? existing : []).map(parseZoByokConfig).filter(Boolean);
|
|
308
|
+
const matched = existingConfigs.find((entry) => {
|
|
309
|
+
const name = typeof entry.name === "string" ? entry.name : "";
|
|
310
|
+
const baseUrl = typeof entry.base_url === "string" ? entry.base_url : "";
|
|
311
|
+
const modelId = typeof entry.model_id === "string" ? entry.model_id : "";
|
|
312
|
+
return name === desiredName || (baseUrl === desiredBaseUrl && modelId === params.model);
|
|
313
|
+
});
|
|
314
|
+
if (matched && typeof matched.id === "string" && matched.id) {
|
|
315
|
+
await zoApiRequest({
|
|
316
|
+
token: accessToken,
|
|
317
|
+
pathname: `/byok/${matched.id}`,
|
|
318
|
+
method: "PATCH",
|
|
319
|
+
body,
|
|
320
|
+
});
|
|
321
|
+
return desiredName;
|
|
322
|
+
}
|
|
323
|
+
await zoApiRequest({
|
|
324
|
+
token: accessToken,
|
|
325
|
+
pathname: "/byok/",
|
|
326
|
+
method: "POST",
|
|
327
|
+
body,
|
|
328
|
+
});
|
|
329
|
+
return desiredName;
|
|
330
|
+
}
|
|
161
331
|
const TRAE_BUNDLE_RELATIVE_PATH = node_path_1.default.join("resources", "app", "node_modules", "@byted-icube", "ai-modules-chat", "dist", "index.js");
|
|
162
332
|
function appendTraeBundlePath(basePath) {
|
|
163
333
|
if (basePath.toLowerCase().endsWith(node_path_1.default.normalize(TRAE_BUNDLE_RELATIVE_PATH).toLowerCase())) {
|
|
@@ -301,9 +471,8 @@ function findProjectConfigFile(params) {
|
|
|
301
471
|
if ((0, node_fs_1.existsSync)(candidate))
|
|
302
472
|
return candidate;
|
|
303
473
|
}
|
|
304
|
-
if ((0, node_fs_1.existsSync)(node_path_1.default.join(current, ".git")))
|
|
474
|
+
if ((0, node_fs_1.existsSync)(node_path_1.default.join(current, ".git")))
|
|
305
475
|
return null;
|
|
306
|
-
}
|
|
307
476
|
if (current === root)
|
|
308
477
|
return null;
|
|
309
478
|
const parent = node_path_1.default.dirname(current);
|
|
@@ -323,11 +492,6 @@ function configDirCandidates(appName) {
|
|
|
323
492
|
}
|
|
324
493
|
return uniqueStrings(dirs);
|
|
325
494
|
}
|
|
326
|
-
function roamingAppDataDir() {
|
|
327
|
-
if (process.env.APPDATA?.trim())
|
|
328
|
-
return process.env.APPDATA;
|
|
329
|
-
return node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Roaming");
|
|
330
|
-
}
|
|
331
495
|
async function readFileIfExists(filePath) {
|
|
332
496
|
try {
|
|
333
497
|
return await promises_1.default.readFile(filePath, "utf8");
|
|
@@ -531,10 +695,15 @@ async function promptForSetupClients(clients) {
|
|
|
531
695
|
stdout.write("\x1b[2J\x1b[H");
|
|
532
696
|
};
|
|
533
697
|
const selectedCount = () => options.filter((option) => option.checked).length;
|
|
698
|
+
const directToggleHint = options.length <= 9
|
|
699
|
+
? `press 1-${options.length} to toggle directly`
|
|
700
|
+
: options.length === 10
|
|
701
|
+
? "press 1-9 or 0 to toggle directly"
|
|
702
|
+
: "press a number key to toggle directly";
|
|
534
703
|
const render = () => {
|
|
535
704
|
clearScreen();
|
|
536
705
|
stdout.write(`${paint("Choose local clients to configure", ansi.bold)}\n`);
|
|
537
|
-
stdout.write(`${paint(`Use ↑/↓ to move, Enter to toggle the highlighted integration, or
|
|
706
|
+
stdout.write(`${paint(`Use ↑/↓ to move, Enter to toggle the highlighted integration, or ${directToggleHint}.`, ansi.dim, ansi.gray)}\n`);
|
|
538
707
|
stdout.write(`${paint("Each tool name links to its official site when your terminal supports it.", ansi.dim, ansi.gray)}\n`);
|
|
539
708
|
stdout.write(`${paint("Move to Apply setup and press Enter when you're ready.", ansi.dim, ansi.gray)}\n\n`);
|
|
540
709
|
for (const [index, option] of options.entries()) {
|
|
@@ -612,7 +781,11 @@ async function promptForSetupClients(clients) {
|
|
|
612
781
|
toggleOption(cursor);
|
|
613
782
|
return;
|
|
614
783
|
}
|
|
615
|
-
const directSelection =
|
|
784
|
+
const directSelection = key.sequence === "0" && options.length >= 10
|
|
785
|
+
? 10
|
|
786
|
+
: /^[1-9]$/.test(key.sequence ?? "")
|
|
787
|
+
? Number.parseInt(key.sequence ?? "", 10)
|
|
788
|
+
: Number.NaN;
|
|
616
789
|
if (Number.isInteger(directSelection) && directSelection >= 1 && directSelection <= options.length) {
|
|
617
790
|
toggleOption(directSelection - 1);
|
|
618
791
|
return;
|
|
@@ -780,6 +953,17 @@ async function detectAiderClient() {
|
|
|
780
953
|
}
|
|
781
954
|
return false;
|
|
782
955
|
}
|
|
956
|
+
async function detectZoClient() {
|
|
957
|
+
for (const candidate of zoProgramCandidates()) {
|
|
958
|
+
if (await pathExists(candidate))
|
|
959
|
+
return true;
|
|
960
|
+
}
|
|
961
|
+
for (const candidate of zoCookieDbCandidates()) {
|
|
962
|
+
if (await pathExists(candidate))
|
|
963
|
+
return true;
|
|
964
|
+
}
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
783
967
|
async function resolveModels(backendUrl, apiKey) {
|
|
784
968
|
const ids = await fetchBackendModelIds(backendUrl, apiKey);
|
|
785
969
|
const available = new Set(ids ?? []);
|
|
@@ -1135,6 +1319,29 @@ function modelOutputLimit(modelId) {
|
|
|
1135
1319
|
return 128000;
|
|
1136
1320
|
return 65536;
|
|
1137
1321
|
}
|
|
1322
|
+
function buildOpenClawModels(models) {
|
|
1323
|
+
const supportedModelMap = new Map((0, supported_models_1.getSupportedModels)().map((model) => [model.id, model]));
|
|
1324
|
+
return models
|
|
1325
|
+
.filter((model) => Boolean(model.id))
|
|
1326
|
+
.map((model) => {
|
|
1327
|
+
const pricing = supportedModelMap.get(model.id);
|
|
1328
|
+
return {
|
|
1329
|
+
id: model.id,
|
|
1330
|
+
name: model.name || model.id,
|
|
1331
|
+
api: "openai-responses",
|
|
1332
|
+
reasoning: true,
|
|
1333
|
+
input: ["text", "image"],
|
|
1334
|
+
cost: {
|
|
1335
|
+
input: pricing?.inputPer1M ?? 0,
|
|
1336
|
+
output: pricing?.outputPer1M ?? 0,
|
|
1337
|
+
cacheRead: pricing?.cachedInputPer1M ?? 0,
|
|
1338
|
+
cacheWrite: pricing?.inputPer1M ?? 0,
|
|
1339
|
+
},
|
|
1340
|
+
contextWindow: modelContextLimit(model.id),
|
|
1341
|
+
maxTokens: modelOutputLimit(model.id),
|
|
1342
|
+
};
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1138
1345
|
function buildOpenCodeModelConfig(model) {
|
|
1139
1346
|
return {
|
|
1140
1347
|
name: model.name || model.id,
|
|
@@ -1176,11 +1383,18 @@ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
|
|
|
1176
1383
|
targets.push({
|
|
1177
1384
|
configPath,
|
|
1178
1385
|
existed: candidate.existed === true,
|
|
1179
|
-
openAiProvider: typeof candidate.openAiProvider === "object" &&
|
|
1386
|
+
openAiProvider: typeof candidate.openAiProvider === "object" &&
|
|
1387
|
+
candidate.openAiProvider !== null &&
|
|
1388
|
+
!Array.isArray(candidate.openAiProvider)
|
|
1180
1389
|
? candidate.openAiProvider
|
|
1181
1390
|
: null,
|
|
1182
1391
|
model: typeof candidate.model === "string" ? candidate.model : null,
|
|
1183
1392
|
schema: typeof candidate.schema === "string" ? candidate.schema : null,
|
|
1393
|
+
plugin: "plugin" in candidate
|
|
1394
|
+
? Array.isArray(candidate.plugin)
|
|
1395
|
+
? candidate.plugin
|
|
1396
|
+
: null
|
|
1397
|
+
: undefined,
|
|
1184
1398
|
});
|
|
1185
1399
|
}
|
|
1186
1400
|
return { version: 2, targets };
|
|
@@ -1272,7 +1486,8 @@ function resolveKiloConfigTargets() {
|
|
|
1272
1486
|
if (existing.length)
|
|
1273
1487
|
return existing;
|
|
1274
1488
|
const home = node_os_1.default.homedir();
|
|
1275
|
-
const primaryDir = uniqueStrings([...configDirCandidates("kilo"), node_path_1.default.join(home, ".kilo")])[0] ??
|
|
1489
|
+
const primaryDir = uniqueStrings([...configDirCandidates("kilo"), node_path_1.default.join(home, ".kilo")])[0] ??
|
|
1490
|
+
node_path_1.default.join(xdgConfigHomeDir(), "kilo");
|
|
1276
1491
|
return uniqueStrings([node_path_1.default.join(primaryDir, "opencode.json")]);
|
|
1277
1492
|
}
|
|
1278
1493
|
function isManagedOpenCodeProvider(provider) {
|
|
@@ -1375,29 +1590,6 @@ async function writeOpenCodeFamilyConfig(params) {
|
|
|
1375
1590
|
}
|
|
1376
1591
|
return normalizedConfigPaths;
|
|
1377
1592
|
}
|
|
1378
|
-
function buildOpenClawModels(models) {
|
|
1379
|
-
const supportedModelMap = new Map((0, supported_models_1.getSupportedModels)().map((model) => [model.id, model]));
|
|
1380
|
-
return models
|
|
1381
|
-
.filter((model) => Boolean(model.id))
|
|
1382
|
-
.map((model) => {
|
|
1383
|
-
const pricing = supportedModelMap.get(model.id);
|
|
1384
|
-
return {
|
|
1385
|
-
id: model.id,
|
|
1386
|
-
name: model.name || model.id,
|
|
1387
|
-
api: "openai-responses",
|
|
1388
|
-
reasoning: true,
|
|
1389
|
-
input: ["text", "image"],
|
|
1390
|
-
cost: {
|
|
1391
|
-
input: pricing?.inputPer1M ?? 0,
|
|
1392
|
-
output: pricing?.outputPer1M ?? 0,
|
|
1393
|
-
cacheRead: pricing?.cachedInputPer1M ?? 0,
|
|
1394
|
-
cacheWrite: pricing?.inputPer1M ?? 0,
|
|
1395
|
-
},
|
|
1396
|
-
contextWindow: modelContextLimit(model.id),
|
|
1397
|
-
maxTokens: modelOutputLimit(model.id),
|
|
1398
|
-
};
|
|
1399
|
-
});
|
|
1400
|
-
}
|
|
1401
1593
|
async function writeOpenCodeConfig(params) {
|
|
1402
1594
|
return writeOpenCodeFamilyConfig({
|
|
1403
1595
|
configPaths: resolveOpenCodeConfigTargets(),
|
|
@@ -1473,7 +1665,7 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1473
1665
|
throw new Error('API key is required. Run "theclawbay setup --api-key <key>".');
|
|
1474
1666
|
const backendRaw = flags.backend ?? (0, api_key_1.tryInferBackendUrlFromApiKey)(apiKey) ?? managed?.backendUrl ?? DEFAULT_BACKEND_URL;
|
|
1475
1667
|
const backendUrl = normalizeUrl(backendRaw, "--backend");
|
|
1476
|
-
const [codexDetected, continueDetected, clineDetected, kiloDetected, rooDetected, traeDetected, aiderDetected] = await Promise.all([
|
|
1668
|
+
const [codexDetected, continueDetected, clineDetected, kiloDetected, rooDetected, traeDetected, aiderDetected, zoDetected] = await Promise.all([
|
|
1477
1669
|
detectCodexClient(),
|
|
1478
1670
|
detectContinueClient(),
|
|
1479
1671
|
detectClineClient(),
|
|
@@ -1481,6 +1673,7 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1481
1673
|
detectRooClient(),
|
|
1482
1674
|
detectTraeClient(),
|
|
1483
1675
|
detectAiderClient(),
|
|
1676
|
+
detectZoClient(),
|
|
1484
1677
|
]);
|
|
1485
1678
|
const setupClients = [
|
|
1486
1679
|
{
|
|
@@ -1564,6 +1757,15 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1564
1757
|
icon: "✦",
|
|
1565
1758
|
siteUrl: "https://aider.chat",
|
|
1566
1759
|
},
|
|
1760
|
+
{
|
|
1761
|
+
id: "zo",
|
|
1762
|
+
label: "Zo (experimental)",
|
|
1763
|
+
summaryLabel: "Zo",
|
|
1764
|
+
detected: zoDetected,
|
|
1765
|
+
recommended: false,
|
|
1766
|
+
icon: "◉",
|
|
1767
|
+
siteUrl: "https://www.zo.computer",
|
|
1768
|
+
},
|
|
1567
1769
|
];
|
|
1568
1770
|
const selectedSetupClients = await resolveSetupClientSelection({
|
|
1569
1771
|
setupClients,
|
|
@@ -1588,6 +1790,7 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1588
1790
|
let rooConfigPaths = [];
|
|
1589
1791
|
let traeBundlePathPatched = null;
|
|
1590
1792
|
let aiderConfigPath = null;
|
|
1793
|
+
let zoConfigName = null;
|
|
1591
1794
|
try {
|
|
1592
1795
|
if (selectedSetupClients.size > 0) {
|
|
1593
1796
|
progress.update("Resolving supported models");
|
|
@@ -1688,6 +1891,14 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1688
1891
|
apiKey,
|
|
1689
1892
|
});
|
|
1690
1893
|
}
|
|
1894
|
+
if (selectedSetupClients.has("zo")) {
|
|
1895
|
+
progress.update("Configuring Zo");
|
|
1896
|
+
zoConfigName = await configureZoByok({
|
|
1897
|
+
backendUrl,
|
|
1898
|
+
model: resolved?.model ?? DEFAULT_ZO_MODEL,
|
|
1899
|
+
apiKey,
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1691
1902
|
}
|
|
1692
1903
|
catch (error) {
|
|
1693
1904
|
progress.fail("Setup failed");
|
|
@@ -1705,6 +1916,9 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1705
1916
|
if (selectedSetupClients.has("trae")) {
|
|
1706
1917
|
summaryNotes.add("Trae uses an experimental patch. Restart Trae and pick a The Claw Bay model to test it.");
|
|
1707
1918
|
}
|
|
1919
|
+
if (selectedSetupClients.has("zo")) {
|
|
1920
|
+
summaryNotes.add("Zo uses an experimental account-level BYOK API integration. Keep Zo signed in while running setup.");
|
|
1921
|
+
}
|
|
1708
1922
|
if (openClawCliWarning)
|
|
1709
1923
|
summaryNotes.add(openClawCliWarning);
|
|
1710
1924
|
if ((stateDbMigration?.failed.length ?? 0) > 0) {
|
|
@@ -1877,6 +2091,16 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1877
2091
|
else {
|
|
1878
2092
|
this.log("- Aider: not detected (skipped)");
|
|
1879
2093
|
}
|
|
2094
|
+
if (selectedSetupClients.has("zo")) {
|
|
2095
|
+
this.log(`- Zo: configured account-level BYOK model (${zoConfigName})`);
|
|
2096
|
+
this.log("- Zo: used the desktop session token from the local Zo app cookie store.");
|
|
2097
|
+
}
|
|
2098
|
+
else if (zoDetected) {
|
|
2099
|
+
this.log("- Zo: detected but skipped");
|
|
2100
|
+
}
|
|
2101
|
+
else {
|
|
2102
|
+
this.log("- Zo: not detected (skipped)");
|
|
2103
|
+
}
|
|
1880
2104
|
if (authSeed?.action === "seeded") {
|
|
1881
2105
|
this.log("- Codex login state: seeded local API-key auth for Codex UI.");
|
|
1882
2106
|
}
|
|
@@ -1895,11 +2119,11 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
1895
2119
|
else if (authSeed) {
|
|
1896
2120
|
this.log("- Codex login state: no change.");
|
|
1897
2121
|
}
|
|
1898
|
-
this.log("Next: restart Continue, Cline, Roo Code, or
|
|
2122
|
+
this.log("Next: restart Continue, Cline, Roo Code, Trae, or Zo only if they were already open during setup.");
|
|
1899
2123
|
});
|
|
1900
2124
|
}
|
|
1901
2125
|
}
|
|
1902
|
-
SetupCommand.description = "One-time setup: configure Codex plus any detected Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Trae, or
|
|
2126
|
+
SetupCommand.description = "One-time setup: configure Codex plus any detected Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Trae, Aider, or Zo installs to use The Claw Bay";
|
|
1903
2127
|
SetupCommand.flags = {
|
|
1904
2128
|
backend: core_1.Flags.string({
|
|
1905
2129
|
required: false,
|
|
@@ -1912,7 +2136,7 @@ SetupCommand.flags = {
|
|
|
1912
2136
|
}),
|
|
1913
2137
|
clients: core_1.Flags.string({
|
|
1914
2138
|
required: false,
|
|
1915
|
-
description: "Detected local clients to configure: codex, continue, cline, openclaw, opencode, kilo, roo, trae, aider",
|
|
2139
|
+
description: "Detected local clients to configure: codex, continue, cline, openclaw, opencode, kilo, roo, trae, aider, zo",
|
|
1916
2140
|
}),
|
|
1917
2141
|
yes: core_1.Flags.boolean({
|
|
1918
2142
|
required: false,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "theclawbay",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "CLI for connecting Codex, Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Aider, and experimental
|
|
3
|
+
"version": "0.3.38",
|
|
4
|
+
"description": "CLI for connecting Codex, Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Aider, experimental Trae, and experimental Zo to The Claw Bay.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
7
7
|
"theclawbay": "dist/index.js"
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"kilo",
|
|
40
40
|
"roo",
|
|
41
41
|
"aider",
|
|
42
|
-
"trae"
|
|
42
|
+
"trae",
|
|
43
|
+
"zo"
|
|
43
44
|
],
|
|
44
45
|
"preferGlobal": true,
|
|
45
46
|
"dependencies": {
|