@versot/vaguspi 0.1.3 → 0.1.4
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
|
@@ -8,7 +8,8 @@ import { parseArgs } from "node:util";
|
|
|
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";
|
|
@@ -1696,9 +1698,17 @@ 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
|
+
process.stderr.write(`vagus: disposing session ${sid} (cwd=${cwd})
|
|
1702
|
+
`);
|
|
1703
|
+
try {
|
|
1704
|
+
session.dispose();
|
|
1705
|
+
} catch (err) {
|
|
1706
|
+
process.stderr.write(`vagus: dispose threw: ${err instanceof Error ? err.stack : String(err)}
|
|
1707
|
+
`);
|
|
1708
|
+
}
|
|
1700
1709
|
this.sessions.delete(sid);
|
|
1701
|
-
void this.options.bus.emit("session.closed", { type: "session.closed", sessionId: sid })
|
|
1710
|
+
void this.options.bus.emit("session.closed", { type: "session.closed", sessionId: sid }).catch(() => {
|
|
1711
|
+
});
|
|
1702
1712
|
}
|
|
1703
1713
|
}
|
|
1704
1714
|
}
|
|
@@ -1760,6 +1770,37 @@ var VagusEngine = class {
|
|
|
1760
1770
|
}
|
|
1761
1771
|
}
|
|
1762
1772
|
/** Restores an archived project: moves its session files back under `sessions/`. */
|
|
1773
|
+
/** Restores an archived project by its encoded dir name (unique identity). */
|
|
1774
|
+
async unarchiveProjectByDir(dirKey) {
|
|
1775
|
+
if (!dirKey || dirKey.includes("/") || dirKey.includes("\\") || dirKey.includes(".."))
|
|
1776
|
+
return;
|
|
1777
|
+
const agentDir = this.agentDirPath();
|
|
1778
|
+
const src = join5(agentDir, "archived", dirKey);
|
|
1779
|
+
if (!existsSync3(src))
|
|
1780
|
+
return;
|
|
1781
|
+
let cwd;
|
|
1782
|
+
try {
|
|
1783
|
+
cwd = this.sessionCwd(readdirSync2(src).filter((f) => f.endsWith(".jsonl"))[0] ? join5(src, readdirSync2(src).filter((f) => f.endsWith(".jsonl"))[0]) : "");
|
|
1784
|
+
} catch {
|
|
1785
|
+
cwd = void 0;
|
|
1786
|
+
}
|
|
1787
|
+
if (cwd) {
|
|
1788
|
+
this.closeSessionForCwd(cwd);
|
|
1789
|
+
const dst = join5(agentDir, "sessions", this.encodeCwd(cwd));
|
|
1790
|
+
mkdirSync4(dst, { recursive: true });
|
|
1791
|
+
for (const entry of readdirSync2(src)) {
|
|
1792
|
+
if (!entry.endsWith(".jsonl"))
|
|
1793
|
+
continue;
|
|
1794
|
+
const from = join5(src, entry);
|
|
1795
|
+
try {
|
|
1796
|
+
if (statSync(from).isFile())
|
|
1797
|
+
renameSync(from, join5(dst, entry));
|
|
1798
|
+
} catch {
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
this.rmDirSafe(src);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1763
1804
|
async restoreProject(cwd) {
|
|
1764
1805
|
const agentDir = this.agentDirPath();
|
|
1765
1806
|
const src = join5(agentDir, "archived", this.encodeCwd(cwd));
|
|
@@ -1778,16 +1819,99 @@ var VagusEngine = class {
|
|
|
1778
1819
|
} catch {
|
|
1779
1820
|
}
|
|
1780
1821
|
}
|
|
1781
|
-
|
|
1822
|
+
this.rmDirSafe(src);
|
|
1823
|
+
}
|
|
1824
|
+
/** Permanently deletes an archived project dir by its encoded dir name.
|
|
1825
|
+
* dirKey (not cwd) is the identity — cwds can repeat across archive dirs. */
|
|
1826
|
+
async deleteArchivedProjectByDir(dirKey) {
|
|
1827
|
+
if (!dirKey || dirKey.includes("/") || dirKey.includes("\\") || dirKey.includes(".."))
|
|
1828
|
+
return;
|
|
1829
|
+
const agentDir = this.agentDirPath();
|
|
1830
|
+
const dir = join5(agentDir, "archived", dirKey);
|
|
1831
|
+
process.stderr.write(`vagus: deleteArchivedProjectByDir dirKey=${dirKey}
|
|
1832
|
+
`);
|
|
1833
|
+
const prefix = dir + sep;
|
|
1834
|
+
for (const [sid, session] of this.sessions) {
|
|
1835
|
+
const file = session.sessionManager?.getSessionFile();
|
|
1836
|
+
if (file && (file === dir || file.startsWith(prefix))) {
|
|
1837
|
+
try {
|
|
1838
|
+
session.dispose();
|
|
1839
|
+
} catch {
|
|
1840
|
+
}
|
|
1841
|
+
this.sessions.delete(sid);
|
|
1842
|
+
void this.options.bus.emit("session.closed", { type: "session.closed", sessionId: sid }).catch(() => {
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
process.stderr.write(`vagus: sessions closed, removing dir...
|
|
1847
|
+
`);
|
|
1848
|
+
this.rmDirSafe(dir);
|
|
1849
|
+
process.stderr.write(`vagus: archived dir removed OK
|
|
1850
|
+
`);
|
|
1782
1851
|
}
|
|
1783
1852
|
/** Permanently deletes an archived project's session dir (JSONL). */
|
|
1784
1853
|
async deleteArchivedProject(cwd) {
|
|
1785
1854
|
const agentDir = this.agentDirPath();
|
|
1786
1855
|
const dir = join5(agentDir, "archived", this.encodeCwd(cwd));
|
|
1856
|
+
process.stderr.write(`vagus: deleteArchivedProject cwd=${cwd} dir=${dir}
|
|
1857
|
+
`);
|
|
1787
1858
|
this.closeSessionForCwd(cwd);
|
|
1788
|
-
|
|
1859
|
+
process.stderr.write(`vagus: sessions closed, removing dir...
|
|
1860
|
+
`);
|
|
1861
|
+
this.rmDirSafe(dir);
|
|
1862
|
+
process.stderr.write(`vagus: archived dir removed OK
|
|
1863
|
+
`);
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* rmSync(recursive) has crashed the daemon natively (0xC0000409 fastfail)
|
|
1867
|
+
* on Windows when deleting archived dirs with non-ASCII names — JS guards
|
|
1868
|
+
* cannot catch a native abort. Delete file-by-file instead: plain unlink
|
|
1869
|
+
* calls are individually catchable and skip whatever is locked/unreadable.
|
|
1870
|
+
*
|
|
1871
|
+
* Iterative (explicit stack, deepest-first) — no JS recursion limit, so
|
|
1872
|
+
* arbitrarily deep trees are safe.
|
|
1873
|
+
*/
|
|
1874
|
+
rmDirSafe(root) {
|
|
1875
|
+
const pendingDirs = [{ path: root, expanded: false }];
|
|
1876
|
+
while (pendingDirs.length > 0) {
|
|
1877
|
+
const top = pendingDirs[pendingDirs.length - 1];
|
|
1878
|
+
if (top.expanded) {
|
|
1879
|
+
pendingDirs.pop();
|
|
1880
|
+
try {
|
|
1881
|
+
rmdirSync(top.path);
|
|
1882
|
+
} catch (err) {
|
|
1883
|
+
process.stderr.write(`vagus: rmDirSafe rmdir failed ${top.path}: ${err instanceof Error ? err.message : String(err)}
|
|
1884
|
+
`);
|
|
1885
|
+
}
|
|
1886
|
+
continue;
|
|
1887
|
+
}
|
|
1888
|
+
let entries;
|
|
1889
|
+
try {
|
|
1890
|
+
entries = readdirSync2(top.path);
|
|
1891
|
+
} catch {
|
|
1892
|
+
pendingDirs.pop();
|
|
1893
|
+
continue;
|
|
1894
|
+
}
|
|
1895
|
+
top.expanded = true;
|
|
1896
|
+
for (const entry of entries) {
|
|
1897
|
+
const full = join5(top.path, entry);
|
|
1898
|
+
try {
|
|
1899
|
+
if (statSync(full).isDirectory()) {
|
|
1900
|
+
pendingDirs.push({ path: full, expanded: false });
|
|
1901
|
+
} else {
|
|
1902
|
+
unlinkSync(full);
|
|
1903
|
+
}
|
|
1904
|
+
} catch (err) {
|
|
1905
|
+
process.stderr.write(`vagus: rmDirSafe skipped ${full}: ${err instanceof Error ? err.message : String(err)}
|
|
1906
|
+
`);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1789
1910
|
}
|
|
1790
1911
|
/** Lists archived projects with their sessions (scans the `archived/` dir). */
|
|
1912
|
+
/** Lists archived projects with their sessions (scans the `archived/` dir).
|
|
1913
|
+
* dirKey is the encoded directory name — the unique identity of an archived
|
|
1914
|
+
* group (cwd from session headers can repeat across dirs). */
|
|
1791
1915
|
async listArchivedProjects() {
|
|
1792
1916
|
const agentDir = this.agentDirPath();
|
|
1793
1917
|
const root = join5(agentDir, "archived");
|
|
@@ -1805,6 +1929,7 @@ var VagusEngine = class {
|
|
|
1805
1929
|
continue;
|
|
1806
1930
|
out.push({
|
|
1807
1931
|
cwd,
|
|
1932
|
+
dirKey: entry,
|
|
1808
1933
|
sessions: infos.map((i) => ({
|
|
1809
1934
|
id: i.id,
|
|
1810
1935
|
path: i.path,
|
|
@@ -1887,6 +2012,54 @@ var VagusEngine = class {
|
|
|
1887
2012
|
const result = await session.navigateTree(targetId, options);
|
|
1888
2013
|
return { editorText: result.editorText, cancelled: result.cancelled };
|
|
1889
2014
|
}
|
|
2015
|
+
/**
|
|
2016
|
+
* Creates a new session forked from a specific user message.
|
|
2017
|
+
* The new session contains only the conversation up to and including that
|
|
2018
|
+
* user message — everything after it is dropped. The original session is
|
|
2019
|
+
* untouched. Returns the new session's info (sessionId, sessionFile, cwd).
|
|
2020
|
+
*/
|
|
2021
|
+
async forkSession(sessionId, entryId) {
|
|
2022
|
+
const session = this.requireSession(sessionId);
|
|
2023
|
+
const sourceFile = session.sessionManager?.getSessionFile();
|
|
2024
|
+
if (!sourceFile)
|
|
2025
|
+
throw new Error("source session has no file path");
|
|
2026
|
+
if (!existsSync3(sourceFile))
|
|
2027
|
+
throw new Error(`source session file not found: ${sourceFile}`);
|
|
2028
|
+
const raw = readFileSync4(sourceFile, "utf8");
|
|
2029
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
2030
|
+
const entries = lines.map((l) => JSON.parse(l));
|
|
2031
|
+
const header = entries[0];
|
|
2032
|
+
if (!header || header.type !== "session")
|
|
2033
|
+
throw new Error("source session has no header");
|
|
2034
|
+
const targetIdx = entries.findIndex((e) => e.id === entryId);
|
|
2035
|
+
if (targetIdx < 0)
|
|
2036
|
+
throw new Error(`entry ${entryId} not found in source session`);
|
|
2037
|
+
let endIdx = targetIdx - 1;
|
|
2038
|
+
if (endIdx < 1)
|
|
2039
|
+
endIdx = 1;
|
|
2040
|
+
const cwd = session.sessionManager.getCwd() || this.options.cwd;
|
|
2041
|
+
const sessionDir = dirname3(sourceFile);
|
|
2042
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
2043
|
+
const newSessionId = randomUUID2();
|
|
2044
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2045
|
+
const fileTimestamp = timestamp.replace(/[:.]/g, "-");
|
|
2046
|
+
const newSessionFile = join5(sessionDir, `${fileTimestamp}_${newSessionId}.jsonl`);
|
|
2047
|
+
const newHeader = {
|
|
2048
|
+
type: "session",
|
|
2049
|
+
version: header.version ?? 1,
|
|
2050
|
+
id: newSessionId,
|
|
2051
|
+
timestamp,
|
|
2052
|
+
cwd,
|
|
2053
|
+
parentSession: sourceFile
|
|
2054
|
+
};
|
|
2055
|
+
const outLines = [JSON.stringify(newHeader)];
|
|
2056
|
+
for (let i = 1; i <= endIdx; i++) {
|
|
2057
|
+
outLines.push(JSON.stringify(entries[i]));
|
|
2058
|
+
}
|
|
2059
|
+
writeFileSync3(newSessionFile, outLines.join("\n") + "\n", "utf8");
|
|
2060
|
+
const manager = SessionManager2.open(newSessionFile);
|
|
2061
|
+
return this.startSession({ cwd, sessionManager: manager });
|
|
2062
|
+
}
|
|
1890
2063
|
/**
|
|
1891
2064
|
* Exports the active branch to JSONL (/export). Returns the output path.
|
|
1892
2065
|
*/
|
|
@@ -6562,7 +6735,7 @@ import { WebSocketServer as WsServer } from "ws";
|
|
|
6562
6735
|
|
|
6563
6736
|
// packages/host/rpc/dist/static-server.js
|
|
6564
6737
|
import { createReadStream, existsSync as existsSync4, statSync as statSync2 } from "node:fs";
|
|
6565
|
-
import { extname, join as join6, resolve as resolve2, sep } from "node:path";
|
|
6738
|
+
import { extname, join as join6, resolve as resolve2, sep as sep2 } from "node:path";
|
|
6566
6739
|
var MIME_TYPES = {
|
|
6567
6740
|
".html": "text/html; charset=utf-8",
|
|
6568
6741
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -6581,7 +6754,7 @@ function resolveStaticFile(rootDir, pathname) {
|
|
|
6581
6754
|
const root = resolve2(rootDir);
|
|
6582
6755
|
const name = pathname === "/" ? "index.html" : pathname;
|
|
6583
6756
|
const candidate = resolve2(join6(root, name));
|
|
6584
|
-
if (candidate !== root && !candidate.startsWith(root +
|
|
6757
|
+
if (candidate !== root && !candidate.startsWith(root + sep2)) {
|
|
6585
6758
|
return void 0;
|
|
6586
6759
|
}
|
|
6587
6760
|
if (existsSync4(candidate)) {
|
|
@@ -6906,6 +7079,28 @@ function requireString(value, label) {
|
|
|
6906
7079
|
}
|
|
6907
7080
|
return value;
|
|
6908
7081
|
}
|
|
7082
|
+
function applyHttpProxy() {
|
|
7083
|
+
try {
|
|
7084
|
+
let proxy;
|
|
7085
|
+
const settingsFile = join8(piAgentDir(), "settings.json");
|
|
7086
|
+
if (existsSync6(settingsFile)) {
|
|
7087
|
+
const s = JSON.parse(readFileSync6(settingsFile, "utf8"));
|
|
7088
|
+
if (typeof s.httpProxy === "string" && s.httpProxy.trim()) proxy = s.httpProxy.trim();
|
|
7089
|
+
}
|
|
7090
|
+
proxy ??= process.env.HTTPS_PROXY ?? process.env.HTTP_PROXY;
|
|
7091
|
+
if (proxy) {
|
|
7092
|
+
const normalized = /^https?:\/\//.test(proxy) ? proxy : `http://${proxy}`;
|
|
7093
|
+
process.env.HTTPS_PROXY = normalized;
|
|
7094
|
+
process.env.HTTP_PROXY = normalized;
|
|
7095
|
+
setGlobalDispatcher(new EnvHttpProxyAgent());
|
|
7096
|
+
process.stderr.write(`vagus: http proxy enabled \u2192 ${normalized}
|
|
7097
|
+
`);
|
|
7098
|
+
}
|
|
7099
|
+
} catch (err) {
|
|
7100
|
+
process.stderr.write(`vagus: proxy setup skipped: ${err instanceof Error ? err.message : String(err)}
|
|
7101
|
+
`);
|
|
7102
|
+
}
|
|
7103
|
+
}
|
|
6909
7104
|
function probeRequestFor(apiType, url, apiKey, modelId) {
|
|
6910
7105
|
const headers = { "Content-Type": "application/json" };
|
|
6911
7106
|
const base = url.replace(/\/chat\/completions$/, "");
|
|
@@ -6939,6 +7134,7 @@ function builtinExtensionPaths() {
|
|
|
6939
7134
|
return out;
|
|
6940
7135
|
}
|
|
6941
7136
|
async function runDaemon() {
|
|
7137
|
+
applyHttpProxy();
|
|
6942
7138
|
const stateDir = defaultStateDir();
|
|
6943
7139
|
mkdirSync6(stateDir, { recursive: true });
|
|
6944
7140
|
const config = new ConfigStore({ dir: stateDir });
|
|
@@ -6997,19 +7193,21 @@ async function runDaemon() {
|
|
|
6997
7193
|
return host.archiveProject(cwd);
|
|
6998
7194
|
});
|
|
6999
7195
|
srv.registerMethod("project.unarchive", (params) => {
|
|
7000
|
-
const cwd =
|
|
7001
|
-
return host.
|
|
7196
|
+
const { cwd, dirKey } = params ?? {};
|
|
7197
|
+
if (typeof dirKey === "string" && dirKey) return host.unarchiveProjectByDir(dirKey);
|
|
7198
|
+
return host.restoreProject(requireString(cwd, "cwd"));
|
|
7002
7199
|
});
|
|
7003
7200
|
srv.registerMethod("project.archived", () => host.listArchivedProjects());
|
|
7004
7201
|
srv.registerMethod("project.delete", (params) => {
|
|
7005
|
-
const cwd =
|
|
7006
|
-
return host.
|
|
7202
|
+
const { cwd, dirKey } = params ?? {};
|
|
7203
|
+
if (typeof dirKey === "string" && dirKey) return host.deleteArchivedProjectByDir(dirKey);
|
|
7204
|
+
return host.deleteArchivedProject(requireString(cwd, "cwd"));
|
|
7007
7205
|
});
|
|
7008
7206
|
srv.registerMethod("project.roots", () => {
|
|
7009
7207
|
const home = homedir2();
|
|
7010
7208
|
const places = [
|
|
7011
7209
|
{ name: "Home", path: home, isDirectory: true },
|
|
7012
|
-
{ name: "Root", path:
|
|
7210
|
+
{ name: "Root", path: sep3, isDirectory: true }
|
|
7013
7211
|
];
|
|
7014
7212
|
const candidates = ["Desktop", "Downloads", "Documents", "Pictures", "Music", "Videos"];
|
|
7015
7213
|
for (const name of candidates) {
|
|
@@ -7382,6 +7580,10 @@ async function runDaemon() {
|
|
|
7382
7580
|
const { sessionId } = params ?? {};
|
|
7383
7581
|
return host.listForkPoints(requireString(sessionId, "sessionId"));
|
|
7384
7582
|
});
|
|
7583
|
+
srv.registerMethod("session.fork", async (params) => {
|
|
7584
|
+
const { sessionId, entryId } = params ?? {};
|
|
7585
|
+
return host.forkSession(requireString(sessionId, "sessionId"), requireString(entryId, "entryId"));
|
|
7586
|
+
});
|
|
7385
7587
|
srv.registerMethod("session.tree", (params) => {
|
|
7386
7588
|
const { sessionId } = params ?? {};
|
|
7387
7589
|
return host.getSessionTree(requireString(sessionId, "sessionId"));
|
|
@@ -7484,9 +7686,12 @@ async function runDaemon() {
|
|
|
7484
7686
|
wsHost.broadcast(event);
|
|
7485
7687
|
});
|
|
7486
7688
|
let shuttingDown = false;
|
|
7487
|
-
const shutdown = () => {
|
|
7689
|
+
const shutdown = (signal) => {
|
|
7488
7690
|
if (shuttingDown) return;
|
|
7489
7691
|
shuttingDown = true;
|
|
7692
|
+
process.stderr.write(`vagus: daemon shutting down (${signal})
|
|
7693
|
+
${new Error("shutdown trace").stack}
|
|
7694
|
+
`);
|
|
7490
7695
|
void (async () => {
|
|
7491
7696
|
try {
|
|
7492
7697
|
wsHost.close();
|
|
@@ -7497,11 +7702,27 @@ async function runDaemon() {
|
|
|
7497
7702
|
}
|
|
7498
7703
|
})();
|
|
7499
7704
|
};
|
|
7500
|
-
process.on("SIGINT", shutdown);
|
|
7501
|
-
process.on("SIGTERM", shutdown);
|
|
7705
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
7706
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
7707
|
+
process.on("beforeExit", (code) => {
|
|
7708
|
+
process.stderr.write(`vagus: daemon beforeExit (event loop empty), code=${code}
|
|
7709
|
+
`);
|
|
7710
|
+
});
|
|
7711
|
+
process.on("exit", (code) => {
|
|
7712
|
+
process.stderr.write(`vagus: daemon exit, code=${code}
|
|
7713
|
+
`);
|
|
7714
|
+
});
|
|
7502
7715
|
transport.start();
|
|
7503
7716
|
process.stderr.write(`pi-web daemon ready (state: ${stateDir})
|
|
7504
7717
|
`);
|
|
7718
|
+
process.on("uncaughtException", (err) => {
|
|
7719
|
+
process.stderr.write(`vagus: uncaught exception: ${err?.stack ?? String(err)}
|
|
7720
|
+
`);
|
|
7721
|
+
});
|
|
7722
|
+
process.on("unhandledRejection", (reason) => {
|
|
7723
|
+
process.stderr.write(`vagus: unhandled rejection: ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}
|
|
7724
|
+
`);
|
|
7725
|
+
});
|
|
7505
7726
|
return new Promise(() => {
|
|
7506
7727
|
});
|
|
7507
7728
|
}
|
|
@@ -7514,8 +7735,38 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
7514
7735
|
|
|
7515
7736
|
// apps/cli/src/daemon.ts
|
|
7516
7737
|
import { spawn } from "node:child_process";
|
|
7738
|
+
import { execSync } from "node:child_process";
|
|
7517
7739
|
import { existsSync as existsSync7 } from "node:fs";
|
|
7518
7740
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7741
|
+
function detectSystemProxy() {
|
|
7742
|
+
try {
|
|
7743
|
+
if (process.platform === "win32") {
|
|
7744
|
+
const key = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
|
|
7745
|
+
const out = execSync(`reg query "${key}" /v ProxyEnable`, { encoding: "utf8", timeout: 2e3 });
|
|
7746
|
+
if (!/0x1/.test(out)) return void 0;
|
|
7747
|
+
const srv = execSync(`reg query "${key}" /v ProxyServer`, { encoding: "utf8", timeout: 2e3 });
|
|
7748
|
+
const m = srv.match(/ProxyServer\s+REG_SZ\s+(\S+)/);
|
|
7749
|
+
if (!m?.[1]) return void 0;
|
|
7750
|
+
let addr;
|
|
7751
|
+
if (m[1].includes("=")) {
|
|
7752
|
+
const https = m[1].split(";").find((p) => p.startsWith("https="));
|
|
7753
|
+
addr = (https ?? m[1].split(";")[0])?.split("=")[1] || void 0;
|
|
7754
|
+
} else {
|
|
7755
|
+
addr = m[1];
|
|
7756
|
+
}
|
|
7757
|
+
return addr && /^https?:\/\//.test(addr) ? addr : addr ? `http://${addr}` : void 0;
|
|
7758
|
+
}
|
|
7759
|
+
if (process.platform === "darwin") {
|
|
7760
|
+
const out = execSync("scutil --proxy", { encoding: "utf8", timeout: 2e3 });
|
|
7761
|
+
const enabled = /HTTPSEnable\s*:\s*1/.test(out);
|
|
7762
|
+
const host = out.match(/HTTPSProxy\s*:\s*(\S+)/)?.[1];
|
|
7763
|
+
const port = out.match(/HTTPSPort\s*:\s*(\d+)/)?.[1];
|
|
7764
|
+
return enabled && host && port ? `http://${host}:${port}` : void 0;
|
|
7765
|
+
}
|
|
7766
|
+
} catch {
|
|
7767
|
+
}
|
|
7768
|
+
return void 0;
|
|
7769
|
+
}
|
|
7519
7770
|
function daemonEntryPath() {
|
|
7520
7771
|
const base = fileURLToPath2(new URL("./bin", import.meta.url));
|
|
7521
7772
|
const source = `${base}.ts`;
|
|
@@ -7524,9 +7775,27 @@ function daemonEntryPath() {
|
|
|
7524
7775
|
function spawnDaemon(options = {}) {
|
|
7525
7776
|
const entry = daemonEntryPath();
|
|
7526
7777
|
const args = entry.endsWith(".ts") ? ["--import", "tsx", entry, "daemon"] : [entry, "daemon"];
|
|
7778
|
+
const injected = {};
|
|
7779
|
+
injected.NODE_USE_ENV_PROXY = "1";
|
|
7780
|
+
if (!process.env.HTTPS_PROXY && !process.env.HTTP_PROXY) {
|
|
7781
|
+
const sysProxy = detectSystemProxy();
|
|
7782
|
+
if (sysProxy) {
|
|
7783
|
+
injected.HTTPS_PROXY = sysProxy;
|
|
7784
|
+
injected.HTTP_PROXY = sysProxy;
|
|
7785
|
+
injected.NO_PROXY = process.env.NO_PROXY ?? "localhost,127.0.0.1";
|
|
7786
|
+
process.stderr.write(`vagus: following system proxy \u2192 ${sysProxy}
|
|
7787
|
+
`);
|
|
7788
|
+
} else {
|
|
7789
|
+
process.stderr.write(`vagus: no system proxy detected (direct connections)
|
|
7790
|
+
`);
|
|
7791
|
+
}
|
|
7792
|
+
} else if (process.env.HTTPS_PROXY || process.env.HTTP_PROXY) {
|
|
7793
|
+
process.stderr.write(`vagus: proxy env already set (HTTPS_PROXY=${process.env.HTTPS_PROXY ?? process.env.HTTP_PROXY})
|
|
7794
|
+
`);
|
|
7795
|
+
}
|
|
7527
7796
|
return spawn(process.execPath, args, {
|
|
7528
7797
|
stdio: options.stdio ?? ["pipe", "pipe", "pipe"],
|
|
7529
|
-
env: { ...process.env, ...options.env }
|
|
7798
|
+
env: { ...process.env, ...injected, ...options.env }
|
|
7530
7799
|
});
|
|
7531
7800
|
}
|
|
7532
7801
|
|
|
@@ -7579,6 +7848,9 @@ async function runWeb() {
|
|
|
7579
7848
|
child.on("exit", (code, signal) => {
|
|
7580
7849
|
if (!opened && code !== 0) {
|
|
7581
7850
|
process.stderr.write(`pi-web: daemon exited (code ${code ?? "?"}, signal ${signal ?? "none"}) before the UI was ready.
|
|
7851
|
+
`);
|
|
7852
|
+
} else {
|
|
7853
|
+
process.stderr.write(`pi-web: daemon exited (code ${code ?? "?"}, signal ${signal ?? "none"}).
|
|
7582
7854
|
`);
|
|
7583
7855
|
}
|
|
7584
7856
|
process.exit(code ?? 0);
|