agent-yes 1.251.0 → 1.252.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-CwSokgZr.js → SUPPORTED_CLIS-ZDqi9LbP.js} +3 -3
- package/dist/{SUPPORTED_CLIS-BXJSjxkF.js → SUPPORTED_CLIS-kkxFgTKh.js} +2 -2
- package/dist/{agentShare-BbLkcQm4.js → agentShare-BXLfevNX.js} +3 -3
- package/dist/{callback-8RWpxE4I.js → callback-D63YERtT.js} +3 -3
- package/dist/{callback-C4O66DTj.js → callback-D_gfdf2c.js} +3 -3
- package/dist/cli.js +7 -7
- package/dist/index.js +2 -2
- package/dist/{notifyDaemon-B1IAqhdC.js → notifyDaemon-FhMvFDm8.js} +3 -3
- package/dist/{remotes-DNLZlk9U.js → remotes-DzPup1dS.js} +1 -1
- package/dist/{remotes-BxQnNxQv.js → remotes-a0bGDae9.js} +2 -2
- package/dist/{rustBinary-ByzIf2js.js → rustBinary-BBOpYC-y.js} +21 -3
- package/dist/{schedule-CcBm_AJi.js → schedule-B5w4JNpg.js} +4 -4
- package/dist/{serve-BXvJ0BVz.js → serve-H8y95HWl.js} +83 -18
- package/dist/{setup-DN7g-GlT.js → setup-CRJSyz9Z.js} +2 -2
- package/dist/{subcommands-CIIal0U8.js → subcommands-BGrZZAZ2.js} +2 -2
- package/dist/{subcommands-DeEoYwPl.js → subcommands-DeUbXdTK.js} +21 -16
- package/dist/{terminal-lMeA3l5c.js → terminal-BMHnUWFm.js} +2 -2
- package/dist/{todoCli-DhD3G8Et.js → todoCli-CVYCQrmX.js} +1 -1
- package/dist/{tray-DpswPpBI.js → tray-LxRESrQs.js} +1 -1
- package/dist/trayApp-DELeG-wq.js +5 -0
- package/dist/trayApp-DEmn5LW0.js +100 -0
- package/dist/{ts-D-lKlowc.js → ts-1rQZA3oo.js} +2 -2
- package/dist/{versionChecker-UbAydyGS.js → versionChecker-hMS7Hkpn.js} +2 -2
- package/dist/{webrtcRemote-D9ENKmzx.js → webrtcRemote-DgoZGUr9.js} +1 -1
- package/dist/{widget-w6D-Sn_o.js → widget-D55SUI6o.js} +3 -3
- package/dist/{ws-Ba4q33_l.js → ws-GVmilTGt.js} +3 -3
- package/package.json +1 -1
- package/ts/rustBinary.ts +22 -0
- package/ts/serve.ts +86 -3
- package/ts/subcommands.ts +5 -0
- package/ts/trayApp.spec.ts +282 -0
- package/ts/trayApp.ts +112 -0
|
@@ -98,7 +98,7 @@ async function cmdTermMint(args) {
|
|
|
98
98
|
if (flags.ro && flags.interactive) throw new Error("--ro and --interactive are mutually exclusive");
|
|
99
99
|
const ttlSec = parseTtlSec(typeof flags.ttl === "string" ? flags.ttl : "15m");
|
|
100
100
|
const canSend = flags.interactive === true;
|
|
101
|
-
const { mintScopedTermToken } = await import("./serve-
|
|
101
|
+
const { mintScopedTermToken } = await import("./serve-H8y95HWl.js");
|
|
102
102
|
const r = await mintScopedTermToken(pid, {
|
|
103
103
|
ttlSec,
|
|
104
104
|
canSend
|
|
@@ -135,4 +135,4 @@ async function cmdTerm(args) {
|
|
|
135
135
|
|
|
136
136
|
//#endregion
|
|
137
137
|
export { cmdTerm };
|
|
138
|
-
//# sourceMappingURL=terminal-
|
|
138
|
+
//# sourceMappingURL=terminal-BMHnUWFm.js.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { n as findTrayLauncher } from "./rustBinary-BBOpYC-y.js";
|
|
2
|
+
import { t as agentYesHome } from "./agentYesHome-CtmsY6I-.js";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { unlink, writeFile } from "node:fs/promises";
|
|
6
|
+
|
|
7
|
+
//#region ts/trayApp.ts
|
|
8
|
+
/** Marker whose PRESENCE means the user has hidden the tray. */
|
|
9
|
+
function trayHiddenMarker() {
|
|
10
|
+
return path.join(agentYesHome(), "tray.hidden");
|
|
11
|
+
}
|
|
12
|
+
function isTrayHidden() {
|
|
13
|
+
return existsSync(trayHiddenMarker());
|
|
14
|
+
}
|
|
15
|
+
/** A desktop session we can show a tray on. Windows/macOS always; Linux needs a
|
|
16
|
+
* display server. Headless servers → no tray. */
|
|
17
|
+
function hasDesktop() {
|
|
18
|
+
if (process.platform === "win32" || process.platform === "darwin") return true;
|
|
19
|
+
return !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Launch the native tray (detached, window-less) if it makes sense to: a desktop
|
|
23
|
+
* session, the tray binary exists, and the user hasn't hidden it. No-op otherwise.
|
|
24
|
+
* The tray also self-gates on `ay serve status`, so this is best-effort. Returns
|
|
25
|
+
* true if a process was spawned.
|
|
26
|
+
*/
|
|
27
|
+
function launchTray() {
|
|
28
|
+
if (!hasDesktop() || isTrayHidden()) return false;
|
|
29
|
+
const bin = findTrayLauncher();
|
|
30
|
+
if (!bin) return false;
|
|
31
|
+
try {
|
|
32
|
+
Bun.spawn([bin], {
|
|
33
|
+
detached: true,
|
|
34
|
+
stdio: [
|
|
35
|
+
"ignore",
|
|
36
|
+
"ignore",
|
|
37
|
+
"ignore"
|
|
38
|
+
]
|
|
39
|
+
}).unref();
|
|
40
|
+
return true;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Stop any running native tray process(es). */
|
|
46
|
+
async function stopTray() {
|
|
47
|
+
const argv = process.platform === "win32" ? [
|
|
48
|
+
"taskkill",
|
|
49
|
+
"/IM",
|
|
50
|
+
"agent-yes-tray.exe",
|
|
51
|
+
"/F"
|
|
52
|
+
] : [
|
|
53
|
+
"pkill",
|
|
54
|
+
"-f",
|
|
55
|
+
"agent-yes-tray"
|
|
56
|
+
];
|
|
57
|
+
try {
|
|
58
|
+
await Bun.spawn(argv, { stdio: [
|
|
59
|
+
"ignore",
|
|
60
|
+
"ignore",
|
|
61
|
+
"ignore"
|
|
62
|
+
] }).exited;
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
async function cmdTray(args) {
|
|
66
|
+
const sub = args[0] ?? "status";
|
|
67
|
+
const w = (s = "") => process.stdout.write(s + "\n");
|
|
68
|
+
if (sub === "show") {
|
|
69
|
+
await unlink(trayHiddenMarker()).catch(() => {});
|
|
70
|
+
if (!hasDesktop()) {
|
|
71
|
+
process.stderr.write("ay tray show: no desktop session detected (headless) — nothing to show\n");
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
if (!findTrayLauncher()) {
|
|
75
|
+
process.stderr.write("ay tray show: tray binary not found — build it with\n cargo build --release --manifest-path rs-tray/Cargo.toml\n");
|
|
76
|
+
return 1;
|
|
77
|
+
}
|
|
78
|
+
w(launchTray() ? "tray shown." : "tray not launched (already running, or serve not installed).");
|
|
79
|
+
w("hint: the tray auto-shows on desktop sessions whenever `ay serve` is installed.");
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
if (sub === "hide") {
|
|
83
|
+
await writeFile(trayHiddenMarker(), "hidden by `ay tray hide`\n").catch(() => {});
|
|
84
|
+
await stopTray();
|
|
85
|
+
w("tray hidden — it will stay hidden until `ay tray show`.");
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
if (sub === "status") {
|
|
89
|
+
w(`hidden: ${isTrayHidden() ? "yes (run `ay tray show` to re-enable)" : "no"}`);
|
|
90
|
+
w(`desktop: ${hasDesktop() ? "yes" : "no (headless)"}`);
|
|
91
|
+
w(`binary: ${findTrayLauncher() ?? "(not found — not built for this platform)"}`);
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
process.stderr.write(`ay tray: unknown subcommand '${sub}' (use show | hide | status)\n`);
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
//#endregion
|
|
99
|
+
export { trayHiddenMarker as a, launchTray as i, hasDesktop as n, isTrayHidden as r, cmdTray as t };
|
|
100
|
+
//# sourceMappingURL=trayApp-DEmn5LW0.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __esmMin, r as __exportAll } from "./chunk-cZBX9HZv.js";
|
|
2
2
|
import { n as logger, t as addTransport } from "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
3
|
+
import { r as getInstalledPackage } from "./versionChecker-hMS7Hkpn.js";
|
|
4
4
|
import { t as agentYesHome } from "./agentYesHome-CtmsY6I-.js";
|
|
5
5
|
import { d as openStore, p as LIFECYCLES, x as removeControlCharacters } from "./todoAutomation-Buo457VO.js";
|
|
6
6
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-DBoONoM-.js";
|
|
@@ -2155,4 +2155,4 @@ function sleep(ms) {
|
|
|
2155
2155
|
|
|
2156
2156
|
//#endregion
|
|
2157
2157
|
export { hasTodoStore as a, permissionBadge as c, answerAsk as i, AgentContext as l, agentYes as n, listAsks as o, config as r, listAsksForProject as s, CLIS_CONFIG as t, CLAUDE_SESSION_PIN_ENV as u };
|
|
2158
|
-
//# sourceMappingURL=ts-
|
|
2158
|
+
//# sourceMappingURL=ts-1rQZA3oo.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.252.0";
|
|
11
11
|
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region ts/versionChecker.ts
|
|
@@ -215,4 +215,4 @@ async function displayVersion() {
|
|
|
215
215
|
|
|
216
216
|
//#endregion
|
|
217
217
|
export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
|
|
218
|
-
//# sourceMappingURL=versionChecker-
|
|
218
|
+
//# sourceMappingURL=versionChecker-hMS7Hkpn.js.map
|
|
@@ -28,7 +28,7 @@ function parseFlags(args, known) {
|
|
|
28
28
|
}
|
|
29
29
|
/** Resolve the local daemon base URL + token (flags override discovery). */
|
|
30
30
|
async function daemonTarget(flags) {
|
|
31
|
-
const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-
|
|
31
|
+
const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-H8y95HWl.js");
|
|
32
32
|
const base = typeof flags.base === "string" ? flags.base : await resolveDaemonHttpBase();
|
|
33
33
|
if (!base) throw new Error("no running ay serve daemon found — start `ay serve` or pass --base <url> (e.g. http://127.0.0.1:PORT)");
|
|
34
34
|
const token = typeof flags.token === "string" ? flags.token : await loadTokenReadOnly() ?? "";
|
|
@@ -186,7 +186,7 @@ async function cmdMint(args) {
|
|
|
186
186
|
const bad = caps.filter((c) => !KNOWN_CAPS.has(c));
|
|
187
187
|
if (bad.length) throw new Error(`unknown cap(s): ${bad.join(", ")} (valid: ${[...KNOWN_CAPS].join(", ")})`);
|
|
188
188
|
const ttlSec = parseTtlSec(typeof flags.ttl === "string" ? flags.ttl : "15m");
|
|
189
|
-
const { mintScopedTermToken } = await import("./serve-
|
|
189
|
+
const { mintScopedTermToken } = await import("./serve-H8y95HWl.js");
|
|
190
190
|
const r = await mintScopedTermToken(target, {
|
|
191
191
|
ttlSec,
|
|
192
192
|
caps
|
|
@@ -202,4 +202,4 @@ async function cmdMint(args) {
|
|
|
202
202
|
|
|
203
203
|
//#endregion
|
|
204
204
|
export { cmdMint, cmdWidget };
|
|
205
|
-
//# sourceMappingURL=widget-
|
|
205
|
+
//# sourceMappingURL=widget-D55SUI6o.js.map
|
|
@@ -3,9 +3,9 @@ import "./globalPidIndex-D9DOSd-c.js";
|
|
|
3
3
|
import "./messageLog-C-YIzhRj.js";
|
|
4
4
|
import "./e2e-CEIdJGZ7.js";
|
|
5
5
|
import "./configShared-IBbnn9b8.js";
|
|
6
|
-
import { C as listRecords } from "./subcommands-
|
|
6
|
+
import { C as listRecords } from "./subcommands-DeUbXdTK.js";
|
|
7
7
|
import "./webrtcLink-BMYneHbu.js";
|
|
8
|
-
import "./remotes-
|
|
8
|
+
import "./remotes-a0bGDae9.js";
|
|
9
9
|
import { a as getProvisionRoot } from "./workspaceConfig-CpilQzMY.js";
|
|
10
10
|
import { existsSync } from "fs";
|
|
11
11
|
import { lstat, opendir } from "fs/promises";
|
|
@@ -418,4 +418,4 @@ async function cmdWs(args) {
|
|
|
418
418
|
|
|
419
419
|
//#endregion
|
|
420
420
|
export { WS_JSON_SCHEMA, cmdWs, collectWorkspaces, isPathInside, loadProvision, workspaceStatus };
|
|
421
|
-
//# sourceMappingURL=ws-
|
|
421
|
+
//# sourceMappingURL=ws-GVmilTGt.js.map
|
package/package.json
CHANGED
package/ts/rustBinary.ts
CHANGED
|
@@ -131,6 +131,28 @@ export function findSpawnHiddenLauncher(): string | undefined {
|
|
|
131
131
|
return undefined;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Locate the `agent-yes-tray` binary (the system-tray companion). Returns
|
|
136
|
+
* undefined when it isn't present (not built / not shipped for this platform —
|
|
137
|
+
* Windows-first today). Unlike the spawn-hidden launcher this is NOT gated to
|
|
138
|
+
* Windows, so macOS/Linux builds are found once they exist. Lives in its OWN
|
|
139
|
+
* crate (rs-tray), so it sits under rs-tray/target rather than rs/target.
|
|
140
|
+
*/
|
|
141
|
+
export function findTrayLauncher(): string | undefined {
|
|
142
|
+
const exe = process.platform === "win32" ? "agent-yes-tray.exe" : "agent-yes-tray";
|
|
143
|
+
const dir = import.meta.dirname ?? import.meta.dir;
|
|
144
|
+
const searchPaths = [
|
|
145
|
+
path.resolve(dir, `../rs-tray/target/release/${exe}`),
|
|
146
|
+
path.resolve(dir, `../rs-tray/target/debug/${exe}`),
|
|
147
|
+
path.join(getBinDir(), exe),
|
|
148
|
+
Bun.which("agent-yes-tray") ?? "",
|
|
149
|
+
];
|
|
150
|
+
for (const p of searchPaths) {
|
|
151
|
+
if (p && existsSync(p)) return p;
|
|
152
|
+
}
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
134
156
|
/**
|
|
135
157
|
* Get GitHub release download URL for the binary
|
|
136
158
|
*/
|
package/ts/serve.ts
CHANGED
|
@@ -53,6 +53,7 @@ import { type MailParty, recordInbox } from "./messageLog.ts";
|
|
|
53
53
|
import { updateGlobalPidStatus } from "./globalPidIndex.ts";
|
|
54
54
|
import { spawnRejectionReason } from "./spawnGate.ts";
|
|
55
55
|
import { findSpawnHiddenLauncher } from "./rustBinary.ts";
|
|
56
|
+
import { launchTray } from "./trayApp.ts";
|
|
56
57
|
import { pgidForWrapper } from "./reaper.ts";
|
|
57
58
|
import { SUPPORTED_CLIS } from "./SUPPORTED_CLIS.ts";
|
|
58
59
|
import { getInstalledPackage } from "./versionChecker.ts";
|
|
@@ -959,6 +960,32 @@ async function readDaemonServeArgs(mgr: DaemonManager): Promise<string[] | null>
|
|
|
959
960
|
}
|
|
960
961
|
}
|
|
961
962
|
|
|
963
|
+
// Whether the daemon PROCESS is currently online per the manager. Distinct from
|
|
964
|
+
// `readDaemonServeArgs` (registered?) and from the HTTP probe (reachable?) — a
|
|
965
|
+
// webrtc-only daemon opens no port, so the manager's own view is the only way to
|
|
966
|
+
// know it's up. oxmgr: `status <name>` → "Status: running"; pm2: jlist status.
|
|
967
|
+
async function readDaemonRunning(mgr: DaemonManager): Promise<boolean> {
|
|
968
|
+
try {
|
|
969
|
+
if (mgr.id === "oxmgr") {
|
|
970
|
+
const p = Bun.spawn([mgr.bin, "status", DAEMON_NAME], {
|
|
971
|
+
stdout: "pipe",
|
|
972
|
+
stderr: "ignore",
|
|
973
|
+
env: liveEnv(),
|
|
974
|
+
});
|
|
975
|
+
const out = await new Response(p.stdout).text();
|
|
976
|
+
if ((await p.exited) !== 0) return false;
|
|
977
|
+
return /^\s*Status:\s*running\b/im.test(out);
|
|
978
|
+
}
|
|
979
|
+
const p = Bun.spawn([mgr.bin, "jlist"], { stdout: "pipe", stderr: "ignore", env: liveEnv() });
|
|
980
|
+
const out = await new Response(p.stdout).text();
|
|
981
|
+
if ((await p.exited) !== 0) return false;
|
|
982
|
+
const list = JSON.parse(out) as Array<{ name?: string; pm2_env?: { status?: string } }>;
|
|
983
|
+
return list.find((x) => x.name === DAEMON_NAME)?.pm2_env?.status === "online";
|
|
984
|
+
} catch {
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
962
989
|
function portFromArgs(args: string[]): number | null {
|
|
963
990
|
const m = /--port[=\s](\d+)/.exec(args.join(" "));
|
|
964
991
|
return m ? Number(m[1]) : null;
|
|
@@ -1064,6 +1091,38 @@ async function fetchDaemonVersion(port: number | null, token: string): Promise<s
|
|
|
1064
1091
|
}
|
|
1065
1092
|
}
|
|
1066
1093
|
|
|
1094
|
+
// ay serve start | stop — control the ALREADY-INSTALLED daemon's run state via
|
|
1095
|
+
// the manager. Used by the tray's toggle and available on the CLI. Starting an
|
|
1096
|
+
// unregistered daemon is a no-op with a hint to `install`.
|
|
1097
|
+
async function cmdServeStartStop(sub: "start" | "stop"): Promise<number> {
|
|
1098
|
+
const mgr = resolveDaemonManager();
|
|
1099
|
+
if (!mgr) {
|
|
1100
|
+
process.stderr.write("ay serve " + sub + ": no process manager found (need pm2 or oxmgr)\n");
|
|
1101
|
+
return 1;
|
|
1102
|
+
}
|
|
1103
|
+
if ((await readDaemonServeArgs(mgr)) === null) {
|
|
1104
|
+
process.stderr.write(
|
|
1105
|
+
`ay serve ${sub}: '${DAEMON_NAME}' is not installed — run 'ay serve install' first\n`,
|
|
1106
|
+
);
|
|
1107
|
+
return 1;
|
|
1108
|
+
}
|
|
1109
|
+
// pm2 restarts a stopped named proc with `start <name>`; oxmgr's `start` takes a
|
|
1110
|
+
// command, so an existing app is (re)started via `restart <name>`.
|
|
1111
|
+
const argv =
|
|
1112
|
+
sub === "stop"
|
|
1113
|
+
? [mgr.bin, "stop", DAEMON_NAME]
|
|
1114
|
+
: mgr.id === "oxmgr"
|
|
1115
|
+
? [mgr.bin, "restart", DAEMON_NAME]
|
|
1116
|
+
: [mgr.bin, "start", DAEMON_NAME];
|
|
1117
|
+
const code = await spawnExit(argv);
|
|
1118
|
+
process.stdout.write(
|
|
1119
|
+
code === 0
|
|
1120
|
+
? `${sub === "stop" ? "stopped" : "started"} '${DAEMON_NAME}' (via ${mgr.id})\n`
|
|
1121
|
+
: `ay serve ${sub}: manager command failed (exit ${code})\n`,
|
|
1122
|
+
);
|
|
1123
|
+
return code;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1067
1126
|
async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
1068
1127
|
// Uninstall first, before any manager resolution/bootstrap: it must sweep
|
|
1069
1128
|
// EVERY present manager, not just the currently-preferred one. After a
|
|
@@ -1293,6 +1352,10 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
|
1293
1352
|
const code = await proc.exited;
|
|
1294
1353
|
if (code === 0) {
|
|
1295
1354
|
const onBoot = await ensureBootAutostart(mgr);
|
|
1355
|
+
// Desktop companion: bring up the tray icon (best-effort; no-op on headless
|
|
1356
|
+
// sessions, when the tray binary is absent, or when the user ran `ay tray
|
|
1357
|
+
// hide`). The tray self-gates on serve being installed.
|
|
1358
|
+
const trayShown = launchTray();
|
|
1296
1359
|
const port = argsUsePortless(effArgs) ? await portlessAppPort() : portFromArgs(effArgs);
|
|
1297
1360
|
const localUrl = argsUsePortless(effArgs) ? await resolvedPortlessConsoleUrl(token) : null;
|
|
1298
1361
|
// Mirror cmdServe's mode resolution: webrtc-only daemons open no HTTP port.
|
|
@@ -1302,6 +1365,7 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
|
1302
1365
|
process.stdout.write(
|
|
1303
1366
|
`\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via ${mgr.id} —`} v${current}\n`,
|
|
1304
1367
|
);
|
|
1368
|
+
if (trayShown) process.stdout.write(`tray: shown (hide it with \`ay tray hide\`)\n`);
|
|
1305
1369
|
if (mgr.id === "oxmgr" && process.platform === "win32")
|
|
1306
1370
|
process.stdout.write(
|
|
1307
1371
|
onBoot
|
|
@@ -1369,6 +1433,8 @@ async function cmdServeStatus(args: string[]): Promise<number> {
|
|
|
1369
1433
|
// A non-null arg list means the daemon is registered with the manager.
|
|
1370
1434
|
const daemonArgs = mgr ? await readDaemonServeArgs(mgr) : null;
|
|
1371
1435
|
const installed = daemonArgs !== null;
|
|
1436
|
+
// Manager-reported process state — the only up/down signal for webrtc daemons.
|
|
1437
|
+
const running = installed ? await readDaemonRunning(mgr!) : false;
|
|
1372
1438
|
const a = daemonArgs ?? [];
|
|
1373
1439
|
const local = argsUsePortless(a);
|
|
1374
1440
|
const port = local ? await portlessAppPort() : portFromArgs(a);
|
|
@@ -1392,6 +1458,7 @@ async function cmdServeStatus(args: string[]): Promise<number> {
|
|
|
1392
1458
|
{
|
|
1393
1459
|
manager: mgr?.id ?? null,
|
|
1394
1460
|
installed,
|
|
1461
|
+
running,
|
|
1395
1462
|
mode,
|
|
1396
1463
|
port: httpish ? port : null,
|
|
1397
1464
|
localUrl: httpish ? localUrl : null,
|
|
@@ -1415,6 +1482,7 @@ async function cmdServeStatus(args: string[]): Promise<number> {
|
|
|
1415
1482
|
w(`manager: ${mgr ? mgr.id : "none — install pm2 or oxmgr to daemonize"}`);
|
|
1416
1483
|
if (installed) {
|
|
1417
1484
|
w(`installed: yes (via ${mgr!.id})`);
|
|
1485
|
+
w(`running: ${running ? "yes" : "no (stopped)"}`);
|
|
1418
1486
|
w(`mode: ${mode}${httpish ? (local ? ` (${localUrl})` : ` (port ${port})`) : ""}`);
|
|
1419
1487
|
if (a.length) w(`args: ${a.join(" ")}`);
|
|
1420
1488
|
} else {
|
|
@@ -1489,6 +1557,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
1489
1557
|
// Daemon subcommands
|
|
1490
1558
|
const sub = rest[0];
|
|
1491
1559
|
if (sub === "status") return cmdServeStatus(rest.slice(1));
|
|
1560
|
+
if (sub === "start" || sub === "stop") return cmdServeStartStop(sub);
|
|
1492
1561
|
if (sub === "healthcheck") {
|
|
1493
1562
|
// oxmgr --health-cmd liveness probe. Exit non-zero only when the heartbeat is
|
|
1494
1563
|
// demonstrably stale (event loop wedged), so the manager restarts us. A
|
|
@@ -3562,7 +3631,12 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
3562
3631
|
process.platform === "win32" && ayBin.toLowerCase().endsWith(".exe")
|
|
3563
3632
|
? [ayBin]
|
|
3564
3633
|
: [process.execPath, ayBin];
|
|
3565
|
-
|
|
3634
|
+
// On Windows, interpose the window-less launcher so this detached spawn
|
|
3635
|
+
// doesn't flash a console window that steals focus. Same shim the daemon
|
|
3636
|
+
// install uses; undefined off Windows / when not installed → raw command.
|
|
3637
|
+
const spawnHidden = findSpawnHiddenLauncher();
|
|
3638
|
+
const restartArgv = [...ayCmd, ...args];
|
|
3639
|
+
const child = Bun.spawn(spawnHidden ? [spawnHidden, ...restartArgv] : restartArgv, {
|
|
3566
3640
|
cwd: record.cwd,
|
|
3567
3641
|
detached: true,
|
|
3568
3642
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -4003,6 +4077,14 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
4003
4077
|
// subagents don't inherit and collide. The caller can then address the agent
|
|
4004
4078
|
// immediately: `ay <verb> <remote>:<agentId>`.
|
|
4005
4079
|
const agentId = randomBytes(6).toString("hex");
|
|
4080
|
+
// On Windows, interpose the window-less launcher (GUI-subsystem shim) so the
|
|
4081
|
+
// detached agent spawn below doesn't flash a console window that grabs focus
|
|
4082
|
+
// — the exact annoyance when spawning an agent from the web console. Mirrors
|
|
4083
|
+
// the daemon-install path; undefined off Windows / when the launcher isn't
|
|
4084
|
+
// installed → fall back to the raw argv. NOTE: when interposed, `child.pid`
|
|
4085
|
+
// is the launcher's pid (a faithful shim that lives exactly as long as the
|
|
4086
|
+
// agent); `agentId` remains the stable handle the console addresses by.
|
|
4087
|
+
const spawnHidden = findSpawnHiddenLauncher();
|
|
4006
4088
|
// Detach the agent into its OWN session (setsid). When `ay serve` runs WITH a
|
|
4007
4089
|
// controlling terminal (started in a shell rather than as a headless daemon),
|
|
4008
4090
|
// an undetached child inherits the daemon's session + controlling tty and lands
|
|
@@ -4029,7 +4111,8 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
4029
4111
|
);
|
|
4030
4112
|
// File-backed stderr never blocks the child (a pipe we stop draining
|
|
4031
4113
|
// after exec would), and bounds our read to the first few KB.
|
|
4032
|
-
const
|
|
4114
|
+
const hookArgv = [shell, "-c", script, "ay-spawn", ...agentArgv];
|
|
4115
|
+
const child = Bun.spawn(spawnHidden ? [spawnHidden, ...hookArgv] : hookArgv, {
|
|
4033
4116
|
cwd,
|
|
4034
4117
|
detached: true,
|
|
4035
4118
|
env: {
|
|
@@ -4081,7 +4164,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
4081
4164
|
...(provisioned ? { provisioned } : {}),
|
|
4082
4165
|
});
|
|
4083
4166
|
}
|
|
4084
|
-
const child = Bun.spawn(agentArgv, {
|
|
4167
|
+
const child = Bun.spawn(spawnHidden ? [spawnHidden, ...agentArgv] : agentArgv, {
|
|
4085
4168
|
cwd,
|
|
4086
4169
|
detached: true,
|
|
4087
4170
|
env: { ...agentEnv, AGENT_YES_AGENT_ID: agentId },
|
package/ts/subcommands.ts
CHANGED
|
@@ -370,6 +370,7 @@ const SUBCOMMANDS = new Set([
|
|
|
370
370
|
"widget",
|
|
371
371
|
"mint",
|
|
372
372
|
"serve",
|
|
373
|
+
"tray",
|
|
373
374
|
"schedule",
|
|
374
375
|
"remote",
|
|
375
376
|
"expose",
|
|
@@ -525,6 +526,10 @@ export async function runSubcommand(argv: string[]): Promise<number | null> {
|
|
|
525
526
|
const { cmdServe } = await import("./serve.ts");
|
|
526
527
|
return cmdServe(rest);
|
|
527
528
|
}
|
|
529
|
+
case "tray": {
|
|
530
|
+
const { cmdTray } = await import("./trayApp.ts");
|
|
531
|
+
return cmdTray(rest);
|
|
532
|
+
}
|
|
528
533
|
case "setup": {
|
|
529
534
|
const { cmdSetup } = await import("./setup.ts");
|
|
530
535
|
return cmdSetup(rest);
|