agent-yes 1.118.0 → 1.118.2

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.
@@ -0,0 +1,8 @@
1
+ import "./ts-BUpRpKjH.js";
2
+ import "./logger-B9h0djqx.js";
3
+ import "./versionChecker-D-drPifS.js";
4
+ import "./pidStore-B5vBu8Px.js";
5
+ import "./globalPidIndex-gZuTvTBs.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CNLy57hz.js";
7
+
8
+ export { SUPPORTED_CLIS };
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-DYzATaI_.js";
1
+ import { t as CLIS_CONFIG } from "./ts-BUpRpKjH.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-CrIGjD28.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-CNLy57hz.js.map
@@ -288,4 +288,4 @@ async function getDefaultConfig() {
288
288
 
289
289
  //#endregion
290
290
  export { agent_yes_config_default as default };
291
- //# sourceMappingURL=agent-yes.config-1LMoK18R.js.map
291
+ //# sourceMappingURL=agent-yes.config-kmtJKJHk.js.map
@@ -0,0 +1,25 @@
1
+ import { homedir } from "os";
2
+ import path from "path";
3
+
4
+ //#region ts/agentYesHome.ts
5
+ /**
6
+ * Root directory for cross-runtime, machine-global agent-yes state:
7
+ * the pid index (`pids.jsonl`), FIFO/named-pipe IPC endpoints (`fifo/`),
8
+ * winsize signals, notes, and the serve token.
9
+ *
10
+ * Durable per-session *logs* deliberately do NOT live here — they go under
11
+ * `<cwd>/.agent-yes/` so they stay colocated with the project that produced
12
+ * them (see `PidStore`). Only ephemeral IPC + the discovery index are global,
13
+ * which keeps FIFOs on the local home filesystem (reliable `mkfifo`) and lets
14
+ * `ay ls`/`ay attach` find every agent regardless of the caller's cwd.
15
+ *
16
+ * Resolved at call time (not module load) so tests and callers can override
17
+ * via `$AGENT_YES_HOME` without juggling the module cache.
18
+ */
19
+ function agentYesHome() {
20
+ return process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
21
+ }
22
+
23
+ //#endregion
24
+ export { agentYesHome as t };
25
+ //# sourceMappingURL=agentYesHome-BvaUOzCV.js.map
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-D-OYziVA.js";
3
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-D-drPifS.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-FtwGxxtc.js");
485
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-CIrlUP1E.js");
486
486
  if (isHelpFlag && process.argv.length === 3) {
487
487
  cmdHelp();
488
488
  process.exit(0);
@@ -496,12 +496,12 @@ await checkAndAutoUpdate();
496
496
  logger.info(versionString());
497
497
  const config = parseCliArgs(process.argv);
498
498
  if (config.tray) {
499
- const { startTray } = await import("./tray-CWQe9DMY.js");
499
+ const { startTray } = await import("./tray-B8_rx1iu.js");
500
500
  await startTray();
501
501
  await new Promise(() => {});
502
502
  }
503
503
  {
504
- const { ensureTray } = await import("./tray-CWQe9DMY.js");
504
+ const { ensureTray } = await import("./tray-B8_rx1iu.js");
505
505
  ensureTray();
506
506
  }
507
507
  if (config.useRust) {
@@ -515,7 +515,7 @@ if (config.useRust) {
515
515
  }
516
516
  }
517
517
  if (rustBinary) {
518
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-BS7EiXxW.js");
518
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-Bq_fBaXd.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}`);
@@ -545,7 +545,7 @@ if (config.showVersion) {
545
545
  process.exit(0);
546
546
  }
547
547
  if (config.appendPrompt) {
548
- const { PidStore } = await import("./pidStore-BLcnCpkX.js");
548
+ const { PidStore } = await import("./pidStore-7y1cTcAE.js");
549
549
  const ipcPath = await PidStore.findActiveFifo(process.cwd());
550
550
  if (!ipcPath) {
551
551
  console.error("No active agent with IPC found in current directory.");
@@ -189,4 +189,4 @@ async function pruneOldLogs(maxAgeMs = retentionMs()) {
189
189
 
190
190
  //#endregion
191
191
  export { updateGlobalPidStatus as a, readGlobalPids as i, maybeCompactGlobalPids as n, pruneOldLogs as r, appendGlobalPid as t };
192
- //# sourceMappingURL=globalPidIndex-yVd3mbsV.js.map
192
+ //# sourceMappingURL=globalPidIndex-gZuTvTBs.js.map
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-DYzATaI_.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-BUpRpKjH.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-D-OYziVA.js";
4
- import "./pidStore-BcGnnKQf.js";
5
- import "./globalPidIndex-yVd3mbsV.js";
3
+ import "./versionChecker-D-drPifS.js";
4
+ import "./pidStore-B5vBu8Px.js";
5
+ import "./globalPidIndex-gZuTvTBs.js";
6
6
 
7
7
  export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
@@ -0,0 +1,5 @@
1
+ import "./logger-B9h0djqx.js";
2
+ import { t as PidStore } from "./pidStore-B5vBu8Px.js";
3
+ import "./globalPidIndex-gZuTvTBs.js";
4
+
5
+ export { PidStore };
@@ -1,31 +1,11 @@
1
1
  import { n as logger } from "./logger-B9h0djqx.js";
2
- import { a as updateGlobalPidStatus, n as maybeCompactGlobalPids, r as pruneOldLogs, t as appendGlobalPid } from "./globalPidIndex-yVd3mbsV.js";
2
+ import { t as agentYesHome } from "./agentYesHome-BvaUOzCV.js";
3
+ import { a as updateGlobalPidStatus, n as maybeCompactGlobalPids, r as pruneOldLogs, t as appendGlobalPid } from "./globalPidIndex-gZuTvTBs.js";
3
4
  import { closeSync, existsSync, fsyncSync, openSync } from "fs";
4
5
  import { appendFile, mkdir, readFile, rename, writeFile } from "fs/promises";
5
- import { homedir } from "os";
6
6
  import path from "path";
7
7
  import { lock } from "proper-lockfile";
8
8
 
9
- //#region ts/agentYesHome.ts
10
- /**
11
- * Root directory for cross-runtime, machine-global agent-yes state:
12
- * the pid index (`pids.jsonl`), FIFO/named-pipe IPC endpoints (`fifo/`),
13
- * winsize signals, notes, and the serve token.
14
- *
15
- * Durable per-session *logs* deliberately do NOT live here — they go under
16
- * `<cwd>/.agent-yes/` so they stay colocated with the project that produced
17
- * them (see `PidStore`). Only ephemeral IPC + the discovery index are global,
18
- * which keeps FIFOs on the local home filesystem (reliable `mkfifo`) and lets
19
- * `ay ls`/`ay attach` find every agent regardless of the caller's cwd.
20
- *
21
- * Resolved at call time (not module load) so tests and callers can override
22
- * via `$AGENT_YES_HOME` without juggling the module cache.
23
- */
24
- function agentYesHome() {
25
- return process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
26
- }
27
-
28
- //#endregion
29
9
  //#region ts/JsonlStore.ts
30
10
  /**
31
11
  * A lightweight NeDB-style JSONL persistence layer.
@@ -381,5 +361,5 @@ pid-db/
381
361
  };
382
362
 
383
363
  //#endregion
384
- export { agentYesHome as n, PidStore as t };
385
- //# sourceMappingURL=pidStore-BcGnnKQf.js.map
364
+ export { PidStore as t };
365
+ //# sourceMappingURL=pidStore-B5vBu8Px.js.map
@@ -147,4 +147,4 @@ async function cmdRemote(rest) {
147
147
 
148
148
  //#endregion
149
149
  export { resolveRemoteSpec as a, readRemotes as i, deleteRemoteAlias as n, writeRemoteAlias as o, parseDirectRemoteSpec as r, cmdRemote as t };
150
- //# sourceMappingURL=remotes-C3xPRtfg.js.map
150
+ //# sourceMappingURL=remotes-BufkGk0e.js.map
@@ -1,3 +1,3 @@
1
- import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-C3xPRtfg.js";
1
+ import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-BufkGk0e.js";
2
2
 
3
3
  export { cmdRemote };
@@ -1,11 +1,11 @@
1
- import "./ts-DYzATaI_.js";
1
+ import "./ts-BUpRpKjH.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import { r as getInstalledPackage } from "./versionChecker-D-OYziVA.js";
4
- import "./pidStore-BcGnnKQf.js";
5
- import "./globalPidIndex-yVd3mbsV.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CrIGjD28.js";
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-tH8JdrBS.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-D-drPifS.js";
4
+ import "./pidStore-B5vBu8Px.js";
5
+ import "./globalPidIndex-gZuTvTBs.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CNLy57hz.js";
7
+ import "./remotes-BufkGk0e.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-BLSPrqA1.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";
@@ -67,13 +67,48 @@ function freshAgentEnv() {
67
67
  return env;
68
68
  }
69
69
  const DAEMON_NAME = "agent-yes";
70
- async function ensureBootAutostart(oxmgrBin) {
70
+ function resolveDaemonManager() {
71
+ const oxmgr = Bun.which("oxmgr");
72
+ const pm2 = Bun.which("pm2");
73
+ const override = process.env.AGENT_YES_DAEMON_MANAGER?.toLowerCase();
74
+ if (override === "pm2") return pm2 ? {
75
+ id: "pm2",
76
+ bin: pm2
77
+ } : null;
78
+ if (override === "oxmgr") return oxmgr ? {
79
+ id: "oxmgr",
80
+ bin: oxmgr
81
+ } : null;
82
+ return (process.platform === "win32" ? [pm2 && {
83
+ id: "pm2",
84
+ bin: pm2
85
+ }, oxmgr && {
86
+ id: "oxmgr",
87
+ bin: oxmgr
88
+ }] : [oxmgr && {
89
+ id: "oxmgr",
90
+ bin: oxmgr
91
+ }, pm2 && {
92
+ id: "pm2",
93
+ bin: pm2
94
+ }]).find((m) => !!m) ?? null;
95
+ }
96
+ function ayServeArgv(args) {
97
+ const ayBin = Bun.which("ay");
98
+ return [
99
+ ...ayBin ? process.platform === "win32" ? [ayBin] : [process.execPath, ayBin] : ["ay"],
100
+ "serve",
101
+ ...args
102
+ ];
103
+ }
104
+ async function ensureBootAutostart(mgr) {
71
105
  try {
72
- return await Bun.spawn([
73
- oxmgrBin,
106
+ const cmd = mgr.id === "oxmgr" ? [
107
+ mgr.bin,
74
108
  "service",
75
109
  "install"
76
- ], { stdio: [
110
+ ] : [mgr.bin, "save"];
111
+ return await Bun.spawn(cmd, { stdio: [
77
112
  "ignore",
78
113
  "ignore",
79
114
  "ignore"
@@ -93,22 +128,34 @@ async function spawnExit(cmd) {
93
128
  return 1;
94
129
  }
95
130
  }
96
- async function readDaemonServeArgs(oxmgrBin) {
131
+ async function readDaemonServeArgs(mgr) {
97
132
  try {
98
- const p = Bun.spawn([
99
- oxmgrBin,
100
- "status",
101
- DAEMON_NAME
102
- ], {
133
+ if (mgr.id === "oxmgr") {
134
+ const p = Bun.spawn([
135
+ mgr.bin,
136
+ "status",
137
+ DAEMON_NAME
138
+ ], {
139
+ stdout: "pipe",
140
+ stderr: "ignore"
141
+ });
142
+ const out = await new Response(p.stdout).text();
143
+ if (await p.exited !== 0) return null;
144
+ const m = /Command:\s*(.+)/.exec(out);
145
+ if (!m) return null;
146
+ const after = /\bserve\b\s*(.*)$/.exec(m[1].trim());
147
+ return after ? after[1].split(/\s+/).filter(Boolean) : [];
148
+ }
149
+ const p = Bun.spawn([mgr.bin, "jlist"], {
103
150
  stdout: "pipe",
104
151
  stderr: "ignore"
105
152
  });
106
153
  const out = await new Response(p.stdout).text();
107
154
  if (await p.exited !== 0) return null;
108
- const m = /Command:\s*(.+)/.exec(out);
109
- if (!m) return null;
110
- const after = /\bserve\b\s*(.*)$/.exec(m[1].trim());
111
- return after ? after[1].split(/\s+/).filter(Boolean) : [];
155
+ const proc = JSON.parse(out).find((x) => x.name === DAEMON_NAME);
156
+ if (!proc) return null;
157
+ const a = proc.pm2_env?.args ?? [];
158
+ return a[0] === "serve" ? a.slice(1) : a;
112
159
  } catch {
113
160
  return null;
114
161
  }
@@ -130,61 +177,68 @@ async function fetchDaemonVersion(port, token) {
130
177
  }
131
178
  }
132
179
  async function cmdServeDaemon(sub, args) {
133
- const oxmgrBin = Bun.which("oxmgr");
134
- if (!oxmgrBin) {
135
- process.stderr.write("ay serve install: oxmgr not found\n install with: cargo install oxmgr\n or: bun add -g oxmgr\n");
180
+ const mgr = resolveDaemonManager();
181
+ if (!mgr) {
182
+ process.stderr.write("ay serve install: no process manager found (need pm2 or oxmgr)\n install with: bun add -g pm2\n or: cargo install oxmgr\n");
136
183
  return 1;
137
184
  }
138
185
  if (sub === "install") {
139
186
  const token = await loadOrCreateToken(void 0);
140
- const priorArgs = await readDaemonServeArgs(oxmgrBin);
187
+ const priorArgs = await readDaemonServeArgs(mgr);
141
188
  const effArgs = args.length ? args : priorArgs ?? [];
142
189
  const current = getInstalledPackage().version;
143
190
  if (priorArgs !== null) {
144
191
  const runningVer = await fetchDaemonVersion(portFromArgs(effArgs), token);
145
192
  if (runningVer === current) {
146
- await ensureBootAutostart(oxmgrBin);
193
+ await ensureBootAutostart(mgr);
147
194
  process.stdout.write(`'${DAEMON_NAME}' already running v${current} (up to date)\n`);
148
195
  return 0;
149
196
  }
150
197
  process.stdout.write(`rolling '${DAEMON_NAME}' ${runningVer ? `v${runningVer}` : "(unknown)"} → v${current}…\n`);
151
198
  await spawnExit([
152
- oxmgrBin,
199
+ mgr.bin,
153
200
  "stop",
154
201
  DAEMON_NAME
155
202
  ]);
156
203
  await spawnExit([
157
- oxmgrBin,
204
+ mgr.bin,
158
205
  "delete",
159
206
  DAEMON_NAME
160
207
  ]);
161
208
  }
162
- const ayBin = Bun.which("ay");
163
- const serveCmd = [
164
- ...ayBin ? [process.execPath, ayBin] : ["ay"],
165
- "serve",
166
- ...effArgs
167
- ].join(" ");
168
- const code = await Bun.spawn([
169
- oxmgrBin,
209
+ const serveArgv = ayServeArgv(effArgs);
210
+ const startArgv = mgr.id === "oxmgr" ? [
211
+ mgr.bin,
170
212
  "start",
171
- serveCmd,
213
+ serveArgv.join(" "),
172
214
  "--name",
173
215
  DAEMON_NAME,
174
216
  "--restart",
175
217
  "always"
176
- ], { stdio: [
218
+ ] : [
219
+ mgr.bin,
220
+ "start",
221
+ serveArgv[0],
222
+ "--name",
223
+ DAEMON_NAME,
224
+ "--interpreter",
225
+ "none",
226
+ "--",
227
+ ...serveArgv.slice(1)
228
+ ];
229
+ const code = await Bun.spawn(startArgv, { stdio: [
177
230
  "ignore",
178
231
  "inherit",
179
232
  "inherit"
180
233
  ] }).exited;
181
234
  if (code === 0) {
182
- const onBoot = await ensureBootAutostart(oxmgrBin);
235
+ const onBoot = await ensureBootAutostart(mgr);
183
236
  const port = portFromArgs(effArgs);
184
237
  const webrtcish = effArgs.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
185
238
  const httpish = effArgs.some((a) => a.startsWith("--http") || a.startsWith("--share")) || !effArgs.some((a) => a.startsWith("--webrtc"));
186
- process.stdout.write(`\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via oxmgr —`} v${current}\n`);
187
- 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`);
239
+ process.stdout.write(`\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via ${mgr.id} —`} v${current}\n`);
240
+ if (mgr.id === "oxmgr") 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`);
241
+ else process.stdout.write(onBoot ? `start-on-boot: pm2 list saved (run \`pm2 startup\` once for boot resurrect)\n` : `start-on-boot: \`pm2 save\` failed — run it manually to persist across reboots\n`);
188
242
  process.stdout.write(`token: ${token}\n\n`);
189
243
  if (httpish) {
190
244
  process.stdout.write(` ay ls ${token}@<host>:${port}\n`);
@@ -196,17 +250,25 @@ async function cmdServeDaemon(sub, args) {
196
250
  }
197
251
  return code ?? 1;
198
252
  }
199
- if (sub === "uninstall") return await Bun.spawn([
200
- oxmgrBin,
201
- "delete",
202
- DAEMON_NAME
203
- ], { stdio: [
204
- "ignore",
205
- "inherit",
206
- "inherit"
207
- ] }).exited ?? 1;
253
+ if (sub === "uninstall") {
254
+ const code = await Bun.spawn([
255
+ mgr.bin,
256
+ "delete",
257
+ DAEMON_NAME
258
+ ], { stdio: [
259
+ "ignore",
260
+ "inherit",
261
+ "inherit"
262
+ ] }).exited ?? 1;
263
+ if (mgr.id === "pm2" && code === 0) await Bun.spawn([mgr.bin, "save"], { stdio: [
264
+ "ignore",
265
+ "ignore",
266
+ "ignore"
267
+ ] }).exited;
268
+ return code;
269
+ }
208
270
  if (sub === "logs") return await Bun.spawn([
209
- oxmgrBin,
271
+ mgr.bin,
210
272
  "logs",
211
273
  DAEMON_NAME,
212
274
  ...args
@@ -234,7 +296,7 @@ Modes (default: --http):
234
296
  --share [URL] Legacy alias for --http --webrtc
235
297
 
236
298
  Options:
237
- --port N Port to listen on (default: ${DEFAULT_PORT})\n --host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)\n --token TOKEN Auth token (auto-generated and saved if omitted)\n -d, --daemon Install these flags as a background daemon via oxmgr\n (same as: ay serve install <flags>)\n --allow-spawn Deprecated no-op — the console can always spawn agents\n --tls-cert FILE TLS certificate PEM\n --tls-key FILE TLS private key PEM\n\nSubcommands:\n ay serve install install as background daemon via oxmgr\n ay serve uninstall remove daemon\n ay serve logs view daemon logs\n\nOnce running, connect from another machine:\n ay ls <token>@<host>:${DEFAULT_PORT}\n ay remote add <alias> http://<token>@<host>:${DEFAULT_PORT}\n`);
299
+ --port N Port to listen on (default: ${DEFAULT_PORT})\n --host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)\n --token TOKEN Auth token (auto-generated and saved if omitted)\n -d, --daemon Install these flags as a background daemon (pm2/oxmgr)\n (same as: ay serve install <flags>)\n --allow-spawn Deprecated no-op — the console can always spawn agents\n --tls-cert FILE TLS certificate PEM\n --tls-key FILE TLS private key PEM\n\nSubcommands:\n ay serve install install as background daemon (pm2 on Windows, else oxmgr)\n ay serve uninstall remove daemon\n ay serve logs view daemon logs\n\nOnce running, connect from another machine:\n ay ls <token>@<host>:${DEFAULT_PORT}\n ay remote add <alias> http://<token>@<host>:${DEFAULT_PORT}\n`);
238
300
  return 0;
239
301
  }
240
302
  const sub = rest[0];
@@ -269,7 +331,7 @@ Options:
269
331
  alias: "d",
270
332
  type: "boolean",
271
333
  default: false,
272
- description: "Install as a background daemon via oxmgr (same as: ay serve install <flags>)"
334
+ description: "Install as a background daemon (same as: ay serve install <flags>)"
273
335
  }).option("allow-spawn", {
274
336
  type: "boolean",
275
337
  default: false,
@@ -758,7 +820,7 @@ Options:
758
820
  const webrtcVal = argv.webrtc ?? argv.share;
759
821
  const explicitUrl = typeof webrtcVal === "string" && webrtcVal.startsWith("webrtc://") ? webrtcVal : void 0;
760
822
  try {
761
- const { startShare, loadOrCreateShareRoom } = await import("./share-DwzKXEsJ.js");
823
+ const { startShare, loadOrCreateShareRoom } = await import("./share-BM-H85FE.js");
762
824
  const { link, close } = await startShare({
763
825
  url: explicitUrl ?? await loadOrCreateShareRoom(),
764
826
  localFetch: apiFetch,
@@ -789,4 +851,4 @@ Options:
789
851
 
790
852
  //#endregion
791
853
  export { cmdServe };
792
- //# sourceMappingURL=serve-uhfwjvBd.js.map
854
+ //# sourceMappingURL=serve-DKpVuQyU.js.map
@@ -0,0 +1,82 @@
1
+ import { t as agentYesHome } from "./agentYesHome-BvaUOzCV.js";
2
+ import { mkdirSync, readFileSync, writeFileSync } from "fs";
3
+ import { homedir } from "os";
4
+ import path from "path";
5
+ import { existsSync as existsSync$1 } from "node:fs";
6
+ import { stdin, stdout } from "node:process";
7
+ import { createInterface } from "node:readline/promises";
8
+
9
+ //#region ts/workspaceConfig.ts
10
+ function configPath() {
11
+ return path.join(agentYesHome(), "config.json");
12
+ }
13
+ function readConfig() {
14
+ try {
15
+ return JSON.parse(readFileSync(configPath(), "utf-8"));
16
+ } catch {
17
+ return {};
18
+ }
19
+ }
20
+ /** Expand a leading `~` (`~` or `~/x`) to an absolute home-based path. */
21
+ function expandTilde(p) {
22
+ const s = p.trim();
23
+ if (s === "~") return homedir();
24
+ if (s.startsWith("~/") || s.startsWith("~\\")) return path.join(homedir(), s.slice(2));
25
+ return s;
26
+ }
27
+ /** The configured workspace root (absolute), or the home dir if unset. */
28
+ function getWorkspaceRoot() {
29
+ const w = readConfig().workspace;
30
+ return w && w.trim() ? w : homedir();
31
+ }
32
+ /** Persist the workspace root, tilde-expanded and resolved to an absolute path. */
33
+ function setWorkspaceRoot(dir) {
34
+ const abs = path.resolve(expandTilde(dir));
35
+ const cfg = readConfig();
36
+ cfg.workspace = abs;
37
+ mkdirSync(agentYesHome(), { recursive: true });
38
+ writeFileSync(configPath(), JSON.stringify(cfg, null, 2));
39
+ return abs;
40
+ }
41
+
42
+ //#endregion
43
+ //#region ts/setup.ts
44
+ async function cmdSetup(rest) {
45
+ if (rest.includes("-h") || rest.includes("--help")) {
46
+ process.stdout.write("Usage: ay setup [workspace-dir] [--no-share] [--port N]\n\nGuided setup:\n 1. pick the workspace root new agents spawn into (default: your home dir)\n 2. share this machine to the agent-yes.com console (a restart-on-boot daemon)\n\nOptions:\n workspace-dir default directory for new agents (skips the prompt)\n --no-share set the workspace only; don't install the share daemon\n --port N HTTP API port for the share daemon (default: 7432)\n");
47
+ return 0;
48
+ }
49
+ const noShare = rest.includes("--no-share");
50
+ const portIdx = rest.indexOf("--port");
51
+ const port = portIdx >= 0 ? rest[portIdx + 1] : void 0;
52
+ let ws = rest.filter((a, i) => !a.startsWith("-") && i !== portIdx + 1)[0];
53
+ if (!ws) {
54
+ const current = getWorkspaceRoot();
55
+ if (stdin.isTTY && stdout.isTTY) {
56
+ const rl = createInterface({
57
+ input: stdin,
58
+ output: stdout
59
+ });
60
+ try {
61
+ ws = (await rl.question(`Workspace root for new agents [${current}]: `)).trim() || current;
62
+ } finally {
63
+ rl.close();
64
+ }
65
+ } else ws = current;
66
+ }
67
+ const abs = setWorkspaceRoot(ws);
68
+ process.stdout.write(`workspace root: ${abs}\n`);
69
+ if (!existsSync$1(abs)) process.stderr.write(` note: that directory doesn't exist yet — create it, or agents spawned there will fail\n`);
70
+ if (noShare) return 0;
71
+ process.stdout.write(`\nsharing this machine to agent-yes.com…\n`);
72
+ const { cmdServe } = await import("./serve-DKpVuQyU.js");
73
+ return cmdServe([
74
+ "install",
75
+ "--share",
76
+ ...port ? ["--port", port] : []
77
+ ]);
78
+ }
79
+
80
+ //#endregion
81
+ export { cmdSetup };
82
+ //# sourceMappingURL=setup-BFiD3CAi.js.map
@@ -215,4 +215,4 @@ async function startShare(opts) {
215
215
 
216
216
  //#endregion
217
217
  export { loadOrCreateShareRoom, startShare };
218
- //# sourceMappingURL=share-DwzKXEsJ.js.map
218
+ //# sourceMappingURL=share-BM-H85FE.js.map
@@ -1,5 +1,5 @@
1
- import { i as readGlobalPids } from "./globalPidIndex-yVd3mbsV.js";
2
- import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-C3xPRtfg.js";
1
+ import { i as readGlobalPids } from "./globalPidIndex-gZuTvTBs.js";
2
+ import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-BufkGk0e.js";
3
3
  import ms from "ms";
4
4
  import yargs from "yargs";
5
5
  import { appendFile, mkdir, open, readFile, stat, writeFile } from "fs/promises";
@@ -231,15 +231,15 @@ async function runSubcommand(argv) {
231
231
  case "restart": return await cmdRestart(rest);
232
232
  case "note": return await cmdNote(rest);
233
233
  case "serve": {
234
- const { cmdServe } = await import("./serve-uhfwjvBd.js");
234
+ const { cmdServe } = await import("./serve-DKpVuQyU.js");
235
235
  return cmdServe(rest);
236
236
  }
237
237
  case "setup": {
238
- const { cmdSetup } = await import("./setup.ts");
238
+ const { cmdSetup } = await import("./setup-BFiD3CAi.js");
239
239
  return cmdSetup(rest);
240
240
  }
241
241
  case "remote": {
242
- const { cmdRemote } = await import("./remotes-C9WMt5PY.js");
242
+ const { cmdRemote } = await import("./remotes-DavR4Hca.js");
243
243
  return cmdRemote(rest);
244
244
  }
245
245
  case "help": return cmdHelp();
@@ -1680,4 +1680,4 @@ async function cmdStatus(rest) {
1680
1680
 
1681
1681
  //#endregion
1682
1682
  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 };
1683
- //# sourceMappingURL=subcommands-tH8JdrBS.js.map
1683
+ //# sourceMappingURL=subcommands-BLSPrqA1.js.map
@@ -1,6 +1,6 @@
1
1
  import "./logger-B9h0djqx.js";
2
- import "./globalPidIndex-yVd3mbsV.js";
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-tH8JdrBS.js";
2
+ import "./globalPidIndex-gZuTvTBs.js";
3
+ import "./remotes-BufkGk0e.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-BLSPrqA1.js";
5
5
 
6
6
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -175,4 +175,4 @@ async function startTray() {
175
175
 
176
176
  //#endregion
177
177
  export { ensureTray, startTray };
178
- //# sourceMappingURL=tray-CWQe9DMY.js.map
178
+ //# sourceMappingURL=tray-B8_rx1iu.js.map
@@ -1,8 +1,9 @@
1
1
  import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
2
- import { r as getInstalledPackage } from "./versionChecker-D-OYziVA.js";
3
- import { n as agentYesHome, t as PidStore } from "./pidStore-BcGnnKQf.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-D-drPifS.js";
3
+ import { t as agentYesHome } from "./agentYesHome-BvaUOzCV.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CJxsoGdb.js";
5
- import { i as readGlobalPids } from "./globalPidIndex-yVd3mbsV.js";
5
+ import { t as PidStore } from "./pidStore-B5vBu8Px.js";
6
+ import { i as readGlobalPids } from "./globalPidIndex-gZuTvTBs.js";
6
7
  import { arch, platform } from "process";
7
8
  import { execSync } from "child_process";
8
9
  import { closeSync, constants, createReadStream, existsSync, mkdirSync, openSync } from "fs";
@@ -1039,7 +1040,7 @@ async function notifyWebhook(status, details, cwd = process.cwd()) {
1039
1040
 
1040
1041
  //#endregion
1041
1042
  //#region ts/index.ts
1042
- const config = await import("./agent-yes.config-1LMoK18R.js").then((mod) => mod.default || mod);
1043
+ const config = await import("./agent-yes.config-kmtJKJHk.js").then((mod) => mod.default || mod);
1043
1044
  const CLIS_CONFIG = config.clis;
1044
1045
  /**
1045
1046
  * Main function to run agent-cli with automatic yes/no responses
@@ -1714,4 +1715,4 @@ function sleep(ms) {
1714
1715
 
1715
1716
  //#endregion
1716
1717
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1717
- //# sourceMappingURL=ts-DYzATaI_.js.map
1718
+ //# sourceMappingURL=ts-BUpRpKjH.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.118.0";
10
+ var version = "1.118.2";
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-D-OYziVA.js.map
224
+ //# sourceMappingURL=versionChecker-D-drPifS.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.118.0",
3
+ "version": "1.118.2",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
package/ts/serve.ts CHANGED
@@ -98,20 +98,53 @@ function freshAgentEnv(): Record<string, string> {
98
98
 
99
99
  const DAEMON_NAME = "agent-yes";
100
100
 
101
- // Register the oxmgr daemon with the platform init system (launchd on macOS,
102
- // systemd on Linux, Task Scheduler on Windows) so managed processes — including
103
- // the agent-yes daemon come back after a *reboot*, not just a crash. Idempotent:
104
- // a no-op if the service is already installed. Best-effort: returns false on any
105
- // failure (e.g. a system-level systemd unit that needs sudo) without aborting the
106
- // install the process is still managed, just not boot-persistent.
107
- async function ensureBootAutostart(oxmgrBin: string): Promise<boolean> {
101
+ type DaemonManager = { id: "oxmgr" | "pm2"; bin: string };
102
+
103
+ // Pick the process manager used to daemonize `ay serve`. oxmgr's daemon talks
104
+ // over a fixed TCP port; on Windows a crashed daemon routinely leaves the
105
+ // socket orphaned on a dead PID, which wedges every subsequent oxmgr command
106
+ // with "daemon did not become ready in time". pm2's named-pipe daemon does not
107
+ // have that failure mode, so we prefer pm2 on Windows. Elsewhere oxmgr stays
108
+ // the default. AGENT_YES_DAEMON_MANAGER=pm2|oxmgr forces a choice.
109
+ function resolveDaemonManager(): DaemonManager | null {
110
+ const oxmgr = Bun.which("oxmgr");
111
+ const pm2 = Bun.which("pm2");
112
+ const override = process.env.AGENT_YES_DAEMON_MANAGER?.toLowerCase();
113
+ if (override === "pm2") return pm2 ? { id: "pm2", bin: pm2 } : null;
114
+ if (override === "oxmgr") return oxmgr ? { id: "oxmgr", bin: oxmgr } : null;
115
+ const order: Array<DaemonManager | null> =
116
+ process.platform === "win32"
117
+ ? [pm2 && { id: "pm2", bin: pm2 }, oxmgr && { id: "oxmgr", bin: oxmgr }]
118
+ : [oxmgr && { id: "oxmgr", bin: oxmgr }, pm2 && { id: "pm2", bin: pm2 }];
119
+ return order.find((m): m is DaemonManager => !!m) ?? null;
120
+ }
121
+
122
+ // Resolve the argv that launches `ay serve …` from the daemon. The daemon's
123
+ // environment may not have ~/.bun/bin on PATH, so we use an absolute path.
124
+ // On Windows the `ay` bin is a self-contained launcher (ay.exe) we exec
125
+ // directly; on POSIX it's a `#!/usr/bin/env bun` script we run through bun.
126
+ function ayServeArgv(args: string[]): string[] {
127
+ const ayBin = Bun.which("ay");
128
+ const launcher = ayBin
129
+ ? process.platform === "win32"
130
+ ? [ayBin]
131
+ : [process.execPath, ayBin]
132
+ : ["ay"];
133
+ return [...launcher, "serve", ...args];
134
+ }
135
+
136
+ // Register the daemon with the platform init system so it comes back after a
137
+ // *reboot*, not just a crash. oxmgr wires launchd/systemd/Task Scheduler via
138
+ // `oxmgr service install`; pm2 persists its process list with `pm2 save` (a
139
+ // once-installed `pm2 startup` hook then resurrects it on boot). Idempotent and
140
+ // best-effort: returns false on any failure without aborting the install — the
141
+ // process is still crash-managed, just not guaranteed boot-persistent.
142
+ async function ensureBootAutostart(mgr: DaemonManager): Promise<boolean> {
108
143
  try {
109
- // --system defaults to "auto" (launchd/systemd/Task Scheduler by platform);
110
- // it's a `service`-level flag, so passing it after `install` is rejected.
111
- const svc = Bun.spawn([oxmgrBin, "service", "install"], {
112
- stdio: ["ignore", "ignore", "ignore"],
113
- });
114
- return (await svc.exited) === 0;
144
+ // oxmgr's --system defaults to "auto" (launchd/systemd/Task Scheduler); it's
145
+ // a `service`-level flag, so it goes before the subcommand, not after.
146
+ const cmd = mgr.id === "oxmgr" ? [mgr.bin, "service", "install"] : [mgr.bin, "save"];
147
+ return (await Bun.spawn(cmd, { stdio: ["ignore", "ignore", "ignore"] }).exited) === 0;
115
148
  } catch {
116
149
  return false;
117
150
  }
@@ -125,18 +158,29 @@ async function spawnExit(cmd: string[]): Promise<number> {
125
158
  }
126
159
  }
127
160
 
128
- // The `serve` args the running daemon was started with, parsed out of oxmgr's
129
- // stored command line (`… ay serve --share --port 7433`). null when no daemon is
130
- // registered. Lets a bare `ay serve install` re-launch with the SAME args.
131
- async function readDaemonServeArgs(oxmgrBin: string): Promise<string[] | null> {
161
+ // The `serve` args the running daemon was started with, so a bare
162
+ // `ay serve install` can re-launch with the SAME args. null when no daemon is
163
+ // registered. oxmgr stores the full command line (`… ay serve --share`); pm2
164
+ // keeps the post-`--` argv in pm2_env.args (with a leading "serve" we strip).
165
+ async function readDaemonServeArgs(mgr: DaemonManager): Promise<string[] | null> {
132
166
  try {
133
- const p = Bun.spawn([oxmgrBin, "status", DAEMON_NAME], { stdout: "pipe", stderr: "ignore" });
167
+ if (mgr.id === "oxmgr") {
168
+ const p = Bun.spawn([mgr.bin, "status", DAEMON_NAME], { stdout: "pipe", stderr: "ignore" });
169
+ const out = await new Response(p.stdout).text();
170
+ if ((await p.exited) !== 0) return null;
171
+ const m = /Command:\s*(.+)/.exec(out);
172
+ if (!m) return null;
173
+ const after = /\bserve\b\s*(.*)$/.exec(m[1]!.trim());
174
+ return after ? after[1]!.split(/\s+/).filter(Boolean) : [];
175
+ }
176
+ const p = Bun.spawn([mgr.bin, "jlist"], { stdout: "pipe", stderr: "ignore" });
134
177
  const out = await new Response(p.stdout).text();
135
178
  if ((await p.exited) !== 0) return null;
136
- const m = /Command:\s*(.+)/.exec(out);
137
- if (!m) return null;
138
- const after = /\bserve\b\s*(.*)$/.exec(m[1]!.trim());
139
- return after ? after[1]!.split(/\s+/).filter(Boolean) : [];
179
+ const list = JSON.parse(out) as Array<{ name?: string; pm2_env?: { args?: string[] } }>;
180
+ const proc = list.find((x) => x.name === DAEMON_NAME);
181
+ if (!proc) return null;
182
+ const a = proc.pm2_env?.args ?? [];
183
+ return a[0] === "serve" ? a.slice(1) : a;
140
184
  } catch {
141
185
  return null;
142
186
  }
@@ -164,12 +208,12 @@ async function fetchDaemonVersion(port: number, token: string): Promise<string |
164
208
  }
165
209
 
166
210
  async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
167
- const oxmgrBin = Bun.which("oxmgr");
168
- if (!oxmgrBin) {
211
+ const mgr = resolveDaemonManager();
212
+ if (!mgr) {
169
213
  process.stderr.write(
170
- "ay serve install: oxmgr not found\n" +
171
- " install with: cargo install oxmgr\n" +
172
- " or: bun add -g oxmgr\n",
214
+ "ay serve install: no process manager found (need pm2 or oxmgr)\n" +
215
+ " install with: bun add -g pm2\n" +
216
+ " or: cargo install oxmgr\n",
173
217
  );
174
218
  return 1;
175
219
  }
@@ -181,7 +225,7 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
181
225
  // started with (so a bare `ay serve install` stays "the same daemon"), unless
182
226
  // new args are given. The persisted room + token mean the share link is
183
227
  // unchanged across the restart.
184
- const priorArgs = await readDaemonServeArgs(oxmgrBin);
228
+ const priorArgs = await readDaemonServeArgs(mgr);
185
229
  const effArgs = args.length ? args : (priorArgs ?? []);
186
230
  const current = getInstalledPackage().version;
187
231
 
@@ -189,34 +233,42 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
189
233
  // A daemon already exists — only disturb it if it's actually outdated.
190
234
  const runningVer = await fetchDaemonVersion(portFromArgs(effArgs), token);
191
235
  if (runningVer === current) {
192
- await ensureBootAutostart(oxmgrBin);
236
+ await ensureBootAutostart(mgr);
193
237
  process.stdout.write(`'${DAEMON_NAME}' already running v${current} (up to date)\n`);
194
238
  return 0;
195
239
  }
196
240
  // Outdated (or unreachable/too-old to report) → graceful roll-forward.
197
- // `oxmgr stop` sends SIGTERM, which cmdServe handles cleanly (closing share
241
+ // `stop` sends SIGTERM, which cmdServe handles cleanly (closing share
198
242
  // peers so browsers reconnect fast), then we re-create with the new binary.
199
243
  process.stdout.write(
200
244
  `rolling '${DAEMON_NAME}' ${runningVer ? `v${runningVer}` : "(unknown)"} → v${current}…\n`,
201
245
  );
202
- await spawnExit([oxmgrBin, "stop", DAEMON_NAME]);
203
- await spawnExit([oxmgrBin, "delete", DAEMON_NAME]);
246
+ await spawnExit([mgr.bin, "stop", DAEMON_NAME]);
247
+ await spawnExit([mgr.bin, "delete", DAEMON_NAME]);
204
248
  }
205
249
 
206
- // Build the ay serve command with forwarded args (port, host, --webrtc, etc.).
207
- // Absolute paths: oxmgr's daemon environment may not have ~/.bun/bin in
208
- // PATH, so a bare `ay` (or its `#!/usr/bin/env bun` shebang) fails to spawn.
209
- const ayBin = Bun.which("ay");
210
- const serveCmd = [...(ayBin ? [process.execPath, ayBin] : ["ay"]), "serve", ...effArgs].join(
211
- " ",
212
- );
213
- const proc = Bun.spawn(
214
- [oxmgrBin, "start", serveCmd, "--name", DAEMON_NAME, "--restart", "always"],
215
- { stdio: ["ignore", "inherit", "inherit"] },
216
- );
250
+ // oxmgr takes the command as one string; pm2 takes the binary plus its
251
+ // args after `--`. Both auto-restart on crash by default (pm2) / via the
252
+ // explicit flag (oxmgr).
253
+ const serveArgv = ayServeArgv(effArgs);
254
+ const startArgv =
255
+ mgr.id === "oxmgr"
256
+ ? [mgr.bin, "start", serveArgv.join(" "), "--name", DAEMON_NAME, "--restart", "always"]
257
+ : [
258
+ mgr.bin,
259
+ "start",
260
+ serveArgv[0]!,
261
+ "--name",
262
+ DAEMON_NAME,
263
+ "--interpreter",
264
+ "none",
265
+ "--",
266
+ ...serveArgv.slice(1),
267
+ ];
268
+ const proc = Bun.spawn(startArgv, { stdio: ["ignore", "inherit", "inherit"] });
217
269
  const code = await proc.exited;
218
270
  if (code === 0) {
219
- const onBoot = await ensureBootAutostart(oxmgrBin);
271
+ const onBoot = await ensureBootAutostart(mgr);
220
272
  const port = portFromArgs(effArgs);
221
273
  // Mirror cmdServe's mode resolution: webrtc-only daemons open no HTTP port.
222
274
  const webrtcish = effArgs.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
@@ -224,13 +276,20 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
224
276
  effArgs.some((a) => a.startsWith("--http") || a.startsWith("--share")) ||
225
277
  !effArgs.some((a) => a.startsWith("--webrtc"));
226
278
  process.stdout.write(
227
- `\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via oxmgr —`} v${current}\n`,
228
- );
229
- process.stdout.write(
230
- onBoot
231
- ? `start-on-boot: enabled (oxmgr registered with the system init)\n`
232
- : `start-on-boot: not registered — run \`oxmgr service install\` to enable\n`,
279
+ `\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via ${mgr.id} —`} v${current}\n`,
233
280
  );
281
+ if (mgr.id === "oxmgr")
282
+ process.stdout.write(
283
+ onBoot
284
+ ? `start-on-boot: enabled (oxmgr registered with the system init)\n`
285
+ : `start-on-boot: not registered — run \`oxmgr service install\` to enable\n`,
286
+ );
287
+ else
288
+ process.stdout.write(
289
+ onBoot
290
+ ? `start-on-boot: pm2 list saved (run \`pm2 startup\` once for boot resurrect)\n`
291
+ : `start-on-boot: \`pm2 save\` failed — run it manually to persist across reboots\n`,
292
+ );
234
293
  process.stdout.write(`token: ${token}\n\n`);
235
294
  if (httpish) {
236
295
  process.stdout.write(` ay ls ${token}@<host>:${port}\n`);
@@ -249,14 +308,18 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
249
308
  }
250
309
 
251
310
  if (sub === "uninstall") {
252
- const proc = Bun.spawn([oxmgrBin, "delete", DAEMON_NAME], {
311
+ const proc = Bun.spawn([mgr.bin, "delete", DAEMON_NAME], {
253
312
  stdio: ["ignore", "inherit", "inherit"],
254
313
  });
255
- return (await proc.exited) ?? 1;
314
+ const code = (await proc.exited) ?? 1;
315
+ // Drop it from the persisted pm2 list too, so `pm2 resurrect` won't revive it.
316
+ if (mgr.id === "pm2" && code === 0)
317
+ await Bun.spawn([mgr.bin, "save"], { stdio: ["ignore", "ignore", "ignore"] }).exited;
318
+ return code;
256
319
  }
257
320
 
258
321
  if (sub === "logs") {
259
- const proc = Bun.spawn([oxmgrBin, "logs", DAEMON_NAME, ...args], {
322
+ const proc = Bun.spawn([mgr.bin, "logs", DAEMON_NAME, ...args], {
260
323
  stdio: ["ignore", "inherit", "inherit"],
261
324
  });
262
325
  return (await proc.exited) ?? 1;
@@ -287,13 +350,13 @@ export async function cmdServe(rest: string[]): Promise<number> {
287
350
  ` --port N Port to listen on (default: ${DEFAULT_PORT})\n` +
288
351
  ` --host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)\n` +
289
352
  ` --token TOKEN Auth token (auto-generated and saved if omitted)\n` +
290
- ` -d, --daemon Install these flags as a background daemon via oxmgr\n` +
353
+ ` -d, --daemon Install these flags as a background daemon (pm2/oxmgr)\n` +
291
354
  ` (same as: ay serve install <flags>)\n` +
292
355
  ` --allow-spawn Deprecated no-op — the console can always spawn agents\n` +
293
356
  ` --tls-cert FILE TLS certificate PEM\n` +
294
357
  ` --tls-key FILE TLS private key PEM\n\n` +
295
358
  `Subcommands:\n` +
296
- ` ay serve install install as background daemon via oxmgr\n` +
359
+ ` ay serve install install as background daemon (pm2 on Windows, else oxmgr)\n` +
297
360
  ` ay serve uninstall remove daemon\n` +
298
361
  ` ay serve logs view daemon logs\n\n` +
299
362
  `Once running, connect from another machine:\n` +
@@ -337,7 +400,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
337
400
  alias: "d",
338
401
  type: "boolean",
339
402
  default: false,
340
- description: "Install as a background daemon via oxmgr (same as: ay serve install <flags>)",
403
+ description: "Install as a background daemon (same as: ay serve install <flags>)",
341
404
  })
342
405
  .option("allow-spawn", {
343
406
  type: "boolean",
@@ -350,7 +413,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
350
413
 
351
414
  const argv = await y.parseAsync();
352
415
 
353
- // --daemon/-d: install these exact flags as the oxmgr daemon instead of
416
+ // --daemon/-d: install these exact flags as the background daemon instead of
354
417
  // serving in the foreground (sugar for `ay serve install <flags>`).
355
418
  if (argv.daemon) {
356
419
  const fwd = rest.filter((a) => a !== "--daemon" && a !== "-d");
@@ -0,0 +1,42 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
+ import { mkdtempSync, rmSync } from "fs";
3
+ import { tmpdir } from "os";
4
+ import path from "path";
5
+ import { cmdSetup } from "./setup.ts";
6
+ import { getWorkspaceRoot } from "./workspaceConfig.ts";
7
+
8
+ // Guards against the regression where `ay setup` was registered + documented but
9
+ // its module was missing ("Cannot find module './setup.ts'"). Exercises the
10
+ // --no-share path only, so no daemon is installed.
11
+ describe("cmdSetup", () => {
12
+ let original: string | undefined;
13
+ let tmp: string;
14
+ beforeEach(() => {
15
+ original = process.env.AGENT_YES_HOME;
16
+ tmp = mkdtempSync(path.join(tmpdir(), "ay-setup-"));
17
+ process.env.AGENT_YES_HOME = tmp;
18
+ });
19
+ afterEach(() => {
20
+ if (original === undefined) delete process.env.AGENT_YES_HOME;
21
+ else process.env.AGENT_YES_HOME = original;
22
+ rmSync(tmp, { recursive: true, force: true });
23
+ });
24
+
25
+ it("--help returns 0 without touching config", async () => {
26
+ expect(await cmdSetup(["--help"])).toBe(0);
27
+ });
28
+
29
+ it("--no-share sets the workspace root and skips the daemon", async () => {
30
+ const dir = path.join(tmp, "myspace");
31
+ const code = await cmdSetup(["--no-share", dir]);
32
+ expect(code).toBe(0);
33
+ expect(getWorkspaceRoot()).toBe(path.resolve(dir));
34
+ });
35
+
36
+ it("--no-share with a --port flag still treats the path as the workspace", async () => {
37
+ const dir = path.join(tmp, "ws");
38
+ const code = await cmdSetup(["--no-share", "--port", "7440", dir]);
39
+ expect(code).toBe(0);
40
+ expect(getWorkspaceRoot()).toBe(path.resolve(dir));
41
+ });
42
+ });
package/ts/setup.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { existsSync } from "node:fs";
2
+ import { stdin, stdout } from "node:process";
3
+ import { createInterface } from "node:readline/promises";
4
+ import { getWorkspaceRoot, setWorkspaceRoot } from "./workspaceConfig.ts";
5
+
6
+ // `ay setup` — guided onboarding. Two steps:
7
+ // 1. choose the workspace root (the default cwd new agents spawn into when the
8
+ // console doesn't pass one — see workspaceConfig), and
9
+ // 2. share this machine to the agent-yes.com console over WebRTC, installed as a
10
+ // restart-on-boot daemon (delegates to `ay serve install --share`).
11
+ //
12
+ // Designed to degrade gracefully: with a TTY it prompts (defaulting to the
13
+ // current/home workspace); piped or in a script it takes the first positional as
14
+ // the workspace and otherwise keeps the current one — never blocking on input.
15
+ export async function cmdSetup(rest: string[]): Promise<number> {
16
+ if (rest.includes("-h") || rest.includes("--help")) {
17
+ process.stdout.write(
18
+ `Usage: ay setup [workspace-dir] [--no-share] [--port N]\n\n` +
19
+ `Guided setup:\n` +
20
+ ` 1. pick the workspace root new agents spawn into (default: your home dir)\n` +
21
+ ` 2. share this machine to the agent-yes.com console (a restart-on-boot daemon)\n\n` +
22
+ `Options:\n` +
23
+ ` workspace-dir default directory for new agents (skips the prompt)\n` +
24
+ ` --no-share set the workspace only; don't install the share daemon\n` +
25
+ ` --port N HTTP API port for the share daemon (default: 7432)\n`,
26
+ );
27
+ return 0;
28
+ }
29
+
30
+ const noShare = rest.includes("--no-share");
31
+ const portIdx = rest.indexOf("--port");
32
+ const port = portIdx >= 0 ? rest[portIdx + 1] : undefined;
33
+ // The workspace is the first non-flag token (and not the value of --port).
34
+ const positional = rest.filter((a, i) => !a.startsWith("-") && i !== portIdx + 1);
35
+
36
+ // 1. Workspace root.
37
+ let ws = positional[0];
38
+ if (!ws) {
39
+ const current = getWorkspaceRoot();
40
+ if (stdin.isTTY && stdout.isTTY) {
41
+ const rl = createInterface({ input: stdin, output: stdout });
42
+ try {
43
+ const ans = (await rl.question(`Workspace root for new agents [${current}]: `)).trim();
44
+ ws = ans || current;
45
+ } finally {
46
+ rl.close();
47
+ }
48
+ } else {
49
+ ws = current; // non-interactive: keep whatever's configured (home by default)
50
+ }
51
+ }
52
+ const abs = setWorkspaceRoot(ws);
53
+ process.stdout.write(`workspace root: ${abs}\n`);
54
+ if (!existsSync(abs)) {
55
+ process.stderr.write(
56
+ ` note: that directory doesn't exist yet — create it, or agents spawned there will fail\n`,
57
+ );
58
+ }
59
+
60
+ if (noShare) return 0;
61
+
62
+ // 2. Share to agent-yes.com as a boot-persistent daemon. `ay serve install`
63
+ // handles oxmgr registration, version roll-forward, and printing the link.
64
+ process.stdout.write(`\nsharing this machine to agent-yes.com…\n`);
65
+ const { cmdServe } = await import("./serve.ts");
66
+ return cmdServe(["install", "--share", ...(port ? ["--port", port] : [])]);
67
+ }
@@ -1,8 +0,0 @@
1
- import "./ts-DYzATaI_.js";
2
- import "./logger-B9h0djqx.js";
3
- import "./versionChecker-D-OYziVA.js";
4
- import "./pidStore-BcGnnKQf.js";
5
- import "./globalPidIndex-yVd3mbsV.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CrIGjD28.js";
7
-
8
- export { SUPPORTED_CLIS };
@@ -1,5 +0,0 @@
1
- import "./logger-B9h0djqx.js";
2
- import { t as PidStore } from "./pidStore-BcGnnKQf.js";
3
- import "./globalPidIndex-yVd3mbsV.js";
4
-
5
- export { PidStore };