@suzuke/agend 1.0.2 → 1.1.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.
- package/dist/backend/claude-code.d.ts +5 -1
- package/dist/backend/claude-code.js +19 -5
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.d.ts +3 -1
- package/dist/backend/codex.js +5 -1
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/gemini-cli.d.ts +3 -1
- package/dist/backend/gemini-cli.js +5 -1
- package/dist/backend/gemini-cli.js.map +1 -1
- package/dist/backend/opencode.d.ts +3 -1
- package/dist/backend/opencode.js +5 -1
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/types.d.ts +8 -0
- package/dist/backend/types.js +14 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/daemon.d.ts +20 -2
- package/dist/daemon.js +121 -22
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.d.ts +1 -0
- package/dist/fleet-manager.js +10 -1
- package/dist/fleet-manager.js.map +1 -1
- package/dist/tmux-control.d.ts +49 -0
- package/dist/tmux-control.js +184 -0
- package/dist/tmux-control.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { spawn, execFile } from "node:child_process";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
|
+
import { createInterface } from "node:readline";
|
|
4
|
+
function execTmux(args) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
execFile("tmux", args, (err, stdout) => {
|
|
7
|
+
if (err)
|
|
8
|
+
reject(err);
|
|
9
|
+
else
|
|
10
|
+
resolve(stdout.trim());
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Persistent tmux control mode client that monitors %output events
|
|
16
|
+
* to detect per-pane idle state. One instance per tmux session.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* const ctrl = new TmuxControlClient("agend", 2000, logger);
|
|
20
|
+
* ctrl.start();
|
|
21
|
+
* await ctrl.waitForIdle("@5"); // wait until window @5 is idle
|
|
22
|
+
* tmux.pasteText(msg);
|
|
23
|
+
*/
|
|
24
|
+
export class TmuxControlClient extends EventEmitter {
|
|
25
|
+
sessionName;
|
|
26
|
+
silenceMs;
|
|
27
|
+
logger;
|
|
28
|
+
proc = null;
|
|
29
|
+
rl = null;
|
|
30
|
+
lastOutputAt = new Map(); // paneId → timestamp
|
|
31
|
+
paneToWindow = new Map(); // paneId → windowId
|
|
32
|
+
stopped = false;
|
|
33
|
+
reconnectTimer = null;
|
|
34
|
+
constructor(sessionName, silenceMs = 2000, logger) {
|
|
35
|
+
super();
|
|
36
|
+
this.sessionName = sessionName;
|
|
37
|
+
this.silenceMs = silenceMs;
|
|
38
|
+
this.logger = logger;
|
|
39
|
+
}
|
|
40
|
+
start() {
|
|
41
|
+
this.stopped = false;
|
|
42
|
+
this.connect();
|
|
43
|
+
}
|
|
44
|
+
// PLACEHOLDER_REST
|
|
45
|
+
stop() {
|
|
46
|
+
this.stopped = true;
|
|
47
|
+
if (this.reconnectTimer) {
|
|
48
|
+
clearTimeout(this.reconnectTimer);
|
|
49
|
+
this.reconnectTimer = null;
|
|
50
|
+
}
|
|
51
|
+
this.cleanup();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Register a window so we can track its pane's output.
|
|
55
|
+
* Call this after createWindow().
|
|
56
|
+
*/
|
|
57
|
+
async registerWindow(windowId) {
|
|
58
|
+
try {
|
|
59
|
+
const paneId = await execTmux([
|
|
60
|
+
"list-panes", "-t", `${this.sessionName}:${windowId}`,
|
|
61
|
+
"-F", "#{pane_id}",
|
|
62
|
+
]);
|
|
63
|
+
if (paneId) {
|
|
64
|
+
this.paneToWindow.set(paneId, windowId);
|
|
65
|
+
this.logger?.debug({ windowId, paneId }, "Registered window→pane mapping");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
this.logger?.debug({ windowId }, "Failed to resolve pane ID for window");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Unregister a window (call on killWindow) */
|
|
73
|
+
unregisterWindow(windowId) {
|
|
74
|
+
for (const [pane, win] of this.paneToWindow) {
|
|
75
|
+
if (win === windowId) {
|
|
76
|
+
this.paneToWindow.delete(pane);
|
|
77
|
+
this.lastOutputAt.delete(pane);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Check if a window's pane has been silent for at least silenceMs */
|
|
83
|
+
isIdle(windowId) {
|
|
84
|
+
const paneId = this.windowToPaneId(windowId);
|
|
85
|
+
if (!paneId)
|
|
86
|
+
return true; // unknown window = assume idle
|
|
87
|
+
const last = this.lastOutputAt.get(paneId);
|
|
88
|
+
if (last == null)
|
|
89
|
+
return true;
|
|
90
|
+
return Date.now() - last >= this.silenceMs;
|
|
91
|
+
}
|
|
92
|
+
// PLACEHOLDER_WAIT
|
|
93
|
+
/**
|
|
94
|
+
* Wait until a window's pane is idle (no output for silenceMs).
|
|
95
|
+
* Returns true if idle detected, false if timeout reached.
|
|
96
|
+
*/
|
|
97
|
+
waitForIdle(windowId, timeoutMs = 30_000) {
|
|
98
|
+
if (this.isIdle(windowId))
|
|
99
|
+
return Promise.resolve(true);
|
|
100
|
+
return new Promise((resolve) => {
|
|
101
|
+
const check = setInterval(() => {
|
|
102
|
+
if (this.isIdle(windowId)) {
|
|
103
|
+
clearInterval(check);
|
|
104
|
+
clearTimeout(timer);
|
|
105
|
+
resolve(true);
|
|
106
|
+
}
|
|
107
|
+
}, 200);
|
|
108
|
+
const timer = setTimeout(() => {
|
|
109
|
+
clearInterval(check);
|
|
110
|
+
this.logger?.warn({ windowId, timeoutMs }, "waitForIdle timed out — forcing delivery");
|
|
111
|
+
resolve(false);
|
|
112
|
+
}, timeoutMs);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Wait until a window's pane produces any output.
|
|
117
|
+
* Used to verify CLI startup — if no output within timeout, CLI likely failed.
|
|
118
|
+
*/
|
|
119
|
+
waitForOutput(windowId, timeoutMs = 15_000) {
|
|
120
|
+
const paneId = this.windowToPaneId(windowId);
|
|
121
|
+
// If already has output recorded, it's alive
|
|
122
|
+
if (paneId && this.lastOutputAt.has(paneId))
|
|
123
|
+
return Promise.resolve(true);
|
|
124
|
+
return new Promise((resolve) => {
|
|
125
|
+
const check = setInterval(() => {
|
|
126
|
+
const pid = this.windowToPaneId(windowId);
|
|
127
|
+
if (pid && this.lastOutputAt.has(pid)) {
|
|
128
|
+
clearInterval(check);
|
|
129
|
+
clearTimeout(timer);
|
|
130
|
+
resolve(true);
|
|
131
|
+
}
|
|
132
|
+
}, 300);
|
|
133
|
+
const timer = setTimeout(() => {
|
|
134
|
+
clearInterval(check);
|
|
135
|
+
resolve(false);
|
|
136
|
+
}, timeoutMs);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
windowToPaneId(windowId) {
|
|
140
|
+
for (const [pane, win] of this.paneToWindow) {
|
|
141
|
+
if (win === windowId)
|
|
142
|
+
return pane;
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
connect() {
|
|
147
|
+
if (this.stopped)
|
|
148
|
+
return;
|
|
149
|
+
this.proc = spawn("tmux", ["-C", "attach", "-t", this.sessionName, "-r"], {
|
|
150
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
151
|
+
});
|
|
152
|
+
this.rl = createInterface({ input: this.proc.stdout });
|
|
153
|
+
this.rl.on("line", (line) => this.parseLine(line));
|
|
154
|
+
this.proc.on("close", () => {
|
|
155
|
+
this.cleanup();
|
|
156
|
+
if (!this.stopped) {
|
|
157
|
+
this.logger?.debug("Control mode disconnected — reconnecting in 2s");
|
|
158
|
+
this.reconnectTimer = setTimeout(() => this.connect(), 2000);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
this.proc.on("error", (err) => {
|
|
162
|
+
this.logger?.warn({ err: err.message }, "Control mode spawn error");
|
|
163
|
+
});
|
|
164
|
+
this.logger?.debug("tmux control mode connected");
|
|
165
|
+
}
|
|
166
|
+
parseLine(line) {
|
|
167
|
+
if (line.startsWith("%output ")) {
|
|
168
|
+
const match = line.match(/^%output (%\d+) /);
|
|
169
|
+
if (match) {
|
|
170
|
+
this.lastOutputAt.set(match[1], Date.now());
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
cleanup() {
|
|
175
|
+
this.rl?.close();
|
|
176
|
+
this.rl = null;
|
|
177
|
+
if (this.proc && !this.proc.killed) {
|
|
178
|
+
this.proc.stdin?.write("detach\n");
|
|
179
|
+
this.proc.kill();
|
|
180
|
+
}
|
|
181
|
+
this.proc = null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=tmux-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux-control.js","sourceRoot":"","sources":["../src/tmux-control.ts"],"names":[],"mappings":"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"}
|