claude-yes 1.73.2 → 1.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/SUPPORTED_CLIS-BCn8YIi2.js +11 -0
- package/dist/{agent-yes.config-CyP5iRZf.js → agent-yes.config-1LMoK18R.js} +1 -1
- package/dist/cli.js +12 -5
- package/dist/globalPidIndex-DNEh8a_O.js +103 -0
- package/dist/index.js +3 -2
- package/dist/{package-DpfHTSW2.js → package-Bn0B_jWZ.js} +2 -2
- package/dist/{pidStore-CPrgJSJi.js → pidStore-CHLHMBEM.js} +25 -4
- package/dist/pidStore-DR1yPY3t.js +5 -0
- package/dist/subcommands-CR1i1sjy.js +387 -0
- package/dist/{tray-Bzb1owBN.js → tray-D5deJPjk.js} +1 -1
- package/dist/{ts-CsdLrLod.js → ts-C8vuG5y-.js} +4 -4
- package/package.json +1 -1
- package/ts/cli.ts +11 -0
- package/ts/globalPidIndex.spec.ts +166 -0
- package/ts/globalPidIndex.ts +143 -0
- package/ts/pidStore.ts +24 -0
- package/ts/subcommands.spec.ts +581 -0
- package/ts/subcommands.ts +521 -0
- package/dist/SUPPORTED_CLIS-C7sGMdKJ.js +0 -10
- package/dist/pidStore-B4yDm3TL.js +0 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { t as CLIS_CONFIG } from "./ts-C8vuG5y-.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-BCn8YIi2.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-
|
|
3
|
+
import { n as version, t as name } from "./package-Bn0B_jWZ.js";
|
|
4
4
|
import { argv } from "process";
|
|
5
5
|
import { execFileSync, spawn } from "child_process";
|
|
6
6
|
import ms from "ms";
|
|
@@ -634,16 +634,23 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
634
634
|
process.exit(0);
|
|
635
635
|
}
|
|
636
636
|
}
|
|
637
|
+
{
|
|
638
|
+
const { isSubcommand, runSubcommand } = await import("./subcommands-CR1i1sjy.js");
|
|
639
|
+
if (isSubcommand(process.argv[2])) {
|
|
640
|
+
const code = await runSubcommand(process.argv);
|
|
641
|
+
process.exit(code ?? 0);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
637
644
|
await checkAndAutoUpdate();
|
|
638
645
|
logger.info(versionString());
|
|
639
646
|
const config = parseCliArgs(process.argv);
|
|
640
647
|
if (config.tray) {
|
|
641
|
-
const { startTray } = await import("./tray-
|
|
648
|
+
const { startTray } = await import("./tray-D5deJPjk.js");
|
|
642
649
|
await startTray();
|
|
643
650
|
await new Promise(() => {});
|
|
644
651
|
}
|
|
645
652
|
{
|
|
646
|
-
const { ensureTray } = await import("./tray-
|
|
653
|
+
const { ensureTray } = await import("./tray-D5deJPjk.js");
|
|
647
654
|
ensureTray();
|
|
648
655
|
}
|
|
649
656
|
if (config.useRust) {
|
|
@@ -657,7 +664,7 @@ if (config.useRust) {
|
|
|
657
664
|
}
|
|
658
665
|
}
|
|
659
666
|
if (rustBinary) {
|
|
660
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
667
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-BCn8YIi2.js");
|
|
661
668
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
662
669
|
if (config.verbose) {
|
|
663
670
|
console.log(`[rust] Using binary: ${rustBinary}`);
|
|
@@ -687,7 +694,7 @@ if (config.showVersion) {
|
|
|
687
694
|
process.exit(0);
|
|
688
695
|
}
|
|
689
696
|
if (config.appendPrompt) {
|
|
690
|
-
const { PidStore } = await import("./pidStore-
|
|
697
|
+
const { PidStore } = await import("./pidStore-DR1yPY3t.js");
|
|
691
698
|
const ipcPath = await PidStore.findActiveFifo(process.cwd());
|
|
692
699
|
if (!ipcPath) {
|
|
693
700
|
console.error("No active agent with IPC found in current directory.");
|
|
@@ -0,0 +1,103 @@
|
|
|
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
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-C8vuG5y-.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./pidStore-
|
|
3
|
+
import "./pidStore-CHLHMBEM.js";
|
|
4
|
+
import "./globalPidIndex-DNEh8a_O.js";
|
|
4
5
|
|
|
5
6
|
export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
|
|
@@ -1,4 +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
3
|
import { closeSync, existsSync, fsyncSync, openSync } from "fs";
|
|
3
4
|
import { appendFile, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
4
5
|
import path from "path";
|
|
@@ -202,14 +203,17 @@ var PidStore = class PidStore {
|
|
|
202
203
|
}
|
|
203
204
|
async registerProcess({ pid, cli, args, prompt, cwd }) {
|
|
204
205
|
const now = Date.now();
|
|
206
|
+
const argsJson = JSON.stringify(args);
|
|
207
|
+
const logFile = path.resolve(this.getLogDir(), `${pid}.log`);
|
|
208
|
+
const fifoFile = this.getFifoPath(pid);
|
|
205
209
|
const record = {
|
|
206
210
|
pid,
|
|
207
211
|
cli,
|
|
208
|
-
args:
|
|
212
|
+
args: argsJson,
|
|
209
213
|
prompt,
|
|
210
214
|
cwd,
|
|
211
|
-
logFile
|
|
212
|
-
fifoFile
|
|
215
|
+
logFile,
|
|
216
|
+
fifoFile,
|
|
213
217
|
status: "active",
|
|
214
218
|
exitReason: "",
|
|
215
219
|
startedAt: now
|
|
@@ -224,6 +228,18 @@ var PidStore = class PidStore {
|
|
|
224
228
|
throw new Error(`Failed to register process ${pid}`);
|
|
225
229
|
}
|
|
226
230
|
logger.debug(`[pidStore] Registered process ${pid}`);
|
|
231
|
+
appendGlobalPid({
|
|
232
|
+
pid,
|
|
233
|
+
cli,
|
|
234
|
+
prompt: prompt ?? null,
|
|
235
|
+
cwd,
|
|
236
|
+
log_file: logFile,
|
|
237
|
+
fifo_file: fifoFile,
|
|
238
|
+
status: "active",
|
|
239
|
+
exit_code: null,
|
|
240
|
+
exit_reason: null,
|
|
241
|
+
started_at: now
|
|
242
|
+
}).catch(() => null);
|
|
227
243
|
return result;
|
|
228
244
|
}
|
|
229
245
|
async updateStatus(pid, status, extra) {
|
|
@@ -234,6 +250,11 @@ var PidStore = class PidStore {
|
|
|
234
250
|
if (extra?.exitCode !== void 0) patch.exitCode = extra.exitCode;
|
|
235
251
|
await this.store.updateById(existing._id, patch);
|
|
236
252
|
logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
|
|
253
|
+
updateGlobalPidStatus(pid, {
|
|
254
|
+
status,
|
|
255
|
+
exit_code: extra?.exitCode ?? null,
|
|
256
|
+
exit_reason: extra?.exitReason ?? null
|
|
257
|
+
}).catch(() => null);
|
|
237
258
|
}
|
|
238
259
|
getAllRecords() {
|
|
239
260
|
return this.store.getAll();
|
|
@@ -316,4 +337,4 @@ pid-db/
|
|
|
316
337
|
|
|
317
338
|
//#endregion
|
|
318
339
|
export { PidStore as t };
|
|
319
|
-
//# sourceMappingURL=pidStore-
|
|
340
|
+
//# sourceMappingURL=pidStore-CHLHMBEM.js.map
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import "./logger-B9h0djqx.js";
|
|
2
|
+
import { n as readGlobalPids } from "./globalPidIndex-DNEh8a_O.js";
|
|
3
|
+
import { readFile, stat } from "fs/promises";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
|
|
7
|
+
//#region ts/subcommands.ts
|
|
8
|
+
/**
|
|
9
|
+
* `cy ls / read / cat / tail / head / send` subcommand implementations.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors the principles of koho's `terminal-ws-lib.ts` (session list, render
|
|
12
|
+
* via @xterm/headless, keyword-keyed input) — but file-based instead of via
|
|
13
|
+
* a daemon. Reads ~/.agent-yes/pids.jsonl (cross-runtime global index, written
|
|
14
|
+
* by both the TS PidStore and the Rust pid_store::PidStore) and the per-pid
|
|
15
|
+
* raw log files.
|
|
16
|
+
*
|
|
17
|
+
* Returns null when argv[2] is not a known subcommand so cli.ts falls through
|
|
18
|
+
* to the normal agent-spawning flow.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Read the per-cwd TS PidStore JSONL and convert to the global record shape,
|
|
22
|
+
* so pre-existing TS agents that were spawned before the global-index mirror
|
|
23
|
+
* shipped still show up in `cy ls`. Merging is done in `mergeRecords`.
|
|
24
|
+
*/
|
|
25
|
+
async function readLocalTsPids(cwd) {
|
|
26
|
+
const jsonlPath = path.join(cwd, ".agent-yes", "pid-records.jsonl");
|
|
27
|
+
let raw;
|
|
28
|
+
try {
|
|
29
|
+
raw = await readFile(jsonlPath, "utf-8");
|
|
30
|
+
} catch {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const docs = /* @__PURE__ */ new Map();
|
|
34
|
+
for (const line of raw.split("\n")) {
|
|
35
|
+
const trimmed = line.trim();
|
|
36
|
+
if (!trimmed) continue;
|
|
37
|
+
try {
|
|
38
|
+
const doc = JSON.parse(trimmed);
|
|
39
|
+
if (!doc._id) continue;
|
|
40
|
+
if (doc.$$deleted) {
|
|
41
|
+
docs.delete(doc._id);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const prev = docs.get(doc._id);
|
|
45
|
+
docs.set(doc._id, prev ? {
|
|
46
|
+
...prev,
|
|
47
|
+
...doc
|
|
48
|
+
} : doc);
|
|
49
|
+
} catch {}
|
|
50
|
+
}
|
|
51
|
+
return Array.from(docs.values()).map((d) => ({
|
|
52
|
+
pid: d.pid,
|
|
53
|
+
cli: d.cli,
|
|
54
|
+
prompt: d.prompt ?? null,
|
|
55
|
+
cwd: d.cwd,
|
|
56
|
+
log_file: d.logFile ?? null,
|
|
57
|
+
fifo_file: d.fifoFile ?? null,
|
|
58
|
+
status: d.status ?? "active",
|
|
59
|
+
exit_code: d.exitCode ?? null,
|
|
60
|
+
exit_reason: d.exitReason ?? null,
|
|
61
|
+
started_at: d.startedAt ?? 0
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
/** Merge by pid; later entries (typically from the global file) win. */
|
|
65
|
+
function mergeRecords(...buckets) {
|
|
66
|
+
const out = /* @__PURE__ */ new Map();
|
|
67
|
+
for (const bucket of buckets) for (const r of bucket) {
|
|
68
|
+
const prev = out.get(r.pid);
|
|
69
|
+
out.set(r.pid, prev ? {
|
|
70
|
+
...prev,
|
|
71
|
+
...r
|
|
72
|
+
} : r);
|
|
73
|
+
}
|
|
74
|
+
return Array.from(out.values());
|
|
75
|
+
}
|
|
76
|
+
const SUBCOMMANDS = new Set([
|
|
77
|
+
"ls",
|
|
78
|
+
"list",
|
|
79
|
+
"ps",
|
|
80
|
+
"read",
|
|
81
|
+
"cat",
|
|
82
|
+
"tail",
|
|
83
|
+
"head",
|
|
84
|
+
"send"
|
|
85
|
+
]);
|
|
86
|
+
function isSubcommand(name) {
|
|
87
|
+
return !!name && SUBCOMMANDS.has(name);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Top-level entry. Returns the desired process exit code, or null if argv
|
|
91
|
+
* is not a subcommand invocation.
|
|
92
|
+
*/
|
|
93
|
+
async function runSubcommand(argv) {
|
|
94
|
+
const sub = argv[2];
|
|
95
|
+
if (!isSubcommand(sub)) return null;
|
|
96
|
+
const rest = argv.slice(3);
|
|
97
|
+
try {
|
|
98
|
+
switch (sub) {
|
|
99
|
+
case "ls":
|
|
100
|
+
case "list":
|
|
101
|
+
case "ps": return await cmdLs(rest);
|
|
102
|
+
case "read":
|
|
103
|
+
case "cat": return await cmdRead(rest, { mode: "cat" });
|
|
104
|
+
case "tail": return await cmdRead(rest, { mode: "tail" });
|
|
105
|
+
case "head": return await cmdRead(rest, { mode: "head" });
|
|
106
|
+
case "send": return await cmdSend(rest);
|
|
107
|
+
default: return null;
|
|
108
|
+
}
|
|
109
|
+
} catch (err) {
|
|
110
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
111
|
+
process.stderr.write(`cy ${sub}: ${msg}\n`);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function parseArgs(rest) {
|
|
116
|
+
const flags = {};
|
|
117
|
+
const positional = [];
|
|
118
|
+
for (let i = 0; i < rest.length; i++) {
|
|
119
|
+
const arg = rest[i];
|
|
120
|
+
if (arg.startsWith("--")) {
|
|
121
|
+
const eq = arg.indexOf("=");
|
|
122
|
+
if (eq >= 0) flags[arg.slice(2, eq)] = arg.slice(eq + 1);
|
|
123
|
+
else {
|
|
124
|
+
const key = arg.slice(2);
|
|
125
|
+
const next = rest[i + 1];
|
|
126
|
+
if ([
|
|
127
|
+
"all",
|
|
128
|
+
"json",
|
|
129
|
+
"latest"
|
|
130
|
+
].includes(key) || !next || next.startsWith("-")) flags[key] = true;
|
|
131
|
+
else {
|
|
132
|
+
flags[key] = next;
|
|
133
|
+
i++;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} else if (arg.startsWith("-") && arg.length > 1) if (arg === "-n") {
|
|
137
|
+
flags["n"] = rest[i + 1] ?? "";
|
|
138
|
+
i++;
|
|
139
|
+
} else flags[arg.slice(1)] = true;
|
|
140
|
+
else positional.push(arg);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
flags,
|
|
144
|
+
positional
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function commonOpts(flags) {
|
|
148
|
+
return {
|
|
149
|
+
all: !!flags.all,
|
|
150
|
+
cwdScope: typeof flags.cwd === "string" ? path.resolve(flags.cwd) : flags.cwd === true ? process.cwd() : null,
|
|
151
|
+
latest: !!flags.latest,
|
|
152
|
+
json: !!flags.json
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function matchKeyword(record, keyword) {
|
|
156
|
+
if (!keyword) return true;
|
|
157
|
+
const kw = keyword.toLowerCase();
|
|
158
|
+
if (/^\d+$/.test(keyword) && record.pid === Number(keyword)) return true;
|
|
159
|
+
if (record.cwd.toLowerCase().includes(kw)) return true;
|
|
160
|
+
if (record.cli.toLowerCase() === kw) return true;
|
|
161
|
+
if (record.prompt && record.prompt.toLowerCase().includes(kw)) return true;
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
async function listRecords(keyword, opts) {
|
|
165
|
+
let records = mergeRecords(await readLocalTsPids(process.cwd()), opts.cwdScope ? await readLocalTsPids(opts.cwdScope) : [], await readGlobalPids());
|
|
166
|
+
if (!opts.all) records = records.filter((r) => r.status !== "exited" && isPidAlive(r.pid));
|
|
167
|
+
if (opts.cwdScope) {
|
|
168
|
+
const scope = opts.cwdScope;
|
|
169
|
+
records = records.filter((r) => r.cwd === scope || r.cwd.startsWith(scope + path.sep));
|
|
170
|
+
}
|
|
171
|
+
if (keyword) records = records.filter((r) => matchKeyword(r, keyword));
|
|
172
|
+
records.sort((a, b) => b.started_at - a.started_at);
|
|
173
|
+
return records;
|
|
174
|
+
}
|
|
175
|
+
function isPidAlive(pid) {
|
|
176
|
+
try {
|
|
177
|
+
process.kill(pid, 0);
|
|
178
|
+
return true;
|
|
179
|
+
} catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function resolveOne(keyword, opts) {
|
|
184
|
+
if (!keyword) throw new Error("keyword required (pid, cwd substring, cli name, or prompt substring)");
|
|
185
|
+
const matches = await listRecords(keyword, opts);
|
|
186
|
+
if (matches.length === 0) throw new Error(`no running agent matched "${keyword}"`);
|
|
187
|
+
if (matches.length === 1) return matches[0];
|
|
188
|
+
if (opts.latest) return matches[0];
|
|
189
|
+
const lines = matches.slice(0, 10).map((r) => ` ${r.pid} ${r.cli} ${r.cwd}`).join("\n");
|
|
190
|
+
throw new Error(`keyword "${keyword}" matched ${matches.length} agents — disambiguate by pid or pass --latest:\n${lines}`);
|
|
191
|
+
}
|
|
192
|
+
async function cmdLs(rest) {
|
|
193
|
+
const { flags, positional } = parseArgs(rest);
|
|
194
|
+
const opts = commonOpts(flags);
|
|
195
|
+
const keyword = positional[0];
|
|
196
|
+
const records = await listRecords(keyword, opts);
|
|
197
|
+
if (opts.json) {
|
|
198
|
+
process.stdout.write(JSON.stringify(records, null, 2) + "\n");
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
if (records.length === 0) {
|
|
202
|
+
process.stderr.write(keyword ? `no running agents matched "${keyword}"\n` : "no running agents\n");
|
|
203
|
+
return 0;
|
|
204
|
+
}
|
|
205
|
+
const rows = records.map((r) => ({
|
|
206
|
+
pid: String(r.pid),
|
|
207
|
+
cli: r.cli,
|
|
208
|
+
status: r.status,
|
|
209
|
+
age: humanizeAge(Date.now() - r.started_at),
|
|
210
|
+
cwd: shortenPath(r.cwd),
|
|
211
|
+
prompt: truncate(r.prompt ?? "", 60)
|
|
212
|
+
}));
|
|
213
|
+
const widths = {
|
|
214
|
+
pid: Math.max(3, ...rows.map((r) => r.pid.length)),
|
|
215
|
+
cli: Math.max(3, ...rows.map((r) => r.cli.length)),
|
|
216
|
+
status: Math.max(6, ...rows.map((r) => r.status.length)),
|
|
217
|
+
age: Math.max(3, ...rows.map((r) => r.age.length)),
|
|
218
|
+
cwd: Math.max(3, ...rows.map((r) => r.cwd.length))
|
|
219
|
+
};
|
|
220
|
+
const header = [
|
|
221
|
+
"PID".padEnd(widths.pid),
|
|
222
|
+
"CLI".padEnd(widths.cli),
|
|
223
|
+
"STATUS".padEnd(widths.status),
|
|
224
|
+
"AGE".padEnd(widths.age),
|
|
225
|
+
"CWD".padEnd(widths.cwd),
|
|
226
|
+
"PROMPT"
|
|
227
|
+
].join(" ") + "\n";
|
|
228
|
+
process.stdout.write(header);
|
|
229
|
+
for (const r of rows) process.stdout.write([
|
|
230
|
+
r.pid.padEnd(widths.pid),
|
|
231
|
+
r.cli.padEnd(widths.cli),
|
|
232
|
+
r.status.padEnd(widths.status),
|
|
233
|
+
r.age.padEnd(widths.age),
|
|
234
|
+
r.cwd.padEnd(widths.cwd),
|
|
235
|
+
r.prompt
|
|
236
|
+
].join(" ") + "\n");
|
|
237
|
+
return 0;
|
|
238
|
+
}
|
|
239
|
+
function humanizeAge(ms) {
|
|
240
|
+
if (ms < 1e3) return "0s";
|
|
241
|
+
const s = Math.floor(ms / 1e3);
|
|
242
|
+
if (s < 60) return `${s}s`;
|
|
243
|
+
const m = Math.floor(s / 60);
|
|
244
|
+
if (m < 60) return `${m}m`;
|
|
245
|
+
const h = Math.floor(m / 60);
|
|
246
|
+
if (h < 24) return `${h}h`;
|
|
247
|
+
return `${Math.floor(h / 24)}d`;
|
|
248
|
+
}
|
|
249
|
+
function shortenPath(p) {
|
|
250
|
+
const home = homedir();
|
|
251
|
+
return p.startsWith(home) ? "~" + p.slice(home.length) : p;
|
|
252
|
+
}
|
|
253
|
+
function truncate(s, n) {
|
|
254
|
+
if (s.length <= n) return s;
|
|
255
|
+
return s.slice(0, n - 1) + "…";
|
|
256
|
+
}
|
|
257
|
+
async function cmdRead(rest, { mode }) {
|
|
258
|
+
const { flags, positional } = parseArgs(rest);
|
|
259
|
+
const opts = commonOpts(flags);
|
|
260
|
+
const keyword = positional[0];
|
|
261
|
+
const nFlag = typeof flags.n === "string" ? Number(flags.n) : void 0;
|
|
262
|
+
const n = nFlag !== void 0 && Number.isFinite(nFlag) && nFlag > 0 ? Math.floor(nFlag) : mode === "cat" ? 0 : 96;
|
|
263
|
+
const record = await resolveOne(keyword, opts);
|
|
264
|
+
const logPath = record.log_file;
|
|
265
|
+
if (!logPath) throw new Error(`pid ${record.pid}: no log_file recorded`);
|
|
266
|
+
let stats;
|
|
267
|
+
try {
|
|
268
|
+
stats = await stat(logPath);
|
|
269
|
+
} catch {
|
|
270
|
+
throw new Error(`pid ${record.pid}: log file not found at ${logPath}`);
|
|
271
|
+
}
|
|
272
|
+
if (!stats.isFile()) throw new Error(`pid ${record.pid}: log path is not a file: ${logPath}`);
|
|
273
|
+
const rendered = await renderRawLog(await readFile(logPath), {
|
|
274
|
+
mode,
|
|
275
|
+
n
|
|
276
|
+
});
|
|
277
|
+
process.stdout.write(rendered);
|
|
278
|
+
if (!rendered.endsWith("\n")) process.stdout.write("\n");
|
|
279
|
+
return 0;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Feed the raw PTY bytes through @xterm/headless and emit plain text.
|
|
283
|
+
* Same approach as koho's renderTerminalBuffer + agent-yes's XtermProxy.
|
|
284
|
+
*/
|
|
285
|
+
async function renderRawLog(buf, { mode, n }) {
|
|
286
|
+
const cols = 200;
|
|
287
|
+
const rows = 50;
|
|
288
|
+
const scrollback = Math.max(5e4, n + rows + 100);
|
|
289
|
+
try {
|
|
290
|
+
const { Terminal } = await import("@xterm/headless");
|
|
291
|
+
const term = new Terminal({
|
|
292
|
+
cols,
|
|
293
|
+
rows,
|
|
294
|
+
scrollback,
|
|
295
|
+
allowProposedApi: true
|
|
296
|
+
});
|
|
297
|
+
await new Promise((resolve) => term.write(buf, resolve));
|
|
298
|
+
const active = term.buffer.active;
|
|
299
|
+
const lines = [];
|
|
300
|
+
for (let i = 0; i < active.length; i++) {
|
|
301
|
+
const line = active.getLine(i);
|
|
302
|
+
lines.push(line ? line.translateToString(false).trimEnd() : "");
|
|
303
|
+
}
|
|
304
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
305
|
+
if (mode === "cat") return lines.join("\n");
|
|
306
|
+
if (mode === "tail") return lines.slice(Math.max(0, lines.length - n)).join("\n");
|
|
307
|
+
return lines.slice(0, n).join("\n");
|
|
308
|
+
} catch {
|
|
309
|
+
let text = new TextDecoder().decode(buf);
|
|
310
|
+
text = text.replace(/\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g, "").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
311
|
+
const lines = text.split("\n");
|
|
312
|
+
if (mode === "cat") return lines.join("\n");
|
|
313
|
+
if (mode === "tail") return lines.slice(Math.max(0, lines.length - n)).join("\n");
|
|
314
|
+
return lines.slice(0, n).join("\n");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async function cmdSend(rest) {
|
|
318
|
+
const { flags, positional } = parseArgs(rest);
|
|
319
|
+
const opts = commonOpts(flags);
|
|
320
|
+
const keyword = positional[0];
|
|
321
|
+
const message = positional.slice(1).join(" ");
|
|
322
|
+
if (!keyword) throw new Error("usage: cy send <keyword> <msg> [--code=enter|esc|ctrl-c|ctrl-y|tab|none]");
|
|
323
|
+
const trailing = controlCodeFromName(typeof flags.code === "string" ? flags.code.toLowerCase() : "enter");
|
|
324
|
+
const record = await resolveOne(keyword, opts);
|
|
325
|
+
const fifoPath = record.fifo_file;
|
|
326
|
+
if (!fifoPath) throw new Error(`pid ${record.pid}: no fifo_file recorded — agent was not started with --stdpush (or was spawned by Rust which doesn't yet support FIFO IPC; see ROADMAP item 10)`);
|
|
327
|
+
const payload = (message ?? "") + trailing;
|
|
328
|
+
await writeToIpc(fifoPath, payload);
|
|
329
|
+
process.stdout.write(`sent to pid ${record.pid} (${record.cli}): ${truncate(payload, 80)}\n`);
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
332
|
+
function controlCodeFromName(name) {
|
|
333
|
+
switch (name) {
|
|
334
|
+
case "enter":
|
|
335
|
+
case "cr":
|
|
336
|
+
case "return": return "\r";
|
|
337
|
+
case "esc":
|
|
338
|
+
case "escape": return "\x1B";
|
|
339
|
+
case "ctrl-c":
|
|
340
|
+
case "ctrlc": return "";
|
|
341
|
+
case "ctrl-y":
|
|
342
|
+
case "ctrly": return "";
|
|
343
|
+
case "ctrl-d":
|
|
344
|
+
case "ctrld": return "";
|
|
345
|
+
case "tab": return " ";
|
|
346
|
+
case "none":
|
|
347
|
+
case "": return "";
|
|
348
|
+
default:
|
|
349
|
+
const m = /^raw:0x([0-9a-f]+)$/i.exec(name);
|
|
350
|
+
if (m) return String.fromCharCode(parseInt(m[1], 16));
|
|
351
|
+
throw new Error(`unknown --code=${name}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async function writeToIpc(ipcPath, payload) {
|
|
355
|
+
if (process.platform === "win32") {
|
|
356
|
+
const { connect } = await import("net");
|
|
357
|
+
await new Promise((resolve, reject) => {
|
|
358
|
+
const client = connect(ipcPath);
|
|
359
|
+
const timer = setTimeout(() => {
|
|
360
|
+
client.destroy();
|
|
361
|
+
reject(/* @__PURE__ */ new Error("named pipe connect timeout"));
|
|
362
|
+
}, 5e3);
|
|
363
|
+
client.on("connect", () => {
|
|
364
|
+
clearTimeout(timer);
|
|
365
|
+
client.write(payload);
|
|
366
|
+
client.end();
|
|
367
|
+
resolve();
|
|
368
|
+
});
|
|
369
|
+
client.on("error", (err) => {
|
|
370
|
+
clearTimeout(timer);
|
|
371
|
+
reject(err);
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
} else {
|
|
375
|
+
const { openSync, writeFileSync, closeSync } = await import("fs");
|
|
376
|
+
const fd = openSync(ipcPath, "w");
|
|
377
|
+
try {
|
|
378
|
+
writeFileSync(fd, payload);
|
|
379
|
+
} finally {
|
|
380
|
+
closeSync(fd);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
//#endregion
|
|
386
|
+
export { isSubcommand, runSubcommand };
|
|
387
|
+
//# sourceMappingURL=subcommands-CR1i1sjy.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-
|
|
2
|
+
import { n as version } from "./package-Bn0B_jWZ.js";
|
|
3
3
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-DQWJSptq.js";
|
|
4
|
-
import { t as PidStore } from "./pidStore-
|
|
4
|
+
import { t as PidStore } from "./pidStore-CHLHMBEM.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";
|
|
@@ -1032,7 +1032,7 @@ async function notifyWebhook(status, details, cwd = process.cwd()) {
|
|
|
1032
1032
|
|
|
1033
1033
|
//#endregion
|
|
1034
1034
|
//#region ts/index.ts
|
|
1035
|
-
const config = await import("./agent-yes.config-
|
|
1035
|
+
const config = await import("./agent-yes.config-1LMoK18R.js").then((mod) => mod.default || mod);
|
|
1036
1036
|
const CLIS_CONFIG = config.clis;
|
|
1037
1037
|
/**
|
|
1038
1038
|
* Main function to run agent-cli with automatic yes/no responses
|
|
@@ -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-
|
|
1682
|
+
//# sourceMappingURL=ts-C8vuG5y-.js.map
|
package/package.json
CHANGED