theclawbay 0.3.52 → 0.3.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/logout.js +173 -0
- package/dist/commands/setup.js +157 -10
- package/dist/lib/device-session-auth.js +30 -0
- package/package.json +3 -2
package/dist/commands/logout.js
CHANGED
|
@@ -31,7 +31,12 @@ const CONTINUE_CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(
|
|
|
31
31
|
const CLINE_GLOBAL_STATE_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".cline", "data", "globalState.json");
|
|
32
32
|
const CLINE_SECRETS_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".cline", "data", "secrets.json");
|
|
33
33
|
const AIDER_CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".aider.conf.yml");
|
|
34
|
+
const GSD_AGENT_DIR = node_path_1.default.join(node_os_1.default.homedir(), ".gsd", "agent");
|
|
35
|
+
const GSD_MODELS_PATH = node_path_1.default.join(GSD_AGENT_DIR, "models.json");
|
|
36
|
+
const GSD_AUTH_PATH = node_path_1.default.join(GSD_AGENT_DIR, "auth.json");
|
|
37
|
+
const GSD_SETTINGS_PATH = node_path_1.default.join(GSD_AGENT_DIR, "settings.json");
|
|
34
38
|
const CLINE_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "cline.restore.json");
|
|
39
|
+
const GSD_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "gsd.restore.json");
|
|
35
40
|
const OPENCODE_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "opencode.restore.json");
|
|
36
41
|
const KILO_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "kilo.restore.json");
|
|
37
42
|
const ROO_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "roo-settings.restore.json");
|
|
@@ -414,6 +419,168 @@ async function cleanupClineConfig() {
|
|
|
414
419
|
await removeFileIfExists(CLINE_RESTORE_STATE_PATH);
|
|
415
420
|
return changed;
|
|
416
421
|
}
|
|
422
|
+
function isEmptyObject(value) {
|
|
423
|
+
return Object.keys(value).length === 0;
|
|
424
|
+
}
|
|
425
|
+
function normalizeGsdRestoreSnapshot(value) {
|
|
426
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
427
|
+
return null;
|
|
428
|
+
const obj = value;
|
|
429
|
+
return {
|
|
430
|
+
version: 1,
|
|
431
|
+
modelsExisted: obj.modelsExisted === true,
|
|
432
|
+
providerConfig: typeof obj.providerConfig === "object" && obj.providerConfig !== null && !Array.isArray(obj.providerConfig)
|
|
433
|
+
? obj.providerConfig
|
|
434
|
+
: null,
|
|
435
|
+
authExisted: obj.authExisted === true,
|
|
436
|
+
authEntry: typeof obj.authEntry === "object" && obj.authEntry !== null && !Array.isArray(obj.authEntry)
|
|
437
|
+
? obj.authEntry
|
|
438
|
+
: null,
|
|
439
|
+
settingsExisted: obj.settingsExisted === true,
|
|
440
|
+
defaultProvider: typeof obj.defaultProvider === "string" ? obj.defaultProvider : null,
|
|
441
|
+
defaultModel: typeof obj.defaultModel === "string" ? obj.defaultModel : null,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function normalizeGsdCredential(value) {
|
|
445
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
446
|
+
return null;
|
|
447
|
+
return { ...value };
|
|
448
|
+
}
|
|
449
|
+
async function cleanupGsdConfig() {
|
|
450
|
+
const snapshotRaw = await readFileIfExists(GSD_RESTORE_STATE_PATH);
|
|
451
|
+
if (snapshotRaw === null || !snapshotRaw.trim())
|
|
452
|
+
return false;
|
|
453
|
+
let snapshot = null;
|
|
454
|
+
try {
|
|
455
|
+
snapshot = normalizeGsdRestoreSnapshot(JSON.parse(snapshotRaw));
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
snapshot = null;
|
|
459
|
+
}
|
|
460
|
+
if (!snapshot)
|
|
461
|
+
return false;
|
|
462
|
+
let changed = false;
|
|
463
|
+
const modelsRaw = await readFileIfExists(GSD_MODELS_PATH);
|
|
464
|
+
if (modelsRaw !== null) {
|
|
465
|
+
let fileChanged = false;
|
|
466
|
+
let doc = objectRecordOr({}, {});
|
|
467
|
+
try {
|
|
468
|
+
doc = modelsRaw.trim() ? objectRecordOr(JSON.parse(modelsRaw), {}) : {};
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
doc = {};
|
|
472
|
+
}
|
|
473
|
+
const providersRoot = objectRecordOr(doc.providers, {});
|
|
474
|
+
if (snapshot.providerConfig) {
|
|
475
|
+
const currentProvider = objectRecordOr(providersRoot[DEFAULT_PROVIDER_ID], {});
|
|
476
|
+
const nextProvider = JSON.stringify(snapshot.providerConfig);
|
|
477
|
+
if (JSON.stringify(currentProvider) !== nextProvider) {
|
|
478
|
+
providersRoot[DEFAULT_PROVIDER_ID] = snapshot.providerConfig;
|
|
479
|
+
fileChanged = true;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
else if (DEFAULT_PROVIDER_ID in providersRoot) {
|
|
483
|
+
delete providersRoot[DEFAULT_PROVIDER_ID];
|
|
484
|
+
fileChanged = true;
|
|
485
|
+
}
|
|
486
|
+
if (isEmptyObject(providersRoot)) {
|
|
487
|
+
if ("providers" in doc)
|
|
488
|
+
fileChanged = true;
|
|
489
|
+
delete doc.providers;
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
doc.providers = providersRoot;
|
|
493
|
+
}
|
|
494
|
+
if (!fileChanged) {
|
|
495
|
+
// Preserve the file exactly if nothing changed.
|
|
496
|
+
}
|
|
497
|
+
else if (!snapshot.modelsExisted && isEmptyObject(doc)) {
|
|
498
|
+
changed = (await removeFileIfExists(GSD_MODELS_PATH)) || changed;
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
await writeJsonFile(GSD_MODELS_PATH, doc);
|
|
502
|
+
changed = true;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const authRaw = await readFileIfExists(GSD_AUTH_PATH);
|
|
506
|
+
if (authRaw !== null) {
|
|
507
|
+
let fileChanged = false;
|
|
508
|
+
let doc = objectRecordOr({}, {});
|
|
509
|
+
try {
|
|
510
|
+
doc = authRaw.trim() ? objectRecordOr(JSON.parse(authRaw), {}) : {};
|
|
511
|
+
}
|
|
512
|
+
catch {
|
|
513
|
+
doc = {};
|
|
514
|
+
}
|
|
515
|
+
if (snapshot.authEntry) {
|
|
516
|
+
const currentEntry = normalizeGsdCredential(doc[DEFAULT_PROVIDER_ID]);
|
|
517
|
+
const nextEntry = JSON.stringify(snapshot.authEntry);
|
|
518
|
+
if (JSON.stringify(currentEntry) !== nextEntry) {
|
|
519
|
+
doc[DEFAULT_PROVIDER_ID] = snapshot.authEntry;
|
|
520
|
+
fileChanged = true;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else if (DEFAULT_PROVIDER_ID in doc) {
|
|
524
|
+
delete doc[DEFAULT_PROVIDER_ID];
|
|
525
|
+
fileChanged = true;
|
|
526
|
+
}
|
|
527
|
+
if (!fileChanged) {
|
|
528
|
+
// Preserve the file exactly if nothing changed.
|
|
529
|
+
}
|
|
530
|
+
else if (!snapshot.authExisted && isEmptyObject(doc)) {
|
|
531
|
+
changed = (await removeFileIfExists(GSD_AUTH_PATH)) || changed;
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
await writeJsonFile(GSD_AUTH_PATH, doc, 0o600);
|
|
535
|
+
changed = true;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
const settingsRaw = await readFileIfExists(GSD_SETTINGS_PATH);
|
|
539
|
+
if (settingsRaw !== null) {
|
|
540
|
+
let fileChanged = false;
|
|
541
|
+
let doc = objectRecordOr({}, {});
|
|
542
|
+
try {
|
|
543
|
+
doc = settingsRaw.trim() ? objectRecordOr(JSON.parse(settingsRaw), {}) : {};
|
|
544
|
+
}
|
|
545
|
+
catch {
|
|
546
|
+
doc = {};
|
|
547
|
+
}
|
|
548
|
+
if (doc.defaultProvider === DEFAULT_PROVIDER_ID) {
|
|
549
|
+
if (snapshot.defaultProvider === null) {
|
|
550
|
+
if ("defaultProvider" in doc) {
|
|
551
|
+
delete doc.defaultProvider;
|
|
552
|
+
fileChanged = true;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
else if (doc.defaultProvider !== snapshot.defaultProvider) {
|
|
556
|
+
doc.defaultProvider = snapshot.defaultProvider;
|
|
557
|
+
fileChanged = true;
|
|
558
|
+
}
|
|
559
|
+
if (snapshot.defaultModel === null) {
|
|
560
|
+
if ("defaultModel" in doc) {
|
|
561
|
+
delete doc.defaultModel;
|
|
562
|
+
fileChanged = true;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
else if (doc.defaultModel !== snapshot.defaultModel) {
|
|
566
|
+
doc.defaultModel = snapshot.defaultModel;
|
|
567
|
+
fileChanged = true;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (!fileChanged) {
|
|
571
|
+
// Preserve the file exactly if nothing changed.
|
|
572
|
+
}
|
|
573
|
+
else if (!snapshot.settingsExisted && isEmptyObject(doc)) {
|
|
574
|
+
changed = (await removeFileIfExists(GSD_SETTINGS_PATH)) || changed;
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
await writeJsonFile(GSD_SETTINGS_PATH, doc);
|
|
578
|
+
changed = true;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
await removeFileIfExists(GSD_RESTORE_STATE_PATH);
|
|
582
|
+
return changed;
|
|
583
|
+
}
|
|
417
584
|
async function cleanupRooConfig() {
|
|
418
585
|
const snapshotRaw = await readFileIfExists(ROO_SETTINGS_STATE_PATH);
|
|
419
586
|
let changed = false;
|
|
@@ -773,6 +940,7 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
773
940
|
let updatedCodexConfig = false;
|
|
774
941
|
let updatedContinueConfig = false;
|
|
775
942
|
let updatedClineConfig = false;
|
|
943
|
+
let updatedGsdConfig = false;
|
|
776
944
|
let updatedOpenClawConfig = false;
|
|
777
945
|
let updatedOpenCodeConfig = false;
|
|
778
946
|
let updatedKiloConfig = false;
|
|
@@ -840,6 +1008,8 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
840
1008
|
updatedContinueConfig = await cleanupContinueConfig();
|
|
841
1009
|
progress.update("Reverting Cline");
|
|
842
1010
|
updatedClineConfig = await cleanupClineConfig();
|
|
1011
|
+
progress.update("Reverting GSD");
|
|
1012
|
+
updatedGsdConfig = await cleanupGsdConfig();
|
|
843
1013
|
progress.update("Reverting OpenClaw");
|
|
844
1014
|
updatedOpenClawConfig = await cleanupOpenClawConfig();
|
|
845
1015
|
progress.update("Reverting OpenCode");
|
|
@@ -881,6 +1051,8 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
881
1051
|
revertedTargets.push("Continue");
|
|
882
1052
|
if (updatedClineConfig)
|
|
883
1053
|
revertedTargets.push("Cline");
|
|
1054
|
+
if (updatedGsdConfig)
|
|
1055
|
+
revertedTargets.push("GSD");
|
|
884
1056
|
if (updatedOpenClawConfig)
|
|
885
1057
|
revertedTargets.push("OpenClaw");
|
|
886
1058
|
if (updatedOpenCodeConfig)
|
|
@@ -981,6 +1153,7 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
981
1153
|
}
|
|
982
1154
|
this.log(`- Continue config cleaned: ${updatedContinueConfig ? "yes" : "no"}`);
|
|
983
1155
|
this.log(`- Cline config cleaned: ${updatedClineConfig ? "yes" : "no"}`);
|
|
1156
|
+
this.log(`- GSD config cleaned: ${updatedGsdConfig ? "yes" : "no"}`);
|
|
984
1157
|
this.log(`- OpenClaw config cleaned: ${updatedOpenClawConfig ? "yes" : "no"}`);
|
|
985
1158
|
this.log(`- OpenCode config cleaned: ${updatedOpenCodeConfig ? "yes" : "no"}`);
|
|
986
1159
|
this.log(`- Kilo config cleaned: ${updatedKiloConfig ? "yes" : "no"}`);
|
package/dist/commands/setup.js
CHANGED
|
@@ -41,12 +41,18 @@ const PREFERRED_MODELS = [...SUPPORTED_MODEL_IDS];
|
|
|
41
41
|
const ENV_KEY_NAME = "THECLAWBAY_API_KEY";
|
|
42
42
|
const CLAUDE_ENV_API_KEY_NAME = "ANTHROPIC_API_KEY";
|
|
43
43
|
const CLAUDE_ENV_BASE_URL_NAME = "ANTHROPIC_BASE_URL";
|
|
44
|
+
const CLAUDE_ENV_SIMPLE_MODE_NAME = "CLAUDE_CODE_SIMPLE";
|
|
44
45
|
const ENV_FILE = node_path_1.default.join(paths_1.theclawbayConfigDir, "env");
|
|
45
46
|
const CONTINUE_CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".continue", "config.yaml");
|
|
46
47
|
const CLINE_GLOBAL_STATE_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".cline", "data", "globalState.json");
|
|
47
48
|
const CLINE_SECRETS_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".cline", "data", "secrets.json");
|
|
48
49
|
const AIDER_CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".aider.conf.yml");
|
|
50
|
+
const GSD_AGENT_DIR = node_path_1.default.join(node_os_1.default.homedir(), ".gsd", "agent");
|
|
51
|
+
const GSD_MODELS_PATH = node_path_1.default.join(GSD_AGENT_DIR, "models.json");
|
|
52
|
+
const GSD_AUTH_PATH = node_path_1.default.join(GSD_AGENT_DIR, "auth.json");
|
|
53
|
+
const GSD_SETTINGS_PATH = node_path_1.default.join(GSD_AGENT_DIR, "settings.json");
|
|
49
54
|
const CLINE_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "cline.restore.json");
|
|
55
|
+
const GSD_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "gsd.restore.json");
|
|
50
56
|
const OPENCODE_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "opencode.restore.json");
|
|
51
57
|
const KILO_RESTORE_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "kilo.restore.json");
|
|
52
58
|
const ROO_SETTINGS_STATE_PATH = node_path_1.default.join(paths_1.theclawbayStateDir, "roo-settings.restore.json");
|
|
@@ -61,7 +67,7 @@ const SHELL_END = "# theclawbay-shell-managed:end";
|
|
|
61
67
|
const OPENCLAW_PROVIDER_ID = DEFAULT_PROVIDER_ID;
|
|
62
68
|
const HISTORY_PROVIDER_NEUTRALIZE_SOURCES = new Set(["openai", "theclawbay-wan", DEFAULT_PROVIDER_ID]);
|
|
63
69
|
const HISTORY_PROVIDER_DB_MIGRATE_SOURCES = ["openai", "theclawbay-wan"];
|
|
64
|
-
const SETUP_CLIENT_IDS = ["codex", "claude", "continue", "cline", "openclaw", "opencode", "kilo", "roo", "trae", "aider", "zo"];
|
|
70
|
+
const SETUP_CLIENT_IDS = ["codex", "claude", "continue", "cline", "gsd", "openclaw", "opencode", "kilo", "roo", "trae", "aider", "zo"];
|
|
65
71
|
const LEGACY_THECLAWBAY_OPENAI_PROXY_SUFFIX = "/api/codex-auth/v1/proxy/v1";
|
|
66
72
|
const CANONICAL_THECLAWBAY_OPENAI_PROXY_SUFFIX = "/v1";
|
|
67
73
|
const CANONICAL_CODEX_NATIVE_PROXY_SUFFIX = "/backend-api/codex";
|
|
@@ -161,7 +167,7 @@ function logSetupCompactSummary(params) {
|
|
|
161
167
|
.filter((client) => params.selectedSetupClients.has(client.id))
|
|
162
168
|
.map((client) => client.summaryLabel);
|
|
163
169
|
const restartTargets = params.setupClients
|
|
164
|
-
.filter((client) => params.selectedSetupClients.has(client.id) && ["continue", "cline", "roo", "trae", "zo"].includes(client.id))
|
|
170
|
+
.filter((client) => params.selectedSetupClients.has(client.id) && ["continue", "cline", "gsd", "roo", "trae", "zo"].includes(client.id))
|
|
165
171
|
.map((client) => client.summaryLabel);
|
|
166
172
|
if (configured.length > 0) {
|
|
167
173
|
params.log(`Configured: ${formatSummaryList(configured)}`);
|
|
@@ -1119,6 +1125,21 @@ async function detectCodexClient() {
|
|
|
1119
1125
|
}
|
|
1120
1126
|
return false;
|
|
1121
1127
|
}
|
|
1128
|
+
async function detectGsdClient() {
|
|
1129
|
+
if (hasCommand("gsd"))
|
|
1130
|
+
return true;
|
|
1131
|
+
const candidates = [
|
|
1132
|
+
GSD_AGENT_DIR,
|
|
1133
|
+
GSD_MODELS_PATH,
|
|
1134
|
+
GSD_AUTH_PATH,
|
|
1135
|
+
GSD_SETTINGS_PATH,
|
|
1136
|
+
];
|
|
1137
|
+
for (const candidate of candidates) {
|
|
1138
|
+
if (await pathExists(candidate))
|
|
1139
|
+
return true;
|
|
1140
|
+
}
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1122
1143
|
async function detectContinueClient() {
|
|
1123
1144
|
if (hasCommand("cn") || hasCommand("continue"))
|
|
1124
1145
|
return true;
|
|
@@ -1371,6 +1392,100 @@ async function writeClineConfig(params) {
|
|
|
1371
1392
|
await writeJsonObjectFile(CLINE_SECRETS_PATH, secrets, 0o600);
|
|
1372
1393
|
return [CLINE_GLOBAL_STATE_PATH, CLINE_SECRETS_PATH];
|
|
1373
1394
|
}
|
|
1395
|
+
function buildGsdModels(models) {
|
|
1396
|
+
const supportedModelMap = new Map((0, supported_models_1.getSupportedModels)().map((model) => [model.id, model]));
|
|
1397
|
+
return models
|
|
1398
|
+
.filter((model) => Boolean(model.id))
|
|
1399
|
+
.map((model) => {
|
|
1400
|
+
const pricing = supportedModelMap.get(model.id);
|
|
1401
|
+
return {
|
|
1402
|
+
id: model.id,
|
|
1403
|
+
name: model.name || model.id,
|
|
1404
|
+
reasoning: true,
|
|
1405
|
+
input: ["text", "image"],
|
|
1406
|
+
cost: {
|
|
1407
|
+
input: pricing?.inputPer1M ?? 0,
|
|
1408
|
+
output: pricing?.outputPer1M ?? 0,
|
|
1409
|
+
cacheRead: pricing?.cachedInputPer1M ?? 0,
|
|
1410
|
+
cacheWrite: pricing?.inputPer1M ?? 0,
|
|
1411
|
+
},
|
|
1412
|
+
contextWindow: modelContextLimit(model.id),
|
|
1413
|
+
maxTokens: modelOutputLimit(model.id),
|
|
1414
|
+
};
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
function normalizeGsdCredential(value) {
|
|
1418
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
1419
|
+
return null;
|
|
1420
|
+
return { ...value };
|
|
1421
|
+
}
|
|
1422
|
+
async function writeGsdConfig(params) {
|
|
1423
|
+
const existingModelsRaw = await readFileIfExists(GSD_MODELS_PATH);
|
|
1424
|
+
const existingAuthRaw = await readFileIfExists(GSD_AUTH_PATH);
|
|
1425
|
+
const existingSettingsRaw = await readFileIfExists(GSD_SETTINGS_PATH);
|
|
1426
|
+
const existingSnapshotRaw = await readFileIfExists(GSD_RESTORE_STATE_PATH);
|
|
1427
|
+
let modelsDoc = {};
|
|
1428
|
+
if (existingModelsRaw?.trim()) {
|
|
1429
|
+
try {
|
|
1430
|
+
modelsDoc = objectRecordOr(JSON.parse(existingModelsRaw), {});
|
|
1431
|
+
}
|
|
1432
|
+
catch {
|
|
1433
|
+
throw new Error(`invalid JSON in GSD config: ${GSD_MODELS_PATH}`);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
let authDoc = {};
|
|
1437
|
+
if (existingAuthRaw?.trim()) {
|
|
1438
|
+
try {
|
|
1439
|
+
authDoc = objectRecordOr(JSON.parse(existingAuthRaw), {});
|
|
1440
|
+
}
|
|
1441
|
+
catch {
|
|
1442
|
+
throw new Error(`invalid JSON in GSD auth: ${GSD_AUTH_PATH}`);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
let settingsDoc = {};
|
|
1446
|
+
if (existingSettingsRaw?.trim()) {
|
|
1447
|
+
try {
|
|
1448
|
+
settingsDoc = objectRecordOr(JSON.parse(existingSettingsRaw), {});
|
|
1449
|
+
}
|
|
1450
|
+
catch {
|
|
1451
|
+
throw new Error(`invalid JSON in GSD settings: ${GSD_SETTINGS_PATH}`);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
if (!existingSnapshotRaw?.trim()) {
|
|
1455
|
+
const providersRoot = objectRecordOr(modelsDoc.providers, {});
|
|
1456
|
+
const snapshot = {
|
|
1457
|
+
version: 1,
|
|
1458
|
+
modelsExisted: existingModelsRaw !== null,
|
|
1459
|
+
providerConfig: objectRecordOr(providersRoot[DEFAULT_PROVIDER_ID], {}),
|
|
1460
|
+
authExisted: existingAuthRaw !== null,
|
|
1461
|
+
authEntry: normalizeGsdCredential(authDoc[DEFAULT_PROVIDER_ID]),
|
|
1462
|
+
settingsExisted: existingSettingsRaw !== null,
|
|
1463
|
+
defaultProvider: typeof settingsDoc.defaultProvider === "string" ? settingsDoc.defaultProvider : null,
|
|
1464
|
+
defaultModel: typeof settingsDoc.defaultModel === "string" ? settingsDoc.defaultModel : null,
|
|
1465
|
+
};
|
|
1466
|
+
if (Object.keys(snapshot.providerConfig ?? {}).length === 0)
|
|
1467
|
+
snapshot.providerConfig = null;
|
|
1468
|
+
await writeJsonObjectFile(GSD_RESTORE_STATE_PATH, snapshot, 0o600);
|
|
1469
|
+
}
|
|
1470
|
+
const providersRoot = objectRecordOr(modelsDoc.providers, {});
|
|
1471
|
+
providersRoot[DEFAULT_PROVIDER_ID] = {
|
|
1472
|
+
baseUrl: openAiCompatibleProxyUrl(params.backendUrl),
|
|
1473
|
+
apiKey: ENV_KEY_NAME,
|
|
1474
|
+
api: "openai-responses",
|
|
1475
|
+
models: buildGsdModels(params.models),
|
|
1476
|
+
};
|
|
1477
|
+
modelsDoc.providers = providersRoot;
|
|
1478
|
+
authDoc[DEFAULT_PROVIDER_ID] = {
|
|
1479
|
+
type: "api_key",
|
|
1480
|
+
key: params.apiKey,
|
|
1481
|
+
};
|
|
1482
|
+
settingsDoc.defaultProvider = DEFAULT_PROVIDER_ID;
|
|
1483
|
+
settingsDoc.defaultModel = params.model.trim() || DEFAULT_CODEX_MODEL;
|
|
1484
|
+
await writeJsonObjectFile(GSD_MODELS_PATH, modelsDoc);
|
|
1485
|
+
await writeJsonObjectFile(GSD_AUTH_PATH, authDoc, 0o600);
|
|
1486
|
+
await writeJsonObjectFile(GSD_SETTINGS_PATH, settingsDoc);
|
|
1487
|
+
return [GSD_MODELS_PATH, GSD_AUTH_PATH, GSD_SETTINGS_PATH];
|
|
1488
|
+
}
|
|
1374
1489
|
async function writeRooConfig(params) {
|
|
1375
1490
|
const hosts = await detectExtensionHosts(["rooveterinaryinc.roo-cline-"]);
|
|
1376
1491
|
if (hosts.length === 0) {
|
|
@@ -1448,7 +1563,7 @@ async function persistApiKeyEnv(params) {
|
|
|
1448
1563
|
`export ${ENV_KEY_NAME}=${shellQuote(params.apiKey)}`,
|
|
1449
1564
|
];
|
|
1450
1565
|
if (params.claudeEnabled) {
|
|
1451
|
-
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))}`);
|
|
1566
|
+
envLines.push("", "# Official Claude Code CLI", `export ${CLAUDE_ENV_API_KEY_NAME}=${shellQuote(params.apiKey)}`, `export ${CLAUDE_ENV_BASE_URL_NAME}=${shellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`, `export ${CLAUDE_ENV_SIMPLE_MODE_NAME}=1`);
|
|
1452
1567
|
}
|
|
1453
1568
|
const envContents = `${envLines.join("\n")}\n`;
|
|
1454
1569
|
await promises_1.default.writeFile(ENV_FILE, envContents, "utf8");
|
|
@@ -1483,10 +1598,12 @@ async function persistApiKeyEnv(params) {
|
|
|
1483
1598
|
if (params.claudeEnabled) {
|
|
1484
1599
|
process.env[CLAUDE_ENV_API_KEY_NAME] = params.apiKey;
|
|
1485
1600
|
process.env[CLAUDE_ENV_BASE_URL_NAME] = anthropicCompatibleProxyUrl(params.backendUrl);
|
|
1601
|
+
process.env[CLAUDE_ENV_SIMPLE_MODE_NAME] = "1";
|
|
1486
1602
|
}
|
|
1487
1603
|
else {
|
|
1488
1604
|
delete process.env[CLAUDE_ENV_API_KEY_NAME];
|
|
1489
1605
|
delete process.env[CLAUDE_ENV_BASE_URL_NAME];
|
|
1606
|
+
delete process.env[CLAUDE_ENV_SIMPLE_MODE_NAME];
|
|
1490
1607
|
}
|
|
1491
1608
|
return updated;
|
|
1492
1609
|
}
|
|
@@ -1498,7 +1615,7 @@ async function persistFishEnv(params) {
|
|
|
1498
1615
|
`set -gx ${ENV_KEY_NAME} ${shellQuote(params.apiKey)}`,
|
|
1499
1616
|
];
|
|
1500
1617
|
if (params.claudeEnabled) {
|
|
1501
|
-
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))}`);
|
|
1618
|
+
lines.push("", "# Official Claude Code CLI", `set -gx ${CLAUDE_ENV_API_KEY_NAME} ${shellQuote(params.apiKey)}`, `set -gx ${CLAUDE_ENV_BASE_URL_NAME} ${shellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`, `set -gx ${CLAUDE_ENV_SIMPLE_MODE_NAME} 1`);
|
|
1502
1619
|
}
|
|
1503
1620
|
await promises_1.default.writeFile(fishConfPath, `${lines.join("\n")}\n`, "utf8");
|
|
1504
1621
|
return [fishConfPath];
|
|
@@ -1523,7 +1640,7 @@ async function persistPowerShellEnv(params) {
|
|
|
1523
1640
|
`$env:${ENV_KEY_NAME} = ${powerShellQuote(params.apiKey)}`,
|
|
1524
1641
|
];
|
|
1525
1642
|
if (params.claudeEnabled) {
|
|
1526
|
-
lines.push(`$env:${CLAUDE_ENV_API_KEY_NAME} = ${powerShellQuote(params.apiKey)}`, `$env:${CLAUDE_ENV_BASE_URL_NAME} = ${powerShellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`);
|
|
1643
|
+
lines.push(`$env:${CLAUDE_ENV_API_KEY_NAME} = ${powerShellQuote(params.apiKey)}`, `$env:${CLAUDE_ENV_BASE_URL_NAME} = ${powerShellQuote(anthropicCompatibleProxyUrl(params.backendUrl))}`, `$env:${CLAUDE_ENV_SIMPLE_MODE_NAME} = "1"`);
|
|
1527
1644
|
}
|
|
1528
1645
|
lines.push(SHELL_END);
|
|
1529
1646
|
const next = appendManagedBlock(cleaned, lines);
|
|
@@ -2012,11 +2129,12 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2012
2129
|
managed?.backendUrl ??
|
|
2013
2130
|
DEFAULT_BACKEND_URL;
|
|
2014
2131
|
const backendUrl = normalizeUrl(backendRaw, "--backend");
|
|
2015
|
-
const [codexDetected, claudeDetected, continueDetected, clineDetected, kiloDetected, rooDetected, traeDetected, aiderDetected, zoDetected] = await Promise.all([
|
|
2132
|
+
const [codexDetected, claudeDetected, continueDetected, clineDetected, gsdDetected, kiloDetected, rooDetected, traeDetected, aiderDetected, zoDetected] = await Promise.all([
|
|
2016
2133
|
detectCodexClient(),
|
|
2017
2134
|
detectClaudeCodeClient(),
|
|
2018
2135
|
detectContinueClient(),
|
|
2019
2136
|
detectClineClient(),
|
|
2137
|
+
detectGsdClient(),
|
|
2020
2138
|
detectKiloClient(),
|
|
2021
2139
|
detectRooClient(),
|
|
2022
2140
|
detectTraeClient(),
|
|
@@ -2060,6 +2178,15 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2060
2178
|
icon: "◈",
|
|
2061
2179
|
siteUrl: "https://cline.bot",
|
|
2062
2180
|
},
|
|
2181
|
+
{
|
|
2182
|
+
id: "gsd",
|
|
2183
|
+
label: "Get Shit Done 2",
|
|
2184
|
+
summaryLabel: "GSD",
|
|
2185
|
+
detected: gsdDetected,
|
|
2186
|
+
recommended: true,
|
|
2187
|
+
icon: "▣",
|
|
2188
|
+
siteUrl: "https://github.com/gsd-build/gsd-2",
|
|
2189
|
+
},
|
|
2063
2190
|
{
|
|
2064
2191
|
id: "openclaw",
|
|
2065
2192
|
label: "OpenClaw",
|
|
@@ -2179,6 +2306,7 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2179
2306
|
let modelCacheMigration = null;
|
|
2180
2307
|
let continueConfigPath = null;
|
|
2181
2308
|
let clineConfigPaths = [];
|
|
2309
|
+
let gsdConfigPaths = [];
|
|
2182
2310
|
let openClawConfigPath = null;
|
|
2183
2311
|
let openClawCliWarning = null;
|
|
2184
2312
|
let openCodeConfigPaths = [];
|
|
@@ -2274,6 +2402,15 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2274
2402
|
apiKey: authCredential,
|
|
2275
2403
|
});
|
|
2276
2404
|
}
|
|
2405
|
+
if (selectedSetupClients.has("gsd")) {
|
|
2406
|
+
progress.update("Configuring GSD");
|
|
2407
|
+
gsdConfigPaths = await writeGsdConfig({
|
|
2408
|
+
backendUrl,
|
|
2409
|
+
model: resolved?.model ?? DEFAULT_CODEX_MODEL,
|
|
2410
|
+
models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
|
|
2411
|
+
apiKey: authCredential,
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2277
2414
|
if (selectedSetupClients.has("openclaw")) {
|
|
2278
2415
|
progress.update("Configuring OpenClaw");
|
|
2279
2416
|
const openClawResult = await setupOpenClaw({
|
|
@@ -2351,8 +2488,8 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2351
2488
|
if (resolved?.note)
|
|
2352
2489
|
summaryNotes.add(resolved.note);
|
|
2353
2490
|
if (claudeAccess?.enabled) {
|
|
2354
|
-
summaryNotes.add("Claude Code: exported ANTHROPIC_BASE_URL and
|
|
2355
|
-
summaryNotes.add("Claude Code
|
|
2491
|
+
summaryNotes.add("Claude Code: exported ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY, and CLAUDE_CODE_SIMPLE=1 for the official client.");
|
|
2492
|
+
summaryNotes.add("If Claude Code is already open, restart it so the custom-key env takes effect cleanly. `claude auth status` should show `authMethod: api_key` once the new env is loaded.");
|
|
2356
2493
|
}
|
|
2357
2494
|
else if (claudeAccess?.note) {
|
|
2358
2495
|
summaryNotes.add(claudeAccess.note);
|
|
@@ -2496,6 +2633,16 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2496
2633
|
else {
|
|
2497
2634
|
this.log("- Cline: not detected (skipped)");
|
|
2498
2635
|
}
|
|
2636
|
+
if (selectedSetupClients.has("gsd")) {
|
|
2637
|
+
this.log(`- GSD: configured (${gsdConfigPaths.join(", ")})`);
|
|
2638
|
+
this.log("- GSD note: configured with the OpenAI Responses API so tool calling works reliably.");
|
|
2639
|
+
}
|
|
2640
|
+
else if (gsdDetected) {
|
|
2641
|
+
this.log("- GSD: detected but skipped");
|
|
2642
|
+
}
|
|
2643
|
+
else {
|
|
2644
|
+
this.log("- GSD: not detected (skipped)");
|
|
2645
|
+
}
|
|
2499
2646
|
const openClawDetected = setupClients.find((client) => client.id === "openclaw")?.detected ?? false;
|
|
2500
2647
|
if (selectedSetupClients.has("openclaw")) {
|
|
2501
2648
|
this.log(`- OpenClaw: configured (${openClawConfigPath})`);
|
|
@@ -2621,7 +2768,7 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
2621
2768
|
});
|
|
2622
2769
|
}
|
|
2623
2770
|
}
|
|
2624
|
-
SetupCommand.description = "One-time setup: configure Codex, Claude Code, plus any detected Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Trae, Aider, or Zo installs to use The Claw Bay";
|
|
2771
|
+
SetupCommand.description = "One-time setup: configure Codex, Claude Code, plus any detected Continue, Cline, GSD, OpenClaw, OpenCode, Kilo, Roo Code, Trae, Aider, or Zo installs to use The Claw Bay";
|
|
2625
2772
|
SetupCommand.flags = {
|
|
2626
2773
|
backend: core_1.Flags.string({
|
|
2627
2774
|
required: false,
|
|
@@ -2639,7 +2786,7 @@ SetupCommand.flags = {
|
|
|
2639
2786
|
}),
|
|
2640
2787
|
clients: core_1.Flags.string({
|
|
2641
2788
|
required: false,
|
|
2642
|
-
description: "Detected local clients to configure: codex, claude, continue, cline, openclaw, opencode, kilo, roo, trae, aider, zo",
|
|
2789
|
+
description: "Detected local clients to configure: codex, claude, continue, cline, gsd, openclaw, opencode, kilo, roo, trae, aider, zo",
|
|
2643
2790
|
}),
|
|
2644
2791
|
yes: core_1.Flags.boolean({
|
|
2645
2792
|
required: false,
|
|
@@ -5,11 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createBrowserLinkedDeviceSession = createBrowserLinkedDeviceSession;
|
|
7
7
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
9
|
const node_http_1 = __importDefault(require("node:http"));
|
|
9
10
|
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
12
|
const node_child_process_1 = require("node:child_process");
|
|
13
|
+
const paths_1 = require("./config/paths");
|
|
11
14
|
const BROWSER_SETUP_TIMEOUT_MS = 15 * 60000;
|
|
12
15
|
const BROWSER_SETUP_POLL_INTERVAL_MS = 2000;
|
|
16
|
+
const INSTALL_ID_PATH = node_path_1.default.join(paths_1.theclawbayConfigDir, "install-id");
|
|
13
17
|
function preferredBrowserCallbackPort() {
|
|
14
18
|
const parsed = Number(process.env.THECLAWBAY_CALLBACK_PORT?.trim() || "");
|
|
15
19
|
if (Number.isFinite(parsed) && parsed > 0 && parsed < 65536) {
|
|
@@ -46,6 +50,30 @@ function openUrlInBrowser(url) {
|
|
|
46
50
|
return false;
|
|
47
51
|
}
|
|
48
52
|
}
|
|
53
|
+
function normalizeInstallId(value) {
|
|
54
|
+
const normalized = value.trim();
|
|
55
|
+
if (normalized.length < 16 || normalized.length > 128)
|
|
56
|
+
return null;
|
|
57
|
+
return normalized;
|
|
58
|
+
}
|
|
59
|
+
async function getOrCreateInstallId() {
|
|
60
|
+
try {
|
|
61
|
+
const existing = normalizeInstallId(await promises_1.default.readFile(INSTALL_ID_PATH, "utf8"));
|
|
62
|
+
if (existing)
|
|
63
|
+
return existing;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const err = error;
|
|
67
|
+
if (err.code !== "ENOENT") {
|
|
68
|
+
throw new Error(`failed to read local install identity: ${err.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const installId = node_crypto_1.default.randomUUID();
|
|
72
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(INSTALL_ID_PATH), { recursive: true });
|
|
73
|
+
await promises_1.default.writeFile(INSTALL_ID_PATH, `${installId}\n`, "utf8");
|
|
74
|
+
await promises_1.default.chmod(INSTALL_ID_PATH, 0o600).catch(() => { });
|
|
75
|
+
return installId;
|
|
76
|
+
}
|
|
49
77
|
function htmlPage(title, message) {
|
|
50
78
|
return `<!doctype html>
|
|
51
79
|
<html lang="en">
|
|
@@ -239,6 +267,7 @@ async function createBrowserLinkedDeviceSession(params) {
|
|
|
239
267
|
const exchangeSignal = exchangeAbortController.signal;
|
|
240
268
|
let exchangeInFlight = null;
|
|
241
269
|
try {
|
|
270
|
+
const installId = await getOrCreateInstallId();
|
|
242
271
|
const startResponse = await fetch(`${params.backendUrl}/api/setup/device-session/start`, {
|
|
243
272
|
method: "POST",
|
|
244
273
|
headers: { "Content-Type": "application/json" },
|
|
@@ -246,6 +275,7 @@ async function createBrowserLinkedDeviceSession(params) {
|
|
|
246
275
|
callbackUrl: callbackServer.callbackUrl,
|
|
247
276
|
state,
|
|
248
277
|
deviceLabel: label,
|
|
278
|
+
installId,
|
|
249
279
|
selectedClients: params.selectedClients,
|
|
250
280
|
}),
|
|
251
281
|
signal: AbortSignal.timeout(20000),
|
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, experimental Trae, and experimental Zo to The Claw Bay.",
|
|
3
|
+
"version": "0.3.54",
|
|
4
|
+
"description": "CLI for connecting Codex, Continue, Cline, GSD, OpenClaw, OpenCode, Kilo, Roo Code, Aider, experimental Trae, and experimental Zo to The Claw Bay.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
7
7
|
"theclawbay": "dist/index.js"
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"api-key",
|
|
36
36
|
"continue",
|
|
37
37
|
"cline",
|
|
38
|
+
"gsd",
|
|
38
39
|
"openclaw",
|
|
39
40
|
"opencode",
|
|
40
41
|
"kilo",
|