anygate 0.6.2 → 0.6.3

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,15 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ ISSUER,
3
4
  addCustomEndpointProvider,
4
5
  addProviderFromTemplate,
5
6
  aggregateAnalytics,
6
7
  buildAntigravityAuthUrl,
7
8
  buildDedupedModelRows,
9
+ buildOpenAiBrowserAuthUrl,
8
10
  checkForUpdates,
9
11
  completeAntigravityExchange,
10
12
  createGatewayModelCatalog,
11
13
  detectConflicts,
12
14
  emitAppEvent,
15
+ exchangeOpenAiBrowserToken,
13
16
  favoriteProviderDisplayName,
14
17
  fetchProviderCatalog,
15
18
  filterServerModelsByFavorites,
@@ -53,6 +56,7 @@ import {
53
56
  requestXaiDeviceCode,
54
57
  resolveInputTypes,
55
58
  resolveProviderCredential,
59
+ resolveReasoning,
56
60
  resolveServerUpstreamApiKey,
57
61
  saveLaunchPresets,
58
62
  saveNativeOAuthCredential,
@@ -66,17 +70,19 @@ import {
66
70
  setServerFreeModelsOnly,
67
71
  setServerListenMode,
68
72
  setServerMaskGatewayIds,
73
+ startCallbackServer,
69
74
  startServer,
70
75
  subscribeToAppEvents,
71
76
  summarizeServerProviders,
72
77
  validateCustomEndpointUrl,
73
78
  writeSecureLogLine
74
- } from "./chunk-EMBABL33.js";
79
+ } from "./chunk-NU4KMZIM.js";
75
80
  import {
76
81
  BACKENDS,
77
82
  GATEWAY_PORT,
78
- MAX_MODEL_CATALOG
79
- } from "./chunk-S5WL3M5G.js";
83
+ MAX_MODEL_CATALOG,
84
+ VERSION
85
+ } from "./chunk-RNW2MGKL.js";
80
86
  import {
81
87
  getTemplateById,
82
88
  listAddableTemplates,
@@ -86,7 +92,6 @@ import "./chunk-UT3JLF3M.js";
86
92
 
87
93
  // src/ui/command.ts
88
94
  import { createServer as createServer2 } from "http";
89
- import { execSync } from "child_process";
90
95
  import {
91
96
  readFileSync,
92
97
  readdirSync,
@@ -785,6 +790,8 @@ function handleUiApiRequest(req, res, opts = {}) {
785
790
  handleGetPresets(res);
786
791
  } else if (url === "/api/presets" && req.method === "POST") {
787
792
  handleSavePresets(req, res);
793
+ } else if (url === "/api/ping" && req.method === "GET") {
794
+ handleGetPing(res);
788
795
  } else if (url === "/api/health" && req.method === "GET") {
789
796
  handleGetHealth(res);
790
797
  } else if (url.startsWith("/api/analytics") && req.method === "GET") {
@@ -865,6 +872,9 @@ async function handleSavePresets(req, res) {
865
872
  sendJson(res, 500, { error: String(err) });
866
873
  }
867
874
  }
875
+ function handleGetPing(res) {
876
+ sendJson(res, 200, { app: "anygate", version: VERSION, pid: process.pid });
877
+ }
868
878
  async function handleGetHealth(res) {
869
879
  try {
870
880
  const gatewayRunning = (await getServerStatus()).running;
@@ -945,7 +955,8 @@ async function handleGetModels(res) {
945
955
  contextWindow: m.contextWindow,
946
956
  cost: m.cost,
947
957
  providerId: p2.id,
948
- inputTypes: resolveInputTypes(m.family, p2.id, m.id)
958
+ inputTypes: resolveInputTypes(m.family, p2.id, m.id),
959
+ reasoning: m.reasoning ?? resolveReasoning(p2.id, m.id)
949
960
  }))
950
961
  }));
951
962
  const materializedIds = new Set(catalog.map((p2) => p2.id));
@@ -1367,12 +1378,141 @@ async function handleAddProvider(req, res) {
1367
1378
  sendJson(res, 400, { error: "templateId required" });
1368
1379
  return;
1369
1380
  }
1370
- const { listSupportedTemplates } = await import("./provider-templates-336QE7ZV.js");
1371
- const template = listSupportedTemplates().find((t) => t.id === templateId);
1381
+ const { getTemplateById: getTemplateById2 } = await import("./provider-templates-336QE7ZV.js");
1382
+ const template = getTemplateById2(templateId);
1372
1383
  if (!template) {
1373
1384
  sendJson(res, 404, { error: `Template '${templateId}' not found` });
1374
1385
  return;
1375
1386
  }
1387
+ if (template.authType === "oauth") {
1388
+ if (templateId === "claude-code") {
1389
+ sendJson(res, 400, {
1390
+ error: "Claude Code OAuth must be completed in the terminal: anygate providers auth claude-code"
1391
+ });
1392
+ return;
1393
+ }
1394
+ const host = req.headers.host ?? "127.0.0.1";
1395
+ const sessionId = randomUUID();
1396
+ if (templateId === "xai-oauth") {
1397
+ const device2 = await requestXaiDeviceCode();
1398
+ const url2 = device2.verification_uri_complete ?? device2.verification_uri;
1399
+ const session2 = {
1400
+ status: "pending",
1401
+ url: url2,
1402
+ userCode: device2.user_code,
1403
+ providerId: templateId
1404
+ };
1405
+ oauthSessions.set(sessionId, session2);
1406
+ pollXaiDeviceCodeToken(device2).then(async (tokens) => {
1407
+ await saveNativeOAuthCredential(templateId, tokens);
1408
+ await refreshOAuthProviderModels(templateId);
1409
+ oauthSessions.set(sessionId, { ...session2, status: "done" });
1410
+ }).catch((err) => {
1411
+ oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1412
+ });
1413
+ sendJson(res, 200, { oauth: true, sessionId, url: url2, userCode: device2.user_code });
1414
+ return;
1415
+ }
1416
+ if (templateId === "github-copilot") {
1417
+ const device2 = await requestGithubDeviceCode();
1418
+ const url2 = device2.verification_uri;
1419
+ const session2 = {
1420
+ status: "pending",
1421
+ url: url2,
1422
+ userCode: device2.user_code,
1423
+ providerId: templateId
1424
+ };
1425
+ oauthSessions.set(sessionId, session2);
1426
+ pollGithubDeviceCodeToken(device2).then(async (tokens) => {
1427
+ await saveNativeOAuthCredential(templateId, tokens);
1428
+ await refreshOAuthProviderModels(templateId);
1429
+ oauthSessions.set(sessionId, { ...session2, status: "done" });
1430
+ }).catch((err) => {
1431
+ oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1432
+ });
1433
+ sendJson(res, 200, { oauth: true, sessionId, url: url2, userCode: device2.user_code });
1434
+ return;
1435
+ }
1436
+ if (templateId === "antigravity") {
1437
+ const redirectUri = guiCallbackRedirectUri(host);
1438
+ const pkce = await buildAntigravityAuthUrl(redirectUri);
1439
+ const { authUrl, codeVerifier, oauthState } = pkce;
1440
+ const session2 = {
1441
+ status: "pending",
1442
+ url: authUrl,
1443
+ providerId: templateId,
1444
+ codeVerifier,
1445
+ oauthState
1446
+ };
1447
+ oauthSessions.set(sessionId, session2);
1448
+ const codePromise = new Promise((resolve, reject) => {
1449
+ session2.codeResolver = resolve;
1450
+ session2.errorRejecter = (err) => reject(err);
1451
+ setTimeout(() => reject(new Error("OAuth timeout \u2014 sign-in not completed")), 10 * 60 * 1e3);
1452
+ });
1453
+ oauthSessions.set(sessionId, session2);
1454
+ codePromise.then(async (code) => {
1455
+ const result2 = await completeAntigravityExchange(code, codeVerifier, redirectUri);
1456
+ const tokens = result2.tokens;
1457
+ const accountId = result2.userInfo.email;
1458
+ const providerData = {};
1459
+ if (result2.projectId) providerData.projectId = result2.projectId;
1460
+ if (result2.tierId) providerData.tier = result2.tierId;
1461
+ await saveNativeOAuthCredential(templateId, tokens, accountId, providerData);
1462
+ await refreshOAuthProviderModels(templateId);
1463
+ oauthSessions.set(sessionId, { ...session2, status: "done" });
1464
+ }).catch((err) => {
1465
+ oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1466
+ });
1467
+ sendJson(res, 200, { oauth: true, sessionId, authUrl, pkce: true });
1468
+ return;
1469
+ }
1470
+ if (templateId === "openai-oauth") {
1471
+ const redirectUri = `${ISSUER}/deviceauth/callback`;
1472
+ const { authUrl, codeVerifier, oauthState } = await buildOpenAiBrowserAuthUrl(redirectUri);
1473
+ const server = await startCallbackServer();
1474
+ let session2;
1475
+ try {
1476
+ session2 = {
1477
+ status: "pending",
1478
+ url: authUrl,
1479
+ providerId: templateId,
1480
+ codeVerifier,
1481
+ oauthState
1482
+ };
1483
+ oauthSessions.set(sessionId, session2);
1484
+ open(authUrl)?.catch(() => {
1485
+ });
1486
+ const { code } = await server.waitForCallback();
1487
+ if (!code) {
1488
+ throw new Error("No authorization code received from callback");
1489
+ }
1490
+ const { tokens, accountId } = await exchangeOpenAiBrowserToken(code, codeVerifier);
1491
+ await saveNativeOAuthCredential(templateId, tokens, accountId);
1492
+ await refreshOAuthProviderModels(templateId);
1493
+ oauthSessions.set(sessionId, { ...session2, status: "done" });
1494
+ } catch (err) {
1495
+ oauthSessions.set(sessionId, { ...session2 ?? {}, status: "error", error: String(err) });
1496
+ } finally {
1497
+ server.close();
1498
+ }
1499
+ sendJson(res, 200, { oauth: true, sessionId, url: authUrl, pkce: true });
1500
+ return;
1501
+ }
1502
+ const device = await requestOpenAiDeviceCode();
1503
+ const url = openAiDeviceCodeUrl();
1504
+ const session = { status: "pending", url, userCode: device.user_code, providerId: templateId };
1505
+ oauthSessions.set(sessionId, session);
1506
+ pollOpenAiDeviceCodeToken(device).then(async ({ tokens, accountId }) => {
1507
+ await saveNativeOAuthCredential(templateId, tokens, accountId);
1508
+ await refreshOAuthProviderModels(templateId);
1509
+ oauthSessions.set(sessionId, { ...session, status: "done" });
1510
+ }).catch((err) => {
1511
+ oauthSessions.set(sessionId, { ...session, status: "error", error: String(err) });
1512
+ });
1513
+ sendJson(res, 200, { oauth: true, sessionId, url, userCode: device.user_code });
1514
+ return;
1515
+ }
1376
1516
  const rawKey = typeof key === "string" ? key.trim() : "";
1377
1517
  if (!rawKey && !template.anonymousFreeModels && !template.apiKeyOptional) {
1378
1518
  sendJson(res, 400, { error: "key must be a non-empty string" });
@@ -1499,43 +1639,47 @@ async function handleOAuthStart(req, res) {
1499
1639
  }
1500
1640
  const sessionId = randomUUID();
1501
1641
  if (providerId === "xai-oauth") {
1502
- const device2 = await requestXaiDeviceCode();
1503
- const url2 = device2.verification_uri_complete ?? device2.verification_uri;
1504
- const session2 = {
1505
- status: "pending",
1506
- url: url2,
1507
- userCode: device2.user_code,
1508
- providerId
1509
- };
1642
+ let session2 = { status: "error", url: "", providerId };
1510
1643
  oauthSessions.set(sessionId, session2);
1511
- pollXaiDeviceCodeToken(device2).then(async (tokens) => {
1512
- await saveNativeOAuthCredential(providerId, tokens);
1513
- await refreshOAuthProviderModels(providerId);
1514
- oauthSessions.set(sessionId, { ...session2, status: "done" });
1515
- }).catch((err) => {
1516
- oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1517
- });
1518
- sendJson(res, 200, { sessionId, url: url2, userCode: device2.user_code });
1644
+ try {
1645
+ const device = await requestXaiDeviceCode();
1646
+ const url = device.verification_uri_complete ?? device.verification_uri;
1647
+ session2 = { status: "pending", url, userCode: device.user_code, providerId };
1648
+ oauthSessions.set(sessionId, session2);
1649
+ pollXaiDeviceCodeToken(device).then(async (tokens) => {
1650
+ await saveNativeOAuthCredential(providerId, tokens);
1651
+ await refreshOAuthProviderModels(providerId);
1652
+ oauthSessions.set(sessionId, { ...session2, status: "done" });
1653
+ }).catch((err) => {
1654
+ oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1655
+ });
1656
+ sendJson(res, 200, { sessionId, url, userCode: device.user_code });
1657
+ } catch (err) {
1658
+ oauthSessions.set(sessionId, { ...session2, error: String(err) });
1659
+ sendJson(res, 500, { error: String(err) });
1660
+ }
1519
1661
  return;
1520
1662
  }
1521
1663
  if (providerId === "github-copilot") {
1522
- const device2 = await requestGithubDeviceCode();
1523
- const url2 = device2.verification_uri;
1524
- const session2 = {
1525
- status: "pending",
1526
- url: url2,
1527
- userCode: device2.user_code,
1528
- providerId
1529
- };
1664
+ let session2 = { status: "error", url: "", providerId };
1530
1665
  oauthSessions.set(sessionId, session2);
1531
- pollGithubDeviceCodeToken(device2).then(async (tokens) => {
1532
- await saveNativeOAuthCredential(providerId, tokens);
1533
- await refreshOAuthProviderModels(providerId);
1534
- oauthSessions.set(sessionId, { ...session2, status: "done" });
1535
- }).catch((err) => {
1536
- oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1537
- });
1538
- sendJson(res, 200, { sessionId, url: url2, userCode: device2.user_code });
1666
+ try {
1667
+ const device = await requestGithubDeviceCode();
1668
+ const url = device.verification_uri;
1669
+ session2 = { status: "pending", url, userCode: device.user_code, providerId };
1670
+ oauthSessions.set(sessionId, session2);
1671
+ pollGithubDeviceCodeToken(device).then(async (tokens) => {
1672
+ await saveNativeOAuthCredential(providerId, tokens);
1673
+ await refreshOAuthProviderModels(providerId);
1674
+ oauthSessions.set(sessionId, { ...session2, status: "done" });
1675
+ }).catch((err) => {
1676
+ oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1677
+ });
1678
+ sendJson(res, 200, { sessionId, url, userCode: device.user_code });
1679
+ } catch (err) {
1680
+ oauthSessions.set(sessionId, { ...session2, error: String(err) });
1681
+ sendJson(res, 500, { error: String(err) });
1682
+ }
1539
1683
  return;
1540
1684
  }
1541
1685
  if (PKCE_PROVIDER_IDS.has(providerId)) {
@@ -1545,64 +1689,72 @@ async function handleOAuthStart(req, res) {
1545
1689
  });
1546
1690
  return;
1547
1691
  }
1548
- const host = req.headers.host ?? "127.0.0.1";
1549
- const redirectUri = guiCallbackRedirectUri(host);
1550
- let pkce;
1551
- if (providerId === "antigravity") {
1552
- pkce = await buildAntigravityAuthUrl(redirectUri);
1553
- } else {
1554
- sendJson(res, 400, { error: `PKCE flow for "${providerId}" not yet implemented` });
1555
- return;
1556
- }
1557
- const { authUrl, codeVerifier, oauthState } = pkce;
1558
- const session2 = {
1559
- status: "pending",
1560
- url: authUrl,
1561
- providerId,
1562
- codeVerifier,
1563
- oauthState
1564
- };
1565
- oauthSessions.set(sessionId, session2);
1566
- const codePromise = new Promise((resolve, reject) => {
1567
- session2.codeResolver = resolve;
1568
- session2.errorRejecter = (err) => reject(new Error(err));
1569
- setTimeout(() => reject(new Error("OAuth timeout \u2014 sign-in not completed")), 10 * 60 * 1e3);
1570
- });
1692
+ let session2 = { status: "error", url: "", providerId };
1571
1693
  oauthSessions.set(sessionId, session2);
1572
- codePromise.then(async (code) => {
1573
- let providerData = {};
1574
- let accountId;
1575
- let tokens;
1694
+ try {
1695
+ const host = req.headers.host ?? "127.0.0.1";
1696
+ const redirectUri = guiCallbackRedirectUri(host);
1697
+ let pkce;
1576
1698
  if (providerId === "antigravity") {
1577
- const result = await completeAntigravityExchange(code, codeVerifier, redirectUri);
1578
- tokens = result.tokens;
1579
- accountId = result.userInfo.email;
1580
- if (result.projectId) providerData.projectId = result.projectId;
1581
- if (result.tierId) providerData.tier = result.tierId;
1699
+ pkce = await buildAntigravityAuthUrl(redirectUri);
1582
1700
  } else {
1583
- throw new Error(`Unknown PKCE provider: ${providerId}`);
1701
+ sendJson(res, 400, { error: `PKCE flow for "${providerId}" not yet implemented` });
1702
+ return;
1584
1703
  }
1585
- await saveNativeOAuthCredential(providerId, tokens, accountId, providerData);
1704
+ const { authUrl, codeVerifier, oauthState } = pkce;
1705
+ session2 = { status: "pending", url: authUrl, providerId, codeVerifier, oauthState };
1706
+ oauthSessions.set(sessionId, session2);
1707
+ const codePromise = new Promise((resolve, reject) => {
1708
+ session2.codeResolver = resolve;
1709
+ session2.errorRejecter = (err) => reject(new Error(err));
1710
+ setTimeout(() => reject(new Error("OAuth timeout \u2014 sign-in not completed")), 10 * 60 * 1e3);
1711
+ });
1712
+ oauthSessions.set(sessionId, session2);
1713
+ codePromise.then(async (code) => {
1714
+ let providerData = {};
1715
+ let accountId;
1716
+ let tokens;
1717
+ if (providerId === "antigravity") {
1718
+ const result = await completeAntigravityExchange(code, codeVerifier, redirectUri);
1719
+ tokens = result.tokens;
1720
+ accountId = result.userInfo.email;
1721
+ if (result.projectId) providerData.projectId = result.projectId;
1722
+ if (result.tierId) providerData.tier = result.tierId;
1723
+ } else {
1724
+ throw new Error(`Unknown PKCE provider: ${providerId}`);
1725
+ }
1726
+ await saveNativeOAuthCredential(providerId, tokens, accountId, providerData);
1727
+ await refreshOAuthProviderModels(providerId);
1728
+ oauthSessions.set(sessionId, { ...session2, status: "done" });
1729
+ }).catch((err) => {
1730
+ oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1731
+ });
1732
+ sendJson(res, 200, { sessionId, authUrl, pkce: true });
1733
+ } catch (err) {
1734
+ oauthSessions.set(sessionId, { ...session2, error: String(err) });
1735
+ sendJson(res, 500, { error: String(err) });
1736
+ }
1737
+ return;
1738
+ }
1739
+ let session = { status: "error", url: "", providerId };
1740
+ oauthSessions.set(sessionId, session);
1741
+ try {
1742
+ const device = await requestOpenAiDeviceCode();
1743
+ const url = openAiDeviceCodeUrl();
1744
+ session = { status: "pending", url, userCode: device.user_code, providerId };
1745
+ oauthSessions.set(sessionId, session);
1746
+ pollOpenAiDeviceCodeToken(device).then(async ({ tokens, accountId }) => {
1747
+ await saveNativeOAuthCredential(providerId, tokens, accountId);
1586
1748
  await refreshOAuthProviderModels(providerId);
1587
- oauthSessions.set(sessionId, { ...session2, status: "done" });
1749
+ oauthSessions.set(sessionId, { ...session, status: "done" });
1588
1750
  }).catch((err) => {
1589
- oauthSessions.set(sessionId, { ...session2, status: "error", error: String(err) });
1751
+ oauthSessions.set(sessionId, { ...session, status: "error", error: String(err) });
1590
1752
  });
1591
- sendJson(res, 200, { sessionId, authUrl, pkce: true });
1592
- return;
1753
+ sendJson(res, 200, { sessionId, url, userCode: device.user_code });
1754
+ } catch (err) {
1755
+ oauthSessions.set(sessionId, { ...session, error: String(err) });
1756
+ sendJson(res, 500, { error: String(err) });
1593
1757
  }
1594
- const device = await requestOpenAiDeviceCode();
1595
- const url = openAiDeviceCodeUrl();
1596
- const session = { status: "pending", url, userCode: device.user_code, providerId };
1597
- oauthSessions.set(sessionId, session);
1598
- pollOpenAiDeviceCodeToken(device).then(async ({ tokens, accountId }) => {
1599
- await saveNativeOAuthCredential(providerId, tokens, accountId);
1600
- await refreshOAuthProviderModels(providerId);
1601
- oauthSessions.set(sessionId, { ...session, status: "done" });
1602
- }).catch((err) => {
1603
- oauthSessions.set(sessionId, { ...session, status: "error", error: String(err) });
1604
- });
1605
- sendJson(res, 200, { sessionId, url, userCode: device.user_code });
1606
1758
  } catch (err) {
1607
1759
  sendJson(res, 500, { error: String(err) });
1608
1760
  }
@@ -1975,30 +2127,41 @@ function removeLock() {
1975
2127
  } catch {
1976
2128
  }
1977
2129
  }
1978
- function checkExistingServer() {
2130
+ function parseUiLock(raw) {
2131
+ try {
2132
+ const { pid, port } = JSON.parse(raw);
2133
+ if (!Number.isInteger(pid) || !Number.isInteger(port)) return null;
2134
+ if (port < 1 || port > 65535) return null;
2135
+ return { pid, port };
2136
+ } catch {
2137
+ return null;
2138
+ }
2139
+ }
2140
+ var PROBE_TIMEOUT_MS = 1500;
2141
+ async function probeUiServer(port, fetchImpl = fetch) {
2142
+ const controller = new AbortController();
2143
+ const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
2144
+ try {
2145
+ const res = await fetchImpl(`http://127.0.0.1:${port}/api/ping`, { signal: controller.signal });
2146
+ if (!res.ok) return false;
2147
+ const body = await res.json();
2148
+ return body?.app === "anygate";
2149
+ } catch {
2150
+ return false;
2151
+ } finally {
2152
+ clearTimeout(timer);
2153
+ }
2154
+ }
2155
+ async function checkExistingServer(fetchImpl = fetch) {
1979
2156
  if (!existsSync3(LOCK_FILE)) return null;
2157
+ let lock = null;
1980
2158
  try {
1981
- const { pid, port } = JSON.parse(readFileSync(LOCK_FILE, "utf8"));
1982
- const isWindows2 = process.platform === "win32";
1983
- let processExists = false;
1984
- if (isWindows2) {
1985
- try {
1986
- const output = execSync(`tasklist /FI "PID eq ${pid}"`, {
1987
- encoding: "utf8",
1988
- stdio: ["pipe", "pipe", "ignore"]
1989
- });
1990
- processExists = output.includes(String(pid));
1991
- } catch {
1992
- processExists = false;
1993
- }
1994
- } else {
1995
- process.kill(pid, 0);
1996
- processExists = true;
1997
- }
1998
- if (processExists) {
1999
- return `http://127.0.0.1:${port}`;
2000
- }
2159
+ lock = parseUiLock(readFileSync(LOCK_FILE, "utf8"));
2001
2160
  } catch {
2161
+ lock = null;
2162
+ }
2163
+ if (lock && await probeUiServer(lock.port, fetchImpl)) {
2164
+ return `http://127.0.0.1:${lock.port}`;
2002
2165
  }
2003
2166
  removeLock();
2004
2167
  return null;
@@ -2022,11 +2185,19 @@ async function resolveUiShutdownDecision(signal, promptClose = () => p.confirm({
2022
2185
  return shouldClose ? "close" : "keep";
2023
2186
  }
2024
2187
  async function runUiCommand(opts = {}) {
2025
- const existing = checkExistingServer();
2188
+ const existing = await checkExistingServer();
2026
2189
  if (existing) {
2027
- console.log(`
2190
+ console.log(
2191
+ `
2028
2192
  ${pc2.bold("anygate UI")} already running at ${pc2.cyan(existing)}
2029
- `);
2193
+ ${pc2.dim("Press Ctrl+C in that terminal to stop it")}
2194
+ `
2195
+ );
2196
+ try {
2197
+ const { default: open2 } = await import("open");
2198
+ await open2(existing);
2199
+ } catch {
2200
+ }
2030
2201
  return 0;
2031
2202
  }
2032
2203
  if (opts.trace) {
@@ -2081,6 +2252,7 @@ async function runUiCommand(opts = {}) {
2081
2252
  const url = `http://127.0.0.1:${port}`;
2082
2253
  mkdirSync(getAppHome(), { recursive: true });
2083
2254
  writeFileSync2(LOCK_FILE, JSON.stringify({ pid: process.pid, port }));
2255
+ process.on("exit", removeLock);
2084
2256
  const cleanup = () => {
2085
2257
  removeLock();
2086
2258
  server.close();
@@ -2115,8 +2287,8 @@ async function runUiCommand(opts = {}) {
2115
2287
  trace?.(`ui server listening ${url}`);
2116
2288
  }
2117
2289
  try {
2118
- const { default: open } = await import("open");
2119
- await open(url);
2290
+ const { default: open2 } = await import("open");
2291
+ await open2(url);
2120
2292
  trace?.(`browser open ${url}`);
2121
2293
  } catch {
2122
2294
  trace?.(`browser open failed ${url}`);
@@ -2126,9 +2298,12 @@ async function runUiCommand(opts = {}) {
2126
2298
  return 0;
2127
2299
  }
2128
2300
  export {
2301
+ checkExistingServer,
2129
2302
  formatUiServerLifecycleMessage,
2130
2303
  isUiApiRoute,
2304
+ parseUiLock,
2305
+ probeUiServer,
2131
2306
  resolveUiShutdownDecision,
2132
2307
  runUiCommand
2133
2308
  };
2134
- //# sourceMappingURL=command-N3R2ZVVS.js.map
2309
+ //# sourceMappingURL=command-JOEFUFLD.js.map