@jacobbd/relay-ai 0.6.0 → 0.6.2

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/cli.js CHANGED
@@ -11,9 +11,8 @@ import {
11
11
  VERSION,
12
12
  VERTEX_ANTHROPIC_NPM,
13
13
  addCustomEndpointProvider,
14
- addGoRegistryStub,
14
+ addOpencodeCloudFromApiKey,
15
15
  addProviderFromTemplate,
16
- addZenRegistryStub,
17
16
  aliasModelId,
18
17
  appendCodexBodyDump,
19
18
  authenticateProvider,
@@ -47,12 +46,10 @@ import {
47
46
  favoriteProviderDisplayName,
48
47
  fetchAnthropicModels,
49
48
  fetchProviderCatalog,
50
- fetchRawOpencodeProviders,
51
49
  fetchTemplateModels,
52
50
  filterServerModelsByFavorites,
53
51
  findBinaryOnPath,
54
52
  findClaudeBinary,
55
- findOpencodeBinary,
56
53
  fmtCommand,
57
54
  fmtCount,
58
55
  fmtEnabledStar,
@@ -91,7 +88,6 @@ import {
91
88
  loadPreferences,
92
89
  loadRegistry,
93
90
  loadServerModels,
94
- localProviderToRegistry,
95
91
  logActiveModel,
96
92
  logConnected,
97
93
  logProxy,
@@ -171,14 +167,14 @@ import {
171
167
  validateCustomEndpointUrl,
172
168
  writeSecureLogLine,
173
169
  zenRegistryStub
174
- } from "./chunk-SV2Y6OCD.js";
170
+ } from "./chunk-I3I5LKZI.js";
175
171
  import {
176
172
  filterTemplates,
177
173
  init_provider_templates,
178
174
  listAddableTemplates,
179
175
  listSupportedTemplates,
180
176
  listVisibleOAuthTemplates
181
- } from "./chunk-MVBA7ABV.js";
177
+ } from "./chunk-EJONCU3B.js";
182
178
 
183
179
  // src/cli.ts
184
180
  import pc12 from "picocolors";
@@ -190,6 +186,144 @@ import { fileURLToPath } from "url";
190
186
  import pc from "picocolors";
191
187
  import * as p2 from "@clack/prompts";
192
188
 
189
+ // src/opencode-serve.ts
190
+ import { execSync, spawn } from "child_process";
191
+ import { existsSync } from "fs";
192
+ import { homedir } from "os";
193
+ import { join } from "path";
194
+ var isWindows = process.platform === "win32";
195
+ var OPENCODE_FALLBACK_PATHS = isWindows ? [
196
+ join(process.env["APPDATA"] ?? homedir(), "npm", "opencode.cmd"),
197
+ join(process.env["APPDATA"] ?? homedir(), "npm", "opencode"),
198
+ join(homedir(), "AppData", "Roaming", "npm", "opencode.cmd")
199
+ ] : [
200
+ join(homedir(), ".opencode", "bin", "opencode"),
201
+ join(homedir(), ".local", "bin", "opencode"),
202
+ join(homedir(), ".npm", "bin", "opencode"),
203
+ "/usr/local/bin/opencode",
204
+ "/opt/homebrew/bin/opencode"
205
+ ];
206
+ function findOpencodeBinary() {
207
+ try {
208
+ const result = execSync(isWindows ? "where.exe opencode" : "which opencode", {
209
+ encoding: "utf8",
210
+ stdio: ["pipe", "pipe", "pipe"]
211
+ });
212
+ const lines = result.trim().split("\n").map((l) => l.trim()).filter(Boolean);
213
+ const path2 = (isWindows ? lines.find((l) => l.toLowerCase().endsWith(".cmd")) : null) ?? lines[0];
214
+ if (path2) return path2;
215
+ } catch {
216
+ }
217
+ for (const path2 of OPENCODE_FALLBACK_PATHS) {
218
+ if (existsSync(path2)) return path2;
219
+ }
220
+ return null;
221
+ }
222
+ async function fetchRawOpencodeProviders() {
223
+ const binary = findOpencodeBinary();
224
+ if (!binary) return null;
225
+ return new Promise((resolve2) => {
226
+ let child = null;
227
+ let settled = false;
228
+ const TIMEOUT_MS = 1e4;
229
+ const finish = (value) => {
230
+ if (settled) return;
231
+ settled = true;
232
+ clearTimeout(timer);
233
+ try {
234
+ child?.kill();
235
+ } catch {
236
+ }
237
+ resolve2(value);
238
+ };
239
+ const timer = setTimeout(() => {
240
+ finish(null);
241
+ }, TIMEOUT_MS);
242
+ try {
243
+ child = isWindows ? spawn("cmd.exe", ["/c", binary, "serve", "--port", "0"], { stdio: ["pipe", "pipe", "pipe"] }) : spawn(binary, ["serve", "--port", "0"], { stdio: ["pipe", "pipe", "pipe"] });
244
+ } catch {
245
+ finish(null);
246
+ return;
247
+ }
248
+ const portRegex = /opencode server listening on http:\/\/127\.0\.0\.1:(\d+)/;
249
+ let portFound = false;
250
+ let stdoutBuf = "";
251
+ const onData = (chunk) => {
252
+ if (portFound) return;
253
+ stdoutBuf += chunk.toString();
254
+ const match = portRegex.exec(stdoutBuf);
255
+ if (!match) return;
256
+ portFound = true;
257
+ const port = match[1];
258
+ fetch(`http://127.0.0.1:${port}/config/providers`).then((res) => res.json()).then((data) => {
259
+ const raw = data.providers;
260
+ if (!Array.isArray(raw)) {
261
+ finish(null);
262
+ return;
263
+ }
264
+ finish(raw);
265
+ }).catch(() => {
266
+ finish(null);
267
+ });
268
+ };
269
+ child.stdout?.on("data", onData);
270
+ child.stderr?.on("data", onData);
271
+ child.on("error", () => {
272
+ finish(null);
273
+ });
274
+ child.on("exit", () => {
275
+ if (!settled) finish(null);
276
+ });
277
+ });
278
+ }
279
+
280
+ // src/registry/convert.ts
281
+ function modelToCached(model) {
282
+ return {
283
+ id: model.id,
284
+ name: model.name,
285
+ upstreamModelId: model.upstreamModelId,
286
+ family: model.family,
287
+ brand: model.brand,
288
+ contextWindow: model.contextWindow,
289
+ cost: model.cost,
290
+ isFree: model.isFree,
291
+ freeStatus: model.freeStatus,
292
+ modelFormat: model.modelFormat,
293
+ npm: model.npm,
294
+ apiUrl: model.apiBaseUrl,
295
+ supportedParameters: model.supportedParameters,
296
+ reasoning: model.reasoning,
297
+ interleavedReasoningField: model.interleavedReasoningField,
298
+ useResponsesLite: model.useResponsesLite,
299
+ preferWebSockets: model.preferWebSockets
300
+ };
301
+ }
302
+ function localProviderToRegistry(provider, opts) {
303
+ if (!isValidProviderId(provider.id)) return null;
304
+ if (provider.models.length === 0) return null;
305
+ const first = provider.models[0];
306
+ const apiUrl = (first.apiBaseUrl ?? first.baseUrl)?.trim();
307
+ const authType = opts?.authType ?? "api";
308
+ return {
309
+ id: provider.id,
310
+ templateId: opts?.templateId ?? provider.id,
311
+ name: provider.name,
312
+ enabled: true,
313
+ authRef: opts?.authRef ?? `keyring:provider:${provider.id}`,
314
+ authType,
315
+ api: {
316
+ npm: first.npm,
317
+ ...apiUrl ? { url: apiUrl } : {}
318
+ },
319
+ addedAt: (/* @__PURE__ */ new Date()).toISOString(),
320
+ modelsCache: {
321
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
322
+ models: provider.models.map(modelToCached)
323
+ }
324
+ };
325
+ }
326
+
193
327
  // src/registry/validate-import-key.ts
194
328
  function reject(reason, detail) {
195
329
  return { canImport: false, reason, detail };
@@ -399,23 +533,23 @@ async function importFromOpencode(options = {}) {
399
533
 
400
534
  // src/key-setup.ts
401
535
  import * as p from "@clack/prompts";
402
- import { appendFileSync, readFileSync, existsSync } from "fs";
403
- import { homedir } from "os";
536
+ import { appendFileSync, readFileSync, existsSync as existsSync2 } from "fs";
537
+ import { homedir as homedir2 } from "os";
404
538
  import { spawnSync } from "child_process";
405
539
  function detectShellProfile() {
406
540
  const shell = process.env["SHELL"] ?? "";
407
541
  if (process.platform === "darwin") {
408
- if (shell.includes("zsh")) return { display: "~/.zshrc", path: `${homedir()}/.zshrc` };
409
- if (shell.includes("bash")) return { display: "~/.bash_profile", path: `${homedir()}/.bash_profile` };
410
- return { display: "~/.profile", path: `${homedir()}/.profile` };
542
+ if (shell.includes("zsh")) return { display: "~/.zshrc", path: `${homedir2()}/.zshrc` };
543
+ if (shell.includes("bash")) return { display: "~/.bash_profile", path: `${homedir2()}/.bash_profile` };
544
+ return { display: "~/.profile", path: `${homedir2()}/.profile` };
411
545
  }
412
546
  if (process.platform === "linux") {
413
- if (shell.includes("zsh")) return { display: "~/.zshrc", path: `${homedir()}/.zshrc` };
414
- if (shell.includes("bash")) return { display: "~/.bashrc", path: `${homedir()}/.bashrc` };
415
- return { display: "~/.profile", path: `${homedir()}/.profile` };
547
+ if (shell.includes("zsh")) return { display: "~/.zshrc", path: `${homedir2()}/.zshrc` };
548
+ if (shell.includes("bash")) return { display: "~/.bashrc", path: `${homedir2()}/.bashrc` };
549
+ return { display: "~/.profile", path: `${homedir2()}/.profile` };
416
550
  }
417
- if (shell.includes("bash")) return { display: "~/.bashrc", path: `${homedir()}/.bashrc` };
418
- return { display: "~/.profile", path: `${homedir()}/.profile` };
551
+ if (shell.includes("bash")) return { display: "~/.bashrc", path: `${homedir2()}/.bashrc` };
552
+ return { display: "~/.profile", path: `${homedir2()}/.profile` };
419
553
  }
420
554
  async function resolveOrCollectApiKey(simulate = false, trace = false) {
421
555
  if (!simulate) {
@@ -423,7 +557,7 @@ async function resolveOrCollectApiKey(simulate = false, trace = false) {
423
557
  if (existing) return existing;
424
558
  }
425
559
  const isMac = process.platform === "darwin";
426
- const isWindows4 = process.platform === "win32";
560
+ const isWindows5 = process.platform === "win32";
427
561
  const isLinux = process.platform === "linux";
428
562
  if (simulate) {
429
563
  printDryRunPanel();
@@ -437,7 +571,7 @@ async function resolveOrCollectApiKey(simulate = false, trace = false) {
437
571
  };
438
572
  const storedKey = await readFromCredentialStore(keyDiag);
439
573
  if (storedKey) {
440
- const storeName = isMac ? "macOS Keychain" : isWindows4 ? "Windows Credential Manager" : "Secret Service";
574
+ const storeName = isMac ? "macOS Keychain" : isWindows5 ? "Windows Credential Manager" : "Secret Service";
441
575
  p.log.success(`Found key in ${storeName}`);
442
576
  process.env["OPENCODE_API_KEY"] = storedKey;
443
577
  return storedKey;
@@ -467,7 +601,7 @@ async function resolveOrCollectApiKey(simulate = false, trace = false) {
467
601
  { value: "session", label: "This session only", hint: "Not saved anywhere \u2014 you'll be asked again next time" }
468
602
  ];
469
603
  }
470
- if (isWindows4) {
604
+ if (isWindows5) {
471
605
  return [
472
606
  { value: "credential-manager", label: "Windows Credential Manager", hint: "Key stored securely; relay-ai reads it automatically next time" },
473
607
  { value: "setx", label: "Persistent environment variable (plaintext)", hint: "Runs setx \u2014 key visible in System Properties \u2192 Environment Variables" },
@@ -489,7 +623,7 @@ async function resolveOrCollectApiKey(simulate = false, trace = false) {
489
623
  const saveChoice = await p.select({
490
624
  message: "Where should we save the key?",
491
625
  options: saveOptions,
492
- initialValue: isMac ? "keychain" : isWindows4 ? "credential-manager" : secretServiceAvailable ? "secret-service" : "profile"
626
+ initialValue: isMac ? "keychain" : isWindows5 ? "credential-manager" : secretServiceAvailable ? "secret-service" : "profile"
493
627
  });
494
628
  if (p.isCancel(saveChoice)) {
495
629
  p.cancel("Cancelled.");
@@ -516,7 +650,7 @@ async function resolveOrCollectApiKey(simulate = false, trace = false) {
516
650
  if (await saveToCredentialStore(trimmedKey)) {
517
651
  try {
518
652
  const autoLoadLine = `export OPENCODE_API_KEY="$(security find-generic-password -s relay-ai -a ${GLOBAL_OPENCODE_KEYRING_ACCOUNT} -w 2>/dev/null)"`;
519
- const existing = existsSync(path2) ? readFileSync(path2, "utf8") : "";
653
+ const existing = existsSync2(path2) ? readFileSync(path2, "utf8") : "";
520
654
  if (!existing.includes(autoLoadLine)) {
521
655
  appendFileSync(path2, `
522
656
  # relay-ai: load API key from macOS Keychain
@@ -553,7 +687,7 @@ ${autoLoadLine}
553
687
  }
554
688
  } else if (saveChoice === "profile") {
555
689
  try {
556
- if (!existsSync(path2)) appendFileSync(path2, "");
690
+ if (!existsSync2(path2)) appendFileSync(path2, "");
557
691
  const escapedKey = trimmedKey.replace(/'/g, "'\\''");
558
692
  appendFileSync(path2, `
559
693
  export OPENCODE_API_KEY='${escapedKey}'
@@ -592,14 +726,14 @@ async function runFirstRunWizard(trace = false) {
592
726
  {
593
727
  value: "providers",
594
728
  label: pc.cyan("Set up your own AI provider"),
595
- hint: hasOpencode ? "Import providers you configured in OpenCode" : "Import from OpenCode or add providers via relay-ai providers"
729
+ hint: "Add Groq, Mistral, OpenAI, \u2026 with relay-ai providers"
596
730
  }
597
731
  ];
598
732
  if (hasOpencode) {
599
733
  options.push({
600
734
  value: "import",
601
- label: pc.cyan("Bring settings from OpenCode"),
602
- hint: "One-time import of your OpenCode provider config"
735
+ label: pc.cyan("Import from OpenCode CLI"),
736
+ hint: "Optional one-time import of providers you already configured"
603
737
  });
604
738
  }
605
739
  const choice = await p2.select({
@@ -618,26 +752,20 @@ async function runFirstRunWizard(trace = false) {
618
752
  p2.log.success("OpenCode Zen ready \u2014 picking a model next.");
619
753
  return "continue";
620
754
  }
621
- if (choice === "import" || choice === "providers") {
622
- if (!hasOpencode && choice === "import") {
623
- p2.log.error("OpenCode CLI not found. Install from https://opencode.ai");
624
- return runFirstRunWizard(trace);
755
+ if (choice === "providers") {
756
+ p2.log.info(`Add providers with ${pc.cyan("relay-ai providers add")}, then run ${pc.cyan("relay-ai claude")} again.`);
757
+ if (hasOpencode) {
758
+ p2.log.info(`Optional: ${pc.cyan("relay-ai providers import")} to pull an existing OpenCode CLI config.`);
625
759
  }
760
+ return "cancel";
761
+ }
762
+ if (choice === "import") {
626
763
  if (!hasOpencode) {
627
- p2.log.info("Run relay-ai providers to add providers, then relay-ai claude again.");
628
- p2.log.info("Quick start with Zen is the fastest path if you have an OpenCode API key.");
629
- const retry = await p2.select({
630
- message: "What next?",
631
- options: [
632
- { value: "zen", label: "Quick start with OpenCode Zen", hint: "" },
633
- { value: "cancel", label: "Cancel", hint: "" }
634
- ]
635
- });
636
- if (p2.isCancel(retry) || retry === "cancel") return "cancel";
764
+ p2.log.error("OpenCode CLI not found. Install from https://opencode.ai \u2014 or use Quick start / providers add instead.");
637
765
  return runFirstRunWizard(trace);
638
766
  }
639
767
  const spinner9 = p2.spinner();
640
- spinner9.start("Importing from OpenCode...");
768
+ spinner9.start("Importing from OpenCode CLI...");
641
769
  const result = await importFromOpencode();
642
770
  spinner9.stop("");
643
771
  if (result.error) {
@@ -645,7 +773,7 @@ async function runFirstRunWizard(trace = false) {
645
773
  return runFirstRunWizard(trace);
646
774
  }
647
775
  if (result.imported.length === 0) {
648
- p2.log.warn("No providers imported. Configure providers in OpenCode first, or use Quick start with Zen.");
776
+ p2.log.warn("No providers imported. Add providers with relay-ai providers add, or Quick start with Zen.");
649
777
  return runFirstRunWizard(trace);
650
778
  }
651
779
  p2.log.success(
@@ -1113,7 +1241,7 @@ function parseProvidersArgs(args) {
1113
1241
  }
1114
1242
  }
1115
1243
  if (positional.length !== 1) {
1116
- return { subcommand: "auth", showHelp: false, error: "Usage: relay-ai providers auth <id> [--native|--broker]" };
1244
+ return { subcommand: "auth", showHelp: false, error: "Usage: relay-ai providers auth <id>" };
1117
1245
  }
1118
1246
  return { subcommand: "auth", showHelp: false, removeId: positional[0], authMethod };
1119
1247
  }
@@ -1139,12 +1267,12 @@ ${pc4.bold("Usage:")}
1139
1267
  relay-ai providers list
1140
1268
  relay-ai providers remove <id>
1141
1269
  relay-ai providers refresh-models [id]
1142
- relay-ai providers auth <id> [--native|--broker]
1270
+ relay-ai providers auth <id>
1143
1271
 
1144
1272
  ${pc4.bold("Subcommands:")}
1145
1273
  (none) Provider hub wizard ${pc4.dim("[Phase 1.1]")}
1146
1274
  add Add a provider (Groq, Mistral, Together AI, \u2026) ${pc4.dim("[Phase 1.1]")}
1147
- import Import providers from OpenCode CLI (one-time) ${pc4.dim("[Phase 1.0]")}
1275
+ import Optional one-time import from OpenCode CLI ${pc4.dim("[Phase 1.0]")}
1148
1276
  auth Sign in with OAuth (GitHub Copilot, xAI, OpenAI)
1149
1277
  list Show configured providers ${pc4.dim("[Phase 1.0]")}
1150
1278
  remove Remove a provider by id ${pc4.dim("[Phase 1.1]")}
@@ -1392,25 +1520,17 @@ async function runTemplateAddFlow() {
1392
1520
  await migrateGlobalOpencodeCredential();
1393
1521
  const spinner10 = p5.spinner();
1394
1522
  spinner10.start(`Adding ${template.name}...`);
1395
- const zenStub = addZenRegistryStub();
1396
- const goStub = addGoRegistryStub();
1397
- if (!zenStub.added && !goStub.added) {
1398
- spinner10.stop("");
1399
- p5.log.warn("OpenCode Zen / Go is already configured.");
1523
+ const result2 = await addOpencodeCloudFromApiKey(apiKey2);
1524
+ spinner10.stop("");
1525
+ if (!result2.added) {
1526
+ p5.log.warn(result2.error ?? "OpenCode Zen / Go is already configured.");
1527
+ if (result2.hint) p5.log.info(result2.hint);
1400
1528
  return 0;
1401
1529
  }
1402
- const registry = loadRegistry();
1403
- const refreshResults = [
1404
- await refreshProviderModels("zen", apiKey2, registry),
1405
- await refreshProviderModels("go", apiKey2, registry)
1406
- ];
1407
- spinner10.stop("");
1408
- const modelCount = refreshResults.reduce((total, result2) => total + (result2.modelCount ?? 0), 0);
1409
- const failed = refreshResults.filter((result2) => !result2.ok);
1410
- if (failed.length === 0) {
1411
- p5.log.success(`Added ${template.name} \u2014 ${fmtCount(modelCount, "model")} updated.`);
1530
+ if (result2.hint) {
1531
+ p5.log.warn(`Added ${template.name}. ${result2.hint}`);
1412
1532
  } else {
1413
- p5.log.warn(`Added ${template.name}, but ${failed.length} catalog refresh${failed.length === 1 ? "" : "es"} failed.`);
1533
+ p5.log.success(`Added ${template.name} \u2014 ${fmtCount(result2.modelCount ?? 0, "model")} updated.`);
1414
1534
  }
1415
1535
  return 0;
1416
1536
  }
@@ -3558,7 +3678,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
3558
3678
  }
3559
3679
 
3560
3680
  // src/codex/profile.ts
3561
- import { join as join2 } from "path";
3681
+ import { join as join3 } from "path";
3562
3682
 
3563
3683
  // src/codex/routing.ts
3564
3684
  function codexCompatibleProviders(providers, agent = "codex") {
@@ -3633,7 +3753,7 @@ function codexProviderEnvKey(providerId) {
3633
3753
  // src/codex/session.ts
3634
3754
  import {
3635
3755
  copyFileSync,
3636
- existsSync as existsSync2,
3756
+ existsSync as existsSync3,
3637
3757
  mkdirSync,
3638
3758
  readdirSync,
3639
3759
  readFileSync as readFileSync2,
@@ -3643,36 +3763,36 @@ import {
3643
3763
  unlinkSync,
3644
3764
  writeFileSync
3645
3765
  } from "fs";
3646
- import { homedir as homedir2 } from "os";
3647
- import { basename, dirname, join } from "path";
3766
+ import { homedir as homedir3 } from "os";
3767
+ import { basename, dirname, join as join2 } from "path";
3648
3768
  var CODEX_PROFILE_NAME = "relay-ai-launch";
3649
3769
  var STALE_SESSION_MS = 5 * 60 * 1e3;
3650
3770
  var MAX_BACKUPS = 5;
3651
3771
  function getCodexHome() {
3652
- return join(homedir2(), ".codex");
3772
+ return join2(homedir3(), ".codex");
3653
3773
  }
3654
3774
  function getCodexProfilePath() {
3655
- return join(getCodexHome(), `${CODEX_PROFILE_NAME}.config.toml`);
3775
+ return join2(getCodexHome(), `${CODEX_PROFILE_NAME}.config.toml`);
3656
3776
  }
3657
3777
  function getRelayAiCodexDir(env = process.env) {
3658
- return join(getAppHome(env), "codex");
3778
+ return join2(getAppHome(env), "codex");
3659
3779
  }
3660
3780
  function getSessionLockPath(env = process.env) {
3661
- return join(getRelayAiCodexDir(env), "session.json");
3781
+ return join2(getRelayAiCodexDir(env), "session.json");
3662
3782
  }
3663
3783
  function getBackupsDir(env = process.env) {
3664
- return join(getRelayAiCodexDir(env), "backups");
3784
+ return join2(getRelayAiCodexDir(env), "backups");
3665
3785
  }
3666
3786
  function getCatalogPath(providerId, env = process.env) {
3667
- return join(getRelayAiCodexDir(env), `models-${providerId}.json`);
3787
+ return join2(getRelayAiCodexDir(env), `models-${providerId}.json`);
3668
3788
  }
3669
3789
  function ownedOverlayPaths(env = process.env) {
3670
3790
  const paths = [getCodexProfilePath(), getSessionLockPath(env)];
3671
3791
  const codexDir = getRelayAiCodexDir(env);
3672
- if (existsSync2(codexDir)) {
3792
+ if (existsSync3(codexDir)) {
3673
3793
  for (const name of readdirSync(codexDir)) {
3674
3794
  if (name.startsWith("models-") && name.endsWith(".json")) {
3675
- paths.push(join(codexDir, name));
3795
+ paths.push(join2(codexDir, name));
3676
3796
  }
3677
3797
  }
3678
3798
  }
@@ -3685,17 +3805,17 @@ function atomicWriteFile(path2, content) {
3685
3805
  renameSync(tmp, path2);
3686
3806
  }
3687
3807
  function rotateBackups(filePath, env = process.env) {
3688
- if (!existsSync2(filePath)) return;
3808
+ if (!existsSync3(filePath)) return;
3689
3809
  const backupsDir = getBackupsDir(env);
3690
3810
  mkdirSync(backupsDir, { recursive: true });
3691
3811
  const base = basename(filePath);
3692
3812
  const stamp = Date.now();
3693
- const backupPath = join(backupsDir, `${base}.${stamp}.bak`);
3813
+ const backupPath = join2(backupsDir, `${base}.${stamp}.bak`);
3694
3814
  copyFileSync(filePath, backupPath);
3695
- const backups = readdirSync(backupsDir).filter((n) => n.startsWith(`${base}.`) && n.endsWith(".bak")).map((n) => ({ name: n, mtime: statSync(join(backupsDir, n)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
3815
+ const backups = readdirSync(backupsDir).filter((n) => n.startsWith(`${base}.`) && n.endsWith(".bak")).map((n) => ({ name: n, mtime: statSync(join2(backupsDir, n)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
3696
3816
  for (const old of backups.slice(MAX_BACKUPS)) {
3697
3817
  try {
3698
- unlinkSync(join(backupsDir, old.name));
3818
+ unlinkSync(join2(backupsDir, old.name));
3699
3819
  } catch {
3700
3820
  }
3701
3821
  }
@@ -3706,7 +3826,7 @@ function writeOverlayFile(path2, content, env = process.env) {
3706
3826
  }
3707
3827
  function readSessionLock(env = process.env) {
3708
3828
  const path2 = getSessionLockPath(env);
3709
- if (!existsSync2(path2)) return null;
3829
+ if (!existsSync3(path2)) return null;
3710
3830
  try {
3711
3831
  const parsed = JSON.parse(readFileSync2(path2, "utf8"));
3712
3832
  if (typeof parsed.pid === "number" && typeof parsed.startedAt === "string") return parsed;
@@ -3735,7 +3855,7 @@ function isConcurrentSession(lock) {
3735
3855
  function restoreCodexOverlay(env = process.env) {
3736
3856
  const removed = [];
3737
3857
  for (const path2 of ownedOverlayPaths(env)) {
3738
- if (!existsSync2(path2)) continue;
3858
+ if (!existsSync3(path2)) continue;
3739
3859
  try {
3740
3860
  rmSync(path2, { force: true });
3741
3861
  removed.push(path2);
@@ -3745,7 +3865,7 @@ function restoreCodexOverlay(env = process.env) {
3745
3865
  return removed;
3746
3866
  }
3747
3867
  function remainingOverlayPaths(env = process.env) {
3748
- return ownedOverlayPaths(env).filter((p15) => existsSync2(p15));
3868
+ return ownedOverlayPaths(env).filter((p15) => existsSync3(p15));
3749
3869
  }
3750
3870
  function recoverInterruptedCodexSession(env = process.env) {
3751
3871
  const before = remainingOverlayPaths(env);
@@ -3822,21 +3942,21 @@ function getCatalogOutputPath(providerId) {
3822
3942
  return getCatalogPath(providerId);
3823
3943
  }
3824
3944
  function getFavoritesCatalogPath() {
3825
- return join2(getRelayAiCodexDir(), "models-favorites.json");
3945
+ return join3(getRelayAiCodexDir(), "models-favorites.json");
3826
3946
  }
3827
3947
  function getFavoritesAppCatalogPath() {
3828
- return join2(getRelayAiCodexDir(), "app-models-favorites.json");
3948
+ return join3(getRelayAiCodexDir(), "app-models-favorites.json");
3829
3949
  }
3830
3950
  function profileName() {
3831
3951
  return CODEX_PROFILE_NAME;
3832
3952
  }
3833
3953
 
3834
3954
  // src/codex/launch.ts
3835
- import { execFileSync, execSync, spawn } from "child_process";
3836
- import { existsSync as existsSync3 } from "fs";
3837
- import { homedir as homedir3 } from "os";
3838
- import { join as join3 } from "path";
3839
- var isWindows = process.platform === "win32";
3955
+ import { execFileSync, execSync as execSync2, spawn as spawn2 } from "child_process";
3956
+ import { existsSync as existsSync4 } from "fs";
3957
+ import { homedir as homedir4 } from "os";
3958
+ import { join as join4 } from "path";
3959
+ var isWindows2 = process.platform === "win32";
3840
3960
  var CODEX_CI_ENV_VARS = [
3841
3961
  "CI",
3842
3962
  "CODEX_CI",
@@ -3855,33 +3975,33 @@ function stripCodexInheritedEnv(env) {
3855
3975
  }
3856
3976
  return out;
3857
3977
  }
3858
- var CODEX_FALLBACK_PATHS = isWindows ? [
3859
- join3(process.env["APPDATA"] ?? homedir3(), "npm", "codex.cmd"),
3860
- join3(process.env["APPDATA"] ?? homedir3(), "npm", "codex")
3978
+ var CODEX_FALLBACK_PATHS = isWindows2 ? [
3979
+ join4(process.env["APPDATA"] ?? homedir4(), "npm", "codex.cmd"),
3980
+ join4(process.env["APPDATA"] ?? homedir4(), "npm", "codex")
3861
3981
  ] : [
3862
- join3(homedir3(), ".local", "bin", "codex"),
3863
- join3(homedir3(), ".npm", "bin", "codex"),
3982
+ join4(homedir4(), ".local", "bin", "codex"),
3983
+ join4(homedir4(), ".npm", "bin", "codex"),
3864
3984
  "/usr/local/bin/codex",
3865
3985
  "/opt/homebrew/bin/codex"
3866
3986
  ];
3867
3987
  function findCodexBinary() {
3868
3988
  const override = getAppPathOverride("codex");
3869
- if (override) return selectCodexBinary([override], existsSync3, canRunCodexBinary);
3989
+ if (override) return selectCodexBinary([override], existsSync4, canRunCodexBinary);
3870
3990
  const candidates = [];
3871
3991
  try {
3872
- const result = execSync(isWindows ? "where.exe codex" : "which codex", {
3992
+ const result = execSync2(isWindows2 ? "where.exe codex" : "which codex", {
3873
3993
  encoding: "utf8",
3874
3994
  stdio: ["pipe", "pipe", "pipe"]
3875
3995
  });
3876
3996
  const lines = result.trim().split("\n").map((l) => l.trim()).filter(Boolean);
3877
- if (isWindows) {
3997
+ if (isWindows2) {
3878
3998
  candidates.push(...lines.filter((l) => l.toLowerCase().endsWith(".cmd")));
3879
3999
  }
3880
4000
  candidates.push(...lines);
3881
4001
  } catch {
3882
4002
  }
3883
4003
  candidates.push(...CODEX_FALLBACK_PATHS);
3884
- return selectCodexBinary(candidates, existsSync3, canRunCodexBinary);
4004
+ return selectCodexBinary(candidates, existsSync4, canRunCodexBinary);
3885
4005
  }
3886
4006
  function selectCodexBinary(candidates, exists, canRun) {
3887
4007
  const seen = /* @__PURE__ */ new Set();
@@ -3898,7 +4018,7 @@ function canRunCodexBinary(path2) {
3898
4018
  encoding: "utf8",
3899
4019
  stdio: ["ignore", "pipe", "pipe"],
3900
4020
  timeout: 5e3,
3901
- shell: isWindows
4021
+ shell: isWindows2
3902
4022
  });
3903
4023
  return true;
3904
4024
  } catch {
@@ -3933,10 +4053,10 @@ function launchCodex(modelId, env, extraArgs) {
3933
4053
  return new Promise((resolve2) => {
3934
4054
  const codexPath = findCodexBinary();
3935
4055
  const args = ["--profile", profileName(), "-m", modelId, ...ensureCodexSandboxArgs(extraArgs)];
3936
- const child = spawn(codexPath, args, {
4056
+ const child = spawn2(codexPath, args, {
3937
4057
  stdio: "inherit",
3938
4058
  env,
3939
- shell: isWindows
4059
+ shell: isWindows2
3940
4060
  });
3941
4061
  const forward = (signal) => {
3942
4062
  child.kill(signal);
@@ -5133,24 +5253,24 @@ import pc8 from "picocolors";
5133
5253
  import * as p10 from "@clack/prompts";
5134
5254
 
5135
5255
  // src/gemini/launch.ts
5136
- import { spawn as spawn2 } from "child_process";
5137
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
5138
- import { homedir as homedir4, tmpdir } from "os";
5139
- import { join as join4 } from "path";
5140
- var isWindows2 = process.platform === "win32";
5256
+ import { spawn as spawn3 } from "child_process";
5257
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
5258
+ import { homedir as homedir5, tmpdir } from "os";
5259
+ import { join as join5 } from "path";
5260
+ var isWindows3 = process.platform === "win32";
5141
5261
  var GEMINI_API_KEY_AUTH_TYPE = "gemini-api-key";
5142
- var GEMINI_FALLBACK_PATHS = isWindows2 ? [
5143
- join4(process.env["APPDATA"] ?? homedir4(), "npm", "gemini.cmd"),
5144
- join4(process.env["APPDATA"] ?? homedir4(), "npm", "gemini")
5262
+ var GEMINI_FALLBACK_PATHS = isWindows3 ? [
5263
+ join5(process.env["APPDATA"] ?? homedir5(), "npm", "gemini.cmd"),
5264
+ join5(process.env["APPDATA"] ?? homedir5(), "npm", "gemini")
5145
5265
  ] : [
5146
- join4(homedir4(), ".local", "bin", "gemini"),
5147
- join4(homedir4(), ".npm", "bin", "gemini"),
5266
+ join5(homedir5(), ".local", "bin", "gemini"),
5267
+ join5(homedir5(), ".npm", "bin", "gemini"),
5148
5268
  "/usr/local/bin/gemini",
5149
5269
  "/opt/homebrew/bin/gemini"
5150
5270
  ];
5151
5271
  function findGeminiBinary() {
5152
5272
  const override = getAppPathOverride("gemini");
5153
- if (override) return existsSync4(override) ? override : null;
5273
+ if (override) return existsSync5(override) ? override : null;
5154
5274
  return findBinaryOnPath("gemini", GEMINI_FALLBACK_PATHS);
5155
5275
  }
5156
5276
  function buildGeminiChildEnv(proxyPort, proxyToken) {
@@ -5165,7 +5285,7 @@ function buildGeminiChildEnv(proxyPort, proxyToken) {
5165
5285
  return env;
5166
5286
  }
5167
5287
  function createGeminiCliHomeOverlay() {
5168
- const cliHome = mkdtempSync(join4(tmpdir(), "relay-ai-gemini-"));
5288
+ const cliHome = mkdtempSync(join5(tmpdir(), "relay-ai-gemini-"));
5169
5289
  const settings = {
5170
5290
  security: {
5171
5291
  auth: {
@@ -5173,9 +5293,9 @@ function createGeminiCliHomeOverlay() {
5173
5293
  }
5174
5294
  }
5175
5295
  };
5176
- const geminiDir = join4(cliHome, ".gemini");
5296
+ const geminiDir = join5(cliHome, ".gemini");
5177
5297
  mkdirSync2(geminiDir);
5178
- writeFileSync2(join4(geminiDir, "settings.json"), `${JSON.stringify(settings, null, 2)}
5298
+ writeFileSync2(join5(geminiDir, "settings.json"), `${JSON.stringify(settings, null, 2)}
5179
5299
  `, {
5180
5300
  encoding: "utf8",
5181
5301
  mode: 384
@@ -5199,10 +5319,10 @@ function prepareGeminiChildEnv(proxyPort, proxyToken) {
5199
5319
  function launchGemini(geminiPath, modelId, env, extraArgs) {
5200
5320
  return new Promise((resolve2) => {
5201
5321
  const args = ["-m", modelId, ...extraArgs];
5202
- const child = spawn2(geminiPath, args, {
5322
+ const child = spawn3(geminiPath, args, {
5203
5323
  stdio: "inherit",
5204
5324
  env,
5205
- shell: isWindows2
5325
+ shell: isWindows3
5206
5326
  });
5207
5327
  const onSigInt = () => child.kill("SIGINT");
5208
5328
  const onSigTerm = () => child.kill("SIGTERM");
@@ -8653,26 +8773,26 @@ async function resolveAntigravityLaunchRoutes(opts) {
8653
8773
  }
8654
8774
 
8655
8775
  // src/antigravity/launch-cli.ts
8656
- import { execFileSync as execFileSync2, execSync as execSync2, spawn as spawn3 } from "child_process";
8657
- import { existsSync as existsSync5 } from "fs";
8658
- import { homedir as homedir5 } from "os";
8659
- import { join as join5 } from "path";
8660
- var isWindows3 = process.platform === "win32";
8661
- var FALLBACK_PATHS = isWindows3 ? [
8662
- join5(process.env["APPDATA"] ?? homedir5(), "npm", "agy.cmd"),
8663
- join5(process.env["APPDATA"] ?? homedir5(), "npm", "agy"),
8664
- join5(homedir5(), "AppData", "Roaming", "npm", "agy.cmd")
8776
+ import { execFileSync as execFileSync2, execSync as execSync3, spawn as spawn4 } from "child_process";
8777
+ import { existsSync as existsSync6 } from "fs";
8778
+ import { homedir as homedir6 } from "os";
8779
+ import { join as join6 } from "path";
8780
+ var isWindows4 = process.platform === "win32";
8781
+ var FALLBACK_PATHS = isWindows4 ? [
8782
+ join6(process.env["APPDATA"] ?? homedir6(), "npm", "agy.cmd"),
8783
+ join6(process.env["APPDATA"] ?? homedir6(), "npm", "agy"),
8784
+ join6(homedir6(), "AppData", "Roaming", "npm", "agy.cmd")
8665
8785
  ] : [
8666
- join5(homedir5(), ".local", "bin", "agy"),
8667
- join5(homedir5(), ".npm", "bin", "agy"),
8786
+ join6(homedir6(), ".local", "bin", "agy"),
8787
+ join6(homedir6(), ".npm", "bin", "agy"),
8668
8788
  "/usr/local/bin/agy",
8669
8789
  "/opt/homebrew/bin/agy"
8670
8790
  ];
8671
8791
  function findAntigravityCliBinary() {
8672
8792
  const override = getAppPathOverride("agy");
8673
- if (override) return existsSync5(override) ? override : null;
8793
+ if (override) return existsSync6(override) ? override : null;
8674
8794
  try {
8675
- const result = execSync2(isWindows3 ? "where.exe agy" : "which agy", {
8795
+ const result = execSync3(isWindows4 ? "where.exe agy" : "which agy", {
8676
8796
  encoding: "utf8",
8677
8797
  stdio: ["pipe", "pipe", "pipe"]
8678
8798
  });
@@ -8681,7 +8801,7 @@ function findAntigravityCliBinary() {
8681
8801
  } catch {
8682
8802
  }
8683
8803
  for (const path2 of FALLBACK_PATHS) {
8684
- if (existsSync5(path2)) return path2;
8804
+ if (existsSync6(path2)) return path2;
8685
8805
  }
8686
8806
  return null;
8687
8807
  }
@@ -8711,10 +8831,10 @@ function launchAntigravityCli(env, extraArgs) {
8711
8831
  resolve2(127);
8712
8832
  return;
8713
8833
  }
8714
- const child = spawn3(binaryPath, extraArgs, {
8834
+ const child = spawn4(binaryPath, extraArgs, {
8715
8835
  stdio: "inherit",
8716
8836
  env,
8717
- shell: isWindows3
8837
+ shell: isWindows4
8718
8838
  });
8719
8839
  const forward = (signal) => {
8720
8840
  child.kill(signal);
@@ -8740,10 +8860,10 @@ function launchAntigravityCli(env, extraArgs) {
8740
8860
  }
8741
8861
 
8742
8862
  // src/antigravity/launch-ide.ts
8743
- import { execFileSync as execFileSync3, execSync as execSync3, spawn as spawn4 } from "child_process";
8744
- import { existsSync as existsSync6 } from "fs";
8745
- import { homedir as homedir6 } from "os";
8746
- import { join as join6 } from "path";
8863
+ import { execFileSync as execFileSync3, execSync as execSync4, spawn as spawn5 } from "child_process";
8864
+ import { existsSync as existsSync7 } from "fs";
8865
+ import { homedir as homedir7 } from "os";
8866
+ import { join as join7 } from "path";
8747
8867
 
8748
8868
  // src/antigravity/ide-profile.ts
8749
8869
  import fs from "fs";
@@ -8781,7 +8901,7 @@ function sleep(ms) {
8781
8901
  return new Promise((resolve2) => setTimeout(resolve2, ms));
8782
8902
  }
8783
8903
  function runPowerShell(script) {
8784
- return execSync3(`powershell.exe -NoProfile -Command ${JSON.stringify(script)}`, {
8904
+ return execSync4(`powershell.exe -NoProfile -Command ${JSON.stringify(script)}`, {
8785
8905
  encoding: "utf8",
8786
8906
  stdio: ["pipe", "pipe", "pipe"]
8787
8907
  }).trim();
@@ -8896,32 +9016,32 @@ function quitAntigravityAppGracefully() {
8896
9016
  }
8897
9017
  function findAntigravityAppBinary() {
8898
9018
  const override = getAppPathOverride("antigravity");
8899
- if (override) return existsSync6(override) ? override : null;
9019
+ if (override) return existsSync7(override) ? override : null;
8900
9020
  if (process.platform === "win32") {
8901
- const localAppData = process.env["LOCALAPPDATA"] ?? join6(homedir6(), "AppData", "Local");
8902
- const winPath = join6(localAppData, "Programs", "Antigravity", "Antigravity.exe");
8903
- return existsSync6(winPath) ? winPath : null;
9021
+ const localAppData = process.env["LOCALAPPDATA"] ?? join7(homedir7(), "AppData", "Local");
9022
+ const winPath = join7(localAppData, "Programs", "Antigravity", "Antigravity.exe");
9023
+ return existsSync7(winPath) ? winPath : null;
8904
9024
  }
8905
9025
  if (process.platform !== "darwin") return null;
8906
9026
  const defaultPath = "/Applications/Antigravity.app/Contents/MacOS/Antigravity";
8907
- if (existsSync6(defaultPath)) return defaultPath;
8908
- const homePath = join6(homedir6(), "Applications", "Antigravity.app", "Contents", "MacOS", "Antigravity");
8909
- if (existsSync6(homePath)) return homePath;
9027
+ if (existsSync7(defaultPath)) return defaultPath;
9028
+ const homePath = join7(homedir7(), "Applications", "Antigravity.app", "Contents", "MacOS", "Antigravity");
9029
+ if (existsSync7(homePath)) return homePath;
8910
9030
  return null;
8911
9031
  }
8912
9032
  function findAntigravityIdeBinary() {
8913
9033
  const override = getAppPathOverride("antigravity-ide");
8914
- if (override) return existsSync6(override) ? override : null;
9034
+ if (override) return existsSync7(override) ? override : null;
8915
9035
  if (process.platform === "win32") {
8916
- const localAppData = process.env["LOCALAPPDATA"] ?? join6(homedir6(), "AppData", "Local");
8917
- const winPath = join6(localAppData, "Programs", "Antigravity IDE", "Antigravity IDE.exe");
8918
- return existsSync6(winPath) ? winPath : null;
9036
+ const localAppData = process.env["LOCALAPPDATA"] ?? join7(homedir7(), "AppData", "Local");
9037
+ const winPath = join7(localAppData, "Programs", "Antigravity IDE", "Antigravity IDE.exe");
9038
+ return existsSync7(winPath) ? winPath : null;
8919
9039
  }
8920
9040
  if (process.platform !== "darwin") return null;
8921
9041
  const defaultPath = "/Applications/Antigravity IDE.app/Contents/Resources/app/bin/antigravity-ide";
8922
- if (existsSync6(defaultPath)) return defaultPath;
8923
- const homePath = join6(homedir6(), "Applications", "Antigravity IDE.app", "Contents", "Resources", "app", "bin", "antigravity-ide");
8924
- if (existsSync6(homePath)) return homePath;
9042
+ if (existsSync7(defaultPath)) return defaultPath;
9043
+ const homePath = join7(homedir7(), "Applications", "Antigravity IDE.app", "Contents", "Resources", "app", "bin", "antigravity-ide");
9044
+ if (existsSync7(homePath)) return homePath;
8925
9045
  return null;
8926
9046
  }
8927
9047
  function launchAntigravityApp(env, profileDir, gatewayUrl, extraArgs) {
@@ -8944,7 +9064,7 @@ function launchAntigravityApp(env, profileDir, gatewayUrl, extraArgs) {
8944
9064
  `--user-data-dir=${profileDir}`,
8945
9065
  ...extraArgs
8946
9066
  ];
8947
- const child = spawn4(binaryPath, args, {
9067
+ const child = spawn5(binaryPath, args, {
8948
9068
  stdio: "inherit",
8949
9069
  env
8950
9070
  });
@@ -8970,13 +9090,13 @@ function launchAntigravityIde(env, profileDir, gatewayUrl, extraArgs) {
8970
9090
  return;
8971
9091
  }
8972
9092
  prepareIdeProfile(profileDir, gatewayUrl);
8973
- const relayExtensionsDir = join6(homedir6(), ".relay-ai", "antigravity", "extensions");
9093
+ const relayExtensionsDir = join7(homedir7(), ".relay-ai", "antigravity", "extensions");
8974
9094
  const args = [
8975
9095
  `--user-data-dir=${profileDir}`,
8976
9096
  `--extensions-dir=${relayExtensionsDir}`,
8977
9097
  ...extraArgs
8978
9098
  ];
8979
- const child = spawn4(binaryPath, args, {
9099
+ const child = spawn5(binaryPath, args, {
8980
9100
  stdio: "inherit",
8981
9101
  env
8982
9102
  });
@@ -8991,8 +9111,8 @@ function launchAntigravityIde(env, profileDir, gatewayUrl, extraArgs) {
8991
9111
  }
8992
9112
 
8993
9113
  // src/antigravity.ts
8994
- import { homedir as homedir7 } from "os";
8995
- import { join as join7 } from "path";
9114
+ import { homedir as homedir8 } from "os";
9115
+ import { join as join8 } from "path";
8996
9116
  var SHUTDOWN_DRAIN_MS = 500;
8997
9117
  var AGY_FAVORITES_PROVIDER_ID = "__relay_agy_favorites__";
8998
9118
  var AGY_FAVORITES_PROVIDER_LABEL = "\u2605 Antigravity CLI Favorites";
@@ -9275,7 +9395,7 @@ async function runAntigravityAppCommand(childArgs, trace = false, boot) {
9275
9395
  trace,
9276
9396
  boot,
9277
9397
  async (env, _routes, gatewayHandle) => {
9278
- const profileDir = join7(homedir7(), ".relay-ai", "antigravity", "app-profile");
9398
+ const profileDir = join8(homedir8(), ".relay-ai", "antigravity", "app-profile");
9279
9399
  if (isAntigravityAppRunning(profileDir)) {
9280
9400
  const restart = await p11.confirm({
9281
9401
  message: "Restart Antigravity to apply this Relay gateway?",
@@ -9323,7 +9443,7 @@ async function runAntigravityIdeCommand(childArgs, trace = false, boot) {
9323
9443
  trace,
9324
9444
  boot,
9325
9445
  async (env, _routes, gatewayHandle) => {
9326
- const profileDir = join7(homedir7(), ".relay-ai", "antigravity", "profile");
9446
+ const profileDir = join8(homedir8(), ".relay-ai", "antigravity", "profile");
9327
9447
  if (isAntigravityIdeRunning(profileDir)) {
9328
9448
  const restart = await p11.confirm({
9329
9449
  message: "Restart Antigravity IDE to apply this Relay gateway?",
@@ -9455,14 +9575,14 @@ async function buildCodexAppProviderCatalogRoutes(provider, apiKey, selectedMode
9455
9575
  }
9456
9576
 
9457
9577
  // src/codex/app-config.ts
9458
- import { existsSync as existsSync7, readFileSync as readFileSync3, rmSync as rmSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
9459
- import { dirname as dirname2, join as join8 } from "path";
9578
+ import { existsSync as existsSync8, readFileSync as readFileSync3, rmSync as rmSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
9579
+ import { dirname as dirname2, join as join9 } from "path";
9460
9580
  import { parse, stringify } from "smol-toml";
9461
9581
  function getCodexConfigPath() {
9462
- return join8(getCodexHome(), "config.toml");
9582
+ return join9(getCodexHome(), "config.toml");
9463
9583
  }
9464
9584
  function getCodexAppSidecarProfilePath() {
9465
- return join8(getCodexHome(), `${CODEX_APP_PROVIDER_ID}.config.toml`);
9585
+ return join9(getCodexHome(), `${CODEX_APP_PROVIDER_ID}.config.toml`);
9466
9586
  }
9467
9587
  function asRecord(value) {
9468
9588
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
@@ -9485,7 +9605,7 @@ function applyRestoreNumber(config, key, had, value) {
9485
9605
  }
9486
9606
  }
9487
9607
  function readCodexConfigText(path2 = getCodexConfigPath()) {
9488
- if (!existsSync7(path2)) return "";
9608
+ if (!existsSync8(path2)) return "";
9489
9609
  return readFileSync3(path2, "utf8");
9490
9610
  }
9491
9611
  function parseCodexConfig(text4) {
@@ -9650,13 +9770,13 @@ function restoreConfigFromState(state, configPath = getCodexConfigPath()) {
9650
9770
  applyRestoreNumber(config, "model_context_window", state.hadModelContextWindow ?? false, state.modelContextWindow);
9651
9771
  applyRestoreNumber(config, "model_auto_compact_token_limit", state.hadModelAutoCompactTokenLimit ?? false, state.modelAutoCompactTokenLimit);
9652
9772
  const sidecar = getCodexAppSidecarProfilePath();
9653
- if (existsSync7(sidecar)) {
9773
+ if (existsSync8(sidecar)) {
9654
9774
  try {
9655
9775
  rmSync3(sidecar, { force: true });
9656
9776
  } catch {
9657
9777
  }
9658
9778
  }
9659
- const hadFile = existsSync7(configPath);
9779
+ const hadFile = existsSync8(configPath);
9660
9780
  const empty = Object.keys(config).length === 0 || Object.keys(config).length === 1 && "model_providers" in config && Object.keys(asRecord(config.model_providers)).length === 0;
9661
9781
  if (!hadFile && empty) return false;
9662
9782
  if (empty) {
@@ -9677,25 +9797,25 @@ function previewAppConfigToml(spec) {
9677
9797
  // src/codex/app-session.ts
9678
9798
  import {
9679
9799
  copyFileSync as copyFileSync2,
9680
- existsSync as existsSync8,
9800
+ existsSync as existsSync9,
9681
9801
  mkdirSync as mkdirSync4,
9682
9802
  readdirSync as readdirSync2,
9683
9803
  readFileSync as readFileSync4,
9684
9804
  rmSync as rmSync4
9685
9805
  } from "fs";
9686
- import { basename as basename2, join as join9 } from "path";
9806
+ import { basename as basename2, join as join10 } from "path";
9687
9807
  function getAppSessionLockPath(env = process.env) {
9688
- return join9(getRelayAiCodexDir(env), "session-app.json");
9808
+ return join10(getRelayAiCodexDir(env), "session-app.json");
9689
9809
  }
9690
9810
  function getAppRestoreStatePath(env = process.env) {
9691
- return join9(getRelayAiCodexDir(env), "app-restore-state.json");
9811
+ return join10(getRelayAiCodexDir(env), "app-restore-state.json");
9692
9812
  }
9693
9813
  function getAppCatalogPath(providerId, env = process.env) {
9694
- return join9(getRelayAiCodexDir(env), `app-models-${providerId}.json`);
9814
+ return join10(getRelayAiCodexDir(env), `app-models-${providerId}.json`);
9695
9815
  }
9696
9816
  function readAppSessionLock(env = process.env) {
9697
9817
  const path2 = getAppSessionLockPath(env);
9698
- if (!existsSync8(path2)) return null;
9818
+ if (!existsSync9(path2)) return null;
9699
9819
  try {
9700
9820
  const parsed = JSON.parse(readFileSync4(path2, "utf8"));
9701
9821
  if (typeof parsed.pid === "number" && typeof parsed.startedAt === "string") return parsed;
@@ -9709,11 +9829,11 @@ function writeAppSessionLock(lock, env = process.env) {
9709
9829
  }
9710
9830
  function clearAppSessionLock(env = process.env) {
9711
9831
  const path2 = getAppSessionLockPath(env);
9712
- if (existsSync8(path2)) rmSync4(path2, { force: true });
9832
+ if (existsSync9(path2)) rmSync4(path2, { force: true });
9713
9833
  }
9714
9834
  function readAppRestoreState(env = process.env) {
9715
9835
  const path2 = getAppRestoreStatePath(env);
9716
- if (!existsSync8(path2)) return null;
9836
+ if (!existsSync9(path2)) return null;
9717
9837
  try {
9718
9838
  return JSON.parse(readFileSync4(path2, "utf8"));
9719
9839
  } catch {
@@ -9727,16 +9847,16 @@ function writeAppRestoreState(state, env = process.env) {
9727
9847
  }
9728
9848
  function clearAppRestoreState(env = process.env) {
9729
9849
  const path2 = getAppRestoreStatePath(env);
9730
- if (existsSync8(path2)) rmSync4(path2, { force: true });
9850
+ if (existsSync9(path2)) rmSync4(path2, { force: true });
9731
9851
  }
9732
9852
  function backupConfigToml(env = process.env) {
9733
9853
  const configPath = getCodexConfigPath();
9734
- if (!existsSync8(configPath)) return void 0;
9854
+ if (!existsSync9(configPath)) return void 0;
9735
9855
  rotateBackups(configPath, env);
9736
9856
  const backupsDir = getBackupsDir(env);
9737
9857
  mkdirSync4(backupsDir, { recursive: true });
9738
9858
  const base = basename2(configPath);
9739
- const backupPath = join9(backupsDir, `${base}.${Date.now()}.bak`);
9859
+ const backupPath = join10(backupsDir, `${base}.${Date.now()}.bak`);
9740
9860
  copyFileSync2(configPath, backupPath);
9741
9861
  return backupPath;
9742
9862
  }
@@ -9752,8 +9872,8 @@ function saveAppRestoreStateBeforePatch(env = process.env) {
9752
9872
  }
9753
9873
  function ownedAppCatalogPaths(env = process.env) {
9754
9874
  const codexDir = getRelayAiCodexDir(env);
9755
- if (!existsSync8(codexDir)) return [];
9756
- return readdirSync2(codexDir).filter((n) => n.startsWith("app-models-") && n.endsWith(".json")).map((n) => join9(codexDir, n));
9875
+ if (!existsSync9(codexDir)) return [];
9876
+ return readdirSync2(codexDir).filter((n) => n.startsWith("app-models-") && n.endsWith(".json")).map((n) => join10(codexDir, n));
9757
9877
  }
9758
9878
  function removeAppCatalogs(env = process.env) {
9759
9879
  const removed = [];
@@ -9785,7 +9905,7 @@ function restoreCodexAppOverlay(env = process.env) {
9785
9905
  }
9786
9906
  if (restoreState) {
9787
9907
  restoreConfigFromState(restoreState);
9788
- } else if (lock?.backupPath && existsSync8(lock.backupPath)) {
9908
+ } else if (lock?.backupPath && existsSync9(lock.backupPath)) {
9789
9909
  copyFileSync2(lock.backupPath, getCodexConfigPath());
9790
9910
  }
9791
9911
  removeAppCatalogs(env);
@@ -10393,25 +10513,25 @@ import pc11 from "picocolors";
10393
10513
  import * as p13 from "@clack/prompts";
10394
10514
 
10395
10515
  // src/claude-desktop/app-config.ts
10396
- import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
10397
- import { homedir as homedir8 } from "os";
10398
- import { join as join10, dirname as dirname3 } from "path";
10516
+ import { existsSync as existsSync10, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
10517
+ import { homedir as homedir9 } from "os";
10518
+ import { join as join11, dirname as dirname3 } from "path";
10399
10519
  import { randomUUID as randomUUID2 } from "crypto";
10400
10520
  function getClaudeDesktopHome() {
10401
10521
  if (process.platform === "win32") {
10402
- return join10(process.env.LOCALAPPDATA || join10(homedir8(), "AppData", "Local"), "Claude-3p");
10522
+ return join11(process.env.LOCALAPPDATA || join11(homedir9(), "AppData", "Local"), "Claude-3p");
10403
10523
  }
10404
- return join10(homedir8(), "Library", "Application Support", "Claude-3p");
10524
+ return join11(homedir9(), "Library", "Application Support", "Claude-3p");
10405
10525
  }
10406
10526
  function getConfigLibraryPath() {
10407
- return join10(getClaudeDesktopHome(), "configLibrary");
10527
+ return join11(getClaudeDesktopHome(), "configLibrary");
10408
10528
  }
10409
10529
  function getMetaJsonPath() {
10410
- return join10(getConfigLibraryPath(), "_meta.json");
10530
+ return join11(getConfigLibraryPath(), "_meta.json");
10411
10531
  }
10412
10532
  function readMetaJson() {
10413
10533
  const metaPath = getMetaJsonPath();
10414
- if (!existsSync9(metaPath)) return null;
10534
+ if (!existsSync10(metaPath)) return null;
10415
10535
  try {
10416
10536
  return JSON.parse(readFileSync5(metaPath, "utf8"));
10417
10537
  } catch {
@@ -10435,7 +10555,7 @@ function buildRelayAiConfig(proxyPort) {
10435
10555
  }
10436
10556
  function writeRelayAiConfig(proxyPort) {
10437
10557
  const uuid = randomUUID2();
10438
- const configPath = join10(getConfigLibraryPath(), `${uuid}.json`);
10558
+ const configPath = join11(getConfigLibraryPath(), `${uuid}.json`);
10439
10559
  const config = buildRelayAiConfig(proxyPort);
10440
10560
  mkdirSync5(dirname3(configPath), { recursive: true });
10441
10561
  writeFileSync4(configPath, `${JSON.stringify(config, null, 2)}
@@ -10450,14 +10570,14 @@ function writeRelayAiConfig(proxyPort) {
10450
10570
  }
10451
10571
 
10452
10572
  // src/claude-desktop/app-session.ts
10453
- import { existsSync as existsSync10, readFileSync as readFileSync6, rmSync as rmSync5, writeFileSync as writeFileSync5, copyFileSync as copyFileSync3, unlinkSync as unlinkSync2 } from "fs";
10454
- import { join as join11 } from "path";
10573
+ import { existsSync as existsSync11, readFileSync as readFileSync6, rmSync as rmSync5, writeFileSync as writeFileSync5, copyFileSync as copyFileSync3, unlinkSync as unlinkSync2 } from "fs";
10574
+ import { join as join12 } from "path";
10455
10575
  function getSessionLockPath2() {
10456
- return join11(getClaudeDesktopHome(), ".relay-ai.lock");
10576
+ return join12(getClaudeDesktopHome(), ".relay-ai.lock");
10457
10577
  }
10458
10578
  function readSessionLock2() {
10459
10579
  const path2 = getSessionLockPath2();
10460
- if (!existsSync10(path2)) return null;
10580
+ if (!existsSync11(path2)) return null;
10461
10581
  try {
10462
10582
  const parsed = JSON.parse(readFileSync6(path2, "utf8"));
10463
10583
  if (typeof parsed.pid === "number" && typeof parsed.startedAt === "string") return parsed;
@@ -10482,21 +10602,21 @@ function isProcessAlive3(pid) {
10482
10602
  function backupMetaJson() {
10483
10603
  const metaPath = getMetaJsonPath();
10484
10604
  const backupPath = `${metaPath}.bak`;
10485
- if (existsSync10(metaPath)) {
10605
+ if (existsSync11(metaPath)) {
10486
10606
  copyFileSync3(metaPath, backupPath);
10487
10607
  }
10488
10608
  }
10489
10609
  function restoreMetaJson() {
10490
10610
  const metaPath = getMetaJsonPath();
10491
10611
  const backupPath = `${metaPath}.bak`;
10492
- if (existsSync10(backupPath)) {
10612
+ if (existsSync11(backupPath)) {
10493
10613
  copyFileSync3(backupPath, metaPath);
10494
10614
  unlinkSync2(backupPath);
10495
10615
  }
10496
10616
  }
10497
10617
  function removeRelayAiConfig(uuid) {
10498
- const configPath = join11(getConfigLibraryPath(), `${uuid}.json`);
10499
- if (existsSync10(configPath)) {
10618
+ const configPath = join12(getConfigLibraryPath(), `${uuid}.json`);
10619
+ if (existsSync11(configPath)) {
10500
10620
  try {
10501
10621
  rmSync5(configPath, { force: true });
10502
10622
  } catch {
@@ -10844,17 +10964,17 @@ ${pc11.bold("Claude Desktop 3P Mode Active")}`);
10844
10964
  }
10845
10965
 
10846
10966
  // src/ai-doc.ts
10847
- import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
10848
- import { homedir as homedir9 } from "os";
10849
- import { join as join12 } from "path";
10967
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
10968
+ import { homedir as homedir10 } from "os";
10969
+ import { join as join13 } from "path";
10850
10970
  var SKILL_DIR_NAME = "relay-ai-cli";
10851
10971
  var SKILL_INSTALL_DIRS = [
10852
- join12(getAppHome(), "skills"),
10853
- join12(homedir9(), ".claude", "skills"),
10854
- join12(homedir9(), ".agents", "skills"),
10855
- join12(homedir9(), ".codex", "skills"),
10856
- join12(homedir9(), ".cursor", "skills"),
10857
- join12(homedir9(), ".cursor", "skills-cursor")
10972
+ join13(getAppHome(), "skills"),
10973
+ join13(homedir10(), ".claude", "skills"),
10974
+ join13(homedir10(), ".agents", "skills"),
10975
+ join13(homedir10(), ".codex", "skills"),
10976
+ join13(homedir10(), ".cursor", "skills"),
10977
+ join13(homedir10(), ".cursor", "skills-cursor")
10858
10978
  ];
10859
10979
  function parseSkillVersion(content) {
10860
10980
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
@@ -10868,8 +10988,8 @@ function parseSkillVersion(content) {
10868
10988
  return null;
10869
10989
  }
10870
10990
  function readInstalledSkillVersion(skillDir) {
10871
- const skillPath = join12(skillDir, "SKILL.md");
10872
- if (!existsSync11(skillPath)) return null;
10991
+ const skillPath = join13(skillDir, "SKILL.md");
10992
+ if (!existsSync12(skillPath)) return null;
10873
10993
  try {
10874
10994
  const head = readFileSync7(skillPath, "utf-8").slice(0, 1024);
10875
10995
  return parseSkillVersion(head.includes("---", 4) ? head : `${head}
@@ -10881,8 +11001,8 @@ function readInstalledSkillVersion(skillDir) {
10881
11001
  }
10882
11002
  function skillInstallTargets() {
10883
11003
  return SKILL_INSTALL_DIRS.map((dir) => {
10884
- const skillDir = join12(dir, SKILL_DIR_NAME);
10885
- return { skillDir, skillPath: join12(skillDir, "SKILL.md") };
11004
+ const skillDir = join13(dir, SKILL_DIR_NAME);
11005
+ return { skillDir, skillPath: join13(skillDir, "SKILL.md") };
10886
11006
  });
10887
11007
  }
10888
11008
  function formatProviderModels(provider) {
@@ -11168,7 +11288,7 @@ OPENAI CODEX CLI
11168
11288
  PROVIDERS REGISTRY
11169
11289
  relay-ai providers interactive hub
11170
11290
  relay-ai providers add add Groq, Mistral, OpenAI, custom URL, \u2026
11171
- relay-ai providers import one-time import from OpenCode config
11291
+ relay-ai providers import optional one-time import from OpenCode CLI
11172
11292
  relay-ai providers list show provider ids and model counts
11173
11293
  relay-ai providers remove <id>
11174
11294
  relay-ai providers refresh-models [id]
@@ -11501,7 +11621,7 @@ function buildHttpProxyChildEnv(baseEnv, proxyUrl, caCertPath) {
11501
11621
  import { randomBytes, randomUUID as randomUUID3 } from "crypto";
11502
11622
  import {
11503
11623
  chmodSync,
11504
- existsSync as existsSync12,
11624
+ existsSync as existsSync13,
11505
11625
  mkdirSync as mkdirSync7,
11506
11626
  readFileSync as readFileSync8,
11507
11627
  readdirSync as readdirSync3,
@@ -11509,7 +11629,7 @@ import {
11509
11629
  statSync as statSync2,
11510
11630
  writeFileSync as writeFileSync7
11511
11631
  } from "fs";
11512
- import { dirname as dirname4, join as join13, resolve } from "path";
11632
+ import { dirname as dirname4, join as join14, resolve } from "path";
11513
11633
  import forge from "node-forge";
11514
11634
  var SESSION_ROOT = "http-proxy-sessions";
11515
11635
  var OWNER_FILE = "owner.pid";
@@ -11528,13 +11648,13 @@ function processIsRunning(pid) {
11528
11648
  }
11529
11649
  }
11530
11650
  function cleanupStaleHttpProxySessions(appHome = getAppHome()) {
11531
- const root = join13(appHome, SESSION_ROOT);
11532
- if (!existsSync12(root)) return;
11651
+ const root = join14(appHome, SESSION_ROOT);
11652
+ if (!existsSync13(root)) return;
11533
11653
  for (const name of readdirSync3(root)) {
11534
- const sessionDir = join13(root, name);
11654
+ const sessionDir = join14(root, name);
11535
11655
  try {
11536
11656
  if (!statSync2(sessionDir).isDirectory()) continue;
11537
- const pid = Number(readFileSync8(join13(sessionDir, OWNER_FILE), "utf8").trim());
11657
+ const pid = Number(readFileSync8(join14(sessionDir, OWNER_FILE), "utf8").trim());
11538
11658
  if (!processIsRunning(pid)) rmSync6(sessionDir, { recursive: true, force: true });
11539
11659
  } catch {
11540
11660
  rmSync6(sessionDir, { recursive: true, force: true });
@@ -11543,13 +11663,13 @@ function cleanupStaleHttpProxySessions(appHome = getAppHome()) {
11543
11663
  }
11544
11664
  function createHttpProxyCertificates(appHome = getAppHome()) {
11545
11665
  cleanupStaleHttpProxySessions(appHome);
11546
- const root = join13(appHome, SESSION_ROOT);
11666
+ const root = join14(appHome, SESSION_ROOT);
11547
11667
  mkdirSync7(root, { recursive: true, mode: 448 });
11548
11668
  chmodSync(root, 448);
11549
- const sessionDir = join13(root, randomUUID3());
11669
+ const sessionDir = join14(root, randomUUID3());
11550
11670
  mkdirSync7(sessionDir, { mode: 448 });
11551
11671
  chmodSync(sessionDir, 448);
11552
- writeFileSync7(join13(sessionDir, OWNER_FILE), `${process.pid}
11672
+ writeFileSync7(join14(sessionDir, OWNER_FILE), `${process.pid}
11553
11673
  `, { mode: 384 });
11554
11674
  try {
11555
11675
  const caKeys = forge.pki.rsa.generateKeyPair(2048);
@@ -11584,7 +11704,7 @@ function createHttpProxyCertificates(appHome = getAppHome()) {
11584
11704
  ]);
11585
11705
  server.sign(caKeys.privateKey, forge.md.sha256.create());
11586
11706
  const caCert = forge.pki.certificateToPem(ca);
11587
- const caCertPath = join13(sessionDir, "relay-ai-ca.pem");
11707
+ const caCertPath = join14(sessionDir, "relay-ai-ca.pem");
11588
11708
  writeFileSync7(caCertPath, caCert, { encoding: "utf8", mode: 384 });
11589
11709
  chmodSync(caCertPath, 384);
11590
11710
  let cleaned = false;
@@ -11630,7 +11750,7 @@ function createHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath) {
11630
11750
  const relayCa = readFileSync8(relayCaCertPath, "utf8").trimEnd();
11631
11751
  const additionalCa = readFileSync8(additionalCaCertPath, "utf8").trim();
11632
11752
  if (!additionalCa) return relayCaCertPath;
11633
- const combinedPath = join13(dirname4(relayCaCertPath), "combined-ca.pem");
11753
+ const combinedPath = join14(dirname4(relayCaCertPath), "combined-ca.pem");
11634
11754
  writeFileSync7(
11635
11755
  combinedPath,
11636
11756
  `${relayCa}
@@ -13479,7 +13599,7 @@ Options:
13479
13599
  --trace Write debug logs under ~/.relay-ai/logs/`);
13480
13600
  return 0;
13481
13601
  }
13482
- const { runUiCommand } = await import("./ui-command-DJZIIIWC.js");
13602
+ const { runUiCommand } = await import("./ui-command-GNCQS7F7.js");
13483
13603
  return runUiCommand({ trace: parsed.trace, serverMode: parsed.uiServerMode });
13484
13604
  }
13485
13605
  if (parsed.command === "models") {