agent-yes 1.121.0 → 1.122.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.
Files changed (39) hide show
  1. package/default.config.yaml +27 -4
  2. package/dist/SUPPORTED_CLIS-BVqe3k7F.js +8 -0
  3. package/dist/{SUPPORTED_CLIS-O57LGUEG.js → SUPPORTED_CLIS-DFYbm2uk.js} +2 -2
  4. package/dist/{agent-yes.config-kmtJKJHk.js → agent-yes.config-z-IPzH5U.js} +3 -2
  5. package/dist/cli.js +5 -5
  6. package/dist/index.js +2 -2
  7. package/dist/reaper-Dj8R7ltI.js +64 -0
  8. package/dist/reaper-HqcUms2d.js +3 -0
  9. package/dist/{remotes-DavR4Hca.js → remotes-CpGcTr7A.js} +1 -1
  10. package/dist/{remotes-BufkGk0e.js → remotes-D2fqaRU8.js} +1 -1
  11. package/dist/schedule-CJaNc82S.js +144 -0
  12. package/dist/{serve-D2czcYNC.js → serve-VczpuYDk.js} +121 -26
  13. package/dist/{setup-f1FIFcZm.js → setup-DveWqmSR.js} +5 -42
  14. package/dist/{share-B6QVr5D1.js → share-ClsUSd_0.js} +69 -9
  15. package/dist/{subcommands-CzpZQHO6.js → subcommands-CmNGbbIA.js} +15 -6
  16. package/dist/{subcommands-DobVXouH.js → subcommands-CzZ4uBIa.js} +2 -2
  17. package/dist/{tray-B8_rx1iu.js → tray-DjCIyakK.js} +22 -10
  18. package/dist/{ts-D91dm1E0.js → ts-7kSDmCpQ.js} +76 -7
  19. package/dist/{versionChecker-CAtpgnoQ.js → versionChecker-B5vxV_hH.js} +13 -19
  20. package/dist/workspaceConfig-XP2NEWmV.js +56 -0
  21. package/lab/ui/index.html +63 -32
  22. package/package.json +1 -1
  23. package/ts/autoRetry.spec.ts +19 -0
  24. package/ts/autoRetry.ts +16 -0
  25. package/ts/configShared.ts +4 -0
  26. package/ts/index.ts +102 -0
  27. package/ts/oxmgrService.ts +36 -0
  28. package/ts/pty.ts +19 -1
  29. package/ts/reaper.spec.ts +45 -0
  30. package/ts/reaper.ts +77 -0
  31. package/ts/schedule.spec.ts +30 -0
  32. package/ts/schedule.ts +161 -0
  33. package/ts/serve.ts +174 -19
  34. package/ts/share.ts +81 -10
  35. package/ts/subcommands.ts +0 -0
  36. package/ts/tray.spec.ts +9 -1
  37. package/ts/tray.ts +30 -14
  38. package/ts/versionChecker.ts +24 -27
  39. package/dist/SUPPORTED_CLIS-CegJgoEf.js +0 -8
package/ts/tray.ts CHANGED
@@ -60,15 +60,6 @@ function isTrayProcessRunning(pid: number): boolean {
60
60
  }
61
61
  }
62
62
 
63
- /**
64
- * Write the current process PID to the tray PID file
65
- */
66
- async function writeTrayPid(): Promise<void> {
67
- const dir = getTrayDir();
68
- await mkdir(dir, { recursive: true });
69
- await writeFile(getTrayPidFile(), String(process.pid), "utf8");
70
- }
71
-
72
63
  /**
73
64
  * Remove the tray PID file
74
65
  */
@@ -80,6 +71,28 @@ async function removeTrayPid(): Promise<void> {
80
71
  }
81
72
  }
82
73
 
74
+ /**
75
+ * Atomically claim the tray singleton. Exclusive create (flag "wx") fails if a
76
+ * pidfile already exists, so two trays racing startup can't BOTH pass a
77
+ * check-then-act and survive (that race is how N agents ended up with N trays).
78
+ * If an existing pidfile belongs to a DEAD tray it's stale — take it over.
79
+ * Returns true if this process is now the singleton owner.
80
+ */
81
+ async function claimTraySingleton(): Promise<boolean> {
82
+ const pidFile = getTrayPidFile();
83
+ await mkdir(getTrayDir(), { recursive: true });
84
+ for (let attempt = 0; attempt < 2; attempt++) {
85
+ try {
86
+ await writeFile(pidFile, String(process.pid), { flag: "wx" });
87
+ return true; // won the exclusive create
88
+ } catch {
89
+ if (await isTrayRunning()) return false; // a live tray owns it
90
+ await removeTrayPid(); // stale pidfile from a dead tray — drop and retry
91
+ }
92
+ }
93
+ return false;
94
+ }
95
+
83
96
  /**
84
97
  * Check if a tray process is already running
85
98
  */
@@ -102,6 +115,11 @@ export async function isTrayRunning(): Promise<boolean> {
102
115
  */
103
116
  export async function ensureTray(): Promise<void> {
104
117
  if (!isDesktopOS()) return;
118
+ // OPT-IN: the systray2 helper busy-loops at ~100% CPU under Bun (its stdio
119
+ // read loop spins), and every TS-runtime agent calls this — so N concurrent
120
+ // agents used to each spawn a tray and pin N cores. Auto-spawn is disabled by
121
+ // default until the busy-loop is fixed; set AGENT_YES_TRAY=1 to opt in.
122
+ if (process.env.AGENT_YES_TRAY !== "1") return;
105
123
  if (await isTrayRunning()) return;
106
124
 
107
125
  try {
@@ -127,15 +145,13 @@ export async function startTray(): Promise<void> {
127
145
  return;
128
146
  }
129
147
 
130
- // Check if another tray is already running
131
- if (await isTrayRunning()) {
148
+ // Atomically claim the singleton (exclusive create) — prevents the
149
+ // check-then-act race that let concurrent agents each keep a tray alive.
150
+ if (!(await claimTraySingleton())) {
132
151
  console.error("Tray is already running.");
133
152
  return;
134
153
  }
135
154
 
136
- // Register our PID
137
- await writeTrayPid();
138
-
139
155
  let SysTray: typeof import("systray2").default;
140
156
  try {
141
157
  SysTray = (await import("systray2")).default;
@@ -237,44 +237,41 @@ export function compareVersions(v1: string, v2: string): number {
237
237
 
238
238
  /**
239
239
  * Detect how agent-yes was installed.
240
- * Returns a short label: "git", "bun link", "bun", "npm", "npx", or "unknown"
240
+ * Returns a short label: "git", "bun", "npm", "npx", "source", or "unknown".
241
+ * A bun-link of a git checkout reports "git" (it runs the working tree).
241
242
  */
242
243
  export function detectInstallMethod(): string {
243
244
  try {
244
- // Check if running from a file path outside node_modules (git clone / bun link dev)
245
- const scriptDir = path.dirname(new URL(import.meta.url).pathname);
246
-
247
- if (!scriptDir.includes("node_modules")) {
248
- // Running directly from source is this a git repo?
249
- const repoRoot = path.resolve(scriptDir, "..");
250
- if (existsSync(path.join(repoRoot, ".git"))) {
251
- return "git";
252
- }
253
- return "source";
245
+ // fileURLToPath handles Windows drive letters correctly; new URL().pathname
246
+ // yields "/C:/…", which breaks existsSync and the substring checks below.
247
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
248
+ const norm = scriptDir.replace(/\\/g, "/");
249
+ const hasGit = (dir: string) => existsSync(path.join(dir, ".git"));
250
+
251
+ if (!norm.includes("node_modules")) {
252
+ // Running directly from a checkout (git clone, or a resolved bun-link).
253
+ return hasGit(path.resolve(scriptDir, "..")) ? "git" : "source";
254
254
  }
255
255
 
256
- // Check if the node_modules entry is a symlink (bun link)
257
- const nodeModulesEntry = scriptDir.replace(/\/dist$/, "");
256
+ // Inside node_modules: a bun-link symlink points back at the local repo.
257
+ const nodeModulesEntry = scriptDir.replace(/[\\/]dist$/, "");
258
258
  try {
259
- const stat = lstatSync(nodeModulesEntry);
260
- if (stat.isSymbolicLink()) {
261
- const target = readlinkSync(nodeModulesEntry);
262
- // bun link creates a symlink to the local repo
263
- const resolvedTarget = path.resolve(path.dirname(nodeModulesEntry), target);
264
- if (existsSync(path.join(resolvedTarget, ".git"))) {
265
- return "bun link (git)";
266
- }
267
- return "bun link";
259
+ if (lstatSync(nodeModulesEntry).isSymbolicLink()) {
260
+ const resolved = path.resolve(
261
+ path.dirname(nodeModulesEntry),
262
+ readlinkSync(nodeModulesEntry),
263
+ );
264
+ return hasGit(resolved) ? "git" : "bun";
268
265
  }
269
266
  } catch {
270
- // not a symlink, continue
267
+ // not a symlink — fall through to package-manager detection
271
268
  }
272
269
 
273
- // Detect package manager from path or env
274
- if (scriptDir.includes(".bun/")) return "bun";
275
- if (scriptDir.includes(".npm/")) return "npx";
276
- if (process.env.npm_execpath?.includes("bun")) return "bun";
270
+ // A real package install figure out the manager.
271
+ if (norm.includes("/.bun/")) return "bun";
272
+ if (norm.includes("/.npm/")) return "npx";
277
273
  if (process.env.npm_config_user_agent?.startsWith("bun")) return "bun";
274
+ if (process.env.npm_execpath?.includes("bun")) return "bun";
278
275
  if (process.env.npm_config_user_agent?.startsWith("npm")) return "npm";
279
276
 
280
277
  return "npm";
@@ -1,8 +0,0 @@
1
- import "./ts-D91dm1E0.js";
2
- import "./logger-B9h0djqx.js";
3
- import "./versionChecker-CAtpgnoQ.js";
4
- import "./pidStore-B5vBu8Px.js";
5
- import "./globalPidIndex-gZuTvTBs.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-O57LGUEG.js";
7
-
8
- export { SUPPORTED_CLIS };