@versot/vaguspi 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // apps/cli/src/bin.ts
4
- import { readFileSync as readFileSync7 } from "node:fs";
4
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
5
5
  import { parseArgs } from "node:util";
6
6
 
7
7
  // apps/cli/src/commands/daemon.ts
8
8
  import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
9
9
  import { fileURLToPath } from "node:url";
10
10
  import { homedir as homedir2 } from "node:os";
11
- import { join as join8, normalize, resolve as resolve3, sep as sep2 } from "node:path";
11
+ import { join as join8, normalize, resolve as resolve3, sep as sep3 } from "node:path";
12
+ import { EnvHttpProxyAgent, setGlobalDispatcher } from "undici";
12
13
 
13
14
  // packages/host/config/dist/config-store.js
14
15
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -301,8 +302,9 @@ var VagModelsStore = class {
301
302
  // packages/host/engine/dist/vagus-engine.js
302
303
  import { createAgentSession, DefaultResourceLoader, ModelRuntime, SessionManager as SessionManager2, SettingsManager } from "@earendil-works/pi-coding-agent";
303
304
  import { homedir } from "node:os";
304
- import { basename, dirname as dirname3, isAbsolute as isAbsolute2, join as join5, resolve } from "node:path";
305
- import { appendFileSync as appendFileSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, renameSync, rmSync, statSync, writeFileSync as writeFileSync3 } from "node:fs";
305
+ import { basename, dirname as dirname3, isAbsolute as isAbsolute2, join as join5, resolve, sep } from "node:path";
306
+ import { randomUUID as randomUUID2 } from "node:crypto";
307
+ import { appendFileSync as appendFileSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
306
308
 
307
309
  // packages/host/engine/dist/display-utils.js
308
310
  import * as Diff from "diff";
@@ -868,7 +870,7 @@ var ExtensionUiBridge = class {
868
870
  };
869
871
 
870
872
  // packages/host/engine/dist/vagus-engine.js
871
- var VagusEngine = class {
873
+ var VagusEngine = class _VagusEngine {
872
874
  options;
873
875
  sessions = /* @__PURE__ */ new Map();
874
876
  /**
@@ -1696,9 +1698,15 @@ var VagusEngine = class {
1696
1698
  closeSessionForCwd(cwd) {
1697
1699
  for (const [sid, session] of this.sessions) {
1698
1700
  if (session.sessionManager?.getCwd() === cwd) {
1699
- session.dispose();
1701
+ try {
1702
+ session.dispose();
1703
+ } catch (err) {
1704
+ process.stderr.write(`vagus: dispose threw: ${err instanceof Error ? err.stack : String(err)}
1705
+ `);
1706
+ }
1700
1707
  this.sessions.delete(sid);
1701
- void this.options.bus.emit("session.closed", { type: "session.closed", sessionId: sid });
1708
+ void this.options.bus.emit("session.closed", { type: "session.closed", sessionId: sid }).catch(() => {
1709
+ });
1702
1710
  }
1703
1711
  }
1704
1712
  }
@@ -1759,7 +1767,37 @@ var VagusEngine = class {
1759
1767
  return void 0;
1760
1768
  }
1761
1769
  }
1762
- /** Restores an archived project: moves its session files back under `sessions/`. */
1770
+ /** Restores an archived project by its encoded dir name (unique identity). */
1771
+ async unarchiveProjectByDir(dirKey) {
1772
+ if (!_VagusEngine.isSafeDirKey(dirKey))
1773
+ return;
1774
+ const agentDir = this.agentDirPath();
1775
+ const src = join5(agentDir, "archived", dirKey);
1776
+ if (!existsSync3(src))
1777
+ return;
1778
+ let cwd;
1779
+ try {
1780
+ cwd = this.sessionCwd(readdirSync2(src).filter((f) => f.endsWith(".jsonl"))[0] ? join5(src, readdirSync2(src).filter((f) => f.endsWith(".jsonl"))[0]) : "");
1781
+ } catch {
1782
+ cwd = void 0;
1783
+ }
1784
+ if (cwd) {
1785
+ this.closeSessionForCwd(cwd);
1786
+ const dst = join5(agentDir, "sessions", this.encodeCwd(cwd));
1787
+ mkdirSync4(dst, { recursive: true });
1788
+ for (const entry of readdirSync2(src)) {
1789
+ if (!entry.endsWith(".jsonl"))
1790
+ continue;
1791
+ const from = join5(src, entry);
1792
+ try {
1793
+ if (statSync(from).isFile())
1794
+ renameSync(from, join5(dst, entry));
1795
+ } catch {
1796
+ }
1797
+ }
1798
+ this.rmDirSafe(src);
1799
+ }
1800
+ }
1763
1801
  async restoreProject(cwd) {
1764
1802
  const agentDir = this.agentDirPath();
1765
1803
  const src = join5(agentDir, "archived", this.encodeCwd(cwd));
@@ -1778,16 +1816,91 @@ var VagusEngine = class {
1778
1816
  } catch {
1779
1817
  }
1780
1818
  }
1781
- rmSync(src, { recursive: true, force: true });
1819
+ this.rmDirSafe(src);
1820
+ }
1821
+ /** Permanently deletes an archived project dir by its encoded dir name.
1822
+ * dirKey (not cwd) is the identity — cwds can repeat across archive dirs. */
1823
+ async deleteArchivedProjectByDir(dirKey) {
1824
+ if (!_VagusEngine.isSafeDirKey(dirKey))
1825
+ return;
1826
+ const agentDir = this.agentDirPath();
1827
+ const dir = join5(agentDir, "archived", dirKey);
1828
+ const prefix = dir + sep;
1829
+ for (const [sid, session] of this.sessions) {
1830
+ const file = session.sessionManager?.getSessionFile();
1831
+ if (file && (file === dir || file.startsWith(prefix))) {
1832
+ try {
1833
+ session.dispose();
1834
+ } catch {
1835
+ }
1836
+ this.sessions.delete(sid);
1837
+ void this.options.bus.emit("session.closed", { type: "session.closed", sessionId: sid }).catch(() => {
1838
+ });
1839
+ }
1840
+ }
1841
+ this.rmDirSafe(dir);
1782
1842
  }
1783
1843
  /** Permanently deletes an archived project's session dir (JSONL). */
1784
1844
  async deleteArchivedProject(cwd) {
1785
1845
  const agentDir = this.agentDirPath();
1786
1846
  const dir = join5(agentDir, "archived", this.encodeCwd(cwd));
1787
1847
  this.closeSessionForCwd(cwd);
1788
- rmSync(dir, { recursive: true, force: true });
1848
+ this.rmDirSafe(dir);
1849
+ }
1850
+ /** Safe archive-dir name: a plain single path segment, never traversal. */
1851
+ static isSafeDirKey(dirKey) {
1852
+ return dirKey.length > 0 && dirKey !== "." && dirKey !== ".." && !dirKey.includes("/") && !dirKey.includes("\\") && !dirKey.includes("..");
1853
+ }
1854
+ /**
1855
+ * rmSync(recursive) has crashed the daemon natively (0xC0000409 fastfail)
1856
+ * on Windows when deleting archived dirs with non-ASCII names — JS guards
1857
+ * cannot catch a native abort. Delete file-by-file instead: plain unlink
1858
+ * calls are individually catchable and skip whatever is locked/unreadable.
1859
+ *
1860
+ * Iterative (explicit stack, deepest-first) — no JS recursion limit, so
1861
+ * arbitrarily deep trees are safe.
1862
+ */
1863
+ rmDirSafe(root) {
1864
+ const pendingDirs = [{ path: root, expanded: false }];
1865
+ while (pendingDirs.length > 0) {
1866
+ const top = pendingDirs[pendingDirs.length - 1];
1867
+ if (top.expanded) {
1868
+ pendingDirs.pop();
1869
+ try {
1870
+ rmdirSync(top.path);
1871
+ } catch (err) {
1872
+ process.stderr.write(`vagus: rmDirSafe rmdir failed ${top.path}: ${err instanceof Error ? err.message : String(err)}
1873
+ `);
1874
+ }
1875
+ continue;
1876
+ }
1877
+ let entries;
1878
+ try {
1879
+ entries = readdirSync2(top.path);
1880
+ } catch {
1881
+ pendingDirs.pop();
1882
+ continue;
1883
+ }
1884
+ top.expanded = true;
1885
+ for (const entry of entries) {
1886
+ const full = join5(top.path, entry);
1887
+ try {
1888
+ if (statSync(full).isDirectory()) {
1889
+ pendingDirs.push({ path: full, expanded: false });
1890
+ } else {
1891
+ unlinkSync(full);
1892
+ }
1893
+ } catch (err) {
1894
+ process.stderr.write(`vagus: rmDirSafe skipped ${full}: ${err instanceof Error ? err.message : String(err)}
1895
+ `);
1896
+ }
1897
+ }
1898
+ }
1789
1899
  }
1790
1900
  /** Lists archived projects with their sessions (scans the `archived/` dir). */
1901
+ /** Lists archived projects with their sessions (scans the `archived/` dir).
1902
+ * dirKey is the encoded directory name — the unique identity of an archived
1903
+ * group (cwd from session headers can repeat across dirs). */
1791
1904
  async listArchivedProjects() {
1792
1905
  const agentDir = this.agentDirPath();
1793
1906
  const root = join5(agentDir, "archived");
@@ -1805,6 +1918,7 @@ var VagusEngine = class {
1805
1918
  continue;
1806
1919
  out.push({
1807
1920
  cwd,
1921
+ dirKey: entry,
1808
1922
  sessions: infos.map((i) => ({
1809
1923
  id: i.id,
1810
1924
  path: i.path,
@@ -1887,6 +2001,57 @@ var VagusEngine = class {
1887
2001
  const result = await session.navigateTree(targetId, options);
1888
2002
  return { editorText: result.editorText, cancelled: result.cancelled };
1889
2003
  }
2004
+ /**
2005
+ * Creates a new session forked from a specific user message.
2006
+ * The new session contains only the conversation up to and including that
2007
+ * user message — everything after it is dropped. The original session is
2008
+ * untouched. Returns the new session's info (sessionId, sessionFile, cwd).
2009
+ */
2010
+ async forkSession(sessionId, entryId) {
2011
+ const session = this.requireSession(sessionId);
2012
+ const sourceFile = session.sessionManager?.getSessionFile();
2013
+ if (!sourceFile)
2014
+ throw new Error("source session has no file path");
2015
+ if (!existsSync3(sourceFile))
2016
+ throw new Error(`source session file not found: ${sourceFile}`);
2017
+ const raw = readFileSync4(sourceFile, "utf8");
2018
+ const entries = raw.split("\n").filter((l) => l.trim().length > 0).flatMap((l) => {
2019
+ try {
2020
+ return [JSON.parse(l)];
2021
+ } catch {
2022
+ return [];
2023
+ }
2024
+ });
2025
+ const header = entries[0];
2026
+ if (!header || header.type !== "session")
2027
+ throw new Error("source session has no header");
2028
+ const targetIdx = entries.findIndex((e) => e.id === entryId);
2029
+ if (targetIdx < 0)
2030
+ throw new Error(`entry ${entryId} not found in source session`);
2031
+ const endIdx = targetIdx - 1;
2032
+ const cwd = session.sessionManager.getCwd() || this.options.cwd;
2033
+ const sessionDir = dirname3(sourceFile);
2034
+ mkdirSync4(sessionDir, { recursive: true });
2035
+ const newSessionId = randomUUID2();
2036
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2037
+ const fileTimestamp = timestamp.replace(/[:.]/g, "-");
2038
+ const newSessionFile = join5(sessionDir, `${fileTimestamp}_${newSessionId}.jsonl`);
2039
+ const newHeader = {
2040
+ type: "session",
2041
+ version: header.version ?? 1,
2042
+ id: newSessionId,
2043
+ timestamp,
2044
+ cwd,
2045
+ parentSession: sourceFile
2046
+ };
2047
+ const outLines = [JSON.stringify(newHeader)];
2048
+ for (let i = 1; i <= endIdx; i++) {
2049
+ outLines.push(JSON.stringify(entries[i]));
2050
+ }
2051
+ writeFileSync3(newSessionFile, outLines.join("\n") + "\n", "utf8");
2052
+ const manager = SessionManager2.open(newSessionFile);
2053
+ return this.startSession({ cwd, sessionManager: manager });
2054
+ }
1890
2055
  /**
1891
2056
  * Exports the active branch to JSONL (/export). Returns the output path.
1892
2057
  */
@@ -6562,7 +6727,7 @@ import { WebSocketServer as WsServer } from "ws";
6562
6727
 
6563
6728
  // packages/host/rpc/dist/static-server.js
6564
6729
  import { createReadStream, existsSync as existsSync4, statSync as statSync2 } from "node:fs";
6565
- import { extname, join as join6, resolve as resolve2, sep } from "node:path";
6730
+ import { extname, join as join6, resolve as resolve2, sep as sep2 } from "node:path";
6566
6731
  var MIME_TYPES = {
6567
6732
  ".html": "text/html; charset=utf-8",
6568
6733
  ".js": "text/javascript; charset=utf-8",
@@ -6581,7 +6746,7 @@ function resolveStaticFile(rootDir, pathname) {
6581
6746
  const root = resolve2(rootDir);
6582
6747
  const name = pathname === "/" ? "index.html" : pathname;
6583
6748
  const candidate = resolve2(join6(root, name));
6584
- if (candidate !== root && !candidate.startsWith(root + sep)) {
6749
+ if (candidate !== root && !candidate.startsWith(root + sep2)) {
6585
6750
  return void 0;
6586
6751
  }
6587
6752
  if (existsSync4(candidate)) {
@@ -6906,6 +7071,29 @@ function requireString(value, label) {
6906
7071
  }
6907
7072
  return value;
6908
7073
  }
7074
+ function applyHttpProxy() {
7075
+ try {
7076
+ let proxy;
7077
+ const settingsFile = join8(piAgentDir(), "settings.json");
7078
+ if (existsSync6(settingsFile)) {
7079
+ const s = JSON.parse(readFileSync6(settingsFile, "utf8"));
7080
+ if (typeof s.httpProxy === "string" && s.httpProxy.trim()) proxy = s.httpProxy.trim();
7081
+ }
7082
+ proxy ??= process.env.HTTPS_PROXY ?? process.env.HTTP_PROXY;
7083
+ if (proxy) {
7084
+ const normalized = /^https?:\/\//.test(proxy) ? proxy : `http://${proxy}`;
7085
+ process.env.HTTPS_PROXY = normalized;
7086
+ process.env.HTTP_PROXY = normalized;
7087
+ process.env.NO_PROXY ??= "localhost,127.0.0.1";
7088
+ setGlobalDispatcher(new EnvHttpProxyAgent());
7089
+ process.stderr.write(`vagus: http proxy enabled \u2192 ${normalized.replace(/\/\/([^@/]*)@/, "//***@")}
7090
+ `);
7091
+ }
7092
+ } catch (err) {
7093
+ process.stderr.write(`vagus: proxy setup skipped: ${err instanceof Error ? err.message : String(err)}
7094
+ `);
7095
+ }
7096
+ }
6909
7097
  function probeRequestFor(apiType, url, apiKey, modelId) {
6910
7098
  const headers = { "Content-Type": "application/json" };
6911
7099
  const base = url.replace(/\/chat\/completions$/, "");
@@ -6939,6 +7127,7 @@ function builtinExtensionPaths() {
6939
7127
  return out;
6940
7128
  }
6941
7129
  async function runDaemon() {
7130
+ applyHttpProxy();
6942
7131
  const stateDir = defaultStateDir();
6943
7132
  mkdirSync6(stateDir, { recursive: true });
6944
7133
  const config = new ConfigStore({ dir: stateDir });
@@ -6997,19 +7186,21 @@ async function runDaemon() {
6997
7186
  return host.archiveProject(cwd);
6998
7187
  });
6999
7188
  srv.registerMethod("project.unarchive", (params) => {
7000
- const cwd = requireString(params?.cwd, "cwd");
7001
- return host.restoreProject(cwd);
7189
+ const { cwd, dirKey } = params ?? {};
7190
+ if (typeof dirKey === "string" && dirKey) return host.unarchiveProjectByDir(dirKey);
7191
+ return host.restoreProject(requireString(cwd, "cwd"));
7002
7192
  });
7003
7193
  srv.registerMethod("project.archived", () => host.listArchivedProjects());
7004
7194
  srv.registerMethod("project.delete", (params) => {
7005
- const cwd = requireString(params?.cwd, "cwd");
7006
- return host.deleteArchivedProject(cwd);
7195
+ const { cwd, dirKey } = params ?? {};
7196
+ if (typeof dirKey === "string" && dirKey) return host.deleteArchivedProjectByDir(dirKey);
7197
+ return host.deleteArchivedProject(requireString(cwd, "cwd"));
7007
7198
  });
7008
7199
  srv.registerMethod("project.roots", () => {
7009
7200
  const home = homedir2();
7010
7201
  const places = [
7011
7202
  { name: "Home", path: home, isDirectory: true },
7012
- { name: "Root", path: sep2, isDirectory: true }
7203
+ { name: "Root", path: sep3, isDirectory: true }
7013
7204
  ];
7014
7205
  const candidates = ["Desktop", "Downloads", "Documents", "Pictures", "Music", "Videos"];
7015
7206
  for (const name of candidates) {
@@ -7382,6 +7573,10 @@ async function runDaemon() {
7382
7573
  const { sessionId } = params ?? {};
7383
7574
  return host.listForkPoints(requireString(sessionId, "sessionId"));
7384
7575
  });
7576
+ srv.registerMethod("session.fork", async (params) => {
7577
+ const { sessionId, entryId } = params ?? {};
7578
+ return host.forkSession(requireString(sessionId, "sessionId"), requireString(entryId, "entryId"));
7579
+ });
7385
7580
  srv.registerMethod("session.tree", (params) => {
7386
7581
  const { sessionId } = params ?? {};
7387
7582
  return host.getSessionTree(requireString(sessionId, "sessionId"));
@@ -7472,10 +7667,10 @@ async function runDaemon() {
7472
7667
  });
7473
7668
  server = new JsonRpcServer({ send: (frame) => transport.send(frame) });
7474
7669
  registerMethods(server);
7475
- const wsPort = Number(process.env.VAGUS_WS_PORT ?? "19707");
7670
+ const requestedPort = Number(process.env.VAGUS_WS_PORT ?? "19707");
7476
7671
  const wsHost = new WsServerHost();
7477
- wsHost.listen({
7478
- port: wsPort,
7672
+ await wsHost.listen({
7673
+ port: requestedPort,
7479
7674
  registerMethods,
7480
7675
  staticDir: process.env.VAGUS_GUI_DIR
7481
7676
  });
@@ -7484,9 +7679,11 @@ async function runDaemon() {
7484
7679
  wsHost.broadcast(event);
7485
7680
  });
7486
7681
  let shuttingDown = false;
7487
- const shutdown = () => {
7682
+ const shutdown = (signal) => {
7488
7683
  if (shuttingDown) return;
7489
7684
  shuttingDown = true;
7685
+ process.stderr.write(`vagus: daemon shutting down (${signal})
7686
+ `);
7490
7687
  void (async () => {
7491
7688
  try {
7492
7689
  wsHost.close();
@@ -7497,11 +7694,19 @@ async function runDaemon() {
7497
7694
  }
7498
7695
  })();
7499
7696
  };
7500
- process.on("SIGINT", shutdown);
7501
- process.on("SIGTERM", shutdown);
7697
+ process.on("SIGINT", () => shutdown("SIGINT"));
7698
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
7502
7699
  transport.start();
7503
7700
  process.stderr.write(`pi-web daemon ready (state: ${stateDir})
7504
7701
  `);
7702
+ process.on("uncaughtException", (err) => {
7703
+ process.stderr.write(`vagus: uncaught exception: ${err?.stack ?? String(err)}
7704
+ `);
7705
+ });
7706
+ process.on("unhandledRejection", (reason) => {
7707
+ process.stderr.write(`vagus: unhandled rejection: ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}
7708
+ `);
7709
+ });
7505
7710
  return new Promise(() => {
7506
7711
  });
7507
7712
  }
@@ -7513,25 +7718,76 @@ import { join as join9 } from "node:path";
7513
7718
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7514
7719
 
7515
7720
  // apps/cli/src/daemon.ts
7516
- import { spawn } from "node:child_process";
7721
+ import { execSync, spawn } from "node:child_process";
7517
7722
  import { existsSync as existsSync7 } from "node:fs";
7518
7723
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7724
+ function detectSystemProxy() {
7725
+ try {
7726
+ if (process.platform === "win32") {
7727
+ const key = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
7728
+ const out = execSync(`reg query "${key}" /v ProxyEnable`, { encoding: "utf8", timeout: 2e3 });
7729
+ if (!/0x1/.test(out)) return void 0;
7730
+ const srv = execSync(`reg query "${key}" /v ProxyServer`, { encoding: "utf8", timeout: 2e3 });
7731
+ const m = srv.match(/ProxyServer\s+REG_SZ\s+(\S+)/);
7732
+ if (!m?.[1]) return void 0;
7733
+ let addr;
7734
+ if (m[1].includes("=")) {
7735
+ const https = m[1].split(";").find((p) => p.startsWith("https="));
7736
+ addr = (https ?? m[1].split(";")[0])?.split("=")[1] || void 0;
7737
+ } else {
7738
+ addr = m[1];
7739
+ }
7740
+ return addr && /^https?:\/\//.test(addr) ? addr : addr ? `http://${addr}` : void 0;
7741
+ }
7742
+ if (process.platform === "darwin") {
7743
+ const out = execSync("scutil --proxy", { encoding: "utf8", timeout: 2e3 });
7744
+ const enabled = /HTTPSEnable\s*:\s*1/.test(out);
7745
+ const host = out.match(/HTTPSProxy\s*:\s*(\S+)/)?.[1];
7746
+ const port = out.match(/HTTPSPort\s*:\s*(\d+)/)?.[1];
7747
+ return enabled && host && port ? `http://${host}:${port}` : void 0;
7748
+ }
7749
+ } catch {
7750
+ }
7751
+ return void 0;
7752
+ }
7519
7753
  function daemonEntryPath() {
7520
7754
  const base = fileURLToPath2(new URL("./bin", import.meta.url));
7521
7755
  const source = `${base}.ts`;
7522
7756
  return existsSync7(source) ? source : `${base}.js`;
7523
7757
  }
7758
+ function defaultWsPort() {
7759
+ return daemonEntryPath().endsWith(".ts") ? 19708 : 19707;
7760
+ }
7524
7761
  function spawnDaemon(options = {}) {
7525
7762
  const entry = daemonEntryPath();
7526
7763
  const args = entry.endsWith(".ts") ? ["--import", "tsx", entry, "daemon"] : [entry, "daemon"];
7764
+ const injected = {};
7765
+ injected.NODE_USE_ENV_PROXY = "1";
7766
+ if (!process.env.HTTPS_PROXY && !process.env.HTTP_PROXY) {
7767
+ const sysProxy = detectSystemProxy();
7768
+ if (sysProxy) {
7769
+ injected.HTTPS_PROXY = sysProxy;
7770
+ injected.HTTP_PROXY = sysProxy;
7771
+ injected.NO_PROXY = process.env.NO_PROXY ?? "localhost,127.0.0.1";
7772
+ const redacted = sysProxy.replace(/\/\/([^@/]*)@/, "//***@");
7773
+ process.stderr.write(`vagus: following system proxy \u2192 ${redacted}
7774
+ `);
7775
+ } else {
7776
+ process.stderr.write(`vagus: no system proxy detected (direct connections)
7777
+ `);
7778
+ }
7779
+ } else if (process.env.HTTPS_PROXY || process.env.HTTP_PROXY) {
7780
+ process.stderr.write(`vagus: proxy env already set (HTTPS_PROXY=${process.env.HTTPS_PROXY ?? process.env.HTTP_PROXY})
7781
+ `);
7782
+ }
7527
7783
  return spawn(process.execPath, args, {
7528
7784
  stdio: options.stdio ?? ["pipe", "pipe", "pipe"],
7529
- env: { ...process.env, ...options.env }
7785
+ env: { ...process.env, ...injected, ...options.env }
7530
7786
  });
7531
7787
  }
7532
7788
 
7533
7789
  // apps/cli/src/commands/web.ts
7534
- var DEFAULT_WS_PORT = 19707;
7790
+ var DEFAULT_WS_PORT = defaultWsPort();
7535
7791
  function guiDistPath() {
7536
7792
  const prod = fileURLToPath3(new URL("../gui", import.meta.url));
7537
7793
  if (existsSync8(join9(prod, "index.html"))) return prod;
@@ -7579,6 +7835,9 @@ async function runWeb() {
7579
7835
  child.on("exit", (code, signal) => {
7580
7836
  if (!opened && code !== 0) {
7581
7837
  process.stderr.write(`pi-web: daemon exited (code ${code ?? "?"}, signal ${signal ?? "none"}) before the UI was ready.
7838
+ `);
7839
+ } else {
7840
+ process.stderr.write(`pi-web: daemon exited (code ${code ?? "?"}, signal ${signal ?? "none"}).
7582
7841
  `);
7583
7842
  }
7584
7843
  process.exit(code ?? 0);
@@ -7593,6 +7852,9 @@ async function runWeb() {
7593
7852
  }
7594
7853
 
7595
7854
  // apps/cli/src/bin.ts
7855
+ if (!process.env.VAGUS_WS_PORT && existsSync9(new URL("./commands/daemon.ts", import.meta.url))) {
7856
+ process.env.VAGUS_WS_PORT = "19708";
7857
+ }
7596
7858
  var VERSION = JSON.parse(
7597
7859
  readFileSync7(new URL("../package.json", import.meta.url), "utf8")
7598
7860
  ).version;