theclawbay 0.3.33 → 0.3.34

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.
@@ -166,6 +166,57 @@ function objectRecordOr(value, fallback) {
166
166
  function isTheClawBayOpenAiCompatibleBaseUrl(value) {
167
167
  return typeof value === "string" && value.trim().endsWith(THECLAWBAY_OPENAI_PROXY_SUFFIX);
168
168
  }
169
+ function uniqueStrings(values) {
170
+ const output = [];
171
+ const seen = new Set();
172
+ for (const value of values) {
173
+ const normalized = value.trim();
174
+ if (!normalized || seen.has(normalized))
175
+ continue;
176
+ seen.add(normalized);
177
+ output.push(normalized);
178
+ }
179
+ return output;
180
+ }
181
+ function xdgConfigHomeDir() {
182
+ const raw = process.env.XDG_CONFIG_HOME?.trim();
183
+ if (raw)
184
+ return raw;
185
+ return node_path_1.default.join(node_os_1.default.homedir(), ".config");
186
+ }
187
+ function configDirCandidates(appName) {
188
+ const home = node_os_1.default.homedir();
189
+ const dirs = [node_path_1.default.join(xdgConfigHomeDir(), appName)];
190
+ if (node_os_1.default.platform() === "darwin") {
191
+ dirs.push(node_path_1.default.join(home, "Library", "Application Support", appName));
192
+ }
193
+ else if (node_os_1.default.platform() === "win32") {
194
+ dirs.push(node_path_1.default.join(roamingAppDataDir(), appName));
195
+ }
196
+ return uniqueStrings(dirs);
197
+ }
198
+ function openCodeConfigCleanupTargets() {
199
+ const home = node_os_1.default.homedir();
200
+ const dirs = configDirCandidates("opencode");
201
+ const candidates = [];
202
+ for (const dir of dirs) {
203
+ candidates.push(node_path_1.default.join(dir, "opencode.json"));
204
+ candidates.push(node_path_1.default.join(dir, ".opencode.json"));
205
+ candidates.push(node_path_1.default.join(dir, "config.json"));
206
+ }
207
+ candidates.push(node_path_1.default.join(home, ".opencode.json"));
208
+ return uniqueStrings(candidates);
209
+ }
210
+ function kiloConfigCleanupTargets() {
211
+ const home = node_os_1.default.homedir();
212
+ const dirs = uniqueStrings([...configDirCandidates("kilo"), node_path_1.default.join(home, ".kilo")]);
213
+ const candidates = [];
214
+ for (const dir of dirs) {
215
+ candidates.push(node_path_1.default.join(dir, "opencode.json"));
216
+ candidates.push(node_path_1.default.join(dir, "config.json"));
217
+ }
218
+ return uniqueStrings(candidates);
219
+ }
169
220
  async function readJsonObjectFile(filePath) {
170
221
  const existingRaw = await readFileIfExists(filePath);
171
222
  if (existingRaw === null || !existingRaw.trim())
@@ -449,85 +500,169 @@ async function cleanupOpenClawConfig() {
449
500
  await promises_1.default.writeFile(configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
450
501
  return true;
451
502
  }
452
- async function cleanupOpenCodeFamilyConfig(params) {
453
- const existingRaw = await readFileIfExists(params.configPath);
454
- if (existingRaw === null || !existingRaw.trim()) {
455
- return removeFileIfExists(params.restoreStatePath);
503
+ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
504
+ if (typeof snapshot !== "object" || snapshot === null || Array.isArray(snapshot))
505
+ return null;
506
+ const obj = snapshot;
507
+ if (obj.version === 2 && Array.isArray(obj.targets)) {
508
+ const targets = [];
509
+ for (const entry of obj.targets) {
510
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
511
+ continue;
512
+ const candidate = entry;
513
+ const configPath = typeof candidate.configPath === "string" ? candidate.configPath.trim() : "";
514
+ if (!configPath)
515
+ continue;
516
+ targets.push({
517
+ configPath,
518
+ existed: candidate.existed === true,
519
+ openAiProvider: typeof candidate.openAiProvider === "object" && candidate.openAiProvider !== null && !Array.isArray(candidate.openAiProvider)
520
+ ? candidate.openAiProvider
521
+ : null,
522
+ model: typeof candidate.model === "string" ? candidate.model : null,
523
+ schema: typeof candidate.schema === "string" ? candidate.schema : null,
524
+ });
525
+ }
526
+ return { version: 2, targets };
456
527
  }
457
- let doc;
528
+ const looksLikeV1 = "openAiProvider" in obj || "model" in obj || "schema" in obj;
529
+ if (!looksLikeV1)
530
+ return null;
531
+ return {
532
+ version: 2,
533
+ targets: [
534
+ {
535
+ configPath: fallbackConfigPath,
536
+ existed: true,
537
+ openAiProvider: typeof obj.openAiProvider === "object" && obj.openAiProvider !== null && !Array.isArray(obj.openAiProvider)
538
+ ? obj.openAiProvider
539
+ : null,
540
+ model: typeof obj.model === "string" ? obj.model : null,
541
+ schema: typeof obj.schema === "string" ? obj.schema : null,
542
+ },
543
+ ],
544
+ };
545
+ }
546
+ async function readOpenCodeRestoreSnapshot(restoreStatePath, fallbackConfigPath) {
547
+ const existingRaw = await readFileIfExists(restoreStatePath);
548
+ if (existingRaw === null || !existingRaw.trim())
549
+ return null;
458
550
  try {
459
- doc = objectRecordOr(JSON.parse(existingRaw), {});
551
+ return normalizeOpenCodeRestoreSnapshot(JSON.parse(existingRaw), fallbackConfigPath);
460
552
  }
461
553
  catch {
462
- return false;
554
+ return null;
463
555
  }
464
- const snapshotRaw = await readFileIfExists(params.restoreStatePath);
556
+ }
557
+ async function cleanupOpenCodeFamilyConfigs(params) {
558
+ const configPaths = uniqueStrings(params.configPaths);
559
+ const fallbackConfigPath = configPaths[0] ?? node_path_1.default.join(xdgConfigHomeDir(), "opencode", "opencode.json");
560
+ const snapshot = await readOpenCodeRestoreSnapshot(params.restoreStatePath, fallbackConfigPath);
561
+ const snapshotTargets = snapshot?.targets ?? [];
465
562
  let changed = false;
466
- const providerRoot = objectRecordOr(doc.provider, {});
467
- if (DEFAULT_PROVIDER_ID in providerRoot) {
468
- delete providerRoot[DEFAULT_PROVIDER_ID];
469
- changed = true;
470
- }
471
- if (snapshotRaw?.trim()) {
563
+ const restoreConfig = async (target) => {
564
+ if (!target.existed) {
565
+ changed = (await removeFileIfExists(target.configPath)) || changed;
566
+ return;
567
+ }
568
+ const existingRaw = await readFileIfExists(target.configPath);
569
+ if (existingRaw === null || !existingRaw.trim()) {
570
+ return;
571
+ }
572
+ let doc;
472
573
  try {
473
- const snapshot = JSON.parse(snapshotRaw);
474
- if (snapshot.openAiProvider) {
475
- providerRoot[OPENAI_PROVIDER_ID] = snapshot.openAiProvider;
476
- }
477
- else if (OPENAI_PROVIDER_ID in providerRoot) {
478
- delete providerRoot[OPENAI_PROVIDER_ID];
479
- }
480
- doc.provider = providerRoot;
481
- if (snapshot.model === null) {
482
- if (isManagedOpenCodeModel(doc.model) || typeof doc.model === "string" && doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`)) {
483
- delete doc.model;
484
- }
485
- }
486
- else {
487
- doc.model = snapshot.model;
488
- }
489
- if (snapshot.schema === null) {
490
- if (typeof doc.$schema === "string")
491
- delete doc.$schema;
574
+ doc = objectRecordOr(JSON.parse(existingRaw), {});
575
+ }
576
+ catch {
577
+ return;
578
+ }
579
+ const providerRoot = objectRecordOr(doc.provider, {});
580
+ if (DEFAULT_PROVIDER_ID in providerRoot) {
581
+ delete providerRoot[DEFAULT_PROVIDER_ID];
582
+ changed = true;
583
+ }
584
+ if (target.openAiProvider) {
585
+ providerRoot[OPENAI_PROVIDER_ID] = target.openAiProvider;
586
+ }
587
+ else if (OPENAI_PROVIDER_ID in providerRoot) {
588
+ delete providerRoot[OPENAI_PROVIDER_ID];
589
+ }
590
+ doc.provider = providerRoot;
591
+ if (target.model === null) {
592
+ if (isManagedOpenCodeModel(doc.model) ||
593
+ (typeof doc.model === "string" && doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`))) {
594
+ delete doc.model;
595
+ changed = true;
492
596
  }
493
- else {
494
- doc.$schema = snapshot.schema;
597
+ }
598
+ else {
599
+ doc.model = target.model;
600
+ changed = true;
601
+ }
602
+ if (target.schema === null) {
603
+ if (typeof doc.$schema === "string") {
604
+ delete doc.$schema;
605
+ changed = true;
495
606
  }
607
+ }
608
+ else {
609
+ doc.$schema = target.schema;
496
610
  changed = true;
497
- await promises_1.default.writeFile(params.configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
498
- await removeFileIfExists(params.restoreStatePath);
499
- return changed;
500
611
  }
501
- catch {
502
- // Fall through to best-effort cleanup below.
612
+ await promises_1.default.writeFile(target.configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
613
+ };
614
+ if (snapshotTargets.length > 0) {
615
+ for (const target of snapshotTargets) {
616
+ await restoreConfig(target);
503
617
  }
618
+ await removeFileIfExists(params.restoreStatePath);
619
+ return changed;
504
620
  }
505
- doc.provider = providerRoot;
506
- const model = doc.model;
507
- if (typeof model === "string" && (model.startsWith(`${DEFAULT_PROVIDER_ID}/`) || isManagedOpenCodeModel(model))) {
508
- delete doc.model;
621
+ for (const configPath of configPaths) {
622
+ const existingRaw = await readFileIfExists(configPath);
623
+ if (existingRaw === null || !existingRaw.trim())
624
+ continue;
625
+ let doc;
626
+ try {
627
+ doc = objectRecordOr(JSON.parse(existingRaw), {});
628
+ }
629
+ catch {
630
+ continue;
631
+ }
632
+ let fileChanged = false;
633
+ const providerRoot = objectRecordOr(doc.provider, {});
634
+ if (DEFAULT_PROVIDER_ID in providerRoot) {
635
+ delete providerRoot[DEFAULT_PROVIDER_ID];
636
+ fileChanged = true;
637
+ }
638
+ const model = doc.model;
639
+ if (typeof model === "string" &&
640
+ (model.startsWith(`${DEFAULT_PROVIDER_ID}/`) || isManagedOpenCodeModel(model))) {
641
+ delete doc.model;
642
+ fileChanged = true;
643
+ }
644
+ if (isManagedOpenCodeProvider(providerRoot[OPENAI_PROVIDER_ID])) {
645
+ delete providerRoot[OPENAI_PROVIDER_ID];
646
+ fileChanged = true;
647
+ }
648
+ if (!fileChanged)
649
+ continue;
509
650
  changed = true;
510
- }
511
- if (isManagedOpenCodeProvider(providerRoot[OPENAI_PROVIDER_ID])) {
512
- delete providerRoot[OPENAI_PROVIDER_ID];
513
651
  doc.provider = providerRoot;
514
- changed = true;
652
+ await promises_1.default.writeFile(configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
515
653
  }
516
- if (!changed)
517
- return false;
518
- await promises_1.default.writeFile(params.configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
519
654
  await removeFileIfExists(params.restoreStatePath);
520
- return true;
655
+ return changed;
521
656
  }
522
657
  async function cleanupOpenCodeConfig() {
523
- return cleanupOpenCodeFamilyConfig({
524
- configPath: node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "opencode.json"),
658
+ return cleanupOpenCodeFamilyConfigs({
659
+ configPaths: openCodeConfigCleanupTargets(),
525
660
  restoreStatePath: OPENCODE_RESTORE_STATE_PATH,
526
661
  });
527
662
  }
528
663
  async function cleanupKiloConfig() {
529
- return cleanupOpenCodeFamilyConfig({
530
- configPath: node_path_1.default.join(node_os_1.default.homedir(), ".config", "kilo", "opencode.json"),
664
+ return cleanupOpenCodeFamilyConfigs({
665
+ configPaths: kiloConfigCleanupTargets(),
531
666
  restoreStatePath: KILO_RESTORE_STATE_PATH,
532
667
  });
533
668
  }
@@ -272,6 +272,35 @@ function openAiCompatibleProxyUrl(backendUrl) {
272
272
  function isTheClawBayOpenAiCompatibleBaseUrl(value) {
273
273
  return typeof value === "string" && value.trim().endsWith(THECLAWBAY_OPENAI_PROXY_SUFFIX);
274
274
  }
275
+ function uniqueStrings(values) {
276
+ const output = [];
277
+ const seen = new Set();
278
+ for (const value of values) {
279
+ const normalized = value.trim();
280
+ if (!normalized || seen.has(normalized))
281
+ continue;
282
+ seen.add(normalized);
283
+ output.push(normalized);
284
+ }
285
+ return output;
286
+ }
287
+ function xdgConfigHomeDir() {
288
+ const raw = process.env.XDG_CONFIG_HOME?.trim();
289
+ if (raw)
290
+ return raw;
291
+ return node_path_1.default.join(node_os_1.default.homedir(), ".config");
292
+ }
293
+ function configDirCandidates(appName) {
294
+ const home = node_os_1.default.homedir();
295
+ const dirs = [node_path_1.default.join(xdgConfigHomeDir(), appName)];
296
+ if (node_os_1.default.platform() === "darwin") {
297
+ dirs.push(node_path_1.default.join(home, "Library", "Application Support", appName));
298
+ }
299
+ else if (node_os_1.default.platform() === "win32") {
300
+ dirs.push(node_path_1.default.join(roamingAppDataDir(), appName));
301
+ }
302
+ return uniqueStrings(dirs);
303
+ }
275
304
  function roamingAppDataDir() {
276
305
  if (process.env.APPDATA?.trim())
277
306
  return process.env.APPDATA;
@@ -1109,6 +1138,99 @@ function buildOpenCodeModelsObject(models) {
1109
1138
  }
1110
1139
  return result;
1111
1140
  }
1141
+ function normalizeOpenCodeRestoreSnapshot(snapshot, fallbackConfigPath) {
1142
+ if (typeof snapshot !== "object" || snapshot === null || Array.isArray(snapshot))
1143
+ return null;
1144
+ const obj = snapshot;
1145
+ if (obj.version === 2 && Array.isArray(obj.targets)) {
1146
+ const targets = [];
1147
+ for (const entry of obj.targets) {
1148
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
1149
+ continue;
1150
+ const candidate = entry;
1151
+ const configPath = typeof candidate.configPath === "string" ? candidate.configPath.trim() : "";
1152
+ if (!configPath)
1153
+ continue;
1154
+ targets.push({
1155
+ configPath,
1156
+ existed: candidate.existed === true,
1157
+ openAiProvider: typeof candidate.openAiProvider === "object" && candidate.openAiProvider !== null && !Array.isArray(candidate.openAiProvider)
1158
+ ? candidate.openAiProvider
1159
+ : null,
1160
+ model: typeof candidate.model === "string" ? candidate.model : null,
1161
+ schema: typeof candidate.schema === "string" ? candidate.schema : null,
1162
+ });
1163
+ }
1164
+ return { version: 2, targets };
1165
+ }
1166
+ const looksLikeV1 = "openAiProvider" in obj || "model" in obj || "schema" in obj;
1167
+ if (!looksLikeV1)
1168
+ return null;
1169
+ return {
1170
+ version: 2,
1171
+ targets: [
1172
+ {
1173
+ configPath: fallbackConfigPath,
1174
+ existed: true,
1175
+ openAiProvider: typeof obj.openAiProvider === "object" && obj.openAiProvider !== null && !Array.isArray(obj.openAiProvider)
1176
+ ? obj.openAiProvider
1177
+ : null,
1178
+ model: typeof obj.model === "string" ? obj.model : null,
1179
+ schema: typeof obj.schema === "string" ? obj.schema : null,
1180
+ },
1181
+ ],
1182
+ };
1183
+ }
1184
+ async function readOpenCodeRestoreSnapshot(restoreStatePath, fallbackConfigPath) {
1185
+ const existingRaw = await readFileIfExists(restoreStatePath);
1186
+ if (existingRaw === null || !existingRaw.trim())
1187
+ return null;
1188
+ try {
1189
+ return normalizeOpenCodeRestoreSnapshot(JSON.parse(existingRaw), fallbackConfigPath);
1190
+ }
1191
+ catch {
1192
+ return null;
1193
+ }
1194
+ }
1195
+ function openCodeConfigFileCandidates() {
1196
+ const home = node_os_1.default.homedir();
1197
+ const dirs = configDirCandidates("opencode");
1198
+ const candidates = [];
1199
+ for (const dir of dirs) {
1200
+ candidates.push(node_path_1.default.join(dir, "opencode.json"));
1201
+ candidates.push(node_path_1.default.join(dir, ".opencode.json"));
1202
+ candidates.push(node_path_1.default.join(dir, "config.json"));
1203
+ }
1204
+ candidates.push(node_path_1.default.join(home, ".opencode.json"));
1205
+ return uniqueStrings(candidates);
1206
+ }
1207
+ function kiloConfigFileCandidates() {
1208
+ const home = node_os_1.default.homedir();
1209
+ const dirs = uniqueStrings([...configDirCandidates("kilo"), node_path_1.default.join(home, ".kilo")]);
1210
+ const candidates = [];
1211
+ for (const dir of dirs) {
1212
+ candidates.push(node_path_1.default.join(dir, "opencode.json"));
1213
+ candidates.push(node_path_1.default.join(dir, "config.json"));
1214
+ }
1215
+ return uniqueStrings(candidates);
1216
+ }
1217
+ function resolveOpenCodeConfigTargets() {
1218
+ const candidates = openCodeConfigFileCandidates();
1219
+ const existing = candidates.filter((candidate) => (0, node_fs_1.existsSync)(candidate));
1220
+ if (existing.length)
1221
+ return existing;
1222
+ const primaryDir = configDirCandidates("opencode")[0] ?? node_path_1.default.join(xdgConfigHomeDir(), "opencode");
1223
+ return uniqueStrings([node_path_1.default.join(primaryDir, "opencode.json"), node_path_1.default.join(primaryDir, ".opencode.json")]);
1224
+ }
1225
+ function resolveKiloConfigTargets() {
1226
+ const candidates = kiloConfigFileCandidates();
1227
+ const existing = candidates.filter((candidate) => (0, node_fs_1.existsSync)(candidate));
1228
+ if (existing.length)
1229
+ return existing;
1230
+ const home = node_os_1.default.homedir();
1231
+ const primaryDir = uniqueStrings([...configDirCandidates("kilo"), node_path_1.default.join(home, ".kilo")])[0] ?? node_path_1.default.join(xdgConfigHomeDir(), "kilo");
1232
+ return uniqueStrings([node_path_1.default.join(primaryDir, "opencode.json")]);
1233
+ }
1112
1234
  function isManagedOpenCodeProvider(provider) {
1113
1235
  const options = objectRecordOr(provider.options, {});
1114
1236
  return isTheClawBayOpenAiCompatibleBaseUrl(options.baseURL);
@@ -1121,58 +1243,74 @@ function isManagedOpenCodeModel(value, supportedModelIds) {
1121
1243
  return supportedModelIds.has(value.slice(`${OPENAI_PROVIDER_ID}/`.length));
1122
1244
  }
1123
1245
  async function writeOpenCodeFamilyConfig(params) {
1124
- await promises_1.default.mkdir(node_path_1.default.dirname(params.configPath), { recursive: true });
1125
- let existingRaw = "";
1126
- try {
1127
- existingRaw = await promises_1.default.readFile(params.configPath, "utf8");
1128
- }
1129
- catch (error) {
1130
- const err = error;
1131
- if (err.code !== "ENOENT")
1132
- throw error;
1133
- }
1134
- let doc = {};
1135
- if (existingRaw.trim()) {
1246
+ const normalizedConfigPaths = uniqueStrings(params.configPaths);
1247
+ const fallbackConfigPath = normalizedConfigPaths[0] ?? node_path_1.default.join(xdgConfigHomeDir(), "opencode", "opencode.json");
1248
+ const restoreState = (await readOpenCodeRestoreSnapshot(params.restoreStatePath, fallbackConfigPath)) ??
1249
+ {
1250
+ version: 2,
1251
+ targets: [],
1252
+ };
1253
+ const supportedModelIds = new Set(params.models.map((entry) => entry.id).filter(Boolean));
1254
+ const restoreHasTarget = (configPath) => restoreState.targets.some((entry) => entry.configPath === configPath);
1255
+ for (const configPath of normalizedConfigPaths) {
1256
+ await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
1257
+ let existed = true;
1258
+ let existingRaw = "";
1136
1259
  try {
1137
- doc = objectRecordOr(JSON.parse(existingRaw), {});
1260
+ existingRaw = await promises_1.default.readFile(configPath, "utf8");
1138
1261
  }
1139
- catch {
1140
- throw new Error(`invalid JSON in config: ${params.configPath}`);
1262
+ catch (error) {
1263
+ const err = error;
1264
+ if (err.code !== "ENOENT")
1265
+ throw error;
1266
+ existed = false;
1141
1267
  }
1268
+ let doc = {};
1269
+ if (existingRaw.trim()) {
1270
+ try {
1271
+ doc = objectRecordOr(JSON.parse(existingRaw), {});
1272
+ }
1273
+ catch {
1274
+ throw new Error(`invalid JSON in config: ${configPath}`);
1275
+ }
1276
+ }
1277
+ const providerRoot = objectRecordOr(doc.provider, {});
1278
+ const openAiProvider = objectRecordOr(providerRoot[OPENAI_PROVIDER_ID], {});
1279
+ const alreadyManaged = isManagedOpenCodeProvider(openAiProvider) && isManagedOpenCodeModel(doc.model, supportedModelIds);
1280
+ if (!alreadyManaged && !restoreHasTarget(configPath)) {
1281
+ restoreState.targets.push({
1282
+ configPath,
1283
+ existed,
1284
+ openAiProvider: Object.keys(openAiProvider).length > 0 ? openAiProvider : null,
1285
+ model: typeof doc.model === "string" && !doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`)
1286
+ ? doc.model
1287
+ : null,
1288
+ schema: typeof doc.$schema === "string" ? doc.$schema : null,
1289
+ });
1290
+ }
1291
+ const managedOpenAiProvider = objectRecordOr(providerRoot[OPENAI_PROVIDER_ID], {});
1292
+ const optionsRoot = objectRecordOr(managedOpenAiProvider.options, {});
1293
+ optionsRoot.baseURL = `${trimTrailingSlash(params.backendUrl)}/api/codex-auth/v1/proxy/v1`;
1294
+ optionsRoot.apiKey = params.apiKey;
1295
+ managedOpenAiProvider.options = optionsRoot;
1296
+ managedOpenAiProvider.models = buildOpenCodeModelsObject(params.models);
1297
+ managedOpenAiProvider.whitelist = params.models.map((entry) => entry.id).filter(Boolean);
1298
+ if ("blacklist" in managedOpenAiProvider) {
1299
+ delete managedOpenAiProvider.blacklist;
1300
+ }
1301
+ providerRoot[OPENAI_PROVIDER_ID] = managedOpenAiProvider;
1302
+ if (DEFAULT_PROVIDER_ID in providerRoot) {
1303
+ delete providerRoot[DEFAULT_PROVIDER_ID];
1304
+ }
1305
+ doc.$schema = params.schemaUrl;
1306
+ doc.provider = providerRoot;
1307
+ doc.model = `${OPENAI_PROVIDER_ID}/${params.model}`;
1308
+ await promises_1.default.writeFile(configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
1142
1309
  }
1143
- const providerRoot = objectRecordOr(doc.provider, {});
1144
- const openAiProvider = objectRecordOr(providerRoot[OPENAI_PROVIDER_ID], {});
1145
- const supportedModelIds = new Set(params.models.map((entry) => entry.id).filter(Boolean));
1146
- const alreadyManaged = isManagedOpenCodeProvider(openAiProvider) && isManagedOpenCodeModel(doc.model, supportedModelIds);
1147
- if (!alreadyManaged) {
1148
- const snapshot = {
1149
- openAiProvider: Object.keys(openAiProvider).length > 0 ? openAiProvider : null,
1150
- model: typeof doc.model === "string" && !doc.model.startsWith(`${DEFAULT_PROVIDER_ID}/`)
1151
- ? doc.model
1152
- : null,
1153
- schema: typeof doc.$schema === "string" ? doc.$schema : null,
1154
- };
1155
- await writeJsonObjectFile(params.restoreStatePath, snapshot, 0o600);
1156
- }
1157
- const managedOpenAiProvider = objectRecordOr(providerRoot[OPENAI_PROVIDER_ID], {});
1158
- const optionsRoot = objectRecordOr(managedOpenAiProvider.options, {});
1159
- optionsRoot.baseURL = `${trimTrailingSlash(params.backendUrl)}/api/codex-auth/v1/proxy/v1`;
1160
- optionsRoot.apiKey = params.apiKey;
1161
- managedOpenAiProvider.options = optionsRoot;
1162
- managedOpenAiProvider.models = buildOpenCodeModelsObject(params.models);
1163
- managedOpenAiProvider.whitelist = params.models.map((entry) => entry.id).filter(Boolean);
1164
- if ("blacklist" in managedOpenAiProvider) {
1165
- delete managedOpenAiProvider.blacklist;
1166
- }
1167
- providerRoot[OPENAI_PROVIDER_ID] = managedOpenAiProvider;
1168
- if (DEFAULT_PROVIDER_ID in providerRoot) {
1169
- delete providerRoot[DEFAULT_PROVIDER_ID];
1170
- }
1171
- doc.$schema = params.schemaUrl;
1172
- doc.provider = providerRoot;
1173
- doc.model = `${OPENAI_PROVIDER_ID}/${params.model}`;
1174
- await promises_1.default.writeFile(params.configPath, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
1175
- return params.configPath;
1310
+ if (restoreState.targets.length > 0) {
1311
+ await writeJsonObjectFile(params.restoreStatePath, restoreState, 0o600);
1312
+ }
1313
+ return normalizedConfigPaths;
1176
1314
  }
1177
1315
  function buildOpenClawModels(models) {
1178
1316
  const supportedModelMap = new Map((0, supported_models_1.getSupportedModels)().map((model) => [model.id, model]));
@@ -1199,7 +1337,7 @@ function buildOpenClawModels(models) {
1199
1337
  }
1200
1338
  async function writeOpenCodeConfig(params) {
1201
1339
  return writeOpenCodeFamilyConfig({
1202
- configPath: node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "opencode.json"),
1340
+ configPaths: resolveOpenCodeConfigTargets(),
1203
1341
  restoreStatePath: OPENCODE_RESTORE_STATE_PATH,
1204
1342
  schemaUrl: OPENCODE_CONFIG_SCHEMA_URL,
1205
1343
  ...params,
@@ -1207,7 +1345,7 @@ async function writeOpenCodeConfig(params) {
1207
1345
  }
1208
1346
  async function writeKiloConfig(params) {
1209
1347
  return writeOpenCodeFamilyConfig({
1210
- configPath: node_path_1.default.join(node_os_1.default.homedir(), ".config", "kilo", "opencode.json"),
1348
+ configPaths: resolveKiloConfigTargets(),
1211
1349
  restoreStatePath: KILO_RESTORE_STATE_PATH,
1212
1350
  schemaUrl: KILO_CONFIG_SCHEMA_URL,
1213
1351
  ...params,
@@ -1382,8 +1520,8 @@ class SetupCommand extends base_command_1.BaseCommand {
1382
1520
  let clineConfigPaths = [];
1383
1521
  let openClawConfigPath = null;
1384
1522
  let openClawCliWarning = null;
1385
- let openCodePath = null;
1386
- let kiloConfigPath = null;
1523
+ let openCodeConfigPaths = [];
1524
+ let kiloConfigPaths = [];
1387
1525
  let rooConfigPaths = [];
1388
1526
  let traeBundlePathPatched = null;
1389
1527
  let aiderConfigPath = null;
@@ -1446,7 +1584,7 @@ class SetupCommand extends base_command_1.BaseCommand {
1446
1584
  }
1447
1585
  if (selectedSetupClients.has("opencode")) {
1448
1586
  progress.update("Configuring OpenCode");
1449
- openCodePath = await writeOpenCodeConfig({
1587
+ openCodeConfigPaths = await writeOpenCodeConfig({
1450
1588
  backendUrl,
1451
1589
  model: resolved?.model ?? DEFAULT_CODEX_MODEL,
1452
1590
  models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
@@ -1455,7 +1593,7 @@ class SetupCommand extends base_command_1.BaseCommand {
1455
1593
  }
1456
1594
  if (selectedSetupClients.has("kilo")) {
1457
1595
  progress.update("Configuring Kilo Code");
1458
- kiloConfigPath = await writeKiloConfig({
1596
+ kiloConfigPaths = await writeKiloConfig({
1459
1597
  backendUrl,
1460
1598
  model: resolved?.model ?? DEFAULT_CODEX_MODEL,
1461
1599
  models: resolved?.models ?? [{ id: DEFAULT_CODEX_MODEL, name: modelDisplayName(DEFAULT_CODEX_MODEL) }],
@@ -1619,7 +1757,7 @@ class SetupCommand extends base_command_1.BaseCommand {
1619
1757
  }
1620
1758
  const openCodeDetected = setupClients.find((client) => client.id === "opencode")?.detected ?? false;
1621
1759
  if (selectedSetupClients.has("opencode")) {
1622
- this.log(`- OpenCode: configured (${openCodePath})`);
1760
+ this.log(`- OpenCode: configured (${openCodeConfigPaths.join(", ")})`);
1623
1761
  }
1624
1762
  else if (openCodeDetected) {
1625
1763
  this.log("- OpenCode: detected but skipped");
@@ -1628,7 +1766,7 @@ class SetupCommand extends base_command_1.BaseCommand {
1628
1766
  this.log("- OpenCode: not detected (skipped)");
1629
1767
  }
1630
1768
  if (selectedSetupClients.has("kilo")) {
1631
- this.log(`- Kilo Code: configured (${kiloConfigPath})`);
1769
+ this.log(`- Kilo Code: configured (${kiloConfigPaths.join(", ")})`);
1632
1770
  }
1633
1771
  else if (kiloDetected) {
1634
1772
  this.log("- Kilo Code: detected but skipped");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "theclawbay",
3
- "version": "0.3.33",
3
+ "version": "0.3.34",
4
4
  "description": "CLI for connecting Codex, Continue, Cline, OpenClaw, OpenCode, Kilo, Roo Code, Aider, and experimental Trae to The Claw Bay.",
5
5
  "license": "MIT",
6
6
  "bin": {