@pushpalsdev/cli 1.0.7 → 1.0.9

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.
Files changed (28) hide show
  1. package/dist/pushpals-cli.js +300 -33
  2. package/monitor-ui/+not-found.html +440 -0
  3. package/monitor-ui/_expo/.routes.json +3 -0
  4. package/monitor-ui/_expo/static/js/web/entry-7e55666b7443eef13f7696e8d422e8cf.js +1218 -0
  5. package/monitor-ui/_expo/static/js/web/index-04194fb85c12ac46718492c2ff687e6c.js +6 -0
  6. package/monitor-ui/_sitemap.html +440 -0
  7. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
  8. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
  9. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
  10. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
  11. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
  12. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
  13. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
  14. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
  15. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
  16. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
  17. package/monitor-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
  18. package/monitor-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
  19. package/monitor-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
  20. package/monitor-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
  21. package/monitor-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
  22. package/monitor-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
  23. package/monitor-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
  24. package/monitor-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
  25. package/monitor-ui/favicon.ico +0 -0
  26. package/monitor-ui/index.html +440 -0
  27. package/monitor-ui/modal.html +440 -0
  28. package/package.json +3 -2
@@ -2,8 +2,16 @@
2
2
  // @bun
3
3
 
4
4
  // ../../scripts/pushpals-cli.ts
5
- import { appendFileSync, chmodSync, cpSync, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
6
- import { dirname, join as join2, resolve as resolve3 } from "path";
5
+ import {
6
+ appendFileSync,
7
+ chmodSync,
8
+ cpSync,
9
+ existsSync as existsSync3,
10
+ mkdirSync,
11
+ readFileSync as readFileSync3,
12
+ writeFileSync
13
+ } from "fs";
14
+ import { dirname, extname, join as join2, resolve as resolve3 } from "path";
7
15
  import { createInterface } from "readline";
8
16
 
9
17
  // ../shared/src/client_preflight.ts
@@ -1228,6 +1236,56 @@ function resolveBundledRuntimeAssetSource() {
1228
1236
  }
1229
1237
  return null;
1230
1238
  }
1239
+ function looksLikeMonitoringHubBuild(root) {
1240
+ return existsSync3(join2(root, "index.html")) && existsSync3(join2(root, "_expo"));
1241
+ }
1242
+ function resolveBundledMonitoringHubRoot() {
1243
+ const candidates = [
1244
+ resolve3(import.meta.dir, "..", "monitor-ui"),
1245
+ resolve3(import.meta.dir, "..", "packages", "cli", "monitor-ui")
1246
+ ];
1247
+ for (const candidate of candidates) {
1248
+ if (looksLikeMonitoringHubBuild(candidate))
1249
+ return candidate;
1250
+ }
1251
+ return null;
1252
+ }
1253
+ function resolveCliSourceCheckoutRoot() {
1254
+ const candidates = [
1255
+ resolve3(import.meta.dir, ".."),
1256
+ resolve3(import.meta.dir, "..", ".."),
1257
+ resolve3(import.meta.dir, "..", "..", "..")
1258
+ ];
1259
+ for (const candidate of candidates) {
1260
+ if (existsSync3(join2(candidate, "package.json")) && existsSync3(join2(candidate, "apps", "client", "app.json")) && existsSync3(join2(candidate, "scripts", "sync-cli-monitor-ui.ts"))) {
1261
+ return candidate;
1262
+ }
1263
+ }
1264
+ return null;
1265
+ }
1266
+ function exportBundledMonitoringHubFromSourceCheckout(sourceRoot) {
1267
+ const exportScriptPath = join2(sourceRoot, "scripts", "sync-cli-monitor-ui.ts");
1268
+ console.log("[pushpals] Packaged monitor UI missing; exporting the shared client monitor...");
1269
+ const proc = Bun.spawnSync([process.execPath, exportScriptPath], {
1270
+ cwd: sourceRoot,
1271
+ stdout: "inherit",
1272
+ stderr: "inherit",
1273
+ env: process.env
1274
+ });
1275
+ if (proc.exitCode !== 0) {
1276
+ throw new Error(`Failed to export packaged monitor UI from source checkout (exit ${proc.exitCode || 1})`);
1277
+ }
1278
+ }
1279
+ async function ensureBundledMonitoringHubRoot() {
1280
+ const existingRoot = resolveBundledMonitoringHubRoot();
1281
+ if (existingRoot)
1282
+ return existingRoot;
1283
+ const sourceRoot = resolveCliSourceCheckoutRoot();
1284
+ if (!sourceRoot)
1285
+ return null;
1286
+ exportBundledMonitoringHubFromSourceCheckout(sourceRoot);
1287
+ return resolveBundledMonitoringHubRoot();
1288
+ }
1231
1289
  function repoLooksLikePushPalsSourceCheckout(repoRoot) {
1232
1290
  return existsSync3(join2(repoRoot, "configs", "default.toml")) || existsSync3(join2(repoRoot, "config", "default.toml"));
1233
1291
  }
@@ -1425,9 +1483,10 @@ function runtimeBinaryFilename(serviceName, platformKey) {
1425
1483
  return `pushpals-runtime-${serviceToken}-${platformKey}${extension}`;
1426
1484
  }
1427
1485
  function buildEmbeddedRuntimeEnv(baseEnv, opts) {
1486
+ const env = normalizeChildProcessEnv(baseEnv);
1428
1487
  const useRuntimeConfig = opts.useRuntimeConfig !== false;
1429
1488
  return {
1430
- ...baseEnv,
1489
+ ...env,
1431
1490
  PUSHPALS_REPO_ROOT_OVERRIDE: opts.repoRoot,
1432
1491
  PUSHPALS_PROJECT_ROOT_OVERRIDE: opts.repoRoot,
1433
1492
  ...useRuntimeConfig ? {
@@ -1436,9 +1495,58 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
1436
1495
  } : {
1437
1496
  PUSHPALS_PROMPTS_ROOT_OVERRIDE: opts.repoRoot
1438
1497
  },
1439
- PUSHPALS_PROTOCOL_SCHEMAS_DIR: join2(opts.runtimeRoot, "protocol", "schemas")
1498
+ PUSHPALS_PROTOCOL_SCHEMAS_DIR: join2(opts.runtimeRoot, "protocol", "schemas"),
1499
+ ...typeof env.PUSHPALS_GIT_BIN === "string" && env.PUSHPALS_GIT_BIN.trim() ? { PUSHPALS_GIT_BIN: env.PUSHPALS_GIT_BIN.trim() } : {}
1440
1500
  };
1441
1501
  }
1502
+ function normalizeChildProcessEnv(baseEnv, platform = process.platform) {
1503
+ const env = {};
1504
+ for (const [key, value] of Object.entries(baseEnv)) {
1505
+ if (typeof value === "string")
1506
+ env[key] = value;
1507
+ }
1508
+ if (platform === "win32") {
1509
+ const resolvedPath = String(env.Path ?? env.PATH ?? process.env.Path ?? process.env.PATH ?? "").trim();
1510
+ if (resolvedPath) {
1511
+ env.Path = resolvedPath;
1512
+ env.PATH = resolvedPath;
1513
+ }
1514
+ const systemRoot = String(env.SystemRoot ?? env.SYSTEMROOT ?? process.env.SystemRoot ?? process.env.SYSTEMROOT ?? "").trim();
1515
+ if (systemRoot) {
1516
+ env.SystemRoot = systemRoot;
1517
+ env.SYSTEMROOT = systemRoot;
1518
+ }
1519
+ const comSpec = String(env.ComSpec ?? env.COMSPEC ?? process.env.ComSpec ?? process.env.COMSPEC ?? "").trim();
1520
+ if (comSpec) {
1521
+ env.ComSpec = comSpec;
1522
+ env.COMSPEC = comSpec;
1523
+ }
1524
+ }
1525
+ return env;
1526
+ }
1527
+ async function resolveCommandPath(command, cwd, env) {
1528
+ const lookupCommands = process.platform === "win32" ? [
1529
+ ["where.exe", command],
1530
+ ["where", command]
1531
+ ] : [["which", command]];
1532
+ for (const lookup of lookupCommands) {
1533
+ try {
1534
+ const proc = Bun.spawn(lookup, {
1535
+ cwd,
1536
+ env,
1537
+ stdout: "pipe",
1538
+ stderr: "ignore"
1539
+ });
1540
+ const [stdout, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
1541
+ if (exitCode !== 0)
1542
+ continue;
1543
+ const resolved = stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
1544
+ if (resolved)
1545
+ return resolved;
1546
+ } catch {}
1547
+ }
1548
+ return null;
1549
+ }
1442
1550
  function timestampFileToken() {
1443
1551
  return new Date().toISOString().replace(/[:.]/g, "-");
1444
1552
  }
@@ -1546,11 +1654,18 @@ function spawnRuntimeService(name, command, cwd, env, logPath) {
1546
1654
  });
1547
1655
  return service;
1548
1656
  }
1657
+ function buildServiceStopCommand(pid, platform = process.platform) {
1658
+ if (platform === "win32" && typeof pid === "number" && pid > 0) {
1659
+ return ["taskkill", "/PID", String(pid), "/T", "/F"];
1660
+ }
1661
+ return null;
1662
+ }
1549
1663
  function stopRuntimeServices(services) {
1550
1664
  for (const service of services) {
1551
1665
  try {
1552
- if (process.platform === "win32" && typeof service.proc.pid === "number" && service.proc.pid > 0) {
1553
- Bun.spawnSync(["taskkill", "/PID", String(service.proc.pid), "/T", "/F"], {
1666
+ const stopCommand = buildServiceStopCommand(service.proc.pid, process.platform);
1667
+ if (stopCommand) {
1668
+ Bun.spawnSync(stopCommand, {
1554
1669
  stdin: "ignore",
1555
1670
  stdout: "ignore",
1556
1671
  stderr: "ignore"
@@ -1561,6 +1676,24 @@ function stopRuntimeServices(services) {
1561
1676
  } catch {}
1562
1677
  }
1563
1678
  }
1679
+ function isOptionalEmbeddedService(name) {
1680
+ return name === "source_control_manager";
1681
+ }
1682
+ async function canSpawnCommand(command, cwd, env) {
1683
+ try {
1684
+ const proc = Bun.spawn(command, {
1685
+ cwd,
1686
+ env,
1687
+ stdin: "ignore",
1688
+ stdout: "ignore",
1689
+ stderr: "ignore"
1690
+ });
1691
+ const exitCode = await proc.exited;
1692
+ return exitCode === 0;
1693
+ } catch {
1694
+ return false;
1695
+ }
1696
+ }
1564
1697
  async function repoHasRemote(repoRoot, remote) {
1565
1698
  const normalizedRemote = remote.trim();
1566
1699
  if (!normalizedRemote)
@@ -1630,6 +1763,10 @@ async function autoStartRuntimeServices(opts) {
1630
1763
  runtimeRoot,
1631
1764
  useRuntimeConfig: opts.preparedRuntime.preflightUsesEmbeddedRuntime
1632
1765
  });
1766
+ const resolvedGitBinary = await resolveCommandPath("git", opts.repoRoot, normalizeChildProcessEnv(process.env));
1767
+ if (resolvedGitBinary) {
1768
+ runtimeEnv.PUSHPALS_GIT_BIN = resolvedGitBinary;
1769
+ }
1633
1770
  const services = [];
1634
1771
  const runToken = timestampFileToken();
1635
1772
  const logDir = join2(runtimeRoot, "logs", "bootstrap");
@@ -1678,13 +1815,24 @@ ${tail}` : ""}`);
1678
1815
  console.log(`[pushpals] remotebuddy log: ${remotebuddyService.logPath}`);
1679
1816
  const scmHealthy = await probeSourceControlManager(opts.sourceControlManagerPort);
1680
1817
  const scmRemoteAvailable = await repoHasRemote(opts.repoRoot, opts.sourceControlManagerRemote);
1818
+ const gitProbeCommand = typeof runtimeEnv.PUSHPALS_GIT_BIN === "string" && runtimeEnv.PUSHPALS_GIT_BIN.trim() ? [runtimeEnv.PUSHPALS_GIT_BIN.trim(), "--version"] : ["git", "--version"];
1819
+ const gitAvailableForScm = await canSpawnCommand(gitProbeCommand, opts.repoRoot, runtimeEnv);
1681
1820
  if (!scmHealthy && scmRemoteAvailable) {
1682
- console.log("[pushpals] Starting embedded SourceControlManager...");
1683
- const sourceControlManagerService = spawnRuntimeService("source_control_manager", [runtimeBinaries.sourceControlManager, "--skip-clean-check"], opts.repoRoot, runtimeEnv, logPathFor("source_control_manager"));
1684
- services.push(sourceControlManagerService);
1685
- console.log(`[pushpals] source_control_manager log: ${sourceControlManagerService.logPath}`);
1821
+ if (!gitAvailableForScm) {
1822
+ console.warn("[pushpals] Git is not available to embedded SourceControlManager; skipping SCM startup.");
1823
+ } else {
1824
+ if (runtimeEnv.PUSHPALS_GIT_BIN) {
1825
+ console.log(`[pushpals] Embedded SourceControlManager git=${runtimeEnv.PUSHPALS_GIT_BIN}`);
1826
+ }
1827
+ console.log("[pushpals] Starting embedded SourceControlManager...");
1828
+ const sourceControlManagerService = spawnRuntimeService("source_control_manager", [runtimeBinaries.sourceControlManager, "--skip-clean-check"], opts.repoRoot, runtimeEnv, logPathFor("source_control_manager"));
1829
+ services.push(sourceControlManagerService);
1830
+ console.log(`[pushpals] source_control_manager log: ${sourceControlManagerService.logPath}`);
1831
+ }
1686
1832
  } else if (!scmRemoteAvailable) {
1687
1833
  console.log(`[pushpals] Repo has no git remote "${opts.sourceControlManagerRemote}"; skipping embedded SourceControlManager.`);
1834
+ } else if (!gitAvailableForScm) {
1835
+ console.warn("[pushpals] Git is not available to embedded SourceControlManager; skipping SCM startup.");
1688
1836
  } else {
1689
1837
  console.log("[pushpals] SourceControlManager already healthy; skipping embedded start.");
1690
1838
  }
@@ -1693,7 +1841,7 @@ ${tail}` : ""}`);
1693
1841
  for (let i = services.length - 1;i >= 0; i--) {
1694
1842
  const service = services[i];
1695
1843
  if (service.exited) {
1696
- if (service.name === "source_control_manager") {
1844
+ if (isOptionalEmbeddedService(service.name)) {
1697
1845
  console.warn(`[pushpals] Embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}); continuing without SCM.`);
1698
1846
  const tail2 = readLogTail(service.logPath);
1699
1847
  if (tail2) {
@@ -1714,9 +1862,20 @@ ${tail}` : ""}`);
1714
1862
  if (health?.ok) {
1715
1863
  const stabilityDeadline = Date.now() + DEFAULT_SERVICE_STABILITY_GRACE_MS;
1716
1864
  while (Date.now() < stabilityDeadline) {
1717
- for (const service of services) {
1865
+ for (let i = services.length - 1;i >= 0; i--) {
1866
+ const service = services[i];
1718
1867
  if (!service.exited)
1719
1868
  continue;
1869
+ if (isOptionalEmbeddedService(service.name)) {
1870
+ const tail2 = readLogTail(service.logPath);
1871
+ console.warn(`[pushpals] Embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}); continuing without SCM.`);
1872
+ if (tail2) {
1873
+ console.warn(`[pushpals] ${service.name} log tail:
1874
+ ${tail2}`);
1875
+ }
1876
+ services.splice(i, 1);
1877
+ continue;
1878
+ }
1720
1879
  const tail = readLogTail(service.logPath);
1721
1880
  stopRuntimeServices(services);
1722
1881
  throw new Error(`Embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}). ` + `See ${service.logPath}${tail ? `
@@ -1778,6 +1937,91 @@ async function looksLikeMonitoringHub(url) {
1778
1937
  return false;
1779
1938
  }
1780
1939
  }
1940
+ function buildMonitoringHubRuntimeBootstrap(opts) {
1941
+ return {
1942
+ serverUrl: opts.serverUrl,
1943
+ localAgentUrl: opts.localAgentUrl,
1944
+ sessionId: opts.sessionId,
1945
+ authToken: opts.authToken
1946
+ };
1947
+ }
1948
+ function injectMonitoringHubBootstrap(html, bootstrap) {
1949
+ const payload = jsonHtmlBootstrap(bootstrap);
1950
+ const script = `<script>globalThis.__PUSHPALS_WEB_BOOTSTRAP__=${payload};</script>`;
1951
+ if (html.includes("</head>")) {
1952
+ return html.replace("</head>", `${script}</head>`);
1953
+ }
1954
+ return `${script}${html}`;
1955
+ }
1956
+ function monitoringHubContentType(pathValue) {
1957
+ switch (extname(pathValue).toLowerCase()) {
1958
+ case ".html":
1959
+ return "text/html; charset=utf-8";
1960
+ case ".js":
1961
+ return "application/javascript; charset=utf-8";
1962
+ case ".css":
1963
+ return "text/css; charset=utf-8";
1964
+ case ".json":
1965
+ return "application/json; charset=utf-8";
1966
+ case ".svg":
1967
+ return "image/svg+xml";
1968
+ case ".png":
1969
+ return "image/png";
1970
+ case ".jpg":
1971
+ case ".jpeg":
1972
+ return "image/jpeg";
1973
+ case ".ico":
1974
+ return "image/x-icon";
1975
+ case ".woff2":
1976
+ return "font/woff2";
1977
+ case ".ttf":
1978
+ return "font/ttf";
1979
+ case ".map":
1980
+ return "application/json; charset=utf-8";
1981
+ default:
1982
+ return "application/octet-stream";
1983
+ }
1984
+ }
1985
+ function resolveMonitoringHubAssetPath(assetRoot, pathname) {
1986
+ const root = resolve3(assetRoot);
1987
+ const rootPrefix = `${root}${root.endsWith("\\") || root.endsWith("/") ? "" : process.platform === "win32" ? "\\" : "/"}`;
1988
+ const decodedPath = decodeURIComponent(pathname);
1989
+ const trimmedPath = decodedPath === "/" ? "/index.html" : decodedPath;
1990
+ const relativePath = trimmedPath.replace(/^\/+/, "");
1991
+ const candidatePath = resolve3(root, relativePath);
1992
+ if (candidatePath !== root && !candidatePath.startsWith(rootPrefix))
1993
+ return null;
1994
+ if (existsSync3(candidatePath))
1995
+ return candidatePath;
1996
+ if (!extname(relativePath)) {
1997
+ const nestedIndexPath = resolve3(root, relativePath, "index.html");
1998
+ if ((nestedIndexPath === root || nestedIndexPath.startsWith(rootPrefix)) && existsSync3(nestedIndexPath)) {
1999
+ return nestedIndexPath;
2000
+ }
2001
+ return join2(root, "index.html");
2002
+ }
2003
+ return null;
2004
+ }
2005
+ async function serveBundledMonitoringHub(assetRoot, pathname, bootstrap) {
2006
+ const assetPath = resolveMonitoringHubAssetPath(assetRoot, pathname);
2007
+ if (!assetPath || !existsSync3(assetPath))
2008
+ return null;
2009
+ if (assetPath.endsWith("index.html")) {
2010
+ const html = injectMonitoringHubBootstrap(readFileSync3(assetPath, "utf8"), bootstrap);
2011
+ return new Response(html, {
2012
+ headers: {
2013
+ "content-type": "text/html; charset=utf-8",
2014
+ "cache-control": "no-store"
2015
+ }
2016
+ });
2017
+ }
2018
+ return new Response(Bun.file(assetPath), {
2019
+ headers: {
2020
+ "content-type": monitoringHubContentType(assetPath),
2021
+ "cache-control": "no-store"
2022
+ }
2023
+ });
2024
+ }
1781
2025
  function buildEmbeddedMonitoringHubHtml(opts) {
1782
2026
  const bootstrap = jsonHtmlBootstrap(opts);
1783
2027
  return `<!doctype html>
@@ -1925,10 +2169,16 @@ async function proxyMonitoringHubRequest(serverUrl, authToken, pathValue) {
1925
2169
  });
1926
2170
  }
1927
2171
  async function startEmbeddedMonitoringHub(opts) {
1928
- const html = buildEmbeddedMonitoringHubHtml({
2172
+ const monitoringHubAssetRoot = opts.assetRoot === undefined ? await ensureBundledMonitoringHubRoot() : opts.assetRoot;
2173
+ if (!monitoringHubAssetRoot || !looksLikeMonitoringHubBuild(monitoringHubAssetRoot)) {
2174
+ console.error("[pushpals] Unified monitoring hub assets are unavailable; build or export the packaged client monitor first.");
2175
+ return null;
2176
+ }
2177
+ const bootstrap = buildMonitoringHubRuntimeBootstrap({
1929
2178
  serverUrl: opts.serverUrl,
1930
2179
  localAgentUrl: opts.localAgentUrl,
1931
- sessionId: opts.sessionId
2180
+ sessionId: opts.sessionId,
2181
+ authToken: opts.authToken
1932
2182
  });
1933
2183
  const candidatePorts = Array.from({ length: MONITOR_SCAN_PORTS }, (_, index) => opts.preferredPort + index).concat(0);
1934
2184
  for (const port of candidatePorts) {
@@ -1938,16 +2188,13 @@ async function startEmbeddedMonitoringHub(opts) {
1938
2188
  idleTimeout: 30,
1939
2189
  fetch: async (req) => {
1940
2190
  const url = new URL(req.url);
1941
- if (url.pathname === "/") {
1942
- return new Response(html, {
1943
- headers: {
1944
- "content-type": "text/html; charset=utf-8",
1945
- "cache-control": "no-store"
1946
- }
1947
- });
1948
- }
1949
2191
  if (url.pathname === "/healthz") {
1950
- return Response.json({ ok: true, port, serverUrl: opts.serverUrl, sessionId: opts.sessionId });
2192
+ return Response.json({
2193
+ ok: true,
2194
+ port,
2195
+ serverUrl: opts.serverUrl,
2196
+ sessionId: opts.sessionId
2197
+ });
1951
2198
  }
1952
2199
  if (url.pathname === "/api/status") {
1953
2200
  return await proxyMonitoringHubRequest(opts.serverUrl, opts.authToken, "/system/status");
@@ -1961,6 +2208,9 @@ async function startEmbeddedMonitoringHub(opts) {
1961
2208
  if (url.pathname === "/api/completions") {
1962
2209
  return await proxyMonitoringHubRequest(opts.serverUrl, opts.authToken, "/completions?status=all&limit=20");
1963
2210
  }
2211
+ const bundledResponse = await serveBundledMonitoringHub(monitoringHubAssetRoot, url.pathname, bootstrap);
2212
+ if (bundledResponse)
2213
+ return bundledResponse;
1964
2214
  return new Response("Not found", { status: 404 });
1965
2215
  }
1966
2216
  });
@@ -2163,15 +2413,17 @@ async function runSessionStream(serverUrl, sessionId, authToken, print, signal)
2163
2413
  }
2164
2414
  }
2165
2415
  }
2166
- async function openMonitoringHub(url) {
2167
- let cmd = null;
2168
- if (process.platform === "win32") {
2169
- cmd = ["cmd", "/c", "start", "", url];
2170
- } else if (process.platform === "darwin") {
2171
- cmd = ["open", url];
2172
- } else {
2173
- cmd = ["xdg-open", url];
2416
+ function buildOpenMonitoringHubCommand(url, platform = process.platform) {
2417
+ if (platform === "win32") {
2418
+ return ["cmd", "/c", "start", "", url];
2174
2419
  }
2420
+ if (platform === "darwin") {
2421
+ return ["open", url];
2422
+ }
2423
+ return ["xdg-open", url];
2424
+ }
2425
+ async function openMonitoringHub(url) {
2426
+ const cmd = buildOpenMonitoringHubCommand(url, process.platform);
2175
2427
  const proc = Bun.spawn(cmd, {
2176
2428
  stdin: "ignore",
2177
2429
  stdout: "ignore",
@@ -2180,6 +2432,10 @@ async function openMonitoringHub(url) {
2180
2432
  const code = await proc.exited;
2181
2433
  return code === 0;
2182
2434
  }
2435
+ function isCliExitCommand(text) {
2436
+ const normalized = String(text ?? "").trim().toLowerCase();
2437
+ return normalized === "/exit" || normalized === "/quit" || normalized === "exit" || normalized === "quit";
2438
+ }
2183
2439
  async function main() {
2184
2440
  const argv = process.argv.slice(2);
2185
2441
  logCliInvocation(argv);
@@ -2304,7 +2560,7 @@ async function main() {
2304
2560
  console.log(`sessionId=${localBuddySessionId}`);
2305
2561
  console.log(`repoRoot=${repoRoot}`);
2306
2562
  console.log(`cliStateFile=${statePath}`);
2307
- console.log("[pushpals] Type a message and press Enter. Use /exit to quit.");
2563
+ console.log("[pushpals] Type a message and press Enter. Use /exit or exit to quit.");
2308
2564
  const streamAbort = new AbortController;
2309
2565
  let rl = null;
2310
2566
  const printIncoming = (line) => {
@@ -2325,12 +2581,16 @@ ${line}
2325
2581
  if (shuttingDown)
2326
2582
  return;
2327
2583
  shuttingDown = true;
2584
+ console.log("[pushpals] Shutting down CLI session...");
2328
2585
  streamAbort.abort();
2329
2586
  if (rl)
2330
2587
  rl.close();
2331
2588
  try {
2332
2589
  monitoringHub?.stop();
2333
2590
  } catch {}
2591
+ if (autoStartedServices.length > 0) {
2592
+ console.log("[pushpals] Stopping embedded runtime services...");
2593
+ }
2334
2594
  stopAutoStartedServices();
2335
2595
  };
2336
2596
  process.once("SIGINT", requestStop);
@@ -2349,7 +2609,7 @@ ${line}
2349
2609
  rl.prompt();
2350
2610
  continue;
2351
2611
  }
2352
- if (text === "/exit" || text === "/quit") {
2612
+ if (isCliExitCommand(text)) {
2353
2613
  requestStop();
2354
2614
  break;
2355
2615
  }
@@ -2395,8 +2655,15 @@ if (import.meta.main) {
2395
2655
  }
2396
2656
  export {
2397
2657
  startEmbeddedMonitoringHub,
2658
+ resolveCommandPath,
2398
2659
  resolveBundledRuntimeAssetSource,
2660
+ resolveBundledMonitoringHubRoot,
2399
2661
  prepareCliRuntime,
2662
+ normalizeChildProcessEnv,
2663
+ isCliExitCommand,
2664
+ injectMonitoringHubBootstrap,
2665
+ buildServiceStopCommand,
2666
+ buildOpenMonitoringHubCommand,
2400
2667
  buildEmbeddedRuntimeEnv,
2401
2668
  buildEmbeddedMonitoringHubHtml
2402
2669
  };