@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 +288 -26
- package/dist/bin.js.map +3 -3
- package/gui/assets/index-DL6d29Yn.js +19 -0
- package/gui/assets/settings-B-1r6vjG.js +60 -0
- package/gui/assets/vendor-D5Ls3_dM.js +41 -0
- package/gui/index.html +7 -2
- package/package.json +1 -1
- package/gui/assets/index-BF1Vavki.js +0 -18
- package/gui/assets/settings-CkJ4eBhw.js +0 -60
- package/gui/assets/vendor-CkEpMl6t.js +0 -40
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
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 +
|
|
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 =
|
|
7001
|
-
return host.
|
|
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 =
|
|
7006
|
-
return host.
|
|
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:
|
|
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
|
|
7670
|
+
const requestedPort = Number(process.env.VAGUS_WS_PORT ?? "19707");
|
|
7476
7671
|
const wsHost = new WsServerHost();
|
|
7477
|
-
wsHost.listen({
|
|
7478
|
-
port:
|
|
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 =
|
|
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;
|