agent-yes 1.169.1 → 1.170.1

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.
Files changed (61) hide show
  1. package/dist/{SUPPORTED_CLIS-Bwg9VqLI.js → SUPPORTED_CLIS-ByppVvWO.js} +2 -2
  2. package/dist/SUPPORTED_CLIS-C_c097RZ.js +8 -0
  3. package/dist/{agent-yes.config-Bl08kMpl.js → agent-yes.config-ugPYTPOd.js} +2 -2
  4. package/dist/{agentYesHome-_eJa3DaX.js → agentYesHome-CtHb5b71.js} +1 -1
  5. package/dist/cli.js +10 -212
  6. package/dist/{configShared-CEyhl0WH.js → configShared-0MnIQ652.js} +1 -1
  7. package/dist/{e2e-BeKjLhmO.js → e2e-jb0Hp43q.js} +1 -1
  8. package/dist/{forkNested-UCKPEgSI.js → forkNested-DhJxa4q4.js} +2 -2
  9. package/dist/{globalPidIndex-DlmmJlO8.js → globalPidIndex-CoNr7tS8.js} +1 -1
  10. package/dist/index.js +4 -4
  11. package/dist/notifyDaemon-D5jtpCAR.js +591 -0
  12. package/dist/{openBrowser-CCF1iuQK.js → openBrowser-ChR4llYa.js} +1 -1
  13. package/dist/{pidStore-BfoBWUjc.js → pidStore-BIvsBQ8X.js} +3 -3
  14. package/dist/pidStore-CLxwp0qI.js +5 -0
  15. package/dist/{reaper-C-eWAxIj.js → reaper-CWF2_ATd.js} +27 -3
  16. package/dist/{reaper-BQgDb85W.js → reaper-jfSmog15.js} +1 -1
  17. package/dist/{remotes-cx_GDFPj.js → remotes-BQMr4_En.js} +3 -3
  18. package/dist/{remotes-BmAPylU_.js → remotes-CC-4GuJb.js} +3 -3
  19. package/dist/{runningLock-BobVW1_A.js → runningLock-CNMl13dC.js} +1 -1
  20. package/dist/rustBinary-DsO0IlNq.js +228 -0
  21. package/dist/{schedule-CtVvSyaN.js → schedule-ChTPIcXf.js} +7 -7
  22. package/dist/{serve-CLmk5ujF.js → serve-De1gAWNI.js} +120 -26
  23. package/dist/{setup-CYbXOcpH.js → setup-nlMqyEPB.js} +3 -3
  24. package/dist/{share-D_e2Fwiy.js → share-QregR8a_.js} +2 -2
  25. package/dist/spawnGate-BFhva-2F.js +5 -0
  26. package/dist/{spawnGate-DzPfa1PZ.js → spawnGate-IJDByl2U.js} +3 -3
  27. package/dist/{subcommands--EUush_X.js → subcommands-BFlpV23b.js} +1049 -44
  28. package/dist/subcommands-DLDpwrfG.js +9 -0
  29. package/dist/{tray-CWUpaZF4.js → tray-DXr7iK3E.js} +2 -2
  30. package/dist/{ts-Bbhf5JbN.js → ts-DsxejviJ.js} +8 -8
  31. package/dist/{versionChecker-BmWkWhcJ.js → versionChecker-Czk3aHqy.js} +2 -2
  32. package/dist/{webrtcLink-BG0Xc4-W.js → webrtcLink-B7REGtK2.js} +2 -2
  33. package/dist/{webrtcRemote-SybKurg9.js → webrtcRemote-Bx-eD_0I.js} +3 -3
  34. package/dist/{workspaceConfig-BC03X4Y1.js → workspaceConfig-BgqK-31W.js} +37 -6
  35. package/lab/ui/console-logic.js +79 -10
  36. package/lab/ui/index.html +174 -8
  37. package/lab/ui/landing.html +11 -1
  38. package/package.json +1 -1
  39. package/scripts/build-rs.ts +9 -5
  40. package/ts/badges.spec.ts +51 -1
  41. package/ts/badges.ts +35 -1
  42. package/ts/notifyDaemon.spec.ts +276 -0
  43. package/ts/notifyDaemon.ts +698 -0
  44. package/ts/notifyInbox.spec.ts +192 -0
  45. package/ts/notifyInbox.ts +294 -0
  46. package/ts/notifyRouter.spec.ts +355 -0
  47. package/ts/notifyRouter.ts +288 -0
  48. package/ts/notifyStore.spec.ts +308 -0
  49. package/ts/notifyStore.ts +439 -0
  50. package/ts/reaper.spec.ts +18 -1
  51. package/ts/reaper.ts +33 -1
  52. package/ts/rustBinary.ts +30 -0
  53. package/ts/serve.ts +180 -14
  54. package/ts/subcommands.spec.ts +341 -0
  55. package/ts/subcommands.ts +764 -29
  56. package/ts/workspaceConfig.spec.ts +48 -0
  57. package/ts/workspaceConfig.ts +46 -3
  58. package/dist/SUPPORTED_CLIS-DiL99G3j.js +0 -8
  59. package/dist/pidStore-5qyLX3Fh.js +0 -5
  60. package/dist/spawnGate-CQ1Il3Xk.js +0 -5
  61. package/dist/subcommands-1ZcaHYkb.js +0 -9
@@ -1,4 +1,4 @@
1
- import { n as isWebrtcSpec } from "./webrtcLink-BG0Xc4-W.js";
1
+ import { n as isWebrtcSpec } from "./webrtcLink-B7REGtK2.js";
2
2
  import { mkdir, readFile, writeFile } from "fs/promises";
3
3
  import { homedir } from "os";
4
4
  import path from "path";
@@ -86,7 +86,7 @@ async function resolveRemoteSpec(spec) {
86
86
  * The bridge lives for the rest of the process (torn down on `process.exit`).
87
87
  */
88
88
  async function resolveWebrtc(link, keyword) {
89
- const { startWebrtcBridge } = await import("./webrtcRemote-SybKurg9.js");
89
+ const { startWebrtcBridge } = await import("./webrtcRemote-Bx-eD_0I.js");
90
90
  const bridge = await startWebrtcBridge(link);
91
91
  return {
92
92
  url: bridge.baseUrl,
@@ -173,4 +173,4 @@ async function cmdRemote(rest) {
173
173
 
174
174
  //#endregion
175
175
  export { resolveRemoteSpec as a, readRemotes as i, deleteRemoteAlias as n, writeRemoteAlias as o, parseDirectRemoteSpec as r, cmdRemote as t };
176
- //# sourceMappingURL=remotes-BmAPylU_.js.map
176
+ //# sourceMappingURL=remotes-CC-4GuJb.js.map
@@ -277,4 +277,4 @@ function shouldUseLock(_cwd) {
277
277
 
278
278
  //#endregion
279
279
  export { shouldUseLock as i, getRunningAgentCount as n, releaseLock as r, acquireLock as t };
280
- //# sourceMappingURL=runningLock-BobVW1_A.js.map
280
+ //# sourceMappingURL=runningLock-CNMl13dC.js.map
@@ -0,0 +1,228 @@
1
+ import { r as getInstalledPackage } from "./versionChecker-Czk3aHqy.js";
2
+ import { execFileSync } from "child_process";
3
+ import { existsSync, mkdirSync, unlinkSync } from "fs";
4
+ import { chmod, copyFile } from "fs/promises";
5
+ import path from "path";
6
+
7
+ //#region ts/rustBinary.ts
8
+ /**
9
+ * Rust binary helper - finds or downloads the appropriate prebuilt binary
10
+ */
11
+ const PLATFORM_MAP = {
12
+ "linux-x64": "agent-yes-linux-x64-musl",
13
+ "linux-arm64": "agent-yes-linux-arm64-musl",
14
+ "darwin-x64": "agent-yes-darwin-x64",
15
+ "darwin-arm64": "agent-yes-darwin-arm64",
16
+ "win32-x64": "agent-yes-win32-x64"
17
+ };
18
+ /**
19
+ * Get the binary name for the current platform
20
+ */
21
+ function getBinaryName() {
22
+ const platform = process.platform;
23
+ const arch = process.arch;
24
+ const binaryName = PLATFORM_MAP[`${platform}-${arch}`];
25
+ if (!binaryName) throw new Error(`Unsupported platform: ${platform}-${arch}. Supported: ${Object.keys(PLATFORM_MAP).join(", ")}`);
26
+ return binaryName + (platform === "win32" ? ".exe" : "");
27
+ }
28
+ /**
29
+ * Get the directory where binaries are stored
30
+ */
31
+ function getBinDir() {
32
+ const packageBinDir = path.resolve(import.meta.dirname ?? import.meta.dir, "../bin");
33
+ if (existsSync(packageBinDir)) return packageBinDir;
34
+ const cacheDir = process.env.AGENT_YES_CACHE_DIR || path.join(process.env.XDG_CACHE_HOME || path.join(process.env.HOME || "/tmp", ".cache"), "agent-yes");
35
+ return path.join(cacheDir, "bin", getInstalledPackage().version);
36
+ }
37
+ /**
38
+ * Find the Rust binary, checking multiple locations
39
+ */
40
+ function findRustBinary(verbose = false) {
41
+ const binaryName = getBinaryName();
42
+ const ext = process.platform === "win32" ? ".exe" : "";
43
+ const searchPaths = [
44
+ path.resolve(import.meta.dirname ?? import.meta.dir, `../rs/target/release/agent-yes${ext}`),
45
+ path.resolve(import.meta.dirname ?? import.meta.dir, `../rs/target/debug/agent-yes${ext}`),
46
+ path.join(getBinDir(), binaryName),
47
+ path.join(getBinDir(), binaryName)
48
+ ];
49
+ if (verbose) {
50
+ console.log(`[rust] Looking for binary: ${binaryName}`);
51
+ console.log(`[rust] Search paths:`);
52
+ }
53
+ for (const p of searchPaths) {
54
+ if (verbose) console.log(`[rust] - ${p}: ${existsSync(p) ? "FOUND" : "not found"}`);
55
+ if (existsSync(p)) return p;
56
+ }
57
+ }
58
+ /**
59
+ * Locate the `ay-spawn-hidden` launcher (Windows-only). Returns undefined off
60
+ * Windows, or when the launcher isn't present — older installs, or a release
61
+ * that predates it. Callers MUST fall back to spawning the program directly.
62
+ *
63
+ * The launcher is a second `[[bin]]` built alongside `agent-yes`, so it sits
64
+ * next to the Rust binary in every install shape: the cargo `target/` build
65
+ * dir, the downloaded release bin dir (getBinDir), and `~/.cargo/bin` on PATH.
66
+ */
67
+ function findSpawnHiddenLauncher() {
68
+ if (process.platform !== "win32") return void 0;
69
+ const dir = import.meta.dirname ?? import.meta.dir;
70
+ const searchPaths = [
71
+ path.resolve(dir, "../rs/target/release/ay-spawn-hidden.exe"),
72
+ path.resolve(dir, "../rs/target/debug/ay-spawn-hidden.exe"),
73
+ path.join(getBinDir(), "ay-spawn-hidden.exe"),
74
+ Bun.which("ay-spawn-hidden") ?? ""
75
+ ];
76
+ for (const p of searchPaths) if (p && existsSync(p)) return p;
77
+ }
78
+ /**
79
+ * Get GitHub release download URL for the binary
80
+ */
81
+ function getDownloadUrl(version = "latest") {
82
+ const binaryName = getBinaryName().replace(/\.exe$/, "");
83
+ const ext = process.platform === "win32" ? ".zip" : ".tar.gz";
84
+ if (version === "latest") return `https://github.com/snomiao/agent-yes/releases/latest/download/${binaryName}${ext}`;
85
+ return `https://github.com/snomiao/agent-yes/releases/download/v${version}/${binaryName}${ext}`;
86
+ }
87
+ /**
88
+ * Download and extract the binary
89
+ */
90
+ async function downloadBinary(verbose = false) {
91
+ const binDir = getBinDir();
92
+ const binaryName = getBinaryName();
93
+ const binaryPath = path.join(binDir, binaryName);
94
+ mkdirSync(binDir, { recursive: true });
95
+ const url = getDownloadUrl(getInstalledPackage().version);
96
+ if (verbose) console.log(`[rust] Downloading binary from: ${url}`);
97
+ const response = await fetch(url);
98
+ if (!response.ok) throw new Error(`Failed to download binary: ${response.status} ${response.statusText}`);
99
+ if (process.platform === "win32") {
100
+ const tempZipPath = path.join(binDir, "temp.zip");
101
+ await Bun.write(tempZipPath, await response.arrayBuffer());
102
+ await Bun.spawn([
103
+ "powershell",
104
+ "-Command",
105
+ `Expand-Archive -Path '${tempZipPath}' -DestinationPath '${binDir}' -Force`
106
+ ], {
107
+ cwd: binDir,
108
+ stdio: [
109
+ "ignore",
110
+ "pipe",
111
+ "pipe"
112
+ ]
113
+ }).exited;
114
+ try {
115
+ unlinkSync(tempZipPath);
116
+ } catch {}
117
+ } else {
118
+ const tarPath = path.join(binDir, "temp.tar.gz");
119
+ await Bun.write(tarPath, await response.arrayBuffer());
120
+ await Bun.spawn([
121
+ "tar",
122
+ "-xzf",
123
+ tarPath,
124
+ "-C",
125
+ binDir
126
+ ], {
127
+ cwd: binDir,
128
+ stdio: [
129
+ "ignore",
130
+ "pipe",
131
+ "pipe"
132
+ ]
133
+ }).exited;
134
+ const possibleNames = [
135
+ "agent-yes",
136
+ binaryName.replace(/-musl$/, "").replace(/-gnu$/, ""),
137
+ binaryName
138
+ ];
139
+ for (const name of possibleNames) {
140
+ const extractedPath = path.join(binDir, name);
141
+ if (existsSync(extractedPath) && extractedPath !== binaryPath) {
142
+ try {
143
+ await copyFile(extractedPath, binaryPath);
144
+ unlinkSync(extractedPath);
145
+ } catch {}
146
+ break;
147
+ }
148
+ }
149
+ try {
150
+ unlinkSync(tarPath);
151
+ } catch {}
152
+ await chmod(binaryPath, 493);
153
+ }
154
+ if (verbose) console.log(`[rust] Binary downloaded to: ${binaryPath}`);
155
+ return binaryPath;
156
+ }
157
+ /**
158
+ * Get the version of a Rust binary by running it with --version
159
+ */
160
+ function getRustBinaryVersion(binaryPath) {
161
+ try {
162
+ const match = execFileSync(binaryPath, ["--version"], {
163
+ timeout: 5e3,
164
+ encoding: "utf8",
165
+ stdio: [
166
+ "ignore",
167
+ "pipe",
168
+ "ignore"
169
+ ]
170
+ }).match(/(\d+\.\d+\.\d+)/);
171
+ return match ? match[1] ?? null : null;
172
+ } catch {
173
+ return null;
174
+ }
175
+ }
176
+ /**
177
+ * Check if a binary path is inside a git repo (dev build), and rebuild if outdated.
178
+ * Returns the same path if up-to-date or rebuilt, undefined if rebuild failed.
179
+ */
180
+ function autoRebuildIfOutdated(binaryPath, verbose) {
181
+ if (!binaryPath.includes("/target/release") && !binaryPath.includes("/target/debug")) return true;
182
+ const binaryVersion = getRustBinaryVersion(binaryPath);
183
+ const pkgVersion = getInstalledPackage().version;
184
+ if (verbose) console.log(`[rust] Binary version: ${binaryVersion}, package version: ${pkgVersion}`);
185
+ if (binaryVersion === pkgVersion) return true;
186
+ const rsDir = binaryPath.replace(/\/target\/(release|debug)\/agent-yes.*$/, "");
187
+ if (!existsSync(path.join(rsDir, "Cargo.toml"))) {
188
+ if (verbose) console.log(`[rust] Cannot find Cargo.toml at ${rsDir}, skipping rebuild`);
189
+ return true;
190
+ }
191
+ process.stderr.write(`\x1b[33m[rust] Binary outdated (${binaryVersion ?? "unknown"} → ${pkgVersion}), rebuilding…\x1b[0m\n`);
192
+ try {
193
+ execFileSync("cargo", ["build", ...binaryPath.includes("/target/release") ? ["--release"] : []], {
194
+ cwd: rsDir,
195
+ stdio: "inherit",
196
+ timeout: 3e5
197
+ });
198
+ process.stderr.write(`\x1b[32m[rust] Rebuild complete\x1b[0m\n`);
199
+ return true;
200
+ } catch {
201
+ process.stderr.write(`\x1b[31m[rust] Auto-rebuild failed, using outdated binary\x1b[0m\n`);
202
+ return true;
203
+ }
204
+ }
205
+ /**
206
+ * Get or download the Rust binary
207
+ */
208
+ async function getRustBinary(options = {}) {
209
+ const { verbose = false, forceDownload = false } = options;
210
+ if (!forceDownload) {
211
+ const existing = findRustBinary(verbose);
212
+ if (existing) {
213
+ if (verbose) console.log(`[rust] Using existing binary: ${existing}`);
214
+ autoRebuildIfOutdated(existing, verbose);
215
+ return existing;
216
+ }
217
+ }
218
+ if (verbose) console.log(`[rust] Binary not found, downloading...`);
219
+ try {
220
+ return await downloadBinary(verbose);
221
+ } catch (err) {
222
+ throw new Error(`Failed to get Rust binary: ${err instanceof Error ? err.message : err}\nYou can build manually with: cd rs && cargo build --release`);
223
+ }
224
+ }
225
+
226
+ //#endregion
227
+ export { getRustBinary as n, findSpawnHiddenLauncher as t };
228
+ //# sourceMappingURL=rustBinary-DsO0IlNq.js.map
@@ -1,10 +1,10 @@
1
- import "./ts-Bbhf5JbN.js";
1
+ import "./ts-DsxejviJ.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-BmWkWhcJ.js";
4
- import "./pidStore-BfoBWUjc.js";
5
- import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Bwg9VqLI.js";
7
- import { l as resolveSpawnCwd } from "./workspaceConfig-BC03X4Y1.js";
3
+ import "./versionChecker-Czk3aHqy.js";
4
+ import "./pidStore-BIvsBQ8X.js";
5
+ import "./globalPidIndex-CoNr7tS8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-ByppVvWO.js";
7
+ import { d as resolveSpawnCwd } from "./workspaceConfig-BgqK-31W.js";
8
8
  import { createHash } from "node:crypto";
9
9
 
10
10
  //#region ts/oxmgrService.ts
@@ -141,4 +141,4 @@ async function cmdSchedule(rest) {
141
141
 
142
142
  //#endregion
143
143
  export { cmdSchedule };
144
- //# sourceMappingURL=schedule-CtVvSyaN.js.map
144
+ //# sourceMappingURL=schedule-ChTPIcXf.js.map
@@ -1,17 +1,18 @@
1
- import "./ts-Bbhf5JbN.js";
1
+ import "./ts-DsxejviJ.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-BmWkWhcJ.js";
4
- import "./pidStore-BfoBWUjc.js";
5
- import { a as updateGlobalPidStatus } from "./globalPidIndex-DlmmJlO8.js";
6
- import { t as pgidForWrapper } from "./reaper-C-eWAxIj.js";
7
- import "./configShared-CEyhl0WH.js";
8
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Bwg9VqLI.js";
9
- import "./e2e-BeKjLhmO.js";
10
- import "./webrtcLink-BG0Xc4-W.js";
11
- import "./remotes-BmAPylU_.js";
12
- import { c as isProvisionAllowed, i as getSpawnHook, l as resolveSpawnCwd, r as getProvisionRoot, s as hasSpawnHook } from "./workspaceConfig-BC03X4Y1.js";
13
- import { M as writeToIpc, S as renderRawLog, _ as listRecords, b as readNotes, i as controlCodeFromName, k as snapshotStatus, l as extractNeedsInput, o as deriveLiveStatus, s as extractBadges, u as extractTaskCounts, w as resolveOne, x as readPtysize } from "./subcommands--EUush_X.js";
14
- import { r as spawnRejectionReason } from "./spawnGate-DzPfa1PZ.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-Czk3aHqy.js";
4
+ import { t as findSpawnHiddenLauncher } from "./rustBinary-DsO0IlNq.js";
5
+ import "./pidStore-BIvsBQ8X.js";
6
+ import { a as updateGlobalPidStatus } from "./globalPidIndex-CoNr7tS8.js";
7
+ import { t as pgidForWrapper } from "./reaper-CWF2_ATd.js";
8
+ import "./configShared-0MnIQ652.js";
9
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-ByppVvWO.js";
10
+ import { A as resolveOne, E as readPtysize, F as snapshotStatus, H as TYPING_BADGE, O as renderRawLog, S as listRecords, T as readNotes, V as writeToIpc, b as isUserTyping, f as extractNeedsInput, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, u as extractBadges } from "./subcommands-BFlpV23b.js";
11
+ import "./e2e-jb0Hp43q.js";
12
+ import "./webrtcLink-B7REGtK2.js";
13
+ import "./remotes-CC-4GuJb.js";
14
+ import { a as getSpawnHook, c as hasProvisionHook, d as resolveSpawnCwd, i as getProvisionRoot, l as hasSpawnHook, r as getProvisionHook, u as isProvisionAllowed } from "./workspaceConfig-BgqK-31W.js";
15
+ import { r as spawnRejectionReason } from "./spawnGate-IJDByl2U.js";
15
16
  import yargs from "yargs";
16
17
  import { mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
17
18
  import { homedir, hostname, userInfo } from "os";
@@ -148,6 +149,70 @@ function freshAgentEnv() {
148
149
  }
149
150
  return env;
150
151
  }
152
+ function originOwnerRepo(cwd) {
153
+ try {
154
+ const r = Bun.spawnSync([
155
+ "git",
156
+ "-C",
157
+ cwd,
158
+ "remote",
159
+ "get-url",
160
+ "origin"
161
+ ]);
162
+ if (r.exitCode !== 0) return null;
163
+ const m = new TextDecoder().decode(r.stdout).trim().match(/github\.com[:/]([^/]+)\/(.+?)(?:\.git)?\/?$/i);
164
+ return m && m[1] && m[2] ? {
165
+ owner: m[1],
166
+ repo: m[2]
167
+ } : null;
168
+ } catch {
169
+ return null;
170
+ }
171
+ }
172
+ async function runProvisionHook(cwd, koho) {
173
+ const hook = getProvisionHook();
174
+ if (!hook) return { ran: false };
175
+ const shell = process.env.AGENT_YES_PROVISION_SHELL?.trim() || process.env.AGENT_YES_SPAWN_SHELL?.trim() || "/bin/sh";
176
+ const outPath = path.join(agentYesHome(), `provision-hook-${process.pid}-${performance.now().toString(36).replace(".", "")}.log`);
177
+ const timeoutMs = Number(process.env.AGENT_YES_PROVISION_HOOK_TIMEOUT_MS) || 6e4;
178
+ let code = null;
179
+ try {
180
+ const child = Bun.spawn([
181
+ shell,
182
+ "-c",
183
+ `set -e\n${hook}`
184
+ ], {
185
+ cwd,
186
+ env: {
187
+ ...freshAgentEnv(),
188
+ ...koho
189
+ },
190
+ stdin: "ignore",
191
+ stdout: Bun.file(outPath),
192
+ stderr: Bun.file(outPath)
193
+ });
194
+ code = await Promise.race([child.exited, new Promise((r) => setTimeout(() => r(124), timeoutMs))]);
195
+ if (code === 124) child.kill();
196
+ } catch (e) {
197
+ return {
198
+ ran: true,
199
+ ok: false,
200
+ code: null,
201
+ detail: e.message
202
+ };
203
+ }
204
+ let detail = "";
205
+ try {
206
+ detail = (await Bun.file(outPath).text()).slice(0, 4096).trimEnd();
207
+ } catch {}
208
+ await unlink(outPath).catch(() => {});
209
+ return {
210
+ ran: true,
211
+ ok: code === 0,
212
+ code,
213
+ detail
214
+ };
215
+ }
151
216
  const DAEMON_NAME = "agent-yes";
152
217
  function resolveDaemonManager() {
153
218
  const oxmgr = Bun.which("oxmgr");
@@ -308,7 +373,7 @@ async function cmdServeDaemon(sub, args) {
308
373
  let shareLink = null;
309
374
  let shareLinkMinted = false;
310
375
  if (webrtcDaemon) try {
311
- const { loadOrCreateShareRoom, shareLinkFromRoomUrl } = await import("./share-D_e2Fwiy.js");
376
+ const { loadOrCreateShareRoom, shareLinkFromRoomUrl } = await import("./share-QregR8a_.js");
312
377
  const explicit = explicitWebrtcUrl(effArgs);
313
378
  shareLink = shareLinkFromRoomUrl(explicit ?? await loadOrCreateShareRoom());
314
379
  shareLinkMinted = !explicit;
@@ -317,7 +382,7 @@ async function cmdServeDaemon(sub, args) {
317
382
  if (!webrtcDaemon) return;
318
383
  if (shareLink) {
319
384
  process.stdout.write(`\nshared over WebRTC — open this link (the token is eaten from the URL on open):\n ${shareLink}\n` + (shareLinkMinted ? ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n` : ``));
320
- const { offerOpenInBrowser } = await import("./openBrowser-CCF1iuQK.js");
385
+ const { offerOpenInBrowser } = await import("./openBrowser-ChR4llYa.js");
321
386
  await offerOpenInBrowser(shareLink);
322
387
  } else process.stdout.write("\nthe WebRTC share link carries a secret, so the daemon does NOT log it —\nread it from ~/.agent-yes/.share-link (mode 0600). The room persists in\n~/.agent-yes/.share-room, so the link survives restarts.\n");
323
388
  };
@@ -343,6 +408,8 @@ async function cmdServeDaemon(sub, args) {
343
408
  ]);
344
409
  }
345
410
  const serveArgv = ayServeArgv(effArgs);
411
+ const spawnHidden = findSpawnHiddenLauncher();
412
+ const managedArgv = spawnHidden ? [spawnHidden, ...serveArgv] : serveArgv;
346
413
  const oxmgrHealth = webrtcDaemon && mgr.id === "oxmgr" ? [
347
414
  "--health-cmd",
348
415
  ayServeArgv(["healthcheck"]).join(" "),
@@ -356,7 +423,7 @@ async function cmdServeDaemon(sub, args) {
356
423
  const startArgv = mgr.id === "oxmgr" ? [
357
424
  mgr.bin,
358
425
  "start",
359
- serveArgv.join(" "),
426
+ managedArgv.join(" "),
360
427
  "--name",
361
428
  DAEMON_NAME,
362
429
  "--restart",
@@ -367,7 +434,7 @@ async function cmdServeDaemon(sub, args) {
367
434
  ] : [
368
435
  mgr.bin,
369
436
  "start",
370
- serveArgv[0],
437
+ managedArgv[0],
371
438
  "--name",
372
439
  DAEMON_NAME,
373
440
  "--interpreter",
@@ -375,7 +442,7 @@ async function cmdServeDaemon(sub, args) {
375
442
  "--exp-backoff-restart-delay",
376
443
  "200",
377
444
  "--",
378
- ...serveArgv.slice(1)
445
+ ...managedArgv.slice(1)
379
446
  ];
380
447
  const code = await Bun.spawn(startArgv, { stdio: [
381
448
  "ignore",
@@ -781,14 +848,16 @@ Options:
781
848
  const withMeta = async (r) => {
782
849
  const status = await deriveLiveStatus(r);
783
850
  const question = status !== "exited" && !r.unresponsive ? await logNeedsInput(r.log_file, r.cli) : null;
851
+ const lastActiveAt = r.log_file ? await stat(r.log_file).then((s) => s.mtimeMs).catch(() => r.started_at) : r.started_at;
784
852
  return {
785
853
  ...r,
854
+ last_active_at: lastActiveAt,
786
855
  status: status === "exited" ? status : r.unresponsive ? "stuck" : question ? "needs_input" : status,
787
856
  question,
788
857
  title: await logTitle(r.log_file),
789
858
  git: status === "exited" ? null : await gitStatus(r.cwd),
790
859
  tasks: status === "exited" ? null : await logTasks(r.log_file),
791
- badges: status === "exited" ? [] : await logBadges(r.log_file)
860
+ badges: status === "exited" ? [] : await logBadges(r.log_file).then(async (b) => await isUserTyping(r.pid) ? [...b, TYPING_BADGE.id] : b)
792
861
  };
793
862
  };
794
863
  const presence = /* @__PURE__ */ new Map();
@@ -894,7 +963,10 @@ Options:
894
963
  return Response.json({ host: user ? `${user}@${host}` : host });
895
964
  }
896
965
  if (req.method === "GET" && p === "/api/version") return Response.json({ version: getInstalledPackage().version });
897
- if (req.method === "GET" && p === "/api/spawn-config") return Response.json({ hasSpawnHook: hasSpawnHook() });
966
+ if (req.method === "GET" && p === "/api/spawn-config") return Response.json({
967
+ hasSpawnHook: hasSpawnHook(),
968
+ hasProvisionHook: hasProvisionHook()
969
+ });
898
970
  if (req.method === "GET" && p === "/api/notes") {
899
971
  const notes = await readNotes();
900
972
  return Response.json(Object.fromEntries(notes));
@@ -1104,7 +1176,9 @@ Options:
1104
1176
  const record = await resolveOne(keyword, defaultOpts({ all: true }));
1105
1177
  const args = ["restart", String(record.pid)];
1106
1178
  if (body.fresh) args.push("--fresh");
1107
- Bun.spawn(["agent-yes", ...args], {
1179
+ const ayBin = Bun.which("ay") ?? process.argv[1];
1180
+ const ayCmd = process.platform === "win32" && ayBin.toLowerCase().endsWith(".exe") ? [ayBin] : [process.execPath, ayBin];
1181
+ Bun.spawn([...ayCmd, ...args], {
1108
1182
  cwd: record.cwd,
1109
1183
  detached: true,
1110
1184
  stdio: [
@@ -1206,6 +1280,16 @@ Options:
1206
1280
  } catch (e) {
1207
1281
  return new Response(`fork needs the 'codehost' package (codehost/provision) — install it (npm i -g codehost) or 'bun link' it for local dev: ${e.message}`, { status: 501 });
1208
1282
  }
1283
+ const forkOrigin = originOwnerRepo(fork.fromCwd);
1284
+ const forkHook = await runProvisionHook(fork.fromCwd, {
1285
+ KOHO_ACTION: "fork",
1286
+ KOHO_FROM_CWD: fork.fromCwd,
1287
+ KOHO_BRANCH: fork.branch,
1288
+ KOHO_OWNER: forkOrigin?.owner ?? "",
1289
+ KOHO_REPO: forkOrigin?.repo ?? "",
1290
+ KOHO_WS_ROOT: getProvisionRoot() ?? ""
1291
+ });
1292
+ if (forkHook.ran && !forkHook.ok) return new Response(`provision hook denied this fork (exit ${forkHook.code})` + (forkHook.detail ? `:\n${forkHook.detail}` : ""), { status: 403 });
1209
1293
  let result;
1210
1294
  try {
1211
1295
  const wsRoot = getProvisionRoot();
@@ -1218,7 +1302,7 @@ Options:
1218
1302
  return new Response(`fork failed: ${e.message}`, { status: 502 });
1219
1303
  }
1220
1304
  if (!result?.ok) return new Response(result?.error ?? "fork failed", { status: 502 });
1221
- if (result.spec && !isProvisionAllowed(result.spec.owner, result.spec.repo)) return new Response(`forking '${result.spec.owner}/${result.spec.repo}' is not allowed — add the owner to provisionAllowlist in ~/.agent-yes/config.json (or "*" to allow all)`, { status: 403 });
1305
+ if (!forkHook.ran && result.spec && !isProvisionAllowed(result.spec.owner, result.spec.repo)) return new Response(`forking '${result.spec.owner}/${result.spec.repo}' is not allowed — add the owner to provisionAllowlist in ~/.agent-yes/config.json (or "*" to allow all), or set a provisionHook to gate it yourself`, { status: 403 });
1222
1306
  cwd = result.folder;
1223
1307
  provisioned = {
1224
1308
  action: result.action,
@@ -1238,7 +1322,17 @@ Options:
1238
1322
  spec = null;
1239
1323
  }
1240
1324
  if (!spec) return new Response(`unrecognized spawn source: ${from}`, { status: 400 });
1241
- if (!isProvisionAllowed(spec.owner, spec.repo)) return new Response(`provisioning '${spec.owner}/${spec.repo}' is not allowed — add the owner to provisionAllowlist in ~/.agent-yes/config.json (or "*" to allow all)`, { status: 403 });
1325
+ const fromHook = await runProvisionHook(getProvisionRoot() ?? homedir(), {
1326
+ KOHO_ACTION: "from",
1327
+ KOHO_SOURCE: from,
1328
+ KOHO_OWNER: spec.owner,
1329
+ KOHO_REPO: spec.repo,
1330
+ KOHO_BRANCH: spec.branch,
1331
+ KOHO_WS_ROOT: getProvisionRoot() ?? ""
1332
+ });
1333
+ if (fromHook.ran) {
1334
+ if (!fromHook.ok) return new Response(`provision hook denied '${spec.owner}/${spec.repo}' (exit ${fromHook.code})` + (fromHook.detail ? `:\n${fromHook.detail}` : ""), { status: 403 });
1335
+ } else if (!isProvisionAllowed(spec.owner, spec.repo)) return new Response(`provisioning '${spec.owner}/${spec.repo}' is not allowed — add the owner to provisionAllowlist in ~/.agent-yes/config.json (or "*" to allow all), or set a provisionHook to gate it yourself`, { status: 403 });
1242
1336
  let result;
1243
1337
  try {
1244
1338
  const wsRoot = getProvisionRoot();
@@ -1405,7 +1499,7 @@ Options:
1405
1499
  const webrtcVal = argv.webrtc ?? argv.share;
1406
1500
  const explicitUrl = typeof webrtcVal === "string" && webrtcVal.startsWith("webrtc://") ? webrtcVal : void 0;
1407
1501
  try {
1408
- const { startShare, loadOrCreateShareRoom } = await import("./share-D_e2Fwiy.js");
1502
+ const { startShare, loadOrCreateShareRoom } = await import("./share-QregR8a_.js");
1409
1503
  const linkFile = path.join(process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes"), ".share-link");
1410
1504
  const announce = async (room, link, rotated) => {
1411
1505
  const lead = rotated ? "the room was rejected by signaling (stale generation) — rotated to a fresh link" : "shared over WebRTC — open this link (the token is eaten from the URL on open)";
@@ -1413,7 +1507,7 @@ Options:
1413
1507
  const persistNote = explicitUrl ? "\n" : ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n\n`;
1414
1508
  process.stdout.write(`${wantHttp ? "\n" : ""}${lead}:\n ${link}\n` + persistNote);
1415
1509
  if (!rotated) {
1416
- const { offerOpenInBrowser } = await import("./openBrowser-CCF1iuQK.js");
1510
+ const { offerOpenInBrowser } = await import("./openBrowser-ChR4llYa.js");
1417
1511
  await offerOpenInBrowser(link);
1418
1512
  }
1419
1513
  } else {
@@ -1464,4 +1558,4 @@ Options:
1464
1558
 
1465
1559
  //#endregion
1466
1560
  export { cmdServe };
1467
- //# sourceMappingURL=serve-CLmk5ujF.js.map
1561
+ //# sourceMappingURL=serve-De1gAWNI.js.map
@@ -1,4 +1,4 @@
1
- import { o as getWorkspaceRoot, u as setWorkspaceRoot } from "./workspaceConfig-BC03X4Y1.js";
1
+ import { f as setWorkspaceRoot, s as getWorkspaceRoot } from "./workspaceConfig-BgqK-31W.js";
2
2
  import { existsSync } from "node:fs";
3
3
  import { stdin, stdout } from "node:process";
4
4
  import { createInterface } from "node:readline/promises";
@@ -32,7 +32,7 @@ async function cmdSetup(rest) {
32
32
  if (!existsSync(abs)) process.stderr.write(` note: that directory doesn't exist yet — create it, or agents spawned there will fail\n`);
33
33
  if (noShare) return 0;
34
34
  process.stdout.write(`\nsharing this machine to agent-yes.com…\n`);
35
- const { cmdServe } = await import("./serve-CLmk5ujF.js");
35
+ const { cmdServe } = await import("./serve-De1gAWNI.js");
36
36
  return cmdServe([
37
37
  "install",
38
38
  "--share",
@@ -42,4 +42,4 @@ async function cmdSetup(rest) {
42
42
 
43
43
  //#endregion
44
44
  export { cmdSetup };
45
- //# sourceMappingURL=setup-CYbXOcpH.js.map
45
+ //# sourceMappingURL=setup-nlMqyEPB.js.map
@@ -1,4 +1,4 @@
1
- import { a as computeTranscriptHash, c as open$1, d as randomHex, f as seal, i as MAX_CHUNK, l as packEnvelope, n as FLAG_CONFIRM, o as deriveAuthToken, p as unpackEnvelope, r as MARKER, s as deriveDirKeys, t as CONFIRM_TIMEOUT_MS, u as parseSecret } from "./e2e-BeKjLhmO.js";
1
+ import { a as computeTranscriptHash, c as open$1, d as randomHex, f as seal, i as MAX_CHUNK, l as packEnvelope, n as FLAG_CONFIRM, o as deriveAuthToken, p as unpackEnvelope, r as MARKER, s as deriveDirKeys, t as CONFIRM_TIMEOUT_MS, u as parseSecret } from "./e2e-jb0Hp43q.js";
2
2
  import { mkdir, readFile, writeFile } from "fs/promises";
3
3
  import { homedir } from "os";
4
4
  import path from "path";
@@ -534,4 +534,4 @@ async function startShare(opts) {
534
534
 
535
535
  //#endregion
536
536
  export { loadOrCreateShareRoom, shareLinkFromRoomUrl, startShare };
537
- //# sourceMappingURL=share-D_e2Fwiy.js.map
537
+ //# sourceMappingURL=share-QregR8a_.js.map
@@ -0,0 +1,5 @@
1
+ import "./logger-CDIsZ-Pp.js";
2
+ import "./globalPidIndex-CoNr7tS8.js";
3
+ import { i as waitForSpawnCapacity, n as spawnGateEnabled, r as spawnRejectionReason, t as memAvailableMb } from "./spawnGate-IJDByl2U.js";
4
+
5
+ export { waitForSpawnCapacity };
@@ -1,5 +1,5 @@
1
- import { i as readGlobalPids } from "./globalPidIndex-DlmmJlO8.js";
2
- import { a as getSpawnWaitMs, n as getMinFreeMb, t as getMaxAgents } from "./workspaceConfig-BC03X4Y1.js";
1
+ import { i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
2
+ import { n as getMinFreeMb, o as getSpawnWaitMs, t as getMaxAgents } from "./workspaceConfig-BgqK-31W.js";
3
3
  import { readFile } from "fs/promises";
4
4
 
5
5
  //#region ts/spawnGate.ts
@@ -104,4 +104,4 @@ async function waitForSpawnCapacity(opts) {
104
104
 
105
105
  //#endregion
106
106
  export { waitForSpawnCapacity as i, spawnGateEnabled as n, spawnRejectionReason as r, memAvailableMb as t };
107
- //# sourceMappingURL=spawnGate-DzPfa1PZ.js.map
107
+ //# sourceMappingURL=spawnGate-IJDByl2U.js.map