@jacobbd/relay-ai 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/dist/{chunk-MVBA7ABV.js → chunk-EJONCU3B.js} +4 -4
- package/dist/chunk-EJONCU3B.js.map +1 -0
- package/dist/{chunk-44KQK6Y5.js → chunk-P4S42QJK.js} +2805 -2778
- package/dist/chunk-P4S42QJK.js.map +1 -0
- package/dist/cli.js +364 -226
- package/dist/cli.js.map +1 -1
- package/dist/{provider-templates-6XYKAZB5.js → provider-templates-BPGB5V2L.js} +2 -2
- package/dist/ui/public/app.js +92 -21
- package/dist/ui/public/index.html +1 -1
- package/dist/ui/public/oauth-device.js +44 -19
- package/dist/ui/public/style.css +24 -0
- package/dist/{ui-command-M7KMKIQC.js → ui-command-EQJIZRZZ.js} +135 -57
- package/dist/ui-command-EQJIZRZZ.js.map +1 -0
- package/package.json +2 -1
- package/dist/chunk-44KQK6Y5.js.map +0 -1
- package/dist/chunk-MVBA7ABV.js.map +0 -1
- package/dist/ui-command-M7KMKIQC.js.map +0 -1
- /package/dist/{provider-templates-6XYKAZB5.js.map → provider-templates-BPGB5V2L.js.map} +0 -0
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
completeAntigravityExchange,
|
|
12
12
|
copilotPlanTier,
|
|
13
13
|
createGatewayModelCatalog,
|
|
14
|
+
ensureOpencodeCloudProviders,
|
|
14
15
|
favoriteProviderDisplayName,
|
|
15
16
|
fetchProviderCatalog,
|
|
16
17
|
filterServerModelsByFavorites,
|
|
@@ -19,11 +20,12 @@ import {
|
|
|
19
20
|
findBinaryOnPath,
|
|
20
21
|
findClaudeApp,
|
|
21
22
|
findCodexApp,
|
|
23
|
+
formatGatewayUrls,
|
|
22
24
|
freeStatusLabel,
|
|
23
25
|
gatewayProviderLabel,
|
|
24
26
|
getAppHome,
|
|
25
27
|
getAppPathOverride,
|
|
26
|
-
|
|
28
|
+
getEnvServerPassword,
|
|
27
29
|
getSavedServerPassword,
|
|
28
30
|
getServerExposedProviders,
|
|
29
31
|
getServerFavoritesOnly,
|
|
@@ -32,6 +34,7 @@ import {
|
|
|
32
34
|
getServerMaskGatewayIds,
|
|
33
35
|
getUiDebugLogPath,
|
|
34
36
|
guiCallbackRedirectUri,
|
|
37
|
+
hostFromHeader,
|
|
35
38
|
loadPreferences,
|
|
36
39
|
loadRegistry,
|
|
37
40
|
loadServerModels,
|
|
@@ -49,6 +52,8 @@ import {
|
|
|
49
52
|
requestGithubDeviceCode,
|
|
50
53
|
requestOpenAiDeviceCode,
|
|
51
54
|
requestXaiDeviceCode,
|
|
55
|
+
resolveAdvertiseAddresses,
|
|
56
|
+
resolveAdvertiseGatewayPort,
|
|
52
57
|
resolveProviderCredential,
|
|
53
58
|
resolveServerUpstreamApiKey,
|
|
54
59
|
saveNativeOAuthCredential,
|
|
@@ -67,14 +72,14 @@ import {
|
|
|
67
72
|
supportsClaudeTransparentMode,
|
|
68
73
|
validateCustomEndpointUrl,
|
|
69
74
|
writeSecureLogLine
|
|
70
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-P4S42QJK.js";
|
|
71
76
|
import {
|
|
72
77
|
__toCommonJS,
|
|
73
78
|
init_provider_templates,
|
|
74
79
|
listAddableTemplates,
|
|
75
80
|
listVisibleOAuthTemplates,
|
|
76
81
|
provider_templates_exports
|
|
77
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-EJONCU3B.js";
|
|
78
83
|
|
|
79
84
|
// src/ui-command.ts
|
|
80
85
|
import { createServer } from "http";
|
|
@@ -381,25 +386,30 @@ function buildModelRows(models, gateway) {
|
|
|
381
386
|
return rows.sort((a, b) => a.providerLabel.localeCompare(b.providerLabel) || a.name.localeCompare(b.name));
|
|
382
387
|
}
|
|
383
388
|
async function buildSavedConfig() {
|
|
389
|
+
const envPassword = getEnvServerPassword();
|
|
384
390
|
return {
|
|
385
391
|
favoritesOnly: getServerFavoritesOnly(),
|
|
386
392
|
freeModelsOnly: getServerFreeModelsOnly(),
|
|
387
393
|
exposedProviders: getServerExposedProviders(),
|
|
388
394
|
maskGatewayIds: getServerMaskGatewayIds(),
|
|
389
395
|
listenMode: getServerListenMode(),
|
|
390
|
-
hasSavedPassword: await hasSavedPasswordCached()
|
|
396
|
+
hasSavedPassword: await hasSavedPasswordCached(),
|
|
397
|
+
hasEnvPassword: Boolean(envPassword),
|
|
398
|
+
...envPassword ? { prefillPassword: envPassword } : {}
|
|
391
399
|
};
|
|
392
400
|
}
|
|
393
|
-
async function getServerStatus() {
|
|
401
|
+
async function getServerStatus(opts) {
|
|
394
402
|
const saved = await buildSavedConfig();
|
|
395
403
|
if (!running) return { running: false, saved };
|
|
396
404
|
const { handle, config, serverPassword, providerSummary, modelRows } = running;
|
|
405
|
+
const publicPort = resolveAdvertiseGatewayPort(handle.port);
|
|
406
|
+
const loopback = formatGatewayUrls("127.0.0.1", publicPort);
|
|
397
407
|
const payload = {
|
|
398
408
|
running: true,
|
|
399
409
|
saved,
|
|
400
410
|
listenMode: config.listenMode,
|
|
401
|
-
anthropicUrl:
|
|
402
|
-
openaiUrl:
|
|
411
|
+
anthropicUrl: loopback.anthropicUrl,
|
|
412
|
+
openaiUrl: loopback.openaiUrl,
|
|
403
413
|
exposedProviders: config.exposedProviders,
|
|
404
414
|
favoritesOnly: config.favoritesOnly,
|
|
405
415
|
freeModelsOnly: config.freeModelsOnly,
|
|
@@ -408,26 +418,27 @@ async function getServerStatus() {
|
|
|
408
418
|
models: modelRows
|
|
409
419
|
};
|
|
410
420
|
if (config.listenMode === "network") {
|
|
411
|
-
payload.networkUrls =
|
|
412
|
-
name,
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
421
|
+
payload.networkUrls = resolveAdvertiseAddresses({ requestHost: opts?.requestHost }).map(
|
|
422
|
+
({ name, address }) => {
|
|
423
|
+
const urls = formatGatewayUrls(address, publicPort);
|
|
424
|
+
return { name, anthropicUrl: urls.anthropicUrl, openaiUrl: urls.openaiUrl };
|
|
425
|
+
}
|
|
426
|
+
);
|
|
416
427
|
payload.apiKey = serverPassword ?? void 0;
|
|
417
428
|
} else {
|
|
418
429
|
payload.apiKey = "any non-empty value";
|
|
419
430
|
}
|
|
420
431
|
return payload;
|
|
421
432
|
}
|
|
422
|
-
function startGatewayServer(req) {
|
|
433
|
+
function startGatewayServer(req, opts) {
|
|
423
434
|
if (running) return Promise.resolve({ ok: false, error: "Server is already running. Stop it first." });
|
|
424
435
|
if (startInFlight) return startInFlight;
|
|
425
|
-
startInFlight = doStartGatewayServer(req).finally(() => {
|
|
436
|
+
startInFlight = doStartGatewayServer(req, opts).finally(() => {
|
|
426
437
|
startInFlight = null;
|
|
427
438
|
});
|
|
428
439
|
return startInFlight;
|
|
429
440
|
}
|
|
430
|
-
async function doStartGatewayServer(req) {
|
|
441
|
+
async function doStartGatewayServer(req, opts) {
|
|
431
442
|
if (req.listenMode !== "local" && req.listenMode !== "network") {
|
|
432
443
|
return { ok: false, error: "Invalid listen mode." };
|
|
433
444
|
}
|
|
@@ -438,16 +449,26 @@ async function doStartGatewayServer(req) {
|
|
|
438
449
|
let serverPassword = null;
|
|
439
450
|
if (req.listenMode === "network") {
|
|
440
451
|
if (req.passwordMode === "saved") {
|
|
441
|
-
const
|
|
442
|
-
if (!
|
|
443
|
-
|
|
452
|
+
const configured = await getSavedServerPassword() ?? getEnvServerPassword();
|
|
453
|
+
if (!configured) {
|
|
454
|
+
return {
|
|
455
|
+
ok: false,
|
|
456
|
+
error: "No configured password found \u2014 set RELAY_AI_SERVER_PASSWORD, or enter a new password."
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
serverPassword = configured;
|
|
444
460
|
} else {
|
|
445
461
|
const trimmed = (req.password ?? "").trim();
|
|
446
|
-
if (!trimmed)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
462
|
+
if (!trimmed) {
|
|
463
|
+
const configured = getEnvServerPassword() ?? await getSavedServerPassword();
|
|
464
|
+
if (!configured) return { ok: false, error: "A server password is required for network mode." };
|
|
465
|
+
serverPassword = configured;
|
|
466
|
+
} else {
|
|
467
|
+
serverPassword = trimmed;
|
|
468
|
+
if (req.savePassword) {
|
|
469
|
+
await setSavedServerPassword(trimmed);
|
|
470
|
+
hasSavedPasswordCache = { value: true, expiresAt: Date.now() + SAVED_PASSWORD_CACHE_TTL_MS };
|
|
471
|
+
}
|
|
451
472
|
}
|
|
452
473
|
}
|
|
453
474
|
}
|
|
@@ -515,7 +536,7 @@ async function doStartGatewayServer(req) {
|
|
|
515
536
|
providerSummary: summarizeServerProviders(models),
|
|
516
537
|
modelRows: buildModelRows(models, gateway)
|
|
517
538
|
};
|
|
518
|
-
return { ok: true, status: await getServerStatus() };
|
|
539
|
+
return { ok: true, status: await getServerStatus({ requestHost: opts?.requestHost }) };
|
|
519
540
|
}
|
|
520
541
|
async function stopGatewayServer() {
|
|
521
542
|
if (running) {
|
|
@@ -605,16 +626,18 @@ function handleUiApiRequest(req, res, opts = {}) {
|
|
|
605
626
|
handleOAuthStatus(req, res);
|
|
606
627
|
} else if (url.startsWith("/oauth/callback") && req.method === "GET") {
|
|
607
628
|
handleOAuthCallback(req, res);
|
|
608
|
-
} else if (url
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
629
|
+
} else if (url.startsWith("/api/apps")) {
|
|
630
|
+
if (opts.uiMode === "server") {
|
|
631
|
+
sendJson(res, 403, { error: "App launch is unavailable in server admin UI mode." });
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
if (url === "/api/apps" && req.method === "GET") handleGetApps(res);
|
|
635
|
+
else if (url === "/api/apps/path" && req.method === "POST") handleSetAppPath(req, res);
|
|
636
|
+
else if (url === "/api/apps/launch" && req.method === "POST") handleLaunchApp(req, res, opts);
|
|
637
|
+
else if (url === "/api/apps/browse-folder" && req.method === "POST") handleBrowseFolder(res);
|
|
638
|
+
else sendJson(res, 404, { error: "Not found" });
|
|
616
639
|
} else if (url === "/api/server/status" && req.method === "GET") {
|
|
617
|
-
handleGetServerStatus(res);
|
|
640
|
+
handleGetServerStatus(req, res);
|
|
618
641
|
} else if (url === "/api/server/providers" && req.method === "GET") {
|
|
619
642
|
handleGetServerProviders(res);
|
|
620
643
|
} else if (url === "/api/server/start" && req.method === "POST") {
|
|
@@ -723,7 +746,7 @@ async function handlePostKeys(req, res) {
|
|
|
723
746
|
if (saved) {
|
|
724
747
|
sendJson(res, 200, { ok: true });
|
|
725
748
|
} else {
|
|
726
|
-
sendJson(res, 500, { error: "
|
|
749
|
+
sendJson(res, 500, { error: "Credential store unavailable \u2014 key not saved" });
|
|
727
750
|
}
|
|
728
751
|
} catch (err) {
|
|
729
752
|
sendJson(res, 400, { error: String(err) });
|
|
@@ -798,7 +821,7 @@ async function handleAddProvider(req, res) {
|
|
|
798
821
|
sendJson(res, 400, { error: "templateId required" });
|
|
799
822
|
return;
|
|
800
823
|
}
|
|
801
|
-
const { listSupportedTemplates } = await import("./provider-templates-
|
|
824
|
+
const { listSupportedTemplates } = await import("./provider-templates-BPGB5V2L.js");
|
|
802
825
|
const template = listSupportedTemplates().find((t) => t.id === templateId);
|
|
803
826
|
if (!template) {
|
|
804
827
|
sendJson(res, 404, { error: `Template '${templateId}' not found` });
|
|
@@ -1199,9 +1222,9 @@ async function handleSetAppPath(req, res) {
|
|
|
1199
1222
|
sendJson(res, 500, { error: String(err) });
|
|
1200
1223
|
}
|
|
1201
1224
|
}
|
|
1202
|
-
async function handleGetServerStatus(res) {
|
|
1225
|
+
async function handleGetServerStatus(req, res) {
|
|
1203
1226
|
try {
|
|
1204
|
-
sendJson(res, 200, await getServerStatus());
|
|
1227
|
+
sendJson(res, 200, await getServerStatus({ requestHost: hostFromHeader(req.headers.host) }));
|
|
1205
1228
|
} catch (err) {
|
|
1206
1229
|
sendJson(res, 500, { error: String(err) });
|
|
1207
1230
|
}
|
|
@@ -1229,17 +1252,20 @@ async function handleStartServer(req, res, opts) {
|
|
|
1229
1252
|
sendJson(res, 400, { error: 'listenMode must be "local" or "network"' });
|
|
1230
1253
|
return;
|
|
1231
1254
|
}
|
|
1255
|
+
const listenMode = opts.uiMode === "server" ? "network" : body.listenMode;
|
|
1232
1256
|
const request = {
|
|
1233
1257
|
favoritesOnly: body.favoritesOnly,
|
|
1234
1258
|
freeModelsOnly: Boolean(body.freeModelsOnly),
|
|
1235
1259
|
exposedProviders: Array.isArray(body.exposedProviders) ? body.exposedProviders : null,
|
|
1236
1260
|
maskGatewayIds: body.maskGatewayIds,
|
|
1237
|
-
listenMode
|
|
1261
|
+
listenMode,
|
|
1238
1262
|
passwordMode: body.passwordMode === "saved" ? "saved" : "new",
|
|
1239
1263
|
password: typeof body.password === "string" ? body.password : void 0,
|
|
1240
1264
|
savePassword: Boolean(body.savePassword)
|
|
1241
1265
|
};
|
|
1242
|
-
const result = await startGatewayServer(request
|
|
1266
|
+
const result = await startGatewayServer(request, {
|
|
1267
|
+
requestHost: hostFromHeader(req.headers.host)
|
|
1268
|
+
});
|
|
1243
1269
|
if (result.ok) {
|
|
1244
1270
|
notifyServerLifecycle(opts, {
|
|
1245
1271
|
type: "started",
|
|
@@ -1337,6 +1363,7 @@ async function handleBrowseFolder(res) {
|
|
|
1337
1363
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1338
1364
|
var PUBLIC_DIR = join2(__dirname, "ui", "public");
|
|
1339
1365
|
var LOCK_FILE = join2(getAppHome(), "ui.lock");
|
|
1366
|
+
var DEFAULT_SERVER_UI_PORT = 8787;
|
|
1340
1367
|
var MIME = {
|
|
1341
1368
|
".html": "text/html; charset=utf-8",
|
|
1342
1369
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -1349,14 +1376,47 @@ function ext(path) {
|
|
|
1349
1376
|
const i = path.lastIndexOf(".");
|
|
1350
1377
|
return i >= 0 ? path.slice(i) : "";
|
|
1351
1378
|
}
|
|
1352
|
-
function
|
|
1379
|
+
function resolveUiMode(opts = {}, env = process.env) {
|
|
1380
|
+
if (opts.serverMode) return "server";
|
|
1381
|
+
return env.RELAY_AI_UI_MODE === "server" ? "server" : "full";
|
|
1382
|
+
}
|
|
1383
|
+
function resolveServerUiPort(opts, env) {
|
|
1384
|
+
if (opts.port != null) return opts.port;
|
|
1385
|
+
const envPort = Number(env.RELAY_AI_UI_PORT);
|
|
1386
|
+
return Number.isFinite(envPort) && envPort > 0 ? envPort : DEFAULT_SERVER_UI_PORT;
|
|
1387
|
+
}
|
|
1388
|
+
function resolveUiRuntimeConfig(opts = {}, env = process.env) {
|
|
1389
|
+
const mode = resolveUiMode(opts, env);
|
|
1390
|
+
if (mode === "server") {
|
|
1391
|
+
return {
|
|
1392
|
+
mode,
|
|
1393
|
+
host: "0.0.0.0",
|
|
1394
|
+
port: resolveServerUiPort(opts, env),
|
|
1395
|
+
openBrowser: false,
|
|
1396
|
+
confirmShutdownOnSigint: false
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
return {
|
|
1400
|
+
mode,
|
|
1401
|
+
host: "127.0.0.1",
|
|
1402
|
+
port: opts.port ?? 0,
|
|
1403
|
+
openBrowser: true,
|
|
1404
|
+
confirmShutdownOnSigint: true
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
function buildStaticCache(mode) {
|
|
1353
1408
|
const cache = /* @__PURE__ */ new Map();
|
|
1354
1409
|
try {
|
|
1355
1410
|
for (const name of readdirSync(PUBLIC_DIR)) {
|
|
1356
1411
|
const mime = MIME[ext(name)];
|
|
1357
1412
|
if (!mime) continue;
|
|
1358
1413
|
const raw = readFileSync(join2(PUBLIC_DIR, name));
|
|
1359
|
-
|
|
1414
|
+
let content = raw;
|
|
1415
|
+
if (name === "index.html") {
|
|
1416
|
+
content = Buffer.from(
|
|
1417
|
+
raw.toString("utf8").replaceAll("{{VERSION}}", VERSION).replaceAll("{{UI_MODE}}", mode)
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1360
1420
|
cache.set(`/${name}`, { content, mime });
|
|
1361
1421
|
}
|
|
1362
1422
|
} catch {
|
|
@@ -1392,13 +1452,16 @@ function formatUiServerLifecycleMessage(event) {
|
|
|
1392
1452
|
async function resolveUiShutdownDecision(signal, promptClose = () => p.confirm({
|
|
1393
1453
|
message: "Relay-AI UI is still running. Close it?",
|
|
1394
1454
|
initialValue: true
|
|
1395
|
-
})) {
|
|
1396
|
-
|
|
1455
|
+
}), opts) {
|
|
1456
|
+
const confirmOnSigint = opts?.confirmOnSigint !== false;
|
|
1457
|
+
if (signal !== "SIGINT" || !confirmOnSigint) return "close";
|
|
1397
1458
|
const shouldClose = await promptClose();
|
|
1398
1459
|
if (p.isCancel(shouldClose)) return "close";
|
|
1399
1460
|
return shouldClose ? "close" : "keep";
|
|
1400
1461
|
}
|
|
1401
1462
|
async function runUiCommand(opts = {}) {
|
|
1463
|
+
const runtime = resolveUiRuntimeConfig(opts);
|
|
1464
|
+
await ensureOpencodeCloudProviders();
|
|
1402
1465
|
const existing = checkExistingServer();
|
|
1403
1466
|
if (existing) {
|
|
1404
1467
|
console.log(`
|
|
@@ -1409,10 +1472,10 @@ async function runUiCommand(opts = {}) {
|
|
|
1409
1472
|
if (opts.trace) {
|
|
1410
1473
|
process.env.RELAY_AI_TRACE = "1";
|
|
1411
1474
|
}
|
|
1412
|
-
const staticCache = buildStaticCache();
|
|
1475
|
+
const staticCache = buildStaticCache(runtime.mode);
|
|
1413
1476
|
const traceLogPath = opts.trace ? getUiDebugLogPath() : void 0;
|
|
1414
1477
|
const trace = traceLogPath ? makeTraceLogger(traceLogPath) : void 0;
|
|
1415
|
-
trace?.(
|
|
1478
|
+
trace?.(`ui server starting mode=${runtime.mode}`);
|
|
1416
1479
|
const server = createServer((req, res) => {
|
|
1417
1480
|
const url2 = req.url ?? "/";
|
|
1418
1481
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
@@ -1420,6 +1483,7 @@ async function runUiCommand(opts = {}) {
|
|
|
1420
1483
|
handleUiApiRequest(req, res, {
|
|
1421
1484
|
trace: opts.trace,
|
|
1422
1485
|
traceLogPath,
|
|
1486
|
+
uiMode: runtime.mode,
|
|
1423
1487
|
onServerLifecycle: (event) => {
|
|
1424
1488
|
console.log(`
|
|
1425
1489
|
${formatUiServerLifecycleMessage(event)}
|
|
@@ -1440,7 +1504,7 @@ async function runUiCommand(opts = {}) {
|
|
|
1440
1504
|
res.end("Not found");
|
|
1441
1505
|
});
|
|
1442
1506
|
await new Promise((resolve, reject) => {
|
|
1443
|
-
server.listen(
|
|
1507
|
+
server.listen(runtime.port, runtime.host, () => resolve());
|
|
1444
1508
|
server.once("error", reject);
|
|
1445
1509
|
});
|
|
1446
1510
|
const addr = server.address();
|
|
@@ -1449,9 +1513,10 @@ async function runUiCommand(opts = {}) {
|
|
|
1449
1513
|
return 1;
|
|
1450
1514
|
}
|
|
1451
1515
|
const port = addr.port;
|
|
1452
|
-
const
|
|
1516
|
+
const displayHost = runtime.host === "0.0.0.0" ? "127.0.0.1" : runtime.host;
|
|
1517
|
+
const url = `http://${displayHost}:${port}`;
|
|
1453
1518
|
mkdirSync(getAppHome(), { recursive: true });
|
|
1454
|
-
writeFileSync2(LOCK_FILE, JSON.stringify({ pid: process.pid, port }));
|
|
1519
|
+
writeFileSync2(LOCK_FILE, JSON.stringify({ pid: process.pid, port, mode: runtime.mode }));
|
|
1455
1520
|
const cleanup = () => {
|
|
1456
1521
|
removeLock();
|
|
1457
1522
|
server.close();
|
|
@@ -1461,7 +1526,11 @@ async function runUiCommand(opts = {}) {
|
|
|
1461
1526
|
const handleSignal = async (signal) => {
|
|
1462
1527
|
if (handlingSignal) return;
|
|
1463
1528
|
handlingSignal = true;
|
|
1464
|
-
const decision = await resolveUiShutdownDecision(
|
|
1529
|
+
const decision = await resolveUiShutdownDecision(
|
|
1530
|
+
signal,
|
|
1531
|
+
void 0,
|
|
1532
|
+
{ confirmOnSigint: runtime.confirmShutdownOnSigint }
|
|
1533
|
+
);
|
|
1465
1534
|
if (decision === "keep") {
|
|
1466
1535
|
handlingSignal = false;
|
|
1467
1536
|
return;
|
|
@@ -1474,21 +1543,28 @@ async function runUiCommand(opts = {}) {
|
|
|
1474
1543
|
process.on("SIGTERM", () => {
|
|
1475
1544
|
void handleSignal("SIGTERM");
|
|
1476
1545
|
});
|
|
1546
|
+
const modeLabel = runtime.mode === "server" ? " (server admin)" : "";
|
|
1477
1547
|
console.log(`
|
|
1478
|
-
${pc.bold("relay-ai UI")} ${pc.cyan(url)}
|
|
1548
|
+
${pc.bold("relay-ai UI")}${modeLabel} ${pc.cyan(url)}
|
|
1479
1549
|
${pc.dim("Press Ctrl+C to stop")}
|
|
1480
1550
|
`);
|
|
1551
|
+
if (runtime.mode === "server") {
|
|
1552
|
+
console.log(` ${pc.dim("Gateway API (when started from Server tab): http://127.0.0.1:17645")}
|
|
1553
|
+
`);
|
|
1554
|
+
}
|
|
1481
1555
|
if (traceLogPath) {
|
|
1482
1556
|
console.log(` ${pc.dim(`Trace log: ${traceLogPath}`)}
|
|
1483
1557
|
`);
|
|
1484
1558
|
trace?.(`ui server listening ${url}`);
|
|
1485
1559
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1560
|
+
if (runtime.openBrowser) {
|
|
1561
|
+
try {
|
|
1562
|
+
const { default: open } = await import("open");
|
|
1563
|
+
await open(url);
|
|
1564
|
+
trace?.(`browser open ${url}`);
|
|
1565
|
+
} catch {
|
|
1566
|
+
trace?.(`browser open failed ${url}`);
|
|
1567
|
+
}
|
|
1492
1568
|
}
|
|
1493
1569
|
await new Promise(() => {
|
|
1494
1570
|
});
|
|
@@ -1497,7 +1573,9 @@ async function runUiCommand(opts = {}) {
|
|
|
1497
1573
|
export {
|
|
1498
1574
|
formatUiServerLifecycleMessage,
|
|
1499
1575
|
isUiApiRoute,
|
|
1576
|
+
resolveUiMode,
|
|
1577
|
+
resolveUiRuntimeConfig,
|
|
1500
1578
|
resolveUiShutdownDecision,
|
|
1501
1579
|
runUiCommand
|
|
1502
1580
|
};
|
|
1503
|
-
//# sourceMappingURL=ui-command-
|
|
1581
|
+
//# sourceMappingURL=ui-command-EQJIZRZZ.js.map
|