@jacobbd/relay-ai 0.7.6 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  BACKENDS,
4
+ CODEX_SUBAGENT_MODEL_CAP,
4
5
  MAX_MODEL_CATALOG,
5
6
  VERSION,
6
7
  addCustomEndpointProvider,
@@ -40,18 +41,24 @@ import {
40
41
  loadRegistry,
41
42
  loadServerModels,
42
43
  makeTraceLogger,
44
+ normalizeFavoriteModels,
43
45
  openAiDeviceCodeUrl,
44
46
  openAiIdCollisions,
47
+ pollClinePassDeviceCode,
45
48
  pollGithubDeviceCodeToken,
46
49
  pollOpenAiDeviceCodeToken,
47
50
  pollXaiDeviceCodeToken,
51
+ preferredRelayCredentialAuthRef,
48
52
  providerOptionsFromCatalog,
53
+ providersForCodexSubagents,
49
54
  providersForTarget,
50
55
  readBody,
56
+ readStoredProviderCredential,
51
57
  recordLaunchFolder,
52
58
  refreshAllProviderModels,
53
59
  refreshProviderModels,
54
60
  removeProviderFromRegistry,
61
+ requestClinePassDeviceCode,
55
62
  requestGithubDeviceCode,
56
63
  requestOpenAiDeviceCode,
57
64
  requestXaiDeviceCode,
@@ -77,14 +84,14 @@ import {
77
84
  supportsClaudeTransparentMode,
78
85
  validateCustomEndpointUrl,
79
86
  writeSecureLogLine
80
- } from "./chunk-GHSURQOK.js";
87
+ } from "./chunk-R4AWEK7T.js";
81
88
  import {
82
89
  __toCommonJS,
83
90
  init_provider_templates,
84
91
  listAddableTemplates,
85
92
  listVisibleOAuthTemplates,
86
93
  provider_templates_exports
87
- } from "./chunk-HXGZ4CTV.js";
94
+ } from "./chunk-Q2FTCICO.js";
88
95
 
89
96
  // src/ui-command.ts
90
97
  import { createServer } from "http";
@@ -239,7 +246,14 @@ function fallbackPathsForApp(id, platform = process.platform) {
239
246
  join(localAppData, "Programs", "Codex", "Codex.exe"),
240
247
  join(localAppData, "Programs", "OpenAI Codex", "Codex.exe"),
241
248
  join(localAppData, "openai-codex-electron", "Codex.exe")
242
- ] : [];
249
+ ] : [
250
+ "/usr/bin/chatgpt",
251
+ "/usr/lib/chatgpt/ChatGPT",
252
+ "/opt/chatgpt/ChatGPT",
253
+ "/usr/local/lib/chatgpt/ChatGPT",
254
+ join(homedir(), ".local", "bin", "chatgpt"),
255
+ join(homedir(), ".local", "share", "chatgpt", "ChatGPT")
256
+ ];
243
257
  default:
244
258
  return [];
245
259
  }
@@ -331,6 +345,12 @@ function getRelayLaunchCommand(appId, options = {}) {
331
345
  }
332
346
  args.push("--http-proxy");
333
347
  }
348
+ if (options.withNative) {
349
+ if (app.id !== "codex" && app.id !== "codex-app") {
350
+ throw new Error("Native Codex mixed mode is available only for Codex CLI and ChatGPT Desktop.");
351
+ }
352
+ args.push("--with-native");
353
+ }
334
354
  if (options.providerId && options.modelId) {
335
355
  args.push("--provider", options.providerId, "--model", options.modelId);
336
356
  } else if (options.providerId || options.modelId) {
@@ -635,7 +655,7 @@ function handleUiApiRequest(req, res, opts = {}) {
635
655
  handlePostConfig(req, res);
636
656
  } else if (url.startsWith("/api/models") && req.method === "GET") {
637
657
  const appId = new URL(url, "http://localhost").searchParams.get("appId") ?? "";
638
- handleGetModels(res, APP_ID_TO_LAUNCH_TARGET[appId]);
658
+ handleGetModels(res, APP_ID_TO_LAUNCH_TARGET[appId], appId === "codex-subagents");
639
659
  } else if (url === "/api/keys" && req.method === "POST") {
640
660
  handlePostKeys(req, res);
641
661
  } else if (url === "/api/providers/refresh" && req.method === "POST") {
@@ -687,6 +707,7 @@ function handleGetConfig(res) {
687
707
  const prefs = loadPreferences();
688
708
  sendJson(res, 200, {
689
709
  favoriteModels: prefs.favoriteModels ?? [],
710
+ codexSubagentModels: prefs.codexSubagentModels ?? [],
690
711
  antigravityCliFavoriteModels: prefs.antigravityCliFavoriteModels ?? []
691
712
  });
692
713
  }
@@ -696,16 +717,25 @@ async function handlePostConfig(req, res) {
696
717
  const update = {};
697
718
  if (Array.isArray(body.favoriteModels)) update.favoriteModels = body.favoriteModels;
698
719
  if (Array.isArray(body.antigravityCliFavoriteModels)) update.antigravityCliFavoriteModels = body.antigravityCliFavoriteModels;
720
+ if (Array.isArray(body.codexSubagentModels)) {
721
+ const normalized = normalizeFavoriteModels(body.codexSubagentModels, CODEX_SUBAGENT_MODEL_CAP + 1);
722
+ if (normalized.length > CODEX_SUBAGENT_MODEL_CAP) {
723
+ sendJson(res, 400, { error: "Codex Sub-agents are limited to 1 model" });
724
+ return;
725
+ }
726
+ update.codexSubagentModels = normalized;
727
+ }
699
728
  if (Object.keys(update).length > 0) savePreferences(update);
700
729
  sendJson(res, 200, { ok: true });
701
730
  } catch (err) {
702
731
  sendJson(res, 400, { error: String(err) });
703
732
  }
704
733
  }
705
- async function handleGetModels(res, target) {
734
+ async function handleGetModels(res, target, codexSubagents = false) {
706
735
  try {
707
736
  let catalog = (await fetchModelsWithTimeout()).filter((provider) => provider.authType !== "oauth" || DEVICE_CODE_PROVIDER_IDS.has(provider.id));
708
- if (target) catalog = providersForTarget(catalog, target);
737
+ if (codexSubagents) catalog = providersForCodexSubagents(catalog);
738
+ else if (target) catalog = providersForTarget(catalog, target);
709
739
  const registry = loadRegistry();
710
740
  const rawCountById = new Map(registry.providers.map((p2) => [p2.id, p2.modelsCache?.models.length ?? 0]));
711
741
  const providers = catalog.map((p2) => ({
@@ -765,7 +795,7 @@ function copilotSubscription(providerData) {
765
795
  async function handlePostKeys(req, res) {
766
796
  try {
767
797
  const body = JSON.parse(await readBody(req));
768
- const { providerId, key } = body;
798
+ const { providerId, key, confirmOverwrite } = body;
769
799
  if (!providerId || typeof providerId !== "string") {
770
800
  sendJson(res, 400, { error: "providerId required" });
771
801
  return;
@@ -774,7 +804,12 @@ async function handlePostKeys(req, res) {
774
804
  sendJson(res, 400, { error: "key must be a non-empty string" });
775
805
  return;
776
806
  }
777
- const authRef = `keyring:provider:${providerId}`;
807
+ const authRef = preferredRelayCredentialAuthRef(providerId, `keyring:provider:${providerId}`);
808
+ const existing = await readStoredProviderCredential(authRef) ?? (providerId === "go" || providerId === "zen" ? await readStoredProviderCredential("keyring:global:opencode") : null);
809
+ if (existing && existing !== key.trim() && confirmOverwrite !== true) {
810
+ sendJson(res, 409, { ok: false, needsConfirmation: true, error: "A different key is already stored in Relay." });
811
+ return;
812
+ }
778
813
  const saved = await saveProviderCredential(authRef, key.trim());
779
814
  if (saved) {
780
815
  sendJson(res, 200, { ok: true });
@@ -792,6 +827,7 @@ var CUSTOM_TEMPLATES = [
792
827
  function handleGetTemplates(res) {
793
828
  const registry = loadRegistry();
794
829
  const configured = new Set(registry.providers.map((p2) => p2.id));
830
+ const templates = /* @__PURE__ */ new Map();
795
831
  const apiTemplates = listAddableTemplates(configured).map((t) => ({
796
832
  id: t.id,
797
833
  name: t.name,
@@ -802,17 +838,32 @@ function handleGetTemplates(res) {
802
838
  accountIdPrompt: t.accountIdPrompt ?? null,
803
839
  defaultBaseUrl: t.defaultBaseUrl ?? null,
804
840
  apiKeyOptional: t.apiKeyOptional ?? false,
841
+ authMethods: t.authMethods ?? [t.authType],
805
842
  custom: false
806
843
  }));
844
+ for (const template of apiTemplates) templates.set(template.id, template);
807
845
  const oauthTemplates = listVisibleOAuthTemplates(configured).map((t) => ({
808
846
  id: t.id,
809
847
  name: t.name,
810
848
  signupUrl: t.signupUrl ?? null,
811
849
  authType: t.authType,
850
+ authMethods: t.authMethods ?? [t.authType],
812
851
  subscriptionRisk: t.subscriptionRisk ?? false,
813
852
  custom: false
814
853
  }));
815
- sendJson(res, 200, { templates: [...apiTemplates, ...oauthTemplates, ...CUSTOM_TEMPLATES] });
854
+ for (const template of oauthTemplates) {
855
+ const existing = templates.get(template.id);
856
+ if (existing) {
857
+ templates.set(template.id, {
858
+ ...existing,
859
+ authMethods: [.../* @__PURE__ */ new Set([...existing.authMethods ?? [], ...template.authMethods ?? []])],
860
+ subscriptionRisk: template.subscriptionRisk
861
+ });
862
+ } else {
863
+ templates.set(template.id, template);
864
+ }
865
+ }
866
+ sendJson(res, 200, { templates: [...templates.values(), ...CUSTOM_TEMPLATES] });
816
867
  }
817
868
  async function handleAddCustomProvider(req, res) {
818
869
  try {
@@ -855,7 +906,7 @@ async function handleAddProvider(req, res) {
855
906
  sendJson(res, 400, { error: "templateId required" });
856
907
  return;
857
908
  }
858
- const { listSupportedTemplates } = await import("./provider-templates-4H3C4DRL.js");
909
+ const { listSupportedTemplates } = await import("./provider-templates-XKNRKAQU.js");
859
910
  const template = listSupportedTemplates().find((t) => t.id === templateId);
860
911
  if (!template) {
861
912
  sendJson(res, 404, { error: `Template '${templateId}' not found` });
@@ -888,7 +939,11 @@ async function handleAddProvider(req, res) {
888
939
  return;
889
940
  }
890
941
  }
891
- const result = await addProviderFromTemplate(template, keyText, { baseUrl: baseUrlOverride });
942
+ const replaceExisting = template.id === "cline-pass" && body.replaceExisting === true;
943
+ const result = await addProviderFromTemplate(template, keyText, {
944
+ baseUrl: baseUrlOverride,
945
+ replaceExisting
946
+ });
892
947
  if (result.added) {
893
948
  sendJson(res, 200, { ok: true, name: template.name, count: result.modelCount ?? 0 });
894
949
  } else {
@@ -935,7 +990,8 @@ async function handleProviderRefresh(req, res) {
935
990
  sendJson(res, 200, { ok: false, error: "Provider not found in registry" });
936
991
  return;
937
992
  }
938
- const apiKey = await resolveProviderCredential(providerId, registryProvider.authRef);
993
+ const explicitKey = typeof body.key === "string" ? body.key.trim() : "";
994
+ const apiKey = explicitKey || await resolveProviderCredential(providerId, registryProvider.authRef);
939
995
  const result = await refreshProviderModels(providerId, apiKey, registry);
940
996
  if (result.ok) {
941
997
  sendJson(res, 200, { ok: true, count: result.modelCount ?? result.previousModelCount ?? 0 });
@@ -964,7 +1020,7 @@ async function handleDeleteProvider(req, res) {
964
1020
  sendJson(res, 500, { error: String(err) });
965
1021
  }
966
1022
  }
967
- var DEVICE_CODE_PROVIDER_IDS = /* @__PURE__ */ new Set(["xai-oauth", "openai-oauth", "github-copilot"]);
1023
+ var DEVICE_CODE_PROVIDER_IDS = /* @__PURE__ */ new Set(["xai-oauth", "openai-oauth", "github-copilot", "cline-pass"]);
968
1024
  var PKCE_PROVIDER_IDS = /* @__PURE__ */ new Set(["claude-code", "antigravity"]);
969
1025
  var NATIVE_OAUTH_PROVIDER_IDS = DEVICE_CODE_PROVIDER_IDS;
970
1026
  async function refreshOAuthProviderModels(providerId) {
@@ -1013,6 +1069,21 @@ async function handleOAuthStart(req, res) {
1013
1069
  sendJson(res, 200, { sessionId, url: url2, userCode: device2.user_code });
1014
1070
  return;
1015
1071
  }
1072
+ if (providerId === "cline-pass") {
1073
+ const device2 = await requestClinePassDeviceCode();
1074
+ const url2 = device2.verification_uri_complete ?? device2.verification_uri;
1075
+ const session2 = { status: "pending", url: url2, userCode: device2.user_code, providerId };
1076
+ oauthSessions.set(sessionId, session2);
1077
+ pollClinePassDeviceCode(device2).then(async (result) => {
1078
+ await saveNativeOAuthCredential(providerId, result.tokens, result.accountId, result.providerData);
1079
+ await refreshOAuthProviderModels(providerId);
1080
+ oauthSessions.set(sessionId, { ...session2, status: "done" });
1081
+ }).catch((err) => {
1082
+ oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1083
+ });
1084
+ sendJson(res, 200, { sessionId, url: url2, userCode: device2.user_code });
1085
+ return;
1086
+ }
1016
1087
  if (PKCE_PROVIDER_IDS.has(providerId)) {
1017
1088
  if (providerId === "claude-code") {
1018
1089
  sendJson(res, 400, {
@@ -1165,6 +1236,7 @@ async function handleLaunchApp(req, res, opts) {
1165
1236
  const body = JSON.parse(await readBody(req));
1166
1237
  const { appId, favorites, cwd } = body;
1167
1238
  const httpProxy = body.httpProxy === true;
1239
+ const withNative = body.withNative === true;
1168
1240
  let { providerId, modelId } = body;
1169
1241
  if (!appId) {
1170
1242
  sendJson(res, 400, { error: "Missing appId" });
@@ -1178,6 +1250,14 @@ async function handleLaunchApp(req, res, opts) {
1178
1250
  sendJson(res, 400, { error: "httpProxy must be true or false." });
1179
1251
  return;
1180
1252
  }
1253
+ if (body.withNative !== void 0 && typeof body.withNative !== "boolean") {
1254
+ sendJson(res, 400, { error: "withNative must be true or false." });
1255
+ return;
1256
+ }
1257
+ if (withNative && appId !== "codex" && appId !== "codex-app") {
1258
+ sendJson(res, 400, { error: "Native Codex mixed mode is available only for Codex CLI and ChatGPT Desktop." });
1259
+ return;
1260
+ }
1181
1261
  if (httpProxy && appId !== "claude") {
1182
1262
  sendJson(res, 400, { error: "Anthropic + Relay mode is available only for Claude Code CLI." });
1183
1263
  return;
@@ -1233,7 +1313,8 @@ async function handleLaunchApp(req, res, opts) {
1233
1313
  modelId,
1234
1314
  cwd: launchFolder,
1235
1315
  trace: opts.trace,
1236
- httpProxy
1316
+ httpProxy,
1317
+ ...withNative ? { withNative: true } : {}
1237
1318
  });
1238
1319
  traceUi(
1239
1320
  opts,
@@ -1672,4 +1753,4 @@ export {
1672
1753
  resolveUiShutdownDecision,
1673
1754
  runUiCommand
1674
1755
  };
1675
- //# sourceMappingURL=ui-command-27X4WJKC.js.map
1756
+ //# sourceMappingURL=ui-command-FARF2BF4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui-command.ts","../src/native-launcher.ts","../src/ui/api.ts","../src/ui/server-control.ts"],"sourcesContent":["import { createServer } from 'node:http';\nimport { readFileSync, readdirSync, writeFileSync, unlinkSync, existsSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { dirname } from 'node:path';\nimport pc from 'picocolors';\nimport * as p from '@clack/prompts';\nimport { getAppHome } from './paths.js';\nimport { handleUiApiRequest, type UiServerLifecycleEvent } from './ui/api.js';\nimport { getUiDebugLogPath, makeTraceLogger } from './trace-log.js';\nimport { VERSION } from './constants.js';\nimport { ensureOpencodeCloudProviders } from './registry/crud.js';\nimport { resolveServerAutostart } from './config.js';\nimport { startGatewayServerFromSavedConfig } from './ui/server-control.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PUBLIC_DIR = join(__dirname, 'ui', 'public');\nconst LOCK_FILE = join(getAppHome(), 'ui.lock');\nconst DEFAULT_SERVER_UI_PORT = 8787;\n\nexport type UiMode = 'full' | 'server';\n\nexport interface UiCommandOptions {\n trace?: boolean;\n /** Admin UI without app launch — for Docker / always-on gateway boxes. */\n serverMode?: boolean;\n /** Override listen port (server mode defaults to 8787). */\n port?: number;\n}\n\nexport interface UiRuntimeConfig {\n mode: UiMode;\n host: string;\n port: number;\n openBrowser: boolean;\n confirmShutdownOnSigint: boolean;\n}\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.png': 'image/png',\n '.webp': 'image/webp',\n '.svg': 'image/svg+xml',\n};\n\nfunction ext(path: string): string {\n const i = path.lastIndexOf('.');\n return i >= 0 ? path.slice(i) : '';\n}\n\n/** Resolve UI mode from opts + env (`RELAY_AI_UI_MODE=server`). */\nexport function resolveUiMode(opts: UiCommandOptions = {}, env: NodeJS.ProcessEnv = process.env): UiMode {\n if (opts.serverMode) return 'server';\n return env.RELAY_AI_UI_MODE === 'server' ? 'server' : 'full';\n}\n\nfunction resolveServerUiPort(opts: UiCommandOptions, env: NodeJS.ProcessEnv): number {\n if (opts.port != null) return opts.port;\n const envPort = Number(env.RELAY_AI_UI_PORT);\n return Number.isFinite(envPort) && envPort > 0 ? envPort : DEFAULT_SERVER_UI_PORT;\n}\n\nexport function resolveUiRuntimeConfig(\n opts: UiCommandOptions = {},\n env: NodeJS.ProcessEnv = process.env,\n): UiRuntimeConfig {\n const mode = resolveUiMode(opts, env);\n if (mode === 'server') {\n return {\n mode,\n host: '0.0.0.0',\n port: resolveServerUiPort(opts, env),\n openBrowser: false,\n confirmShutdownOnSigint: false,\n };\n }\n return {\n mode,\n host: '127.0.0.1',\n port: opts.port ?? 0,\n openBrowser: true,\n confirmShutdownOnSigint: true,\n };\n}\n\nfunction buildStaticCache(mode: UiMode): Map<string, { content: Buffer; mime: string }> {\n const cache = new Map<string, { content: Buffer; mime: string }>();\n try {\n for (const name of readdirSync(PUBLIC_DIR)) {\n const mime = MIME[ext(name)];\n if (!mime) continue;\n const raw = readFileSync(join(PUBLIC_DIR, name));\n let content = raw;\n if (name === 'index.html') {\n content = Buffer.from(\n raw.toString('utf8')\n .replaceAll('{{VERSION}}', VERSION)\n .replaceAll('{{UI_MODE}}', mode),\n );\n }\n cache.set(`/${name}`, { content, mime });\n }\n } catch {}\n return cache;\n}\n\nfunction removeLock(): void {\n try { unlinkSync(LOCK_FILE); } catch {}\n}\n\nexport async function probeServerHealth(port: number): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 500);\n const res = await fetch(`http://127.0.0.1:${port}/api/config`, { signal: controller.signal });\n clearTimeout(timer);\n if (res.status === 200) {\n const data = await res.json().catch(() => null);\n if (data && typeof data === 'object') return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport async function checkExistingServer(): Promise<string | null> {\n if (!existsSync(LOCK_FILE)) return null;\n try {\n const { pid, port } = JSON.parse(readFileSync(LOCK_FILE, 'utf8'));\n if (pid === process.pid) {\n removeLock();\n return null;\n }\n process.kill(pid, 0);\n\n const isAlive = await probeServerHealth(port ?? DEFAULT_SERVER_UI_PORT);\n if (!isAlive) {\n removeLock();\n return null;\n }\n\n return `http://127.0.0.1:${port}`;\n } catch {\n removeLock();\n return null;\n }\n}\n\nexport function isUiApiRoute(url: string): boolean {\n return url.startsWith('/api/') || url.startsWith('/oauth/callback');\n}\n\nexport function formatUiServerLifecycleMessage(event: UiServerLifecycleEvent): string {\n if (event.type === 'stopped') return '◇ Server Gateway stopped';\n const mode = event.listenMode === 'network' ? 'Network' : 'Local';\n const modelLabel = event.modelCount === 1 ? 'model' : 'models';\n return `◆ Server Gateway started · ${mode} mode · ${event.modelCount} ${modelLabel} exposed`;\n}\n\nexport async function resolveUiShutdownDecision(\n signal: NodeJS.Signals,\n promptClose: () => Promise<boolean | symbol> = () => p.confirm({\n message: 'Relay-AI UI is still running. Close it?',\n initialValue: true,\n }),\n opts?: { confirmOnSigint?: boolean },\n): Promise<'close' | 'keep'> {\n const confirmOnSigint = opts?.confirmOnSigint !== false;\n if (signal !== 'SIGINT' || !confirmOnSigint) return 'close';\n const shouldClose = await promptClose();\n if (p.isCancel(shouldClose)) return 'close';\n return shouldClose ? 'close' : 'keep';\n}\n\nexport async function runUiCommand(opts: UiCommandOptions = {}): Promise<number> {\n const runtime = resolveUiRuntimeConfig(opts);\n\n // Docker / empty RELAY_AI_HOME: seed Zen/Go once at UI boot (not on every catalog load).\n await ensureOpencodeCloudProviders();\n\n const existing = await checkExistingServer();\n if (existing) {\n console.log(`\\n ${pc.bold('relay-ai UI')} already running at ${pc.cyan(existing)}\\n`);\n return 0;\n }\n\n if (opts.trace) {\n process.env.RELAY_AI_TRACE = '1';\n }\n\n const staticCache = buildStaticCache(runtime.mode);\n const traceLogPath = opts.trace ? getUiDebugLogPath() : undefined;\n const trace = traceLogPath ? makeTraceLogger(traceLogPath) : undefined;\n trace?.(`ui server starting mode=${runtime.mode}`);\n\n const server = createServer((req, res) => {\n const url = req.url ?? '/';\n\n res.setHeader('X-Content-Type-Options', 'nosniff');\n\n if (isUiApiRoute(url)) {\n handleUiApiRequest(req, res, {\n trace: opts.trace,\n traceLogPath,\n uiMode: runtime.mode,\n onServerLifecycle: event => {\n console.log(`\\n ${formatUiServerLifecycleMessage(event)}\\n`);\n },\n });\n return;\n }\n\n const key = url === '/' ? '/index.html' : url.split('?')[0];\n trace?.(`static ${req.method ?? 'GET'} ${url} -> ${key}`);\n const cached = staticCache.get(key);\n if (cached) {\n res.writeHead(200, { 'Content-Type': cached.mime });\n res.end(cached.content);\n return;\n }\n\n res.writeHead(404);\n res.end('Not found');\n });\n\n await new Promise<void>((resolve, reject) => {\n server.listen(runtime.port, runtime.host, () => resolve());\n server.once('error', reject);\n });\n\n const addr = server.address();\n if (!addr || typeof addr === 'string') {\n console.error('Failed to bind server');\n return 1;\n }\n\n const port = addr.port;\n const displayHost = runtime.host === '0.0.0.0' ? '127.0.0.1' : runtime.host;\n const url = `http://${displayHost}:${port}`;\n\n mkdirSync(getAppHome(), { recursive: true });\n writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, port, mode: runtime.mode }));\n\n if (resolveServerAutostart()) {\n startGatewayServerFromSavedConfig().catch(err => {\n trace?.(`gateway autostart error: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n const cleanup = () => {\n removeLock();\n server.close();\n process.exit(0);\n };\n let handlingSignal = false;\n const handleSignal = async (signal: NodeJS.Signals) => {\n if (handlingSignal) return;\n handlingSignal = true;\n const decision = await resolveUiShutdownDecision(\n signal,\n undefined,\n { confirmOnSigint: runtime.confirmShutdownOnSigint },\n );\n if (decision === 'keep') {\n handlingSignal = false;\n return;\n }\n cleanup();\n };\n process.on('SIGINT', () => { void handleSignal('SIGINT'); });\n process.on('SIGTERM', () => { void handleSignal('SIGTERM'); });\n\n const modeLabel = runtime.mode === 'server' ? ' (server admin)' : '';\n console.log(`\\n ${pc.bold('relay-ai UI')}${modeLabel} ${pc.cyan(url)}\\n ${pc.dim('Press Ctrl+C to stop')}\\n`);\n if (runtime.mode === 'server') {\n console.log(` ${pc.dim('Gateway API (when started from Server tab): http://127.0.0.1:17645')}\\n`);\n }\n if (traceLogPath) {\n console.log(` ${pc.dim(`Trace log: ${traceLogPath}`)}\\n`);\n trace?.(`ui server listening ${url}`);\n }\n\n if (runtime.openBrowser) {\n try {\n const { default: open } = await import('open');\n await open(url);\n trace?.(`browser open ${url}`);\n } catch {\n trace?.(`browser open failed ${url}`);\n }\n }\n\n await new Promise<void>(() => {}); // keep alive until signal\n return 0;\n}\n","import { chmodSync, existsSync, mkdtempSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { getAppPathOverride } from './config.js';\nimport { findBinaryOnPath } from './binary-lookup.js';\nimport { findClaudeApp } from './claude-desktop/app-launch.js';\nimport { findCodexApp } from './codex/app-launch.js';\n\nconst isWindows = process.platform === 'win32';\nconst isMac = process.platform === 'darwin';\n\nexport interface AppInfo {\n id: string;\n name: string;\n type: 'cli' | 'app';\n installed: boolean;\n path: string | null;\n pathSource: 'auto' | 'override' | null;\n relayCommand: string;\n launchCommand: string | null;\n}\n\nexport interface RelayLaunchOptions {\n providerId?: string;\n modelId?: string;\n favorites?: boolean;\n cwd?: string;\n trace?: boolean;\n httpProxy?: boolean;\n withNative?: boolean;\n}\n\ninterface SupportedAppDefinition {\n id: string;\n name: string;\n type: 'cli' | 'app';\n detectId: string;\n relayCommand: string;\n}\n\nconst SUPPORTED_APPS: SupportedAppDefinition[] = [\n { id: 'claude', name: 'Claude Code CLI', type: 'cli', detectId: 'claude', relayCommand: 'claude' },\n { id: 'codex', name: 'Codex CLI', type: 'cli', detectId: 'codex', relayCommand: 'codex' },\n { id: 'gemini', name: 'Gemini CLI', type: 'cli', detectId: 'gemini', relayCommand: 'gemini' },\n { id: 'agy', name: 'Antigravity CLI', type: 'cli', detectId: 'agy', relayCommand: 'agy' },\n {\n id: 'antigravity',\n name: 'Antigravity (App)',\n type: 'app',\n detectId: 'antigravity',\n relayCommand: 'antigravity',\n },\n {\n id: 'antigravity-ide',\n name: 'Antigravity IDE (App)',\n type: 'app',\n detectId: 'antigravity-ide',\n relayCommand: 'antigravity-ide',\n },\n {\n id: 'claude-app',\n name: 'Claude Code Desktop',\n type: 'app',\n detectId: 'claude-app',\n relayCommand: 'claude-app',\n },\n {\n id: 'codex-app',\n name: 'ChatGPT Desktop (Codex)',\n type: 'app',\n detectId: 'codex-app',\n relayCommand: 'codex-app',\n },\n];\n\nexport function fallbackPathsForApp(id: string, platform: NodeJS.Platform = process.platform): string[] {\n const windows = platform === 'win32';\n const mac = platform === 'darwin';\n const appData = process.env['APPDATA'] ?? homedir();\n const localAppData = process.env['LOCALAPPDATA'] ?? join(homedir(), 'AppData', 'Local');\n\n switch (id) {\n case 'claude':\n return windows\n ? [\n join(appData, 'npm', 'claude.cmd'),\n join(appData, 'npm', 'claude'),\n ]\n : [\n join(homedir(), '.local', 'bin', 'claude'),\n join(homedir(), '.npm', 'bin', 'claude'),\n '/usr/local/bin/claude',\n '/opt/homebrew/bin/claude',\n ];\n case 'codex':\n return windows\n ? [\n join(appData, 'npm', 'codex.cmd'),\n join(appData, 'npm', 'codex'),\n ]\n : [\n join(homedir(), '.local', 'bin', 'codex'),\n join(homedir(), '.npm', 'bin', 'codex'),\n '/usr/local/bin/codex',\n '/opt/homebrew/bin/codex',\n ];\n case 'gemini':\n return windows\n ? [\n join(appData, 'npm', 'gemini.cmd'),\n join(appData, 'npm', 'gemini'),\n ]\n : [\n join(homedir(), '.local', 'bin', 'gemini'),\n join(homedir(), '.npm', 'bin', 'gemini'),\n '/usr/local/bin/gemini',\n '/opt/homebrew/bin/gemini',\n ];\n case 'agy':\n return windows\n ? [\n join(appData, 'npm', 'agy.cmd'),\n join(appData, 'npm', 'agy'),\n join(localAppData, 'Antigravity', 'agy.exe'),\n ]\n : [\n join(homedir(), '.local', 'bin', 'agy'),\n join(homedir(), '.npm', 'bin', 'agy'),\n '/usr/local/bin/agy',\n '/opt/homebrew/bin/agy',\n ];\n case 'antigravity-ide':\n if (mac) {\n return [\n '/Applications/Antigravity IDE.app/Contents/Resources/app/bin/antigravity-ide',\n join(homedir(), 'Applications', 'Antigravity IDE.app', 'Contents', 'Resources', 'app', 'bin', 'antigravity-ide'),\n ];\n }\n if (windows) {\n return [\n join(localAppData, 'Programs', 'Antigravity IDE', 'Antigravity IDE.exe'),\n join(localAppData, 'Programs', 'antigravity-ide', 'Antigravity IDE.exe'),\n join(localAppData, 'Programs', 'Antigravity', 'Antigravity IDE.exe'),\n ];\n }\n return ['/opt/antigravity-ide/Antigravity-IDE'];\n case 'antigravity':\n if (mac) {\n return [\n '/Applications/Antigravity.app/Contents/MacOS/Antigravity',\n join(homedir(), 'Applications', 'Antigravity.app', 'Contents', 'MacOS', 'Antigravity'),\n ];\n }\n if (windows) {\n return [\n join(localAppData, 'Programs', 'Antigravity', 'Antigravity.exe'),\n ];\n }\n return [\n '/opt/antigravity/antigravity',\n '/usr/local/bin/antigravity',\n '/usr/bin/antigravity',\n ];\n case 'claude-app':\n if (mac) {\n return [\n '/Applications/Claude.app/Contents/MacOS/Claude',\n join(homedir(), 'Applications', 'Claude.app', 'Contents', 'MacOS', 'Claude'),\n ];\n }\n return windows\n ? [join(localAppData, 'Programs', 'claude', 'Claude.exe')]\n : [];\n case 'codex-app':\n if (mac) {\n // OpenAI merged the Codex desktop app into the ChatGPT desktop app\n // (2026-07-09) — it's now named \"ChatGPT.app\" on disk. Codex.app is\n // kept as a fallback for installs that haven't updated yet.\n return [\n '/Applications/ChatGPT.app',\n join(homedir(), 'Applications', 'ChatGPT.app'),\n '/Applications/Codex.app',\n join(homedir(), 'Applications', 'Codex.app'),\n ];\n }\n // Windows exe/folder naming after the rename is unverified against a\n // real install (see src/codex/app-launch.ts) — best-effort guess\n // mirroring the confirmed macOS convention, with Codex.exe kept as\n // a fallback for installs that haven't updated yet.\n return windows\n ? [\n join(localAppData, 'Programs', 'ChatGPT', 'ChatGPT.exe'),\n join(localAppData, 'Programs', 'OpenAI ChatGPT', 'ChatGPT.exe'),\n join(localAppData, 'openai-chatgpt-electron', 'ChatGPT.exe'),\n join(localAppData, 'Programs', 'Codex', 'Codex.exe'),\n join(localAppData, 'Programs', 'OpenAI Codex', 'Codex.exe'),\n join(localAppData, 'openai-codex-electron', 'Codex.exe'),\n ]\n : [\n '/usr/bin/chatgpt',\n '/usr/lib/chatgpt/ChatGPT',\n '/opt/chatgpt/ChatGPT',\n '/usr/local/lib/chatgpt/ChatGPT',\n join(homedir(), '.local', 'bin', 'chatgpt'),\n join(homedir(), '.local', 'share', 'chatgpt', 'ChatGPT'),\n ];\n default:\n return [];\n }\n}\n\nconst FALLBACKS: Record<string, string[]> = Object.fromEntries(\n SUPPORTED_APPS.map(app => [app.detectId, fallbackPathsForApp(app.detectId)]),\n);\n\nexport function getSupportedApp(id: string): SupportedAppDefinition | undefined {\n return SUPPORTED_APPS.find(app => app.id === id);\n}\n\n// Check if a specific binary/app is installed\nexport function detectApp(id: string): { installed: boolean; path: string | null; pathSource: 'auto' | 'override' | null } {\n const override = getAppPathOverride(id);\n if (override) {\n return existsSync(override)\n ? { installed: true, path: override, pathSource: 'override' }\n : { installed: false, path: override, pathSource: 'override' };\n }\n\n const resolvedPath = findBinaryOnPath(id, FALLBACKS[id] ?? [], { verifyWhichResult: true });\n if (resolvedPath) {\n return { installed: true, path: resolvedPath, pathSource: 'auto' };\n }\n\n // Microsoft Store (MSIX) installs live under version-stamped WindowsApps\n // paths the static fallback lists can't cover — the app-launch finders\n // handle them via Get-StartApps (returning a shell:AppsFolder moniker).\n const appFinder = id === 'claude-app' ? findClaudeApp : id === 'codex-app' ? findCodexApp : null;\n if (appFinder) {\n const appPath = appFinder();\n if (appPath) return { installed: true, path: appPath, pathSource: 'auto' };\n }\n\n return { installed: false, path: null, pathSource: null };\n}\n\n// Generate the shell command to open a new native terminal running the chosen tool/args\nexport function getTerminalLaunchCommand(\n binPath: string,\n args: string[],\n opts: { cwd?: string; displayCommand?: string } = {},\n): string {\n const fullCmd = [binPath, ...args]\n .map(arg => {\n // Whitelist: alphanumeric, dash, underscore, dot, slash, colon, at-sign.\n // Anything else is rejected outright rather than escaped — the values\n // here (relay-ai binary path, provider/model ids) are always simple\n // identifiers, and there is no single escaping scheme that is safe for\n // all three downstream shells (macOS sh, Linux sh, Windows cmd.exe).\n if (!/^[a-zA-Z0-9\\-_./:@]+$/.test(arg)) {\n throw new Error(`Unsafe launch argument: ${JSON.stringify(arg)}`);\n }\n return arg;\n })\n .join(' ');\n const cwdPrefix = opts.cwd ? `cd ${quoteShellArg(opts.cwd)} && ` : '';\n const runCmd = `${cwdPrefix}${fullCmd}`;\n\n if (isMac) {\n const dir = mkdtempSync(join(tmpdir(), 'relay-ai-launch-'));\n const scriptPath = join(dir, 'launch.command');\n const displayCommand = opts.displayCommand ?? [binPath, ...args].join(' ');\n writeFileSync(scriptPath, [\n '#!/bin/sh',\n 'trap \\'rm -f \"$0\"; rmdir \"$(dirname \"$0\")\" 2>/dev/null\\' EXIT',\n 'clear',\n opts.cwd ? `cd ${quoteShellArg(opts.cwd)} || exit 1` : '',\n `printf '%s\\\\n\\\\n' ${quoteShellArg(`$ ${displayCommand}`)}`,\n fullCmd,\n 'status=$?',\n 'printf \"\\\\nRelay AI session exited with code %s. Press Return to close this window. \" \"$status\"',\n 'read _',\n 'exit \"$status\"',\n '',\n ].join('\\n'), { encoding: 'utf8', mode: 0o700 });\n chmodSync(scriptPath, 0o700);\n return `open -a Terminal ${quoteShellArg(scriptPath)}`;\n }\n\n if (isWindows) {\n // Windows Start command: launches in cmd.exe in a new window.\n // First parameter to start is the window title; /d sets the working\n // directory — cwd must NOT be passed as a `cd '...'` prefix because\n // cmd.exe does not understand the POSIX single-quoting used by\n // quoteShellArg (\"The filename, directory name, or volume label\n // syntax is incorrect\").\n const dirFlag = opts.cwd ? `/d \"${opts.cwd}\" ` : '';\n return `start \"Relay AI Terminal\" ${dirFlag}cmd.exe /k \"${fullCmd}\"`;\n }\n\n // Linux: probe terminal emulators (fallback to generic execution)\n // Write runCmd to a script file rather than embedding it inline — runCmd may\n // itself contain single-quoted segments (from quoteShellArg), which breaks\n // nested single-quoting if interpolated directly into `sh -c '...'`.\n const dir = mkdtempSync(join(tmpdir(), 'relay-ai-launch-'));\n const scriptPath = join(dir, 'launch.sh');\n writeFileSync(scriptPath, [\n '#!/bin/sh',\n runCmd,\n 'exec sh',\n '',\n ].join('\\n'), { encoding: 'utf8', mode: 0o700 });\n chmodSync(scriptPath, 0o700);\n const scriptArg = quoteShellArg(scriptPath);\n return `x-terminal-emulator -e sh ${scriptArg} || gnome-terminal -- sh ${scriptArg} || xterm -e sh ${scriptArg}`;\n}\n\nfunction quoteShellArg(value: string): string {\n if (/^[a-zA-Z0-9\\-_./]+$/.test(value)) return value;\n return `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nfunction relayCliPath(): string {\n return 'relay-ai';\n}\n\nexport function getRelayLaunchCommand(appId: string, options: RelayLaunchOptions = {}): string {\n const app = getSupportedApp(appId);\n if (!app) throw new Error(`Unsupported app: ${appId}`);\n\n const args = [app.relayCommand];\n if (options.trace) {\n args.push('--trace');\n }\n if (options.httpProxy) {\n if (app.id !== 'claude') {\n throw new Error('Transparent proxy mode is available only for Claude Code CLI.');\n }\n args.push('--http-proxy');\n }\n if (options.withNative) {\n if (app.id !== 'codex' && app.id !== 'codex-app') {\n throw new Error('Native Codex mixed mode is available only for Codex CLI and ChatGPT Desktop.');\n }\n args.push('--with-native');\n }\n if (options.providerId && options.modelId) {\n args.push('--provider', options.providerId, '--model', options.modelId);\n } else if (options.providerId || options.modelId) {\n throw new Error('Both providerId and modelId are required for an explicit Relay launch.');\n }\n\n return getTerminalLaunchCommand(relayCliPath(), args, {\n cwd: options.cwd,\n displayCommand: ['relay-ai', ...args].join(' '),\n });\n}\n\n// Get lists of all supported apps and status\nexport function getSupportedApps(): AppInfo[] {\n return SUPPORTED_APPS.map(app => {\n const { installed, path, pathSource } = detectApp(app.detectId);\n return {\n id: app.id,\n name: app.name,\n type: app.type,\n installed,\n path,\n pathSource,\n relayCommand: app.relayCommand,\n launchCommand: installed ? getRelayLaunchCommand(app.id) : null,\n };\n });\n}\n","import { getSupportedApps, getSupportedApp, getRelayLaunchCommand, detectApp } from '../native-launcher.js';\nimport { exec } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execAsync = promisify(exec);\nimport { existsSync, statSync } from 'node:fs';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { randomUUID } from 'node:crypto';\nimport { loadPreferences, recordLaunchFolder, savePreferences, setAppPathOverride, setServerAutostart } from '../config.js';\nimport { fetchProviderCatalog } from '../provider-catalog.js';\nimport { providersForCodexSubagents, providersForTarget, type RelayLaunchTarget } from '../target-compatibility.js';\nimport { normalizeFavoriteModels } from '../favorites.js';\nimport { CODEX_SUBAGENT_MODEL_CAP } from '../constants.js';\nimport { favoriteProviderDisplayName } from '../favorite-provider-display.js';\nimport {\n preferredRelayCredentialAuthRef,\n readStoredProviderCredential,\n saveProviderCredential,\n resolveProviderCredential,\n} from '../env.js';\nimport { readBody, sendJson } from '../http-utils.js';\nimport { loadRegistry } from '../registry/io.js';\nimport { refreshProviderModels, refreshAllProviderModels } from '../registry/refresh-models.js';\nimport { listAddableTemplates, listVisibleOAuthTemplates, PROVIDER_TEMPLATES } from '../provider-templates.js';\nimport { addProviderFromTemplate, type AddTemplateResult } from '../registry/add-template.js';\nimport { addCustomEndpointProvider, type CustomEndpointKind } from '../registry/custom-endpoint.js';\nimport { validateCustomEndpointUrl } from '../registry/url-security.js';\nimport { saveNativeOAuthCredential } from '../registry/provider-auth.js';\nimport { removeProviderFromRegistry } from '../registry/crud.js';\nimport { requestXaiDeviceCode, pollXaiDeviceCodeToken } from '../oauth/xai.js';\nimport { requestOpenAiDeviceCode, pollOpenAiDeviceCodeToken, openAiDeviceCodeUrl } from '../oauth/openai.js';\nimport { requestGithubDeviceCode, pollGithubDeviceCodeToken } from '../oauth/github.js';\nimport { requestClinePassDeviceCode, pollClinePassDeviceCode } from '../oauth/cline-pass.js';\nimport {\n guiCallbackRedirectUri,\n} from '../oauth/claude-code.js';\nimport {\n buildAntigravityAuthUrl,\n completeAntigravityExchange,\n} from '../oauth/antigravity-oauth.js';\nimport { writeSecureLogLine } from '../trace-log.js';\nimport { providerOptionsFromCatalog } from '../server/index.js';\nimport { getServerStatus, startGatewayServer, stopGatewayServer, type ServerStartRequest } from './server-control.js';\nimport { hostFromHeader } from '../server/advertise-addrs.js';\nimport { freeStatusLabel } from '../free-models.js';\nimport { checkForUpdates } from '../update-check.js';\nimport { supportsClaudeTransparentMode } from '../http-proxy/routes.js';\nimport { copilotPlanTier } from '../registry/copilot-models.js';\n\nconst MODELS_TIMEOUT_MS = 30_000;\n\nexport type UiServerLifecycleEvent =\n | { type: 'started'; listenMode: 'local' | 'network'; modelCount: number }\n | { type: 'stopped' };\n\nexport interface UiApiOptions {\n trace?: boolean;\n traceLogPath?: string;\n /** When `server`, app launch APIs are disabled (Docker admin UI). */\n uiMode?: 'full' | 'server';\n onServerLifecycle?: (event: UiServerLifecycleEvent) => void;\n}\n\n// ── OAuth device-code session store ──────────────────────────────────────────\n// Keyed by random session ID returned to the client. Sessions auto-purge on\n// first terminal status read so the map doesn't grow unboundedly.\n\ntype OAuthSessionStatus = 'pending' | 'done' | 'error';\n\ninterface OAuthSession {\n status: OAuthSessionStatus;\n url: string;\n userCode?: string; // device code flows only\n providerId: string;\n error?: string;\n // PKCE flows only — never sent to client:\n codeVerifier?: string;\n oauthState?: string;\n codeResolver?: (code: string) => void;\n errorRejecter?: (err: string) => void;\n}\n\nconst oauthSessions = new Map<string, OAuthSession>();\n\nasync function fetchModelsWithTimeout(opts?: Parameters<typeof fetchProviderCatalog>[0]) {\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('timeout')), MODELS_TIMEOUT_MS),\n );\n return Promise.race([fetchProviderCatalog(opts), timeout]);\n}\n\n/** Shared 500/504 mapping for the two provider-catalog-fetching routes below. */\nfunction sendCatalogFetchError(res: ServerResponse, err: unknown, label: string): void {\n const isTimeout = String(err).includes('timeout');\n sendJson(res, isTimeout ? 504 : 500, { error: isTimeout ? `${label} timed out` : String(err) });\n}\n\nfunction isLoopbackOrigin(origin: string | undefined): boolean {\n if (!origin) return false;\n try {\n const hostname = new URL(origin).hostname;\n return hostname === '127.0.0.1' || hostname === 'localhost' || hostname === '::1';\n } catch {\n return false;\n }\n}\n\nfunction sendCors(req: IncomingMessage, res: ServerResponse): void {\n const origin = req.headers.origin;\n const originValue = Array.isArray(origin) ? origin[0] : origin;\n if (isLoopbackOrigin(originValue)) {\n res.setHeader('Access-Control-Allow-Origin', originValue!);\n }\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n}\n\nfunction traceUi(opts: UiApiOptions | undefined, message: string): void {\n if (!opts?.trace || !opts.traceLogPath) return;\n writeSecureLogLine(opts.traceLogPath, `${new Date().toISOString()} ${message}`);\n}\n\nfunction notifyServerLifecycle(opts: UiApiOptions, event: UiServerLifecycleEvent): void {\n try {\n opts.onServerLifecycle?.(event);\n } catch {\n // Terminal output must not affect the gateway lifecycle or API response.\n }\n}\n\nexport function handleUiApiRequest(req: IncomingMessage, res: ServerResponse, opts: UiApiOptions = {}): void {\n sendCors(req, res);\n if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }\n\n const url = req.url ?? '';\n traceUi(opts, `${req.method ?? 'GET'} ${url}`);\n\n if (url === '/api/config' && req.method === 'GET') {\n handleGetConfig(res);\n } else if (url === '/api/update-status' && req.method === 'GET') {\n handleGetUpdateStatus(res);\n } else if (url === '/api/config' && req.method === 'POST') {\n handlePostConfig(req, res);\n } else if (url.startsWith('/api/models') && req.method === 'GET') {\n const appId = new URL(url, 'http://localhost').searchParams.get('appId') ?? '';\n handleGetModels(res, APP_ID_TO_LAUNCH_TARGET[appId], appId === 'codex-subagents');\n } else if (url === '/api/keys' && req.method === 'POST') {\n handlePostKeys(req, res);\n } else if (url === '/api/providers/refresh' && req.method === 'POST') {\n handleProviderRefresh(req, res);\n } else if (url === '/api/providers/refresh-all' && req.method === 'POST') {\n handleRefreshAll(res);\n } else if (url === '/api/providers/templates' && req.method === 'GET') {\n handleGetTemplates(res);\n } else if (url === '/api/providers/add' && req.method === 'POST') {\n handleAddProvider(req, res);\n } else if (url === '/api/providers/add-custom' && req.method === 'POST') {\n handleAddCustomProvider(req, res);\n } else if (url === '/api/providers/delete' && req.method === 'POST') {\n handleDeleteProvider(req, res);\n } else if (url === '/api/providers/oauth/start' && req.method === 'POST') {\n handleOAuthStart(req, res);\n } else if (url.startsWith('/api/providers/oauth/status') && req.method === 'GET') {\n handleOAuthStatus(req, res);\n } else if (url.startsWith('/oauth/callback') && req.method === 'GET') {\n handleOAuthCallback(req, res);\n } else if (url.startsWith('/api/apps')) {\n if (opts.uiMode === 'server') {\n sendJson(res, 403, { error: 'App launch is unavailable in server admin UI mode.' });\n return;\n }\n if (url === '/api/apps' && req.method === 'GET') handleGetApps(res);\n else if (url === '/api/apps/path' && req.method === 'POST') handleSetAppPath(req, res);\n else if (url === '/api/apps/launch' && req.method === 'POST') handleLaunchApp(req, res, opts);\n else if (url === '/api/apps/browse-folder' && req.method === 'POST') handleBrowseFolder(res);\n else sendJson(res, 404, { error: 'Not found' });\n } else if (url === '/api/server/status' && req.method === 'GET') {\n handleGetServerStatus(req, res);\n } else if (url === '/api/server/providers' && req.method === 'GET') {\n handleGetServerProviders(res);\n } else if (url === '/api/server/start' && req.method === 'POST') {\n handleStartServer(req, res, opts);\n } else if (url === '/api/server/stop' && req.method === 'POST') {\n handleStopServer(res, opts);\n } else if (url === '/api/server/config' && req.method === 'POST') {\n handlePostServerConfig(req, res);\n } else {\n sendJson(res, 404, { error: 'Not found' });\n }\n}\n\nasync function handleGetUpdateStatus(res: ServerResponse): Promise<void> {\n sendJson(res, 200, await checkForUpdates());\n}\n\nfunction handleGetConfig(res: ServerResponse): void {\n const prefs = loadPreferences();\n sendJson(res, 200, {\n favoriteModels: prefs.favoriteModels ?? [],\n codexSubagentModels: prefs.codexSubagentModels ?? [],\n antigravityCliFavoriteModels: prefs.antigravityCliFavoriteModels ?? [],\n });\n}\n\nasync function handlePostConfig(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req));\n const update: Parameters<typeof savePreferences>[0] = {};\n if (Array.isArray(body.favoriteModels)) update.favoriteModels = body.favoriteModels;\n if (Array.isArray(body.antigravityCliFavoriteModels)) update.antigravityCliFavoriteModels = body.antigravityCliFavoriteModels;\n if (Array.isArray(body.codexSubagentModels)) {\n const normalized = normalizeFavoriteModels(body.codexSubagentModels, CODEX_SUBAGENT_MODEL_CAP + 1);\n if (normalized.length > CODEX_SUBAGENT_MODEL_CAP) {\n sendJson(res, 400, { error: 'Codex Sub-agents are limited to 1 model' });\n return;\n }\n update.codexSubagentModels = normalized;\n }\n if (Object.keys(update).length > 0) savePreferences(update);\n sendJson(res, 200, { ok: true });\n } catch (err) {\n sendJson(res, 400, { error: String(err) });\n }\n}\n\nasync function handleGetModels(res: ServerResponse, target?: RelayLaunchTarget, codexSubagents = false): Promise<void> {\n try {\n let catalog = (await fetchModelsWithTimeout())\n .filter(provider => provider.authType !== 'oauth' || DEVICE_CODE_PROVIDER_IDS.has(provider.id));\n // Per-app launch pickers pass their target so unsupported/too-small models (context\n // floor, format compatibility) are filtered the same way the CLI wizards filter them.\n if (codexSubagents) catalog = providersForCodexSubagents(catalog);\n else if (target) catalog = providersForTarget(catalog, target);\n const registry = loadRegistry();\n const rawCountById = new Map(registry.providers.map(p => [p.id, p.modelsCache?.models.length ?? 0]));\n const providers = catalog.map(p => ({\n id: p.id,\n name: p.name,\n favoriteName: favoriteProviderDisplayName(p),\n hasKey: Boolean(p.apiKey),\n freeAccess: !p.apiKey && (() => {\n const t = (registry.providers.find(rp => rp.id === p.id)?.templateId ?? p.id);\n const { getTemplateById } = require('../provider-templates.js');\n return getTemplateById(t)?.anonymousFreeModels === true;\n })(),\n authType: p.authType ?? 'api',\n // Copilot's runtime catalog is policy-filtered by account plan. Never replace\n // that safe count with the larger raw cache count.\n modelCount: p.id === 'github-copilot' ? p.models.length : (rawCountById.get(p.id) ?? p.models.length),\n ...(p.id === 'github-copilot' ? { subscription: copilotSubscription(p.providerData) } : {}),\n models: p.models.map(m => ({\n id: m.id,\n name: m.name,\n isFree: m.isFree ?? false,\n freeStatus: m.freeStatus,\n freeLabel: freeStatusLabel(m.freeStatus),\n contextWindow: m.contextWindow,\n cost: m.cost,\n claudeTransparentCompatible: supportsClaudeTransparentMode(m),\n })),\n }));\n\n // OAuth providers with 0 models are excluded by materializeOne (no models = not materialized).\n // Surface them anyway so their card appears and the user can click Refresh Models.\n const materializedIds = new Set(catalog.map(p => p.id));\n for (const rp of registry.providers) {\n if (rp.authType !== 'oauth'\n || !DEVICE_CODE_PROVIDER_IDS.has(rp.id)\n || !rp.enabled\n || materializedIds.has(rp.id)) continue;\n const credential = await resolveProviderCredential(rp.id, rp.authRef).catch(() => null);\n if (!credential) continue;\n providers.push({\n id: rp.id,\n name: rp.name,\n favoriteName: favoriteProviderDisplayName({ id: rp.id, name: rp.name, authType: rp.authType }),\n hasKey: true,\n freeAccess: false,\n authType: 'oauth',\n modelCount: 0,\n ...(rp.id === 'github-copilot' ? { subscription: copilotSubscription(undefined) } : {}),\n models: [],\n });\n }\n\n sendJson(res, 200, { providers });\n } catch (err) {\n sendCatalogFetchError(res, err, 'Model fetch');\n }\n}\n\nfunction copilotSubscription(providerData?: Record<string, unknown>): { tier: 'free' | 'paid' | 'unknown'; label: string } {\n const tier = copilotPlanTier(providerData);\n if (tier === 'free') return { tier, label: 'Copilot Free' };\n if (tier === 'paid') return { tier, label: 'Copilot Paid' };\n return { tier, label: 'Plan unverified' };\n}\n\nasync function handlePostKeys(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req));\n const { providerId, key, confirmOverwrite } = body;\n if (!providerId || typeof providerId !== 'string') {\n sendJson(res, 400, { error: 'providerId required' }); return;\n }\n if (!key || typeof key !== 'string' || key.trim().length === 0) {\n sendJson(res, 400, { error: 'key must be a non-empty string' }); return;\n }\n const authRef = preferredRelayCredentialAuthRef(providerId, `keyring:provider:${providerId}`);\n const existing = await readStoredProviderCredential(authRef)\n ?? (providerId === 'go' || providerId === 'zen'\n ? await readStoredProviderCredential('keyring:global:opencode')\n : null);\n if (existing && existing !== key.trim() && confirmOverwrite !== true) {\n sendJson(res, 409, { ok: false, needsConfirmation: true, error: 'A different key is already stored in Relay.' });\n return;\n }\n const saved = await saveProviderCredential(authRef, key.trim());\n if (saved) {\n sendJson(res, 200, { ok: true });\n } else {\n sendJson(res, 500, { error: 'Credential store unavailable — key not saved' });\n }\n } catch (err) {\n sendJson(res, 400, { error: String(err) });\n }\n}\n\nconst CUSTOM_TEMPLATES = [\n { id: '__custom_openai__', name: 'Custom OpenAI-compatible', signupUrl: null, authType: 'api', custom: true },\n { id: '__custom_anthropic__', name: 'Custom Anthropic-compatible', signupUrl: null, authType: 'api', custom: true },\n] as const;\n\nfunction handleGetTemplates(res: ServerResponse): void {\n const registry = loadRegistry();\n const configured = new Set(registry.providers.map(p => p.id));\n\n const templates = new Map<string, any>();\n const apiTemplates = listAddableTemplates(configured).map(t => ({\n id: t.id,\n name: t.name,\n signupUrl: t.signupUrl ?? null,\n authType: t.authType,\n anonymousFreeModels: t.anonymousFreeModels ?? false,\n urlPrompt: t.urlPrompt ?? null,\n accountIdPrompt: t.accountIdPrompt ?? null,\n defaultBaseUrl: t.defaultBaseUrl ?? null,\n apiKeyOptional: t.apiKeyOptional ?? false,\n authMethods: t.authMethods ?? [t.authType],\n custom: false,\n }));\n for (const template of apiTemplates) templates.set(template.id, template);\n\n const oauthTemplates = listVisibleOAuthTemplates(configured)\n .map(t => ({\n id: t.id,\n name: t.name,\n signupUrl: t.signupUrl ?? null,\n authType: t.authType,\n authMethods: t.authMethods ?? [t.authType],\n subscriptionRisk: t.subscriptionRisk ?? false,\n custom: false,\n }));\n for (const template of oauthTemplates) {\n const existing = templates.get(template.id);\n if (existing) {\n templates.set(template.id, {\n ...existing,\n authMethods: [...new Set([...(existing.authMethods ?? []), ...(template.authMethods ?? [])])],\n subscriptionRisk: template.subscriptionRisk,\n });\n } else {\n templates.set(template.id, template);\n }\n }\n\n sendJson(res, 200, { templates: [...templates.values(), ...CUSTOM_TEMPLATES] });\n}\n\nasync function handleAddCustomProvider(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req)) as {\n kind?: string; displayName?: string; baseUrl?: string; apiKey?: string; headers?: Record<string, string>;\n };\n const { kind, displayName, baseUrl, apiKey = '', headers } = body;\n if (kind !== 'openai' && kind !== 'anthropic') {\n sendJson(res, 400, { error: 'kind must be \"openai\" or \"anthropic\"' }); return;\n }\n if (!displayName?.trim()) {\n sendJson(res, 400, { error: 'displayName required' }); return;\n }\n if (!baseUrl?.trim()) {\n sendJson(res, 400, { error: 'baseUrl required' }); return;\n }\n const result = await addCustomEndpointProvider({\n kind: kind as CustomEndpointKind,\n displayName: displayName.trim(),\n baseUrl: baseUrl.trim(),\n apiKey: apiKey.trim(),\n allowInsecureLocal: true,\n headers: headers && Object.keys(headers).length > 0 ? headers : undefined,\n });\n if (result.added) {\n sendJson(res, 200, { ok: true, name: displayName.trim(), count: result.modelCount ?? 0 });\n } else {\n sendJson(res, 200, { ok: false, error: result.error, hint: result.hint });\n }\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n\nasync function handleAddProvider(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req));\n const { templateId, key, baseUrl, accountId } = body;\n if (!templateId || typeof templateId !== 'string') {\n sendJson(res, 400, { error: 'templateId required' }); return;\n }\n const { listSupportedTemplates } = await import('../provider-templates.js');\n const template = listSupportedTemplates().find(t => t.id === templateId);\n if (!template) {\n sendJson(res, 404, { error: `Template '${templateId}' not found` }); return;\n }\n const rawKey = typeof key === 'string' ? key.trim() : '';\n if (!rawKey && !template.anonymousFreeModels && !template.apiKeyOptional) {\n sendJson(res, 400, { error: 'key must be a non-empty string' }); return;\n }\n const keyText = template.apiKeyOptional && !rawKey && !template.anonymousFreeModels ? template.id : rawKey;\n\n let baseUrlOverride: string | undefined;\n if (template.accountIdPrompt) {\n const rawAccountId = typeof accountId === 'string' ? accountId.trim() : '';\n if (!rawAccountId) {\n sendJson(res, 400, { error: 'accountId required' }); return;\n }\n baseUrlOverride = template.defaultBaseUrl?.replace('{ACCOUNT_ID}', rawAccountId);\n } else if (template.urlPrompt) {\n baseUrlOverride = typeof baseUrl === 'string' ? baseUrl.trim() : '';\n if (!baseUrlOverride) {\n sendJson(res, 400, { error: 'baseUrl required' }); return;\n }\n const usesHttp = /^http:\\/\\//i.test(baseUrlOverride);\n const valid = await validateCustomEndpointUrl(baseUrlOverride, { allowInsecureLocal: usesHttp });\n if (!valid.ok) {\n sendJson(res, 400, { error: valid.error ?? 'Invalid URL', hint: valid.hint }); return;\n }\n }\n\n const replaceExisting = template.id === 'cline-pass' && body.replaceExisting === true;\n const result: AddTemplateResult = await addProviderFromTemplate(template, keyText, {\n baseUrl: baseUrlOverride,\n replaceExisting,\n });\n if (result.added) {\n sendJson(res, 200, { ok: true, name: template.name, count: result.modelCount ?? 0 });\n } else {\n sendJson(res, 200, { ok: false, error: result.error, hint: result.hint });\n }\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n\nasync function handleRefreshAll(res: ServerResponse): Promise<void> {\n try {\n const result = await refreshAllProviderModels(async provider => {\n if (!provider.authRef) return null;\n return resolveProviderCredential(provider.id, provider.authRef);\n });\n // Return per-provider summary: id, name, ok, count\n const summary = result.refreshed.map(r => {\n // OAuth providers can't refresh model lists via the standard API endpoint —\n // the token is for user sessions, not model discovery. This is expected, not broken.\n const isOAuthExpected = !r.ok && !r.skipped && r.reason?.includes('OAuth token');\n return {\n id: r.id,\n name: r.name,\n ok: r.ok || isOAuthExpected,\n count: r.modelCount ?? r.previousModelCount ?? 0,\n skipped: r.skipped ?? isOAuthExpected,\n oauthWarning: isOAuthExpected,\n reason: r.reason,\n };\n });\n sendJson(res, 200, { ok: true, providers: summary, total: summary.reduce((n, p) => n + p.count, 0) });\n } catch (err) {\n sendJson(res, 500, { ok: false, error: String(err) });\n }\n}\n\nasync function handleProviderRefresh(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req));\n const { providerId } = body;\n if (!providerId || typeof providerId !== 'string') {\n sendJson(res, 400, { error: 'providerId required' }); return;\n }\n const registry = loadRegistry();\n const registryProvider = registry.providers.find(p => p.id === providerId);\n if (!registryProvider) {\n sendJson(res, 200, { ok: false, error: 'Provider not found in registry' }); return;\n }\n // A key typed into the UI is a one-request override. Do not let env/keychain\n // resolution silently replace it before the provider API sees the request.\n const explicitKey = typeof body.key === 'string' ? body.key.trim() : '';\n const apiKey = explicitKey || await resolveProviderCredential(providerId, registryProvider.authRef);\n // Use the same refresh path as `relay-ai providers refresh-models` so counts match CLI\n const result = await refreshProviderModels(providerId, apiKey, registry);\n if (result.ok) {\n sendJson(res, 200, { ok: true, count: result.modelCount ?? result.previousModelCount ?? 0 });\n } else {\n sendJson(res, 200, { ok: false, error: result.reason ?? 'Refresh failed' });\n }\n } catch (err) {\n sendJson(res, 200, { ok: false, error: String(err) });\n }\n}\n\nasync function handleDeleteProvider(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req)) as { providerId?: string };\n const { providerId } = body;\n if (!providerId || typeof providerId !== 'string') {\n sendJson(res, 400, { error: 'providerId required' }); return;\n }\n const result = await removeProviderFromRegistry(providerId);\n if (result.removed) {\n sendJson(res, 200, { ok: true, name: result.name });\n } else {\n sendJson(res, 200, { ok: false, error: result.error ?? 'Provider not found' });\n }\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n\nconst DEVICE_CODE_PROVIDER_IDS = new Set(['xai-oauth', 'openai-oauth', 'github-copilot', 'cline-pass']);\nconst PKCE_PROVIDER_IDS = new Set(['claude-code', 'antigravity']);\nconst NATIVE_OAUTH_PROVIDER_IDS = DEVICE_CODE_PROVIDER_IDS;\n\nasync function refreshOAuthProviderModels(providerId: string): Promise<void> {\n const registry = loadRegistry();\n const entry = registry.providers.find(p => p.id === providerId);\n if (!entry) return;\n const apiKey = await resolveProviderCredential(providerId, entry.authRef);\n await refreshProviderModels(providerId, apiKey, registry);\n}\n\nasync function handleOAuthStart(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req)) as { providerId?: string };\n const { providerId } = body;\n if (!providerId || !NATIVE_OAUTH_PROVIDER_IDS.has(providerId)) {\n sendJson(res, 400, { error: 'Unsupported OAuth provider.' }); return;\n }\n\n const sessionId = randomUUID();\n\n if (providerId === 'xai-oauth') {\n const device = await requestXaiDeviceCode();\n const url = device.verification_uri_complete ?? device.verification_uri;\n const session: OAuthSession = { status: 'pending', url, userCode: device.user_code, providerId };\n oauthSessions.set(sessionId, session);\n\n pollXaiDeviceCodeToken(device).then(async tokens => {\n await saveNativeOAuthCredential(providerId, tokens);\n await refreshOAuthProviderModels(providerId);\n oauthSessions.set(sessionId, { ...session, status: 'done' });\n }).catch(err => {\n oauthSessions.set(sessionId, { ...session, status: 'error', error: String(err) });\n });\n\n sendJson(res, 200, { sessionId, url, userCode: device.user_code });\n return;\n }\n\n if (providerId === 'github-copilot') {\n const device = await requestGithubDeviceCode();\n const url = device.verification_uri;\n const session: OAuthSession = { status: 'pending', url, userCode: device.user_code, providerId };\n oauthSessions.set(sessionId, session);\n\n pollGithubDeviceCodeToken(device).then(async tokens => {\n await saveNativeOAuthCredential(providerId, tokens);\n await refreshOAuthProviderModels(providerId);\n oauthSessions.set(sessionId, { ...session, status: 'done' });\n }).catch(err => {\n oauthSessions.set(sessionId, { ...session, status: 'error', error: String(err) });\n });\n\n sendJson(res, 200, { sessionId, url, userCode: device.user_code });\n return;\n }\n\n if (providerId === 'cline-pass') {\n const device = await requestClinePassDeviceCode();\n const url = device.verification_uri_complete ?? device.verification_uri;\n const session: OAuthSession = { status: 'pending', url, userCode: device.user_code, providerId };\n oauthSessions.set(sessionId, session);\n\n pollClinePassDeviceCode(device).then(async result => {\n await saveNativeOAuthCredential(providerId, result.tokens, result.accountId, result.providerData);\n await refreshOAuthProviderModels(providerId);\n oauthSessions.set(sessionId, { ...session, status: 'done' });\n }).catch(err => {\n oauthSessions.set(sessionId, { ...session, status: 'error', error: String(err) });\n });\n\n sendJson(res, 200, { sessionId, url, userCode: device.user_code });\n return;\n }\n\n if (PKCE_PROVIDER_IDS.has(providerId)) {\n if (providerId === 'claude-code') {\n sendJson(res, 400, {\n error: 'Claude Code OAuth must be completed in the terminal: relay-ai providers auth claude-code',\n });\n return;\n }\n\n // PKCE / browser-redirect flow (claude-code, future: antigravity).\n const host = (req.headers.host as string | undefined) ?? '127.0.0.1';\n const redirectUri = guiCallbackRedirectUri(host);\n\n let pkce: Awaited<ReturnType<typeof buildAntigravityAuthUrl>>;\n if (providerId === 'antigravity') {\n pkce = await buildAntigravityAuthUrl(redirectUri);\n } else {\n sendJson(res, 400, { error: `PKCE flow for \"${providerId}\" not yet implemented` }); return;\n }\n\n const { authUrl, codeVerifier, oauthState } = pkce;\n const session: OAuthSession = {\n status: 'pending',\n url: authUrl,\n providerId,\n codeVerifier,\n oauthState,\n };\n oauthSessions.set(sessionId, session);\n\n // The callback route will call session.codeResolver when the code arrives.\n const codePromise = new Promise<string>((resolve, reject) => {\n session.codeResolver = resolve;\n session.errorRejecter = (err: string) => reject(new Error(err));\n setTimeout(() => reject(new Error('OAuth timeout — sign-in not completed')), 10 * 60 * 1000);\n });\n oauthSessions.set(sessionId, session); // re-set with resolvers attached\n\n codePromise.then(async (code) => {\n let providerData: Record<string, unknown> = {};\n let accountId: string | undefined;\n let tokens: import('../oauth/types.js').OAuthTokenResponse;\n\n if (providerId === 'antigravity') {\n const result = await completeAntigravityExchange(code, codeVerifier, redirectUri);\n tokens = result.tokens;\n accountId = result.userInfo.email;\n if (result.projectId) providerData.projectId = result.projectId;\n if (result.tierId) providerData.tier = result.tierId;\n } else {\n throw new Error(`Unknown PKCE provider: ${providerId}`);\n }\n\n await saveNativeOAuthCredential(providerId, tokens, accountId, providerData);\n\n await refreshOAuthProviderModels(providerId);\n oauthSessions.set(sessionId, { ...session, status: 'done' });\n }).catch(err => {\n oauthSessions.set(sessionId, { ...session, status: 'error', error: String(err) });\n });\n\n sendJson(res, 200, { sessionId, authUrl, pkce: true });\n return;\n }\n\n // openai-oauth\n const device = await requestOpenAiDeviceCode();\n const url = openAiDeviceCodeUrl();\n const session: OAuthSession = { status: 'pending', url, userCode: device.user_code, providerId };\n oauthSessions.set(sessionId, session);\n\n pollOpenAiDeviceCodeToken(device).then(async ({ tokens, accountId }) => {\n await saveNativeOAuthCredential(providerId, tokens, accountId);\n await refreshOAuthProviderModels(providerId);\n oauthSessions.set(sessionId, { ...session, status: 'done' });\n }).catch(err => {\n oauthSessions.set(sessionId, { ...session, status: 'error', error: String(err) });\n });\n\n sendJson(res, 200, { sessionId, url, userCode: device.user_code });\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n\nfunction handleOAuthStatus(req: IncomingMessage, res: ServerResponse): void {\n const searchParams = new URL(req.url ?? '', 'http://localhost').searchParams;\n const sessionId = searchParams.get('sessionId') ?? '';\n const session = oauthSessions.get(sessionId);\n if (!session) { sendJson(res, 404, { error: 'Session not found or expired' }); return; }\n sendJson(res, 200, { status: session.status, error: session.error });\n if (session.status !== 'pending') oauthSessions.delete(sessionId);\n}\n\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/'/g, '&#39;');\n}\n\nfunction callbackPage(type: 'success' | 'error', message: string): string {\n const icon = type === 'success' ? '&#10003;' : '&#10007;';\n const color = type === 'success' ? '#22c55e' : '#ef4444';\n const title = type === 'success' ? 'Authentication successful' : 'Authentication failed';\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>${title}</title></head>\n<body style=\"font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0\">\n<div style=\"text-align:center;padding:2rem;background:#fff;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,.1);max-width:400px\">\n<div style=\"color:${color};font-size:2.5rem\">${icon}</div>\n<h1 style=\"margin:.5rem 0\">${title}</h1>\n<p style=\"color:#666\">${escapeHtml(message)}</p>\n</div></body></html>`;\n}\n\nfunction handleOAuthCallback(req: IncomingMessage, res: ServerResponse): void {\n const sp = new URL(req.url ?? '', 'http://localhost').searchParams;\n const code = sp.get('code') ?? '';\n const state = sp.get('state') ?? '';\n const error = sp.get('error') ?? '';\n\n let matchedSession: OAuthSession | undefined;\n for (const session of oauthSessions.values()) {\n if (session.oauthState === state) { matchedSession = session; break; }\n }\n\n if (!matchedSession) {\n res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(callbackPage('error', 'Unknown or expired OAuth session. Please try signing in again.'));\n return;\n }\n\n if (error) {\n matchedSession.errorRejecter?.(error);\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(callbackPage('error', `Authorization denied: ${error}`));\n return;\n }\n\n if (!code) {\n matchedSession.errorRejecter?.('No authorization code received');\n res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(callbackPage('error', 'No authorization code received. Please try again.'));\n return;\n }\n\n matchedSession.codeResolver?.(code);\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(callbackPage('success', 'You can close this tab and return to relay-ai.'));\n}\n\nfunction handleGetApps(res: ServerResponse): void {\n try {\n const apps = getSupportedApps();\n sendJson(res, 200, { apps, recentLaunchFolders: loadPreferences().recentLaunchFolders ?? [] });\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n\nconst AGY_APP_IDS = new Set(['antigravity', 'agy', 'antigravity-ide']);\n\n/** Maps a `relay-ai ui` app card id to the launch target `target-compatibility.ts` understands. */\nconst APP_ID_TO_LAUNCH_TARGET: Record<string, RelayLaunchTarget> = {\n claude: 'claude',\n 'claude-app': 'claude-app',\n codex: 'codex',\n 'codex-app': 'codex-app',\n gemini: 'gemini',\n agy: 'antigravity',\n antigravity: 'antigravity',\n 'antigravity-ide': 'antigravity',\n};\n\nasync function handleLaunchApp(req: IncomingMessage, res: ServerResponse, opts: UiApiOptions): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req));\n const { appId, favorites, cwd } = body;\n const httpProxy = body.httpProxy === true;\n const withNative = body.withNative === true;\n let { providerId, modelId } = body as { providerId?: string; modelId?: string };\n if (!appId) {\n sendJson(res, 400, { error: 'Missing appId' });\n return;\n }\n if (!getSupportedApp(appId)) {\n sendJson(res, 400, { error: `Unknown app: ${appId}` });\n return;\n }\n if (body.httpProxy !== undefined && typeof body.httpProxy !== 'boolean') {\n sendJson(res, 400, { error: 'httpProxy must be true or false.' });\n return;\n }\n if (body.withNative !== undefined && typeof body.withNative !== 'boolean') {\n sendJson(res, 400, { error: 'withNative must be true or false.' });\n return;\n }\n if (withNative && appId !== 'codex' && appId !== 'codex-app') {\n sendJson(res, 400, { error: 'Native Codex mixed mode is available only for Codex CLI and ChatGPT Desktop.' });\n return;\n }\n if (httpProxy && appId !== 'claude') {\n sendJson(res, 400, { error: 'Anthropic + Relay mode is available only for Claude Code CLI.' });\n return;\n }\n\n const { installed, path } = detectApp(appId);\n if (!installed || !path) {\n sendJson(res, 400, { error: `App ${appId} is not installed on this system.` });\n return;\n }\n\n if (!favorites && (providerId || modelId) && (!providerId || !modelId)) {\n sendJson(res, 400, { error: 'Both providerId and modelId are required to launch a specific Relay model.' });\n return;\n }\n if (httpProxy && providerId && modelId) {\n let catalog;\n try {\n catalog = await fetchModelsWithTimeout();\n } catch (err) {\n sendCatalogFetchError(res, err, 'Model validation');\n return;\n }\n const selectedModel = catalog\n .find(provider => provider.id === providerId)\n ?.models.find(model => model.id === modelId);\n if (!selectedModel || !supportsClaudeTransparentMode(selectedModel)) {\n sendJson(res, 400, {\n error: 'The selected model cannot be combined with your Anthropic login.',\n });\n return;\n }\n }\n\n // Resolve the first favorite so the terminal can skip its interactive picker.\n // Without this the launch command has no --provider/--model and the terminal\n // shows the full provider wizard even though the user already chose \"Favorites\".\n if (favorites && !httpProxy && !providerId && !modelId) {\n const prefs = loadPreferences();\n const favList = AGY_APP_IDS.has(appId)\n ? (prefs.antigravityCliFavoriteModels ?? [])\n : (prefs.favoriteModels ?? []);\n if (favList.length > 0) {\n providerId = favList[0]!.providerId;\n modelId = favList[0]!.modelId;\n }\n }\n\n const launchFolder = typeof cwd === 'string' && cwd.trim() ? cwd.trim() : undefined;\n if (launchFolder) {\n try {\n if (!statSync(launchFolder).isDirectory()) {\n sendJson(res, 400, { error: 'Launch folder must be a directory.' });\n return;\n }\n } catch {\n sendJson(res, 400, { error: 'Launch folder does not exist.' });\n return;\n }\n recordLaunchFolder(launchFolder);\n }\n\n const launchCmd = getRelayLaunchCommand(appId, {\n providerId,\n modelId,\n cwd: launchFolder,\n trace: opts.trace,\n httpProxy,\n ...(withNative ? { withNative: true } : {}),\n });\n traceUi(\n opts,\n `launch app=${appId} provider=${providerId ?? ''} model=${modelId ?? ''} favorites=${Boolean(favorites)} http-proxy=${httpProxy} resolved-from-favorites=${Boolean(favorites && providerId)} cwd=${launchFolder ?? ''} command=${launchCmd}`,\n );\n\n // Execute command asynchronously to open the terminal window detached\n exec(launchCmd, (err) => {\n if (err) {\n traceUi(opts, `launch error app=${appId} error=${err.message}`);\n console.error('Failed to spawn native terminal window:', err);\n }\n });\n\n sendJson(res, 200, { ok: true, command: launchCmd });\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n\nasync function handleSetAppPath(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req));\n const { appId, path } = body;\n if (!appId || typeof appId !== 'string') {\n sendJson(res, 400, { error: 'Missing appId' });\n return;\n }\n\n if (path !== null && (typeof path !== 'string' || !path.trim())) {\n sendJson(res, 400, { error: 'path must be a non-empty string, or null to clear the override.' });\n return;\n }\n\n const trimmed = typeof path === 'string' ? path.trim() : null;\n if (trimmed && !existsSync(trimmed)) {\n sendJson(res, 400, { error: 'That path does not exist.' });\n return;\n }\n\n setAppPathOverride(appId, trimmed);\n sendJson(res, 200, { ok: true, apps: getSupportedApps() });\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n\nasync function handleGetServerStatus(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n sendJson(res, 200, await getServerStatus({ requestHost: hostFromHeader(req.headers.host) }));\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n\nasync function handleGetServerProviders(res: ServerResponse): Promise<void> {\n try {\n const catalog = await fetchModelsWithTimeout({ agent: 'server' });\n sendJson(res, 200, { providers: providerOptionsFromCatalog(catalog) });\n } catch (err) {\n sendCatalogFetchError(res, err, 'Provider fetch');\n }\n}\n\nasync function handleStartServer(req: IncomingMessage, res: ServerResponse, opts: UiApiOptions): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req)) as Partial<ServerStartRequest>;\n if (typeof body.favoritesOnly !== 'boolean') {\n sendJson(res, 400, { error: 'favoritesOnly must be a boolean' }); return;\n }\n if (typeof body.maskGatewayIds !== 'boolean') {\n sendJson(res, 400, { error: 'maskGatewayIds must be a boolean' }); return;\n }\n if (body.listenMode !== 'local' && body.listenMode !== 'network') {\n sendJson(res, 400, { error: 'listenMode must be \"local\" or \"network\"' }); return;\n }\n // Server admin UI / Docker: published ports only reach 0.0.0.0.\n const listenMode = opts.uiMode === 'server' ? 'network' : body.listenMode;\n const request: ServerStartRequest = {\n favoritesOnly: body.favoritesOnly,\n freeModelsOnly: Boolean(body.freeModelsOnly),\n exposedProviders: Array.isArray(body.exposedProviders) ? body.exposedProviders : null,\n maskGatewayIds: body.maskGatewayIds,\n listenMode,\n autostart: typeof body.autostart === 'boolean' ? body.autostart : undefined,\n passwordMode: body.passwordMode === 'saved' ? 'saved' : 'new',\n password: typeof body.password === 'string' ? body.password : undefined,\n savePassword: Boolean(body.savePassword),\n };\n const result = await startGatewayServer(request, {\n requestHost: hostFromHeader(req.headers.host),\n });\n if (result.ok) {\n notifyServerLifecycle(opts, {\n type: 'started',\n listenMode: request.listenMode,\n modelCount: result.status.models?.length ?? 0,\n });\n }\n sendJson(res, 200, result);\n } catch (err) {\n sendJson(res, 500, { ok: false, error: String(err) });\n }\n}\n\nasync function handlePostServerConfig(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = JSON.parse(await readBody(req));\n if (typeof body.autostart === 'boolean') {\n setServerAutostart(body.autostart);\n }\n sendJson(res, 200, { ok: true });\n } catch (err) {\n sendJson(res, 400, { error: String(err) });\n }\n}\n\nasync function handleStopServer(res: ServerResponse, opts: UiApiOptions): Promise<void> {\n try {\n const result = await stopGatewayServer();\n if (result.stopped) notifyServerLifecycle(opts, { type: 'stopped' });\n sendJson(res, 200, result);\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n\nasync function handleBrowseFolder(res: ServerResponse): Promise<void> {\n try {\n let resultPath = '';\n const isMac = process.platform === 'darwin';\n const isWindows = process.platform === 'win32';\n\n if (isMac) {\n const script = 'POSIX path of (choose folder with prompt \"Select launch folder:\")';\n try {\n const { stdout } = await execAsync(`osascript -e '${script}'`);\n resultPath = stdout.trim();\n } catch (err: any) {\n if (err.code === 1 || String(err.stderr).includes('-128') || String(err.stdout).includes('-128')) {\n sendJson(res, 200, { ok: true, canceled: true });\n return;\n }\n throw err;\n }\n } else if (isWindows) {\n // The try/catch + exit 1 is load-bearing: without it PowerShell reports\n // dialog failures (e.g. \"not running in UserInteractive mode\" when the\n // UI server was started from a non-interactive session like SSH) as\n // non-terminating errors and still exits 0 with empty stdout, which\n // looks identical to the user canceling.\n //\n // The owner form must be Show()n (invisible: opacity 0, 1x1, no\n // border/taskbar) — WinForms only applies TopMost to the native handle\n // when the form is actually shown, and without a shown TopMost owner\n // the dialog opens at the bottom of the z-order, behind the browser,\n // with no taskbar button: it exists but the user never sees it.\n const psCommand = [\n 'try {',\n ' Add-Type -AssemblyName System.Windows.Forms',\n ' $f = New-Object System.Windows.Forms.FolderBrowserDialog',\n ' $f.Description = \"Select launch folder\"',\n ' $owner = New-Object System.Windows.Forms.Form',\n ' $owner.TopMost = $true',\n ' $owner.ShowInTaskbar = $false',\n ' $owner.FormBorderStyle = \"None\"',\n ' $owner.Opacity = 0',\n ' $owner.Width = 1',\n ' $owner.Height = 1',\n ' $owner.StartPosition = \"CenterScreen\"',\n ' $owner.Show()',\n ' $owner.Activate()',\n ' if ($f.ShowDialog($owner) -eq \"OK\") { $f.SelectedPath }',\n ' $owner.Close()',\n '} catch {',\n ' [Console]::Error.WriteLine($_.Exception.Message)',\n ' exit 1',\n '}',\n ].join('\\n');\n try {\n // -Sta is required: System.Windows.Forms dialogs throw\n // ThreadStateException under PowerShell's default MTA apartment state.\n // -EncodedCommand sidesteps cmd.exe quoting: -Command \"...\" mangled\n // the script (its newlines were never actually stripped).\n const encoded = Buffer.from(psCommand, 'utf16le').toString('base64');\n const { stdout } = await execAsync(`powershell -NoProfile -Sta -EncodedCommand ${encoded}`);\n resultPath = stdout.trim();\n } catch (err) {\n sendJson(res, 500, { error: `Failed to open folder picker: ${err instanceof Error ? err.message : String(err)}` });\n return;\n }\n } else {\n try {\n const { stdout } = await execAsync('zenity --file-selection --directory --title=\"Select launch folder\"');\n resultPath = stdout.trim();\n } catch {\n try {\n const { stdout } = await execAsync('kdialog --getexistingdirectory .');\n resultPath = stdout.trim();\n } catch {\n sendJson(res, 500, { error: 'No GUI folder picker available on this platform' });\n return;\n }\n }\n }\n\n if (!resultPath) {\n sendJson(res, 200, { ok: true, canceled: true });\n return;\n }\n\n sendJson(res, 200, { ok: true, path: resultPath });\n } catch (err) {\n sendJson(res, 500, { error: String(err) });\n }\n}\n","// In-process lifecycle for the `relay-ai server` gateway, launched from the web UI.\n// Runs inside the same Node process as `relay-ai ui` — no child process, no PID file.\n// Stops automatically when the UI process exits, same as closing a terminal running\n// `relay-ai server` with Ctrl+C.\n\nimport { BACKENDS, MAX_MODEL_CATALOG } from '../constants.js';\nimport {\n getEnvServerPassword,\n getSavedServerPassword,\n getServerAutostart,\n getServerExposedProviders,\n getServerFavoritesOnly,\n getServerFreeModelsOnly,\n getServerListenMode,\n getServerMaskGatewayIds,\n loadPreferences,\n resolveServerAutostart,\n setSavedServerPassword,\n setServerAutostart,\n setServerExposedProviders,\n setServerFavoritesOnly,\n setServerFreeModelsOnly,\n setServerListenMode,\n setServerMaskGatewayIds,\n} from '../config.js';\nimport type { FavoriteModel } from '../types.js';\nimport { startServer, type ServerHandle } from '../server/router.js';\nimport { getServerDebugLogPath } from '../trace-log.js';\nimport {\n buildDedupedModelRows,\n createGatewayModelCatalog,\n gatewayProviderLabel,\n openAiIdCollisions,\n type GatewayModelOptions,\n type ServerModelInfo,\n} from '../server/models.js';\nimport {\n filterServerModelsByFavorites,\n filterServerModelsByFreeStatus,\n filterServerModelsByProviders,\n summarizeServerProviders,\n} from '../server/catalog-filter.js';\nimport { resolveAdvertiseAddresses, resolveAdvertiseGatewayPort, formatGatewayUrls } from '../server/advertise-addrs.js';\nimport { loadServerModels, resolveServerUpstreamApiKey } from '../server/index.js';\n\nexport type ServerListenMode = 'local' | 'network';\n\nexport interface ServerStartRequest {\n favoritesOnly: boolean;\n freeModelsOnly: boolean;\n exposedProviders: string[] | null;\n maskGatewayIds: boolean;\n listenMode: ServerListenMode;\n autostart?: boolean;\n /** Only relevant when listenMode is 'network'. */\n passwordMode?: 'saved' | 'new';\n password?: string;\n savePassword?: boolean;\n}\n\ntype RunningConfig = Omit<ServerStartRequest, 'passwordMode' | 'password' | 'savePassword'>;\n\ninterface RunningState {\n handle: ServerHandle;\n config: RunningConfig;\n serverPassword: string | null;\n /** Derived once from `models` at start time — the exposed model set never changes while running. */\n providerSummary: string;\n modelRows: ServerModelRow[];\n}\n\nexport interface ServerModelRow {\n providerLabel: string;\n name: string;\n anthropicId: string;\n openaiId: string;\n}\n\nexport interface ServerSavedConfig {\n favoritesOnly: boolean;\n freeModelsOnly: boolean;\n exposedProviders: string[] | null;\n maskGatewayIds: boolean;\n listenMode: ServerListenMode;\n autostart: boolean;\n hasSavedPassword: boolean;\n /** True when RELAY_AI_SERVER_PASSWORD is set. */\n hasEnvPassword: boolean;\n /**\n * Prefills the Server form from RELAY_AI_SERVER_PASSWORD (Docker / Compose).\n * Not used for keychain-saved passwords. Shown masked with a reveal toggle in the UI.\n */\n prefillPassword?: string;\n}\n\nexport interface ServerNetworkUrl {\n name: string;\n anthropicUrl: string;\n openaiUrl: string;\n}\n\nexport interface ServerStatusPayload {\n running: boolean;\n saved: ServerSavedConfig;\n listenMode?: ServerListenMode;\n anthropicUrl?: string;\n openaiUrl?: string;\n networkUrls?: ServerNetworkUrl[];\n apiKey?: string;\n exposedProviders?: string[] | null;\n favoritesOnly?: boolean;\n freeModelsOnly?: boolean;\n maskGatewayIds?: boolean;\n providerSummary?: string;\n models?: ServerModelRow[];\n}\n\nlet running: RunningState | null = null;\nlet startInFlight: Promise<{ ok: true; status: ServerStatusPayload } | { ok: false; error: string }> | null = null;\n\n// The OS keychain read behind getSavedServerPassword() is a blocking native call, and this\n// flag is polled every few seconds from the UI — cache it briefly so the poll loop doesn't\n// hit the keychain on every tick. Refreshed immediately below whenever we save a password.\nconst SAVED_PASSWORD_CACHE_TTL_MS = 30_000;\nlet hasSavedPasswordCache: { value: boolean; expiresAt: number } | null = null;\n\nasync function hasSavedPasswordCached(): Promise<boolean> {\n const now = Date.now();\n if (hasSavedPasswordCache && hasSavedPasswordCache.expiresAt > now) return hasSavedPasswordCache.value;\n const value = Boolean(await getSavedServerPassword());\n hasSavedPasswordCache = { value, expiresAt: now + SAVED_PASSWORD_CACHE_TTL_MS };\n return value;\n}\n\nfunction buildModelRows(models: ServerModelInfo[], gateway?: GatewayModelOptions): ServerModelRow[] {\n const groups = new Map<string, ServerModelInfo[]>();\n for (const model of models) {\n const label = gatewayProviderLabel(model);\n const list = groups.get(label);\n if (list) list.push(model);\n else groups.set(label, [model]);\n }\n\n // Collisions must be computed across the full exposed catalog, not per provider group,\n // or a cross-provider OpenAI id clash never gets scoped.\n const collisions = openAiIdCollisions(models);\n const rows: ServerModelRow[] = [];\n for (const [providerLabel, groupModels] of groups) {\n for (const row of buildDedupedModelRows(groupModels, gateway, collisions)) rows.push({ providerLabel, ...row });\n }\n return rows.sort((a, b) => a.providerLabel.localeCompare(b.providerLabel) || a.name.localeCompare(b.name));\n}\n\nasync function buildSavedConfig(): Promise<ServerSavedConfig> {\n const envPassword = getEnvServerPassword();\n return {\n favoritesOnly: getServerFavoritesOnly(),\n freeModelsOnly: getServerFreeModelsOnly(),\n exposedProviders: getServerExposedProviders(),\n maskGatewayIds: getServerMaskGatewayIds(),\n listenMode: getServerListenMode(),\n autostart: resolveServerAutostart(),\n hasSavedPassword: await hasSavedPasswordCached(),\n hasEnvPassword: Boolean(envPassword),\n ...(envPassword ? { prefillPassword: envPassword } : {}),\n };\n}\n\nexport async function getServerStatus(opts?: { requestHost?: string }): Promise<ServerStatusPayload> {\n const saved = await buildSavedConfig();\n if (!running) return { running: false, saved };\n\n const { handle, config, serverPassword, providerSummary, modelRows } = running;\n const publicPort = resolveAdvertiseGatewayPort(handle.port);\n const loopback = formatGatewayUrls('127.0.0.1', publicPort);\n\n const payload: ServerStatusPayload = {\n running: true,\n saved,\n listenMode: config.listenMode,\n anthropicUrl: loopback.anthropicUrl,\n openaiUrl: loopback.openaiUrl,\n exposedProviders: config.exposedProviders,\n favoritesOnly: config.favoritesOnly,\n freeModelsOnly: config.freeModelsOnly,\n maskGatewayIds: config.maskGatewayIds,\n providerSummary,\n models: modelRows,\n };\n\n if (config.listenMode === 'network') {\n payload.networkUrls = resolveAdvertiseAddresses({ requestHost: opts?.requestHost }).map(\n ({ name, address }) => {\n const urls = formatGatewayUrls(address, publicPort);\n return { name, anthropicUrl: urls.anthropicUrl, openaiUrl: urls.openaiUrl };\n },\n );\n payload.apiKey = serverPassword ?? undefined;\n } else {\n payload.apiKey = 'any non-empty value';\n }\n\n return payload;\n}\n\nexport function startGatewayServer(\n req: ServerStartRequest,\n opts?: { requestHost?: string },\n): Promise<{ ok: true; status: ServerStatusPayload } | { ok: false; error: string }> {\n if (running) return Promise.resolve({ ok: false, error: 'Server is already running. Stop it first.' });\n // Two near-simultaneous start requests would otherwise both pass the `running`\n // check above and race through the async setup below — serialize on the\n // in-flight promise instead of just the (only-set-at-the-end) `running` flag.\n if (startInFlight) return startInFlight;\n startInFlight = doStartGatewayServer(req, opts).finally(() => { startInFlight = null; });\n return startInFlight;\n}\n\nasync function doStartGatewayServer(\n req: ServerStartRequest,\n opts?: { requestHost?: string },\n): Promise<{ ok: true; status: ServerStatusPayload } | { ok: false; error: string }> {\n if (req.listenMode !== 'local' && req.listenMode !== 'network') {\n return { ok: false, error: 'Invalid listen mode.' };\n }\n\n const apiKey = await resolveServerUpstreamApiKey();\n if (!apiKey) {\n return { ok: false, error: 'No providers configured. Add a provider in Providers & Keys first.' };\n }\n\n let serverPassword: string | null = null;\n if (req.listenMode === 'network') {\n if (req.passwordMode === 'saved') {\n const configured = (await getSavedServerPassword()) ?? getEnvServerPassword();\n if (!configured) {\n return {\n ok: false,\n error: 'No configured password found — set RELAY_AI_SERVER_PASSWORD, or enter a new password.',\n };\n }\n serverPassword = configured;\n } else {\n const trimmed = (req.password ?? '').trim();\n // Empty field: prefer env (Docker Compose), then keychain-saved.\n if (!trimmed) {\n const configured = getEnvServerPassword() ?? (await getSavedServerPassword());\n if (!configured) return { ok: false, error: 'A server password is required for network mode.' };\n serverPassword = configured;\n } else {\n serverPassword = trimmed;\n if (req.savePassword) {\n await setSavedServerPassword(trimmed);\n hasSavedPasswordCache = { value: true, expiresAt: Date.now() + SAVED_PASSWORD_CACHE_TTL_MS };\n }\n }\n }\n }\n\n let models: ServerModelInfo[];\n try {\n models = await loadServerModels();\n } catch (err) {\n return { ok: false, error: `Failed to load models: ${err instanceof Error ? err.message : String(err)}` };\n }\n\n if (req.exposedProviders) models = filterServerModelsByProviders(models, req.exposedProviders);\n\n if (req.favoritesOnly) {\n const favorites: FavoriteModel[] = loadPreferences().favoriteModels ?? [];\n if (favorites.length === 0) {\n return { ok: false, error: 'No favorite models configured. Add favorites in the Favorites tab first.' };\n }\n models = filterServerModelsByFavorites(models, favorites).slice(0, MAX_MODEL_CATALOG);\n if (models.length === 0) {\n return { ok: false, error: 'No favorite models matched the current provider filter.' };\n }\n }\n\n if (req.freeModelsOnly) {\n models = filterServerModelsByFreeStatus(models);\n if (models.length === 0) {\n return { ok: false, error: 'No free models matched the current server filters.' };\n }\n }\n\n if (models.length === 0) {\n return { ok: false, error: 'No models to expose. Add providers or adjust the exposed-provider filter.' };\n }\n\n // Persist wizard choices so the terminal `relay-ai server` quick-start path and this\n // panel stay in sync, matching the CLI wizard's own save-as-you-go behavior.\n setServerFavoritesOnly(req.favoritesOnly);\n setServerFreeModelsOnly(req.freeModelsOnly);\n if (!req.favoritesOnly) {\n // null / empty = all providers (used with free-models-only to expose every free model).\n setServerExposedProviders(req.exposedProviders ?? []);\n }\n setServerMaskGatewayIds(req.maskGatewayIds);\n setServerListenMode(req.listenMode);\n if (req.autostart !== undefined) {\n setServerAutostart(req.autostart);\n }\n\n const host = req.listenMode === 'network' ? '0.0.0.0' : '127.0.0.1';\n const gateway = req.maskGatewayIds ? { maskGatewayIds: true as const } : undefined;\n\n let handle: ServerHandle;\n try {\n handle = await startServer({\n host,\n port: 17645,\n apiKey,\n serverPassword,\n catalog: createGatewayModelCatalog(models, gateway),\n backends: BACKENDS,\n gateway,\n debugLogPath: getServerDebugLogPath(),\n });\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n const message = code === 'EADDRINUSE'\n ? 'Port 17645 is already in use — stop the other relay-ai server instance first.'\n : `Failed to start server: ${err instanceof Error ? err.message : String(err)}`;\n return { ok: false, error: message };\n }\n\n running = {\n handle,\n serverPassword,\n config: {\n favoritesOnly: req.favoritesOnly,\n freeModelsOnly: req.freeModelsOnly,\n exposedProviders: req.exposedProviders,\n maskGatewayIds: req.maskGatewayIds,\n listenMode: req.listenMode,\n autostart: req.autostart ?? resolveServerAutostart(),\n },\n providerSummary: summarizeServerProviders(models),\n modelRows: buildModelRows(models, gateway),\n };\n\n return { ok: true, status: await getServerStatus({ requestHost: opts?.requestHost }) };\n}\n\nexport async function stopGatewayServer(): Promise<{ ok: true; stopped: boolean }> {\n if (running) {\n await running.handle.close();\n running = null;\n return { ok: true, stopped: true };\n }\n return { ok: true, stopped: false };\n}\n\n/**\n * Auto-starts the in-process API Gateway from saved configuration or env parameters.\n * Does not throw — returns null and logs warning on configuration or startup error.\n */\nexport async function startGatewayServerFromSavedConfig(opts?: { requestHost?: string }): Promise<ServerStatusPayload | null> {\n const req: ServerStartRequest = {\n favoritesOnly: getServerFavoritesOnly(),\n freeModelsOnly: getServerFreeModelsOnly(),\n exposedProviders: getServerExposedProviders(),\n maskGatewayIds: getServerMaskGatewayIds(),\n listenMode: getServerListenMode(),\n autostart: resolveServerAutostart(),\n passwordMode: 'saved',\n };\n\n const res = await startGatewayServer(req, opts);\n if (res.ok) {\n return res.status;\n }\n console.warn(`[server-autostart] Warning: ${res.error}`);\n return null;\n}\n\n"],"mappings":"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","names":["writeFileSync","existsSync","join","dir","scriptPath","existsSync","p","device","url","session","existsSync","isMac","isWindows","join","existsSync","url","writeFileSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacobbd/relay-ai",
3
- "version": "0.7.6",
3
+ "version": "0.9.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -83,10 +83,11 @@
83
83
  "@types/node": "^22.0.0",
84
84
  "@types/node-forge": "^1.3.14",
85
85
  "@types/ws": "^8.18.1",
86
- "@vitest/coverage-v8": "^2.1.9",
86
+ "@vitest/coverage-v8": "^4.1.10",
87
87
  "tsup": "^8.0.0",
88
88
  "typescript": "^5.5.0",
89
- "vitest": "^2.0.0"
89
+ "vite-node": "^6.0.0",
90
+ "vitest": "^4.1.10"
90
91
  },
91
92
  "optionalDependencies": {
92
93
  "@napi-rs/keyring": "^1.3.0"