agent-yes 1.75.0 → 1.75.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.
package/README.md CHANGED
@@ -130,6 +130,25 @@ claude-yes --exit-on-idle=60s "run all tests and commit current changes"
130
130
  claude-code-execute claude-yes "your task here"
131
131
  ```
132
132
 
133
+ ### Inspect and message running agents (`cy ls / read / send`)
134
+
135
+ From any terminal you can list and interact with agents that are already
136
+ running on the machine — both TS- and Rust-spawned ones:
137
+
138
+ ```bash
139
+ cy ls # list all running agents
140
+ cy ls codex # filter (matches pid, cwd, cli, or prompt)
141
+ cy tail <keyword> # render last 96 lines via @xterm/headless
142
+ cy read <keyword> # full rendered log
143
+ cy send <keyword> "next: run tests" # append a prompt to that agent's stdin
144
+ cy send <keyword> "" --code=ctrl-c # send a Ctrl+C
145
+ ```
146
+
147
+ `cy` (and `ay` / `agent-yes`) writes to a shared registry at
148
+ `~/.agent-yes/pids.jsonl` and a per-pid FIFO at `~/.agent-yes/fifo/<pid>.stdin`,
149
+ so subcommands work whether the target agent is the TS or Rust runtime.
150
+ Detailed reference (Japanese): [`docs/cy-subcommands.md`](./docs/cy-subcommands.md).
151
+
133
152
  ### Docker Usage
134
153
 
135
154
  You can run `agent-yes` in a Docker container with all AI CLI tools pre-installed.
@@ -0,0 +1,11 @@
1
+ import { t as CLIS_CONFIG } from "./ts-Cuys5-PF.js";
2
+ import "./logger-B9h0djqx.js";
3
+ import "./pidStore-C1JXxoPi.js";
4
+ import "./globalPidIndex-Cr-g75QF.js";
5
+
6
+ //#region ts/SUPPORTED_CLIS.ts
7
+ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
8
+
9
+ //#endregion
10
+ export { SUPPORTED_CLIS };
11
+ //# sourceMappingURL=SUPPORTED_CLIS-C2w9JqbM.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 { n as version, t as name } from "./package-CE0J-uFT.js";
3
+ import { n as version, t as name } from "./package-8zpT1iww.js";
4
4
  import { argv } from "process";
5
5
  import { execFileSync, spawn } from "child_process";
6
6
  import ms from "ms";
@@ -635,7 +635,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
635
635
  }
636
636
  }
637
637
  {
638
- const { isSubcommand, runSubcommand } = await import("./subcommands-DpOqSs-b.js");
638
+ const { isSubcommand, runSubcommand } = await import("./subcommands-DSm9WL-6.js");
639
639
  if (isSubcommand(process.argv[2])) {
640
640
  const code = await runSubcommand(process.argv);
641
641
  process.exit(code ?? 0);
@@ -664,7 +664,7 @@ if (config.useRust) {
664
664
  }
665
665
  }
666
666
  if (rustBinary) {
667
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-Dzf60ENT.js");
667
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-C2w9JqbM.js");
668
668
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
669
669
  if (config.verbose) {
670
670
  console.log(`[rust] Using binary: ${rustBinary}`);
@@ -694,7 +694,7 @@ if (config.showVersion) {
694
694
  process.exit(0);
695
695
  }
696
696
  if (config.appendPrompt) {
697
- const { PidStore } = await import("./pidStore-DR1yPY3t.js");
697
+ const { PidStore } = await import("./pidStore-BCsY5BW3.js");
698
698
  const ipcPath = await PidStore.findActiveFifo(process.cwd());
699
699
  if (!ipcPath) {
700
700
  console.error("No active agent with IPC found in current directory.");
@@ -0,0 +1,144 @@
1
+ import { n as logger } from "./logger-B9h0djqx.js";
2
+ import { appendFile, mkdir, readFile, rename, writeFile } from "fs/promises";
3
+ import { homedir } from "os";
4
+ import path from "path";
5
+ import { lock } from "proper-lockfile";
6
+
7
+ //#region ts/globalPidIndex.ts
8
+ /**
9
+ * Resolved at call time (not module load time) so tests and other callers
10
+ * can override via $AGENT_YES_HOME without juggling module-cache resets.
11
+ * Falls back to `~/.agent-yes` for normal user runs.
12
+ */
13
+ function resolveGlobalDir() {
14
+ return process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
15
+ }
16
+ function resolveGlobalFile() {
17
+ return path.join(resolveGlobalDir(), "pids.jsonl");
18
+ }
19
+ async function ensureDir() {
20
+ await mkdir(resolveGlobalDir(), { recursive: true });
21
+ }
22
+ async function withLock(fn) {
23
+ await ensureDir();
24
+ const file = resolveGlobalFile();
25
+ const dir = resolveGlobalDir();
26
+ let release;
27
+ try {
28
+ release = await lock(dir, {
29
+ lockfilePath: file + ".lock",
30
+ retries: {
31
+ retries: 5,
32
+ minTimeout: 50,
33
+ maxTimeout: 500
34
+ }
35
+ });
36
+ return await fn();
37
+ } finally {
38
+ await release?.();
39
+ }
40
+ }
41
+ /** Append one full record line. Caller must provide all required fields. */
42
+ async function appendGlobalPid(record) {
43
+ try {
44
+ await withLock(async () => {
45
+ await appendFile(resolveGlobalFile(), JSON.stringify(record) + "\n");
46
+ });
47
+ } catch (error) {
48
+ logger.debug("[globalPidIndex] append failed:", error);
49
+ }
50
+ }
51
+ /** Append a partial status update by pid (status, exit_code, exit_reason). */
52
+ async function updateGlobalPidStatus(pid, patch) {
53
+ try {
54
+ await withLock(async () => {
55
+ const existing = (await readGlobalPidsRaw()).find((r) => r.pid === pid);
56
+ if (!existing) return;
57
+ const merged = {
58
+ ...existing,
59
+ ...patch
60
+ };
61
+ await appendFile(resolveGlobalFile(), JSON.stringify(merged) + "\n");
62
+ });
63
+ } catch (error) {
64
+ logger.debug("[globalPidIndex] updateStatus failed:", error);
65
+ }
66
+ }
67
+ /**
68
+ * Read the file once without merge logic — internal helper for status updates.
69
+ */
70
+ async function readGlobalPidsRaw() {
71
+ let raw;
72
+ try {
73
+ raw = await readFile(resolveGlobalFile(), "utf-8");
74
+ } catch (err) {
75
+ if (err.code === "ENOENT") return [];
76
+ throw err;
77
+ }
78
+ const merged = /* @__PURE__ */ new Map();
79
+ for (const line of raw.split("\n")) {
80
+ const trimmed = line.trim();
81
+ if (!trimmed) continue;
82
+ try {
83
+ const doc = JSON.parse(trimmed);
84
+ if (typeof doc.pid !== "number") continue;
85
+ const prev = merged.get(doc.pid);
86
+ merged.set(doc.pid, prev ? {
87
+ ...prev,
88
+ ...doc
89
+ } : doc);
90
+ } catch {}
91
+ }
92
+ return Array.from(merged.values());
93
+ }
94
+ /**
95
+ * Read all records, last-line-per-pid wins (events get merged).
96
+ * Optionally filter to live processes only.
97
+ */
98
+ async function readGlobalPids(opts = {}) {
99
+ const records = await readGlobalPidsRaw();
100
+ if (!opts.liveOnly) return records;
101
+ return records.filter((r) => r.status !== "exited" && isProcessAlive(r.pid));
102
+ }
103
+ function isProcessAlive(pid) {
104
+ try {
105
+ process.kill(pid, 0);
106
+ return true;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+ const COMPACT_THRESHOLD_LINES = 500;
112
+ /**
113
+ * Best-effort compaction: rewrite the JSONL file with one line per known pid,
114
+ * dropping records whose pid is dead AND status is exited (those won't be
115
+ * referenced by `cy ls` anyway). Triggered opportunistically when the raw
116
+ * file grows past `COMPACT_THRESHOLD_LINES`. Safe to call unconditionally;
117
+ * it no-ops when the file is already small enough.
118
+ */
119
+ async function maybeCompactGlobalPids() {
120
+ let raw;
121
+ try {
122
+ raw = await readFile(resolveGlobalFile(), "utf-8");
123
+ } catch (err) {
124
+ if (err.code === "ENOENT") return;
125
+ return;
126
+ }
127
+ const lineCount = raw.split("\n").filter((l) => l.trim()).length;
128
+ if (lineCount < COMPACT_THRESHOLD_LINES) return;
129
+ try {
130
+ await withLock(async () => {
131
+ const keep = (await readGlobalPidsRaw()).filter((r) => r.status !== "exited" || isProcessAlive(r.pid));
132
+ const tmpFile = resolveGlobalFile() + ".compact";
133
+ await writeFile(tmpFile, keep.map((r) => JSON.stringify(r)).join("\n") + (keep.length ? "\n" : ""));
134
+ await rename(tmpFile, resolveGlobalFile());
135
+ logger.debug(`[globalPidIndex] compacted ${lineCount} → ${keep.length} lines`);
136
+ });
137
+ } catch (error) {
138
+ logger.debug("[globalPidIndex] compact failed:", error);
139
+ }
140
+ }
141
+
142
+ //#endregion
143
+ export { updateGlobalPidStatus as i, maybeCompactGlobalPids as n, readGlobalPids as r, appendGlobalPid as t };
144
+ //# sourceMappingURL=globalPidIndex-Cr-g75QF.js.map
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-D_iRstH9.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-Cuys5-PF.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./pidStore-CHLHMBEM.js";
4
- import "./globalPidIndex-DNEh8a_O.js";
3
+ import "./pidStore-C1JXxoPi.js";
4
+ import "./globalPidIndex-Cr-g75QF.js";
5
5
 
6
6
  export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
@@ -1,7 +1,7 @@
1
1
  //#region package.json
2
2
  var name = "agent-yes";
3
- var version = "1.74.0";
3
+ var version = "1.75.1";
4
4
 
5
5
  //#endregion
6
6
  export { version as n, name as t };
7
- //# sourceMappingURL=package-CE0J-uFT.js.map
7
+ //# sourceMappingURL=package-8zpT1iww.js.map
@@ -0,0 +1,5 @@
1
+ import "./logger-B9h0djqx.js";
2
+ import { t as PidStore } from "./pidStore-C1JXxoPi.js";
3
+ import "./globalPidIndex-Cr-g75QF.js";
4
+
5
+ export { PidStore };
@@ -1,5 +1,5 @@
1
1
  import { n as logger } from "./logger-B9h0djqx.js";
2
- import { r as updateGlobalPidStatus, t as appendGlobalPid } from "./globalPidIndex-DNEh8a_O.js";
2
+ import { i as updateGlobalPidStatus, n as maybeCompactGlobalPids, t as appendGlobalPid } from "./globalPidIndex-Cr-g75QF.js";
3
3
  import { closeSync, existsSync, fsyncSync, openSync } from "fs";
4
4
  import { appendFile, mkdir, readFile, rename, writeFile } from "fs/promises";
5
5
  import path from "path";
@@ -239,7 +239,7 @@ var PidStore = class PidStore {
239
239
  exit_code: null,
240
240
  exit_reason: null,
241
241
  started_at: now
242
- }).catch(() => null);
242
+ }).then(() => maybeCompactGlobalPids()).catch(() => null);
243
243
  return result;
244
244
  }
245
245
  async updateStatus(pid, status, extra) {
@@ -337,4 +337,4 @@ pid-db/
337
337
 
338
338
  //#endregion
339
339
  export { PidStore as t };
340
- //# sourceMappingURL=pidStore-CHLHMBEM.js.map
340
+ //# sourceMappingURL=pidStore-C1JXxoPi.js.map
@@ -1,5 +1,5 @@
1
1
  import "./logger-B9h0djqx.js";
2
- import { n as readGlobalPids } from "./globalPidIndex-DNEh8a_O.js";
2
+ import { r as readGlobalPids } from "./globalPidIndex-Cr-g75QF.js";
3
3
  import { readFile, stat } from "fs/promises";
4
4
  import { homedir } from "os";
5
5
  import path from "path";
@@ -388,4 +388,4 @@ async function writeToIpc(ipcPath, payload) {
388
388
 
389
389
  //#endregion
390
390
  export { isSubcommand, runSubcommand };
391
- //# sourceMappingURL=subcommands-DpOqSs-b.js.map
391
+ //# sourceMappingURL=subcommands-DSm9WL-6.js.map
@@ -1,7 +1,7 @@
1
1
  import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
2
- import { n as version } from "./package-CE0J-uFT.js";
2
+ import { n as version } from "./package-8zpT1iww.js";
3
3
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-DQWJSptq.js";
4
- import { t as PidStore } from "./pidStore-CHLHMBEM.js";
4
+ import { t as PidStore } from "./pidStore-C1JXxoPi.js";
5
5
  import { arch, platform } from "process";
6
6
  import { execSync } from "child_process";
7
7
  import { closeSync, constants, createReadStream, existsSync, mkdirSync, openSync } from "fs";
@@ -1679,4 +1679,4 @@ function sleep(ms) {
1679
1679
 
1680
1680
  //#endregion
1681
1681
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1682
- //# sourceMappingURL=ts-D_iRstH9.js.map
1682
+ //# sourceMappingURL=ts-Cuys5-PF.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.75.0",
3
+ "version": "1.75.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",
@@ -142,6 +142,69 @@ describe("globalPidIndex", () => {
142
142
  expect(records).toEqual([]);
143
143
  });
144
144
 
145
+ it("maybeCompactGlobalPids no-ops when below threshold", async () => {
146
+ const mod = await loadModule();
147
+ await mod.appendGlobalPid({
148
+ pid: 1234,
149
+ cli: "claude",
150
+ prompt: null,
151
+ cwd: "/a",
152
+ log_file: null,
153
+ status: "active",
154
+ exit_code: null,
155
+ exit_reason: null,
156
+ started_at: 1,
157
+ });
158
+ const before = (await import("fs/promises")).readFile;
159
+ const beforeContent = await before(mod.getGlobalPidIndexPath(), "utf-8");
160
+ await mod.maybeCompactGlobalPids();
161
+ const afterContent = await before(mod.getGlobalPidIndexPath(), "utf-8");
162
+ expect(afterContent).toBe(beforeContent);
163
+ });
164
+
165
+ it("maybeCompactGlobalPids collapses event spam to one line per pid", async () => {
166
+ const mod = await loadModule();
167
+ // Emit > 500 status events across two pids (one alive, one will be exited+dead)
168
+ for (let i = 0; i < 260; i++) {
169
+ await mod.appendGlobalPid({
170
+ pid: process.pid,
171
+ cli: "claude",
172
+ prompt: null,
173
+ cwd: "/a",
174
+ log_file: null,
175
+ status: "active",
176
+ exit_code: null,
177
+ exit_reason: null,
178
+ started_at: 1,
179
+ });
180
+ await mod.appendGlobalPid({
181
+ pid: 999999, // dead
182
+ cli: "codex",
183
+ prompt: null,
184
+ cwd: "/b",
185
+ log_file: null,
186
+ status: "exited",
187
+ exit_code: 0,
188
+ exit_reason: "done",
189
+ started_at: 1,
190
+ });
191
+ }
192
+ const fs = await import("fs/promises");
193
+ const before = (await fs.readFile(mod.getGlobalPidIndexPath(), "utf-8")).split("\n").length;
194
+ await mod.maybeCompactGlobalPids();
195
+ const after = (await fs.readFile(mod.getGlobalPidIndexPath(), "utf-8")).split("\n").length;
196
+ // Compaction must have shrunk the file dramatically and dropped the
197
+ // dead-and-exited pid 999999 entirely.
198
+ expect(after).toBeLessThan(before / 10);
199
+ const records = await mod.readGlobalPids();
200
+ expect(records.map((r) => r.pid)).toEqual([process.pid]);
201
+ });
202
+
203
+ it("maybeCompactGlobalPids on missing file is a noop", async () => {
204
+ const mod = await loadModule();
205
+ await mod.maybeCompactGlobalPids(); // no throw, no error
206
+ });
207
+
145
208
  it("skips corrupt lines without throwing", async () => {
146
209
  const mod = await loadModule();
147
210
  await mod.appendGlobalPid({
@@ -1,4 +1,4 @@
1
- import { appendFile, mkdir, readFile } from "fs/promises";
1
+ import { appendFile, mkdir, readFile, rename, writeFile } from "fs/promises";
2
2
  import { homedir } from "os";
3
3
  import path from "path";
4
4
  import { lock } from "proper-lockfile";
@@ -38,23 +38,35 @@ export interface GlobalPidRecord {
38
38
  started_at: number;
39
39
  }
40
40
 
41
- const GLOBAL_DIR = path.join(homedir(), ".agent-yes");
42
- const GLOBAL_FILE = path.join(GLOBAL_DIR, "pids.jsonl");
41
+ /**
42
+ * Resolved at call time (not module load time) so tests and other callers
43
+ * can override via $AGENT_YES_HOME without juggling module-cache resets.
44
+ * Falls back to `~/.agent-yes` for normal user runs.
45
+ */
46
+ function resolveGlobalDir(): string {
47
+ return process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
48
+ }
49
+
50
+ function resolveGlobalFile(): string {
51
+ return path.join(resolveGlobalDir(), "pids.jsonl");
52
+ }
43
53
 
44
54
  export function getGlobalPidIndexPath(): string {
45
- return GLOBAL_FILE;
55
+ return resolveGlobalFile();
46
56
  }
47
57
 
48
58
  async function ensureDir() {
49
- await mkdir(GLOBAL_DIR, { recursive: true });
59
+ await mkdir(resolveGlobalDir(), { recursive: true });
50
60
  }
51
61
 
52
62
  async function withLock<R>(fn: () => Promise<R>): Promise<R> {
53
63
  await ensureDir();
64
+ const file = resolveGlobalFile();
65
+ const dir = resolveGlobalDir();
54
66
  let release: (() => Promise<void>) | undefined;
55
67
  try {
56
- release = await lock(GLOBAL_DIR, {
57
- lockfilePath: GLOBAL_FILE + ".lock",
68
+ release = await lock(dir, {
69
+ lockfilePath: file + ".lock",
58
70
  retries: { retries: 5, minTimeout: 50, maxTimeout: 500 },
59
71
  });
60
72
  return await fn();
@@ -67,7 +79,7 @@ async function withLock<R>(fn: () => Promise<R>): Promise<R> {
67
79
  export async function appendGlobalPid(record: GlobalPidRecord): Promise<void> {
68
80
  try {
69
81
  await withLock(async () => {
70
- await appendFile(GLOBAL_FILE, JSON.stringify(record) + "\n");
82
+ await appendFile(resolveGlobalFile(), JSON.stringify(record) + "\n");
71
83
  });
72
84
  } catch (error) {
73
85
  logger.debug("[globalPidIndex] append failed:", error);
@@ -85,7 +97,7 @@ export async function updateGlobalPidStatus(
85
97
  const existing = current.find((r) => r.pid === pid);
86
98
  if (!existing) return; // unknown pid — nothing to update
87
99
  const merged: GlobalPidRecord = { ...existing, ...patch };
88
- await appendFile(GLOBAL_FILE, JSON.stringify(merged) + "\n");
100
+ await appendFile(resolveGlobalFile(), JSON.stringify(merged) + "\n");
89
101
  });
90
102
  } catch (error) {
91
103
  logger.debug("[globalPidIndex] updateStatus failed:", error);
@@ -98,7 +110,7 @@ export async function updateGlobalPidStatus(
98
110
  async function readGlobalPidsRaw(): Promise<GlobalPidRecord[]> {
99
111
  let raw: string;
100
112
  try {
101
- raw = await readFile(GLOBAL_FILE, "utf-8");
113
+ raw = await readFile(resolveGlobalFile(), "utf-8");
102
114
  } catch (err: any) {
103
115
  if (err.code === "ENOENT") return [];
104
116
  throw err;
@@ -141,3 +153,40 @@ function isProcessAlive(pid: number): boolean {
141
153
  return false;
142
154
  }
143
155
  }
156
+
157
+ const COMPACT_THRESHOLD_LINES = 500; // raw events; one merged record per pid
158
+
159
+ /**
160
+ * Best-effort compaction: rewrite the JSONL file with one line per known pid,
161
+ * dropping records whose pid is dead AND status is exited (those won't be
162
+ * referenced by `cy ls` anyway). Triggered opportunistically when the raw
163
+ * file grows past `COMPACT_THRESHOLD_LINES`. Safe to call unconditionally;
164
+ * it no-ops when the file is already small enough.
165
+ */
166
+ export async function maybeCompactGlobalPids(): Promise<void> {
167
+ let raw: string;
168
+ try {
169
+ raw = await readFile(resolveGlobalFile(), "utf-8");
170
+ } catch (err: any) {
171
+ if (err.code === "ENOENT") return;
172
+ return;
173
+ }
174
+ const lineCount = raw.split("\n").filter((l) => l.trim()).length;
175
+ if (lineCount < COMPACT_THRESHOLD_LINES) return;
176
+
177
+ try {
178
+ await withLock(async () => {
179
+ const merged = await readGlobalPidsRaw();
180
+ // Drop dead-and-exited entries; keep dead-but-not-yet-exited so a later
181
+ // status-update from elsewhere can still be matched against them.
182
+ const keep = merged.filter((r) => r.status !== "exited" || isProcessAlive(r.pid));
183
+ const tmpFile = resolveGlobalFile() + ".compact";
184
+ const content = keep.map((r) => JSON.stringify(r)).join("\n") + (keep.length ? "\n" : "");
185
+ await writeFile(tmpFile, content);
186
+ await rename(tmpFile, resolveGlobalFile());
187
+ logger.debug(`[globalPidIndex] compacted ${lineCount} → ${keep.length} lines`);
188
+ });
189
+ } catch (error) {
190
+ logger.debug("[globalPidIndex] compact failed:", error);
191
+ }
192
+ }
@@ -10,6 +10,11 @@ const TEST_DIR = isWindows
10
10
 
11
11
  describe("PidStore", () => {
12
12
  let store: PidStore;
13
+ // Isolate the cross-runtime global pid index too — without this, the
14
+ // synthetic pid=12345 records leak into the user's real
15
+ // ~/.agent-yes/pids.jsonl via the mirror writer wired into PidStore.
16
+ const GLOBAL_TEST_DIR = path.join(TEST_DIR, "global");
17
+ let originalAgentYesHome: string | undefined;
13
18
 
14
19
  beforeEach(async () => {
15
20
  try {
@@ -17,12 +22,19 @@ describe("PidStore", () => {
17
22
  } catch {
18
23
  // ignore cleanup failures (e.g. Windows lock files from previous test)
19
24
  }
25
+ originalAgentYesHome = process.env.AGENT_YES_HOME;
26
+ process.env.AGENT_YES_HOME = GLOBAL_TEST_DIR;
20
27
  store = new PidStore(TEST_DIR);
21
28
  await store.init();
22
29
  });
23
30
 
24
31
  afterEach(async () => {
25
32
  await store.close();
33
+ if (originalAgentYesHome === undefined) {
34
+ delete process.env.AGENT_YES_HOME;
35
+ } else {
36
+ process.env.AGENT_YES_HOME = originalAgentYesHome;
37
+ }
26
38
  try {
27
39
  await rm(TEST_DIR, { recursive: true, force: true });
28
40
  } catch {
package/ts/pidStore.ts CHANGED
@@ -2,7 +2,11 @@ import { mkdir, writeFile } from "fs/promises";
2
2
  import path from "path";
3
3
  import { logger } from "./logger.ts";
4
4
  import { JsonlStore } from "./JsonlStore.ts";
5
- import { appendGlobalPid, updateGlobalPidStatus } from "./globalPidIndex.ts";
5
+ import {
6
+ appendGlobalPid,
7
+ maybeCompactGlobalPids,
8
+ updateGlobalPidStatus,
9
+ } from "./globalPidIndex.ts";
6
10
 
7
11
  export interface PidRecord {
8
12
  _id?: string;
@@ -100,7 +104,9 @@ export class PidStore {
100
104
  exit_code: null,
101
105
  exit_reason: null,
102
106
  started_at: now,
103
- }).catch(() => null);
107
+ })
108
+ .then(() => maybeCompactGlobalPids())
109
+ .catch(() => null);
104
110
 
105
111
  return result;
106
112
  }
@@ -1,11 +0,0 @@
1
- import { t as CLIS_CONFIG } from "./ts-D_iRstH9.js";
2
- import "./logger-B9h0djqx.js";
3
- import "./pidStore-CHLHMBEM.js";
4
- import "./globalPidIndex-DNEh8a_O.js";
5
-
6
- //#region ts/SUPPORTED_CLIS.ts
7
- const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
8
-
9
- //#endregion
10
- export { SUPPORTED_CLIS };
11
- //# sourceMappingURL=SUPPORTED_CLIS-Dzf60ENT.js.map
@@ -1,103 +0,0 @@
1
- import { n as logger } from "./logger-B9h0djqx.js";
2
- import { appendFile, mkdir, readFile } from "fs/promises";
3
- import { homedir } from "os";
4
- import path from "path";
5
- import { lock } from "proper-lockfile";
6
-
7
- //#region ts/globalPidIndex.ts
8
- const GLOBAL_DIR = path.join(homedir(), ".agent-yes");
9
- const GLOBAL_FILE = path.join(GLOBAL_DIR, "pids.jsonl");
10
- async function ensureDir() {
11
- await mkdir(GLOBAL_DIR, { recursive: true });
12
- }
13
- async function withLock(fn) {
14
- await ensureDir();
15
- let release;
16
- try {
17
- release = await lock(GLOBAL_DIR, {
18
- lockfilePath: GLOBAL_FILE + ".lock",
19
- retries: {
20
- retries: 5,
21
- minTimeout: 50,
22
- maxTimeout: 500
23
- }
24
- });
25
- return await fn();
26
- } finally {
27
- await release?.();
28
- }
29
- }
30
- /** Append one full record line. Caller must provide all required fields. */
31
- async function appendGlobalPid(record) {
32
- try {
33
- await withLock(async () => {
34
- await appendFile(GLOBAL_FILE, JSON.stringify(record) + "\n");
35
- });
36
- } catch (error) {
37
- logger.debug("[globalPidIndex] append failed:", error);
38
- }
39
- }
40
- /** Append a partial status update by pid (status, exit_code, exit_reason). */
41
- async function updateGlobalPidStatus(pid, patch) {
42
- try {
43
- await withLock(async () => {
44
- const existing = (await readGlobalPidsRaw()).find((r) => r.pid === pid);
45
- if (!existing) return;
46
- const merged = {
47
- ...existing,
48
- ...patch
49
- };
50
- await appendFile(GLOBAL_FILE, JSON.stringify(merged) + "\n");
51
- });
52
- } catch (error) {
53
- logger.debug("[globalPidIndex] updateStatus failed:", error);
54
- }
55
- }
56
- /**
57
- * Read the file once without merge logic — internal helper for status updates.
58
- */
59
- async function readGlobalPidsRaw() {
60
- let raw;
61
- try {
62
- raw = await readFile(GLOBAL_FILE, "utf-8");
63
- } catch (err) {
64
- if (err.code === "ENOENT") return [];
65
- throw err;
66
- }
67
- const merged = /* @__PURE__ */ new Map();
68
- for (const line of raw.split("\n")) {
69
- const trimmed = line.trim();
70
- if (!trimmed) continue;
71
- try {
72
- const doc = JSON.parse(trimmed);
73
- if (typeof doc.pid !== "number") continue;
74
- const prev = merged.get(doc.pid);
75
- merged.set(doc.pid, prev ? {
76
- ...prev,
77
- ...doc
78
- } : doc);
79
- } catch {}
80
- }
81
- return Array.from(merged.values());
82
- }
83
- /**
84
- * Read all records, last-line-per-pid wins (events get merged).
85
- * Optionally filter to live processes only.
86
- */
87
- async function readGlobalPids(opts = {}) {
88
- const records = await readGlobalPidsRaw();
89
- if (!opts.liveOnly) return records;
90
- return records.filter((r) => r.status !== "exited" && isProcessAlive(r.pid));
91
- }
92
- function isProcessAlive(pid) {
93
- try {
94
- process.kill(pid, 0);
95
- return true;
96
- } catch {
97
- return false;
98
- }
99
- }
100
-
101
- //#endregion
102
- export { readGlobalPids as n, updateGlobalPidStatus as r, appendGlobalPid as t };
103
- //# sourceMappingURL=globalPidIndex-DNEh8a_O.js.map
@@ -1,5 +0,0 @@
1
- import "./logger-B9h0djqx.js";
2
- import { t as PidStore } from "./pidStore-CHLHMBEM.js";
3
- import "./globalPidIndex-DNEh8a_O.js";
4
-
5
- export { PidStore };