agent-yes 1.115.0 → 1.116.0
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/{SUPPORTED_CLIS-VayLM5qX.js → SUPPORTED_CLIS-DuCEAzP9.js} +2 -2
- package/dist/SUPPORTED_CLIS-vFwB3bnK.js +8 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +2 -2
- package/dist/{serve-CP61tKuJ.js → serve-CSupgu7Q.js} +97 -11
- package/dist/{subcommands-SOHKtDbk.js → subcommands-CxHjPXEH.js} +1 -1
- package/dist/{subcommands-t1uOb17r.js → subcommands-DLJqD_Yj.js} +2 -2
- package/dist/{ts-B62nAAfY.js → ts-DvnOmOAf.js} +5 -6
- package/dist/{versionChecker-BdkE7S2A.js → versionChecker-DeXuTfJ0.js} +2 -2
- package/lab/ui/index.html +28 -23
- package/package.json +1 -1
- package/ts/serve.ts +116 -7
- package/dist/SUPPORTED_CLIS-DUVB1HyL.js +0 -8
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { t as CLIS_CONFIG } from "./ts-DvnOmOAf.js";
|
|
2
2
|
|
|
3
3
|
//#region ts/SUPPORTED_CLIS.ts
|
|
4
4
|
const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
|
|
5
5
|
|
|
6
6
|
//#endregion
|
|
7
7
|
export { SUPPORTED_CLIS as t };
|
|
8
|
-
//# sourceMappingURL=SUPPORTED_CLIS-
|
|
8
|
+
//# sourceMappingURL=SUPPORTED_CLIS-DuCEAzP9.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./ts-DvnOmOAf.js";
|
|
2
|
+
import "./logger-B9h0djqx.js";
|
|
3
|
+
import "./versionChecker-DeXuTfJ0.js";
|
|
4
|
+
import "./pidStore-DBjlqzo8.js";
|
|
5
|
+
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DuCEAzP9.js";
|
|
7
|
+
|
|
8
|
+
export { SUPPORTED_CLIS };
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { n as logger } from "./logger-B9h0djqx.js";
|
|
3
|
-
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-
|
|
3
|
+
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-DeXuTfJ0.js";
|
|
4
4
|
import { argv } from "process";
|
|
5
5
|
import { execFileSync, spawn } from "child_process";
|
|
6
6
|
import ms from "ms";
|
|
@@ -482,7 +482,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
482
482
|
{
|
|
483
483
|
const rawArg = process.argv[2];
|
|
484
484
|
const isHelpFlag = rawArg === "-h" || rawArg === "--help";
|
|
485
|
-
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-
|
|
485
|
+
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-CxHjPXEH.js");
|
|
486
486
|
if (isHelpFlag && process.argv.length === 3) {
|
|
487
487
|
cmdHelp();
|
|
488
488
|
process.exit(0);
|
|
@@ -515,7 +515,7 @@ if (config.useRust) {
|
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
517
|
if (rustBinary) {
|
|
518
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
518
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-vFwB3bnK.js");
|
|
519
519
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
520
520
|
if (config.verbose) {
|
|
521
521
|
console.log(`[rust] Using binary: ${rustBinary}`);
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-DvnOmOAf.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-DeXuTfJ0.js";
|
|
4
4
|
import "./pidStore-DBjlqzo8.js";
|
|
5
5
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
6
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-DvnOmOAf.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import { r as getInstalledPackage } from "./versionChecker-DeXuTfJ0.js";
|
|
4
4
|
import "./pidStore-DBjlqzo8.js";
|
|
5
5
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DuCEAzP9.js";
|
|
7
7
|
import "./remotes-C3xPRtfg.js";
|
|
8
|
-
import { c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, m as snapshotStatus, r as controlCodeFromName, u as readNotes } from "./subcommands-
|
|
8
|
+
import { c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, m as snapshotStatus, r as controlCodeFromName, u as readNotes } from "./subcommands-DLJqD_Yj.js";
|
|
9
9
|
import yargs from "yargs";
|
|
10
10
|
import { mkdir, open, readFile, writeFile } from "fs/promises";
|
|
11
11
|
import { homedir, hostname, userInfo } from "os";
|
|
@@ -52,6 +52,68 @@ const defaultOpts = (overrides = {}) => ({
|
|
|
52
52
|
...overrides
|
|
53
53
|
});
|
|
54
54
|
const DAEMON_NAME = "agent-yes";
|
|
55
|
+
async function ensureBootAutostart(oxmgrBin) {
|
|
56
|
+
try {
|
|
57
|
+
return await Bun.spawn([
|
|
58
|
+
oxmgrBin,
|
|
59
|
+
"service",
|
|
60
|
+
"install"
|
|
61
|
+
], { stdio: [
|
|
62
|
+
"ignore",
|
|
63
|
+
"ignore",
|
|
64
|
+
"ignore"
|
|
65
|
+
] }).exited === 0;
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function spawnExit(cmd) {
|
|
71
|
+
try {
|
|
72
|
+
return await Bun.spawn(cmd, { stdio: [
|
|
73
|
+
"ignore",
|
|
74
|
+
"ignore",
|
|
75
|
+
"ignore"
|
|
76
|
+
] }).exited ?? 1;
|
|
77
|
+
} catch {
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function readDaemonServeArgs(oxmgrBin) {
|
|
82
|
+
try {
|
|
83
|
+
const p = Bun.spawn([
|
|
84
|
+
oxmgrBin,
|
|
85
|
+
"status",
|
|
86
|
+
DAEMON_NAME
|
|
87
|
+
], {
|
|
88
|
+
stdout: "pipe",
|
|
89
|
+
stderr: "ignore"
|
|
90
|
+
});
|
|
91
|
+
const out = await new Response(p.stdout).text();
|
|
92
|
+
if (await p.exited !== 0) return null;
|
|
93
|
+
const m = /Command:\s*(.+)/.exec(out);
|
|
94
|
+
if (!m) return null;
|
|
95
|
+
const after = /\bserve\b\s*(.*)$/.exec(m[1].trim());
|
|
96
|
+
return after ? after[1].split(/\s+/).filter(Boolean) : [];
|
|
97
|
+
} catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function portFromArgs(args) {
|
|
102
|
+
const m = /--port[=\s](\d+)/.exec(args.join(" "));
|
|
103
|
+
return m ? Number(m[1]) : DEFAULT_PORT;
|
|
104
|
+
}
|
|
105
|
+
async function fetchDaemonVersion(port, token) {
|
|
106
|
+
try {
|
|
107
|
+
const r = await fetch(`http://127.0.0.1:${port}/api/version`, {
|
|
108
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
109
|
+
signal: AbortSignal.timeout(3e3)
|
|
110
|
+
});
|
|
111
|
+
if (!r.ok) return null;
|
|
112
|
+
return (await r.json()).version ?? null;
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
55
117
|
async function cmdServeDaemon(sub, args) {
|
|
56
118
|
const oxmgrBin = Bun.which("oxmgr");
|
|
57
119
|
if (!oxmgrBin) {
|
|
@@ -60,11 +122,33 @@ async function cmdServeDaemon(sub, args) {
|
|
|
60
122
|
}
|
|
61
123
|
if (sub === "install") {
|
|
62
124
|
const token = await loadOrCreateToken(void 0);
|
|
125
|
+
const priorArgs = await readDaemonServeArgs(oxmgrBin);
|
|
126
|
+
const effArgs = args.length ? args : priorArgs ?? [];
|
|
127
|
+
const current = getInstalledPackage().version;
|
|
128
|
+
if (priorArgs !== null) {
|
|
129
|
+
const runningVer = await fetchDaemonVersion(portFromArgs(effArgs), token);
|
|
130
|
+
if (runningVer === current) {
|
|
131
|
+
await ensureBootAutostart(oxmgrBin);
|
|
132
|
+
process.stdout.write(`'${DAEMON_NAME}' already running v${current} (up to date)\n`);
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
process.stdout.write(`rolling '${DAEMON_NAME}' ${runningVer ? `v${runningVer}` : "(unknown)"} → v${current}…\n`);
|
|
136
|
+
await spawnExit([
|
|
137
|
+
oxmgrBin,
|
|
138
|
+
"stop",
|
|
139
|
+
DAEMON_NAME
|
|
140
|
+
]);
|
|
141
|
+
await spawnExit([
|
|
142
|
+
oxmgrBin,
|
|
143
|
+
"delete",
|
|
144
|
+
DAEMON_NAME
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
63
147
|
const ayBin = Bun.which("ay");
|
|
64
148
|
const serveCmd = [
|
|
65
149
|
...ayBin ? [process.execPath, ayBin] : ["ay"],
|
|
66
150
|
"serve",
|
|
67
|
-
...
|
|
151
|
+
...effArgs
|
|
68
152
|
].join(" ");
|
|
69
153
|
const code = await Bun.spawn([
|
|
70
154
|
oxmgrBin,
|
|
@@ -80,11 +164,12 @@ async function cmdServeDaemon(sub, args) {
|
|
|
80
164
|
"inherit"
|
|
81
165
|
] }).exited;
|
|
82
166
|
if (code === 0) {
|
|
83
|
-
const
|
|
84
|
-
const port =
|
|
85
|
-
const webrtcish =
|
|
86
|
-
const httpish =
|
|
87
|
-
process.stdout.write(`\
|
|
167
|
+
const onBoot = await ensureBootAutostart(oxmgrBin);
|
|
168
|
+
const port = portFromArgs(effArgs);
|
|
169
|
+
const webrtcish = effArgs.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
|
|
170
|
+
const httpish = effArgs.some((a) => a.startsWith("--http") || a.startsWith("--share")) || !effArgs.some((a) => a.startsWith("--webrtc"));
|
|
171
|
+
process.stdout.write(`\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via oxmgr —`} v${current}\n`);
|
|
172
|
+
process.stdout.write(onBoot ? `start-on-boot: enabled (oxmgr registered with the system init)\n` : `start-on-boot: not registered — run \`oxmgr service install\` to enable\n`);
|
|
88
173
|
process.stdout.write(`token: ${token}\n\n`);
|
|
89
174
|
if (httpish) {
|
|
90
175
|
process.stdout.write(` ay ls ${token}@<host>:${port}\n`);
|
|
@@ -372,6 +457,7 @@ Options:
|
|
|
372
457
|
const host = hostname();
|
|
373
458
|
return Response.json({ host: user ? `${user}@${host}` : host });
|
|
374
459
|
}
|
|
460
|
+
if (req.method === "GET" && p === "/api/version") return Response.json({ version: getInstalledPackage().version });
|
|
375
461
|
if (req.method === "GET" && p === "/api/notes") {
|
|
376
462
|
const notes = await readNotes();
|
|
377
463
|
return Response.json(Object.fromEntries(notes));
|
|
@@ -687,4 +773,4 @@ Options:
|
|
|
687
773
|
|
|
688
774
|
//#endregion
|
|
689
775
|
export { cmdServe };
|
|
690
|
-
//# sourceMappingURL=serve-
|
|
776
|
+
//# sourceMappingURL=serve-CSupgu7Q.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./logger-B9h0djqx.js";
|
|
2
2
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
3
3
|
import "./remotes-C3xPRtfg.js";
|
|
4
|
-
import { a as finalizedLines, c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, h as stopTipForCli, i as cursorAbs, l as matchKeyword, m as snapshotStatus, n as cmdHelp, o as isPidAlive, p as runSubcommand, r as controlCodeFromName, s as isSubcommand, t as GRACEFUL_EXIT_COMMANDS, u as readNotes } from "./subcommands-
|
|
4
|
+
import { a as finalizedLines, c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, h as stopTipForCli, i as cursorAbs, l as matchKeyword, m as snapshotStatus, n as cmdHelp, o as isPidAlive, p as runSubcommand, r as controlCodeFromName, s as isSubcommand, t as GRACEFUL_EXIT_COMMANDS, u as readNotes } from "./subcommands-DLJqD_Yj.js";
|
|
5
5
|
|
|
6
6
|
export { cmdHelp, isSubcommand, runSubcommand };
|
|
@@ -163,7 +163,7 @@ async function runSubcommand(argv) {
|
|
|
163
163
|
case "restart": return await cmdRestart(rest);
|
|
164
164
|
case "note": return await cmdNote(rest);
|
|
165
165
|
case "serve": {
|
|
166
|
-
const { cmdServe } = await import("./serve-
|
|
166
|
+
const { cmdServe } = await import("./serve-CSupgu7Q.js");
|
|
167
167
|
return cmdServe(rest);
|
|
168
168
|
}
|
|
169
169
|
case "setup": {
|
|
@@ -1595,4 +1595,4 @@ async function cmdStatus(rest) {
|
|
|
1595
1595
|
|
|
1596
1596
|
//#endregion
|
|
1597
1597
|
export { finalizedLines as a, listRecords as c, renderRawLog as d, resolveOne as f, writeToIpc as g, stopTipForCli as h, cursorAbs as i, matchKeyword as l, snapshotStatus as m, cmdHelp as n, isPidAlive as o, runSubcommand as p, controlCodeFromName as r, isSubcommand as s, GRACEFUL_EXIT_COMMANDS as t, readNotes as u };
|
|
1598
|
-
//# sourceMappingURL=subcommands-
|
|
1598
|
+
//# sourceMappingURL=subcommands-DLJqD_Yj.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
|
|
2
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
2
|
+
import { r as getInstalledPackage } from "./versionChecker-DeXuTfJ0.js";
|
|
3
3
|
import { n as agentYesHome, t as PidStore } from "./pidStore-DBjlqzo8.js";
|
|
4
4
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CJxsoGdb.js";
|
|
5
5
|
import { i as readGlobalPids } from "./globalPidIndex-yVd3mbsV.js";
|
|
@@ -10,7 +10,7 @@ import { mkdir, readFile, readdir, unlink, writeFile } from "fs/promises";
|
|
|
10
10
|
import { homedir } from "os";
|
|
11
11
|
import path, { dirname, join } from "path";
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
|
-
import { execaCommandSync, parseCommandString } from "execa";
|
|
13
|
+
import { execaCommandSync, execaSync, parseCommandString } from "execa";
|
|
14
14
|
import { fromReadable, fromWritable } from "from-node-stream";
|
|
15
15
|
import DIE from "phpdie";
|
|
16
16
|
import sflow from "sflow";
|
|
@@ -378,11 +378,10 @@ async function createLinuxFifo(cli, customPath) {
|
|
|
378
378
|
logger.warn(`[${cli}-yes] Failed to create FIFO directory: ${dirError}`);
|
|
379
379
|
return null;
|
|
380
380
|
}
|
|
381
|
-
const
|
|
382
|
-
const mkfifoResult = execaCommandSync(`mkfifo '${escapedPath}'`, { reject: false });
|
|
381
|
+
const mkfifoResult = execaSync("mkfifo", [fifoPath], { reject: false });
|
|
383
382
|
if (mkfifoResult.exitCode !== 0) {
|
|
384
383
|
logger.warn(`[${cli}-yes] mkfifo command failed with exit code ${mkfifoResult.exitCode}`);
|
|
385
|
-
logger.warn(`[${cli}-yes] Command: mkfifo
|
|
384
|
+
logger.warn(`[${cli}-yes] Command: mkfifo ${fifoPath}`);
|
|
386
385
|
if (mkfifoResult.stderr) logger.warn(`[${cli}-yes] mkfifo stderr: ${mkfifoResult.stderr}`);
|
|
387
386
|
if (mkfifoResult.stdout) logger.warn(`[${cli}-yes] mkfifo stdout: ${mkfifoResult.stdout}`);
|
|
388
387
|
return null;
|
|
@@ -1714,4 +1713,4 @@ function sleep(ms) {
|
|
|
1714
1713
|
|
|
1715
1714
|
//#endregion
|
|
1716
1715
|
export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
|
|
1717
|
-
//# sourceMappingURL=ts-
|
|
1716
|
+
//# sourceMappingURL=ts-DvnOmOAf.js.map
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
|
|
|
7
7
|
|
|
8
8
|
//#region package.json
|
|
9
9
|
var name = "agent-yes";
|
|
10
|
-
var version = "1.
|
|
10
|
+
var version = "1.116.0";
|
|
11
11
|
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region ts/versionChecker.ts
|
|
@@ -221,4 +221,4 @@ async function displayVersion() {
|
|
|
221
221
|
|
|
222
222
|
//#endregion
|
|
223
223
|
export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
|
|
224
|
-
//# sourceMappingURL=versionChecker-
|
|
224
|
+
//# sourceMappingURL=versionChecker-DeXuTfJ0.js.map
|
package/lab/ui/index.html
CHANGED
|
@@ -1618,10 +1618,11 @@
|
|
|
1618
1618
|
});
|
|
1619
1619
|
// Adapt: drive the agent's PTY to the browser terminal size (POST
|
|
1620
1620
|
// /api/resize → winsize + SIGWINCH) so its TUI reflows to match what we
|
|
1621
|
-
// render. Suppressed
|
|
1622
|
-
|
|
1621
|
+
// render. Suppressed during the initial fit below, which does its own
|
|
1622
|
+
// one-shot "push only if the agent is out of sync" check.
|
|
1623
|
+
let suppressPush = true;
|
|
1623
1624
|
const pushSize = () => {
|
|
1624
|
-
if (term && sel === e._key && !
|
|
1625
|
+
if (term && sel === e._key && !suppressPush)
|
|
1625
1626
|
tx.post("/api/resize/" + encodeURIComponent(pid), {
|
|
1626
1627
|
cols: term.cols,
|
|
1627
1628
|
rows: term.rows,
|
|
@@ -1642,28 +1643,32 @@
|
|
|
1642
1643
|
};
|
|
1643
1644
|
term.onData(fwd);
|
|
1644
1645
|
term.onBinary(fwd);
|
|
1645
|
-
//
|
|
1646
|
-
//
|
|
1647
|
-
//
|
|
1646
|
+
// On switch, render at OUR pane size and make the agent match it: fit
|
|
1647
|
+
// xterm to the viewport, then — if the agent's PTY winsize differs from
|
|
1648
|
+
// what we're now rendering — push a resize so its TUI reflows to fill the
|
|
1649
|
+
// pane. The agent may have been sized by a different viewer/terminal, or
|
|
1650
|
+
// our window changed while this agent was in the background; either way a
|
|
1651
|
+
// mismatch leaves the TUI clipped or boxed-in until something nudges it.
|
|
1652
|
+
// Only push when it actually differs, so an already-matching agent isn't
|
|
1653
|
+
// poked with a redundant SIGWINCH. After this, live window resizes push
|
|
1654
|
+
// automatically (suppressPush flips off).
|
|
1648
1655
|
const selKey = e._key;
|
|
1656
|
+
const fitAndSync = (sz) => {
|
|
1657
|
+
if (sel !== selKey || !term) return;
|
|
1658
|
+
try {
|
|
1659
|
+
fit.fit();
|
|
1660
|
+
} catch {}
|
|
1661
|
+
if (!sz || sz.cols !== term.cols || sz.rows !== term.rows) {
|
|
1662
|
+
tx.post("/api/resize/" + encodeURIComponent(pid), {
|
|
1663
|
+
cols: term.cols,
|
|
1664
|
+
rows: term.rows,
|
|
1665
|
+
}).catch(() => {});
|
|
1666
|
+
}
|
|
1667
|
+
suppressPush = false;
|
|
1668
|
+
};
|
|
1649
1669
|
tx.fetchJSON("/api/size/" + encodeURIComponent(pid))
|
|
1650
|
-
.then(
|
|
1651
|
-
|
|
1652
|
-
if (sz && sz.cols && sz.rows) {
|
|
1653
|
-
adoptingAgentSize = true;
|
|
1654
|
-
term.resize(sz.cols, sz.rows);
|
|
1655
|
-
adoptingAgentSize = false;
|
|
1656
|
-
} else {
|
|
1657
|
-
try {
|
|
1658
|
-
fit.fit();
|
|
1659
|
-
} catch {}
|
|
1660
|
-
}
|
|
1661
|
-
})
|
|
1662
|
-
.catch(() => {
|
|
1663
|
-
try {
|
|
1664
|
-
fit.fit();
|
|
1665
|
-
} catch {}
|
|
1666
|
-
});
|
|
1670
|
+
.then(fitAndSync)
|
|
1671
|
+
.catch(() => fitAndSync(null));
|
|
1667
1672
|
|
|
1668
1673
|
// True live tail via ay serve's SSE stream. First event is an xterm-rendered
|
|
1669
1674
|
// tail snapshot; later events are incremental deltas. We normalise terminal
|
package/package.json
CHANGED
package/ts/serve.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type CommonOpts,
|
|
17
17
|
} from "./subcommands.ts";
|
|
18
18
|
import { SUPPORTED_CLIS } from "./SUPPORTED_CLIS.ts";
|
|
19
|
+
import { getInstalledPackage } from "./versionChecker.ts";
|
|
19
20
|
|
|
20
21
|
const DEFAULT_PORT = 7432;
|
|
21
22
|
|
|
@@ -70,6 +71,71 @@ const defaultOpts = (overrides: Partial<CommonOpts> = {}): CommonOpts => ({
|
|
|
70
71
|
|
|
71
72
|
const DAEMON_NAME = "agent-yes";
|
|
72
73
|
|
|
74
|
+
// Register the oxmgr daemon with the platform init system (launchd on macOS,
|
|
75
|
+
// systemd on Linux, Task Scheduler on Windows) so managed processes — including
|
|
76
|
+
// the agent-yes daemon — come back after a *reboot*, not just a crash. Idempotent:
|
|
77
|
+
// a no-op if the service is already installed. Best-effort: returns false on any
|
|
78
|
+
// failure (e.g. a system-level systemd unit that needs sudo) without aborting the
|
|
79
|
+
// install — the process is still managed, just not boot-persistent.
|
|
80
|
+
async function ensureBootAutostart(oxmgrBin: string): Promise<boolean> {
|
|
81
|
+
try {
|
|
82
|
+
// --system defaults to "auto" (launchd/systemd/Task Scheduler by platform);
|
|
83
|
+
// it's a `service`-level flag, so passing it after `install` is rejected.
|
|
84
|
+
const svc = Bun.spawn([oxmgrBin, "service", "install"], {
|
|
85
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
86
|
+
});
|
|
87
|
+
return (await svc.exited) === 0;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function spawnExit(cmd: string[]): Promise<number> {
|
|
94
|
+
try {
|
|
95
|
+
return (await Bun.spawn(cmd, { stdio: ["ignore", "ignore", "ignore"] }).exited) ?? 1;
|
|
96
|
+
} catch {
|
|
97
|
+
return 1;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// The `serve` args the running daemon was started with, parsed out of oxmgr's
|
|
102
|
+
// stored command line (`… ay serve --share --port 7433`). null when no daemon is
|
|
103
|
+
// registered. Lets a bare `ay serve install` re-launch with the SAME args.
|
|
104
|
+
async function readDaemonServeArgs(oxmgrBin: string): Promise<string[] | null> {
|
|
105
|
+
try {
|
|
106
|
+
const p = Bun.spawn([oxmgrBin, "status", DAEMON_NAME], { stdout: "pipe", stderr: "ignore" });
|
|
107
|
+
const out = await new Response(p.stdout).text();
|
|
108
|
+
if ((await p.exited) !== 0) return null;
|
|
109
|
+
const m = /Command:\s*(.+)/.exec(out);
|
|
110
|
+
if (!m) return null;
|
|
111
|
+
const after = /\bserve\b\s*(.*)$/.exec(m[1]!.trim());
|
|
112
|
+
return after ? after[1]!.split(/\s+/).filter(Boolean) : [];
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function portFromArgs(args: string[]): number {
|
|
119
|
+
const m = /--port[=\s](\d+)/.exec(args.join(" "));
|
|
120
|
+
return m ? Number(m[1]) : DEFAULT_PORT;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Ask the live daemon its version over the local HTTP API. null if it's not
|
|
124
|
+
// listening (webrtc-only) or too old to expose /api/version — both of which we
|
|
125
|
+
// treat as "outdated" so a re-install rolls it forward.
|
|
126
|
+
async function fetchDaemonVersion(port: number, token: string): Promise<string | null> {
|
|
127
|
+
try {
|
|
128
|
+
const r = await fetch(`http://127.0.0.1:${port}/api/version`, {
|
|
129
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
130
|
+
signal: AbortSignal.timeout(3000),
|
|
131
|
+
});
|
|
132
|
+
if (!r.ok) return null;
|
|
133
|
+
return ((await r.json()) as { version?: string }).version ?? null;
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
73
139
|
async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
74
140
|
const oxmgrBin = Bun.which("oxmgr");
|
|
75
141
|
if (!oxmgrBin) {
|
|
@@ -83,25 +149,61 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
|
83
149
|
|
|
84
150
|
if (sub === "install") {
|
|
85
151
|
const token = await loadOrCreateToken(undefined);
|
|
152
|
+
|
|
153
|
+
// Re-running install rolls a stale daemon forward: reuse the args it was
|
|
154
|
+
// started with (so a bare `ay serve install` stays "the same daemon"), unless
|
|
155
|
+
// new args are given. The persisted room + token mean the share link is
|
|
156
|
+
// unchanged across the restart.
|
|
157
|
+
const priorArgs = await readDaemonServeArgs(oxmgrBin);
|
|
158
|
+
const effArgs = args.length ? args : (priorArgs ?? []);
|
|
159
|
+
const current = getInstalledPackage().version;
|
|
160
|
+
|
|
161
|
+
if (priorArgs !== null) {
|
|
162
|
+
// A daemon already exists — only disturb it if it's actually outdated.
|
|
163
|
+
const runningVer = await fetchDaemonVersion(portFromArgs(effArgs), token);
|
|
164
|
+
if (runningVer === current) {
|
|
165
|
+
await ensureBootAutostart(oxmgrBin);
|
|
166
|
+
process.stdout.write(`'${DAEMON_NAME}' already running v${current} (up to date)\n`);
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
// Outdated (or unreachable/too-old to report) → graceful roll-forward.
|
|
170
|
+
// `oxmgr stop` sends SIGTERM, which cmdServe handles cleanly (closing share
|
|
171
|
+
// peers so browsers reconnect fast), then we re-create with the new binary.
|
|
172
|
+
process.stdout.write(
|
|
173
|
+
`rolling '${DAEMON_NAME}' ${runningVer ? `v${runningVer}` : "(unknown)"} → v${current}…\n`,
|
|
174
|
+
);
|
|
175
|
+
await spawnExit([oxmgrBin, "stop", DAEMON_NAME]);
|
|
176
|
+
await spawnExit([oxmgrBin, "delete", DAEMON_NAME]);
|
|
177
|
+
}
|
|
178
|
+
|
|
86
179
|
// Build the ay serve command with forwarded args (port, host, --webrtc, etc.).
|
|
87
180
|
// Absolute paths: oxmgr's daemon environment may not have ~/.bun/bin in
|
|
88
181
|
// PATH, so a bare `ay` (or its `#!/usr/bin/env bun` shebang) fails to spawn.
|
|
89
182
|
const ayBin = Bun.which("ay");
|
|
90
|
-
const serveCmd = [...(ayBin ? [process.execPath, ayBin] : ["ay"]), "serve", ...
|
|
183
|
+
const serveCmd = [...(ayBin ? [process.execPath, ayBin] : ["ay"]), "serve", ...effArgs].join(
|
|
184
|
+
" ",
|
|
185
|
+
);
|
|
91
186
|
const proc = Bun.spawn(
|
|
92
187
|
[oxmgrBin, "start", serveCmd, "--name", DAEMON_NAME, "--restart", "always"],
|
|
93
188
|
{ stdio: ["ignore", "inherit", "inherit"] },
|
|
94
189
|
);
|
|
95
190
|
const code = await proc.exited;
|
|
96
191
|
if (code === 0) {
|
|
97
|
-
const
|
|
98
|
-
const port =
|
|
192
|
+
const onBoot = await ensureBootAutostart(oxmgrBin);
|
|
193
|
+
const port = portFromArgs(effArgs);
|
|
99
194
|
// Mirror cmdServe's mode resolution: webrtc-only daemons open no HTTP port.
|
|
100
|
-
const webrtcish =
|
|
195
|
+
const webrtcish = effArgs.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
|
|
101
196
|
const httpish =
|
|
102
|
-
|
|
103
|
-
!
|
|
104
|
-
process.stdout.write(
|
|
197
|
+
effArgs.some((a) => a.startsWith("--http") || a.startsWith("--share")) ||
|
|
198
|
+
!effArgs.some((a) => a.startsWith("--webrtc"));
|
|
199
|
+
process.stdout.write(
|
|
200
|
+
`\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via oxmgr —`} v${current}\n`,
|
|
201
|
+
);
|
|
202
|
+
process.stdout.write(
|
|
203
|
+
onBoot
|
|
204
|
+
? `start-on-boot: enabled (oxmgr registered with the system init)\n`
|
|
205
|
+
: `start-on-boot: not registered — run \`oxmgr service install\` to enable\n`,
|
|
206
|
+
);
|
|
105
207
|
process.stdout.write(`token: ${token}\n\n`);
|
|
106
208
|
if (httpish) {
|
|
107
209
|
process.stdout.write(` ay ls ${token}@<host>:${port}\n`);
|
|
@@ -486,6 +588,13 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
486
588
|
return Response.json({ host: user ? `${user}@${host}` : host });
|
|
487
589
|
}
|
|
488
590
|
|
|
591
|
+
// GET /api/version — the running daemon's package version, so a re-run of
|
|
592
|
+
// `ay serve install` can tell whether the live daemon is stale and roll it
|
|
593
|
+
// forward. A daemon too old to expose this just 404s → treated as outdated.
|
|
594
|
+
if (req.method === "GET" && p === "/api/version") {
|
|
595
|
+
return Response.json({ version: getInstalledPackage().version });
|
|
596
|
+
}
|
|
597
|
+
|
|
489
598
|
// GET /api/notes
|
|
490
599
|
if (req.method === "GET" && p === "/api/notes") {
|
|
491
600
|
const notes = await readNotes();
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "./ts-B62nAAfY.js";
|
|
2
|
-
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-BdkE7S2A.js";
|
|
4
|
-
import "./pidStore-DBjlqzo8.js";
|
|
5
|
-
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-VayLM5qX.js";
|
|
7
|
-
|
|
8
|
-
export { SUPPORTED_CLIS };
|