@songsid/agend 2.3.0-beta.14 → 2.3.0-beta.15
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/credential-profile.d.ts +2 -0
- package/dist/backend/credential-profile.js +2 -0
- package/dist/backend/credential-profile.js.map +1 -1
- package/dist/fleet-manager.d.ts +1 -0
- package/dist/fleet-manager.js +17 -1
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +4 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +1 -1
- package/dist/tmux-exec-helper.d.ts +94 -0
- package/dist/tmux-exec-helper.js +407 -0
- package/dist/tmux-exec-helper.js.map +1 -0
- package/dist/tmux-manager.js +26 -11
- package/dist/tmux-manager.js.map +1 -1
- package/dist/usage/providers.d.ts +10 -1
- package/dist/usage/providers.js +63 -3
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/usage-api.d.ts +3 -1
- package/dist/usage/usage-api.js +5 -4
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/view-api.js +4 -2
- package/dist/view-api.js.map +1 -1
- package/package.json +1 -1
|
@@ -212,7 +212,10 @@ and log it in, so report it as such rather than as a missing subscription.
|
|
|
212
212
|
Two profiles logged into the same account do not double anything, and it is
|
|
213
213
|
worth saying so before someone sets this up expecting more headroom.
|
|
214
214
|
- Claude Code profiles now each have their own usage row (`Claude (<profile>)`),
|
|
215
|
-
reading quota from that profile's own login.
|
|
215
|
+
reading quota from that profile's own login. Every Claude login on the host has a row, whether or not an
|
|
216
|
+
instance runs on it (#2236): `Claude (default)` for the default login, plus every profile. A full subscription
|
|
217
|
+
nothing runs on any more still shows, at 100% with its reset time. Asked "where is my other account?", it is one
|
|
218
|
+
of these rows; it is not missing because no agent uses it.
|
|
216
219
|
- Switching a kiro profile does not carry the conversation across; the agent
|
|
217
220
|
gets a summary of the work, not the transcript. Say anything that must survive
|
|
218
221
|
verbatim in the channel before switching. For claude-code, AgEnD copies the
|
|
@@ -238,7 +238,7 @@ chat; View, Log and Details are the switch under the header.
|
|
|
238
238
|
|
|
239
239
|
**Usage.** The **Usage** header button: a right-hand rail at 1200 px or wider (shown/hidden per device; on Chat,
|
|
240
240
|
View, Log, Details and Fleet), otherwise a dialog — **AI Subscription Usage**. One row per provider in use (Claude,
|
|
241
|
-
Codex, Muse, Grok, Kiro, Antigravity): Claude's **Session** (5-hour window) and **Weekly** (7-day), per-model weekly
|
|
241
|
+
Codex, Muse, Grok, Kiro, Antigravity; every Claude login on the host, used or not — `Claude (default)` and each profile, #2236): Claude's **Session** (5-hour window) and **Weekly** (7-day), per-model weekly
|
|
242
242
|
rows, "binding" on the window that will stop you, **Extra usage** in dollars. "high" at 70%, "near limit" at 90%.
|
|
243
243
|
Refreshes every 60 s while open; **Refresh** forces it (at most once per 30 s); **Move up / Move down** reorders rows.
|
|
244
244
|
`web.usage_panel: false` hides the rail and dialog (the `/usage` chat command and `get_usage` still work).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
export interface HelperExecOptions {
|
|
3
|
+
timeout?: number;
|
|
4
|
+
maxBuffer?: number;
|
|
5
|
+
env?: NodeJS.ProcessEnv;
|
|
6
|
+
/** Written to the command's stdin, then closed (load-buffer). */
|
|
7
|
+
input?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface HelperExecResult {
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
}
|
|
13
|
+
/** The binaries the helper may start (by name; resolved to absolute paths on PATH when the helper starts). */
|
|
14
|
+
export declare const HELPER_BINARIES: readonly ["tmux", "stty"];
|
|
15
|
+
/** How long a request may wait for the helper's "started" before the helper is taken as wedged. */
|
|
16
|
+
export declare const HELPER_START_TIMEOUT_MS = 1500;
|
|
17
|
+
/** After "started": how long past the command's own timeout its answer may take (no timeout: this alone). */
|
|
18
|
+
export declare const HELPER_ANSWER_GRACE_MS = 5000;
|
|
19
|
+
export declare const HELPER_NO_TIMEOUT_DEADLINE_MS = 120000;
|
|
20
|
+
/**
|
|
21
|
+
* The helper's program (`node -e`): JSON lines in on stdin, JSON lines out on stdout. It exits when its stdin ends
|
|
22
|
+
* (the fleet went away) or its parent no longer exists (the fleet died while something else held the pipe).
|
|
23
|
+
*/
|
|
24
|
+
export declare const HELPER_SOURCE: string;
|
|
25
|
+
/** The absolute path `name` resolves to on this PATH (an executable file), or null. */
|
|
26
|
+
export declare function resolveOnPath(name: string, path: string | undefined): string | null;
|
|
27
|
+
export interface ExecHelperOptions {
|
|
28
|
+
/** The direct call, used when the helper isn't (or can no longer be) used for a call. */
|
|
29
|
+
direct(file: string, args: string[], opts: HelperExecOptions): Promise<HelperExecResult>;
|
|
30
|
+
/** Start the helper process (tests: another program). */
|
|
31
|
+
spawnHelper?(allow: string[]): ChildProcess;
|
|
32
|
+
/** The PATH binaries are resolved on (default: the fleet's, when the helper starts). */
|
|
33
|
+
path?(): string | undefined;
|
|
34
|
+
now?(): number;
|
|
35
|
+
startTimeoutMs?: number;
|
|
36
|
+
answerGraceMs?: number;
|
|
37
|
+
log?(event: string, detail?: Record<string, unknown>): void;
|
|
38
|
+
}
|
|
39
|
+
export declare class ExecHelper {
|
|
40
|
+
private readonly options;
|
|
41
|
+
private proc;
|
|
42
|
+
private allow;
|
|
43
|
+
private pending;
|
|
44
|
+
private nextId;
|
|
45
|
+
private trusted;
|
|
46
|
+
private backoffMs;
|
|
47
|
+
private restartAt;
|
|
48
|
+
private stopped;
|
|
49
|
+
private resolvedPath;
|
|
50
|
+
constructor(options: ExecHelperOptions);
|
|
51
|
+
private now;
|
|
52
|
+
/** Whether calls go to the helper now. */
|
|
53
|
+
usable(): boolean;
|
|
54
|
+
/** Start the helper (the fleet's own start and timer only). */
|
|
55
|
+
start(): void;
|
|
56
|
+
/** The fleet's timer: start a helper that is not running, after the backoff. */
|
|
57
|
+
maintain(): void;
|
|
58
|
+
/** A fleet started again after a stop may start the helper again. */
|
|
59
|
+
reopen(): void;
|
|
60
|
+
/** The fleet is stopping: no more calls go to the helper; its stdin ends, and it exits once its commands have answered. */
|
|
61
|
+
stop(): void;
|
|
62
|
+
/**
|
|
63
|
+
* A command, through the helper when it can be used, else directly — exactly once either way. `direct` is the
|
|
64
|
+
* caller's own call as it has always been made (so a direct call is byte-for-byte the old one); default: directExec.
|
|
65
|
+
*/
|
|
66
|
+
exec<R = HelperExecResult>(file: string, args: string[], opts?: HelperExecOptions, direct?: () => Promise<R>): Promise<R>;
|
|
67
|
+
/** The helper's absolute path for `file` when the call would start the same binary; else null (the call goes direct). */
|
|
68
|
+
private absoluteFor;
|
|
69
|
+
private receive;
|
|
70
|
+
/**
|
|
71
|
+
* While a call waits on it, the helper keeps the process alive, as a direct call's child would; with none waiting it
|
|
72
|
+
* never does (the fleet's exit is not held up by an idle helper).
|
|
73
|
+
*/
|
|
74
|
+
private holdOpen;
|
|
75
|
+
private settle;
|
|
76
|
+
/** What a direct call that timed out would have rejected with. */
|
|
77
|
+
private timeoutError;
|
|
78
|
+
/**
|
|
79
|
+
* Stop trusting this helper and end it; the calls it never started go direct once it is gone (gone()). Taken as
|
|
80
|
+
* wedged (no_start) while commands it started still run, it is not killed — killing it would not stop them, and their
|
|
81
|
+
* calls would fail though they ran: its stdin is ended, and it exits once they have answered (or their deadline kills it).
|
|
82
|
+
*/
|
|
83
|
+
private retire;
|
|
84
|
+
/** The helper is gone and its output has been read to the end. */
|
|
85
|
+
private gone;
|
|
86
|
+
private scheduleRestart;
|
|
87
|
+
}
|
|
88
|
+
/** execFile as a promise, the way the direct call has always run it (with its sync-work attribution done by callers). */
|
|
89
|
+
export declare function directExec(file: string, args: string[], opts: HelperExecOptions): Promise<HelperExecResult>;
|
|
90
|
+
/**
|
|
91
|
+
* The fleet's one helper. Until the fleet starts it (and whenever it can't be used) every call is the direct one, its
|
|
92
|
+
* synchronous fork attributed as before (#1235's "tmux.spawn").
|
|
93
|
+
*/
|
|
94
|
+
export declare const tmuxExecHelper: ExecHelper;
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
// #1956 item 3: the fleet's tmux (and stty) commands, started by a small helper process instead of the fleet itself.
|
|
2
|
+
//
|
|
3
|
+
// Node starts a child process by forking the parent synchronously, and that fork costs in proportion to the parent's
|
|
4
|
+
// resident memory: on a 550 MB fleet a spawn holds the event loop ~17 ms on an idle host, and ~110 ms (up to 750 ms)
|
|
5
|
+
// on a busy one (daemon.log's "tmux.spawn"). The helper is a small node process started once with the fleet; the fleet
|
|
6
|
+
// writes a request to its stdin (a pipe write, ~0.05 ms) and the helper's own, cheap fork runs the command.
|
|
7
|
+
//
|
|
8
|
+
// Semantics are the direct call's: the same file (by absolute path, from an allowlist: tmux and stty), argv, env,
|
|
9
|
+
// timeout, maxBuffer and stdin input; the same result ({ stdout, stderr }) or error (code, signal, killed, stdout,
|
|
10
|
+
// stderr). Requests run concurrently; a caller that awaits its call before the next keeps its order.
|
|
11
|
+
//
|
|
12
|
+
// A command never runs twice. The helper writes "started" to its stdout with a synchronous write BEFORE it forks:
|
|
13
|
+
// - a call the helper has not said "started" for may go direct — but only once the helper is gone (killed, and its
|
|
14
|
+
// output read to the end, which carries every "started" it wrote), so it can never start the command later;
|
|
15
|
+
// - a call it has said "started" for is the helper's: its answer, or — if none comes by its deadline — the error a
|
|
16
|
+
// timed-out direct call would give. It is never run again directly.
|
|
17
|
+
// A helper taken as wedged while commands it started still run is not killed (that would not stop them, and their calls
|
|
18
|
+
// would fail though they ran): it gets no more calls, its stdin is ended, and it exits once they have answered.
|
|
19
|
+
// A helper that is not running, or not trusted, is not used: the call goes direct, as before. Only the fleet starts
|
|
20
|
+
// and restarts the helper (start/maintain, from its own lifecycle and timer), never a tool handler.
|
|
21
|
+
import { execFile, spawn } from "node:child_process";
|
|
22
|
+
import { accessSync, constants as fsConstants } from "node:fs";
|
|
23
|
+
import { delimiter, isAbsolute, join } from "node:path";
|
|
24
|
+
import { StringDecoder } from "node:string_decoder";
|
|
25
|
+
import { measureSyncWork } from "./sync-work-attribution.js";
|
|
26
|
+
/** The binaries the helper may start (by name; resolved to absolute paths on PATH when the helper starts). */
|
|
27
|
+
export const HELPER_BINARIES = ["tmux", "stty"];
|
|
28
|
+
/** How long a request may wait for the helper's "started" before the helper is taken as wedged. */
|
|
29
|
+
export const HELPER_START_TIMEOUT_MS = 1500;
|
|
30
|
+
/** After "started": how long past the command's own timeout its answer may take (no timeout: this alone). */
|
|
31
|
+
export const HELPER_ANSWER_GRACE_MS = 5000;
|
|
32
|
+
export const HELPER_NO_TIMEOUT_DEADLINE_MS = 120_000;
|
|
33
|
+
const RESTART_BACKOFF_MIN_MS = 1000, RESTART_BACKOFF_MAX_MS = 60_000;
|
|
34
|
+
const DEFAULT_MAX_BUFFER = 1024 * 1024; // execFile's
|
|
35
|
+
/**
|
|
36
|
+
* The helper's program (`node -e`): JSON lines in on stdin, JSON lines out on stdout. It exits when its stdin ends
|
|
37
|
+
* (the fleet went away) or its parent no longer exists (the fleet died while something else held the pipe).
|
|
38
|
+
*/
|
|
39
|
+
export const HELPER_SOURCE = String.raw `
|
|
40
|
+
"use strict";
|
|
41
|
+
const { execFile } = require("node:child_process");
|
|
42
|
+
const { writeSync } = require("node:fs");
|
|
43
|
+
const allow = new Set(JSON.parse(process.argv[1] || "[]"));
|
|
44
|
+
const parent = Number(process.argv[2]) || process.ppid; // the fleet passes none (tests: a given pid)
|
|
45
|
+
function send(o) {
|
|
46
|
+
const b = Buffer.from(JSON.stringify(o) + "\n");
|
|
47
|
+
let off = 0;
|
|
48
|
+
while (off < b.length) {
|
|
49
|
+
try { off += writeSync(1, b, off); }
|
|
50
|
+
catch (e) { if (e && e.code === "EAGAIN") { Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2); continue; } process.exit(0); }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
let running = 0, ending = false;
|
|
54
|
+
function finish() { if (ending && running === 0) process.exit(0); }
|
|
55
|
+
function handle(line) {
|
|
56
|
+
let r; try { r = JSON.parse(line); } catch { return; }
|
|
57
|
+
if (!r || typeof r.id !== "number") return;
|
|
58
|
+
if (typeof r.file !== "string" || !allow.has(r.file) || !Array.isArray(r.args)) return send({ id: r.id, refused: "not-allowed" });
|
|
59
|
+
send({ id: r.id, started: true }); // in the pipe before the fork: see the fleet side
|
|
60
|
+
let child;
|
|
61
|
+
running++;
|
|
62
|
+
try {
|
|
63
|
+
child = execFile(r.file, r.args, { timeout: r.timeout || 0, maxBuffer: r.maxBuffer || 1048576, env: r.env || process.env, encoding: "utf8" },
|
|
64
|
+
(err, stdout, stderr) => {
|
|
65
|
+
send({ id: r.id, done: true, stdout: String(stdout || ""), stderr: String(stderr || ""),
|
|
66
|
+
error: err ? { message: err.message, code: err.code === undefined ? null : err.code, signal: err.signal || null, killed: !!err.killed } : null });
|
|
67
|
+
running--; finish();
|
|
68
|
+
});
|
|
69
|
+
} catch (e) {
|
|
70
|
+
running--;
|
|
71
|
+
send({ id: r.id, done: true, stdout: "", stderr: "", error: { message: String(e && e.message || e), code: e && e.code || null, signal: null, killed: false } });
|
|
72
|
+
return finish();
|
|
73
|
+
}
|
|
74
|
+
if (typeof r.input === "string") {
|
|
75
|
+
if (child.stdin) { child.stdin.on("error", () => {}); child.stdin.end(r.input); }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
let buf = "";
|
|
79
|
+
process.stdin.setEncoding("utf8");
|
|
80
|
+
process.stdin.on("data", (c) => { buf += c; let i; while ((i = buf.indexOf("\n")) >= 0) { const line = buf.slice(0, i); buf = buf.slice(i + 1); handle(line); } });
|
|
81
|
+
// Its stdin ended (the fleet stopped, or no longer trusts it): every request has been read; it exits once the commands
|
|
82
|
+
// it started have answered.
|
|
83
|
+
process.stdin.on("end", () => { ending = true; finish(); });
|
|
84
|
+
process.stdin.on("close", () => { ending = true; finish(); });
|
|
85
|
+
// Its parent gone (the fleet died while something else holds the pipe): process.ppid is read once by node, so ask
|
|
86
|
+
// whether that process still exists (ESRCH: it does not).
|
|
87
|
+
setInterval(() => { try { process.kill(parent, 0); } catch (e) { if (e && e.code === "ESRCH") process.exit(0); } }, 1000);
|
|
88
|
+
`;
|
|
89
|
+
/** The absolute path `name` resolves to on this PATH (an executable file), or null. */
|
|
90
|
+
export function resolveOnPath(name, path) {
|
|
91
|
+
if (isAbsolute(name))
|
|
92
|
+
return name;
|
|
93
|
+
for (const dir of (path ?? "").split(delimiter)) {
|
|
94
|
+
if (!dir)
|
|
95
|
+
continue;
|
|
96
|
+
const candidate = join(dir, name);
|
|
97
|
+
try {
|
|
98
|
+
accessSync(candidate, fsConstants.X_OK);
|
|
99
|
+
return candidate;
|
|
100
|
+
}
|
|
101
|
+
catch { /* next */ }
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
export class ExecHelper {
|
|
106
|
+
options;
|
|
107
|
+
proc = null;
|
|
108
|
+
allow = new Map(); // name → absolute path
|
|
109
|
+
pending = new Map();
|
|
110
|
+
nextId = 1;
|
|
111
|
+
trusted = false;
|
|
112
|
+
backoffMs = RESTART_BACKOFF_MIN_MS;
|
|
113
|
+
restartAt = 0;
|
|
114
|
+
stopped = false;
|
|
115
|
+
resolvedPath = null;
|
|
116
|
+
constructor(options) {
|
|
117
|
+
this.options = options;
|
|
118
|
+
}
|
|
119
|
+
now() { return this.options.now ? this.options.now() : performance.now(); }
|
|
120
|
+
/** Whether calls go to the helper now. */
|
|
121
|
+
usable() { return this.trusted && this.proc !== null && !this.stopped; }
|
|
122
|
+
/** Start the helper (the fleet's own start and timer only). */
|
|
123
|
+
start() {
|
|
124
|
+
if (this.proc || this.stopped)
|
|
125
|
+
return;
|
|
126
|
+
const path = this.options.path ? this.options.path() : process.env.PATH;
|
|
127
|
+
this.allow = new Map();
|
|
128
|
+
for (const name of HELPER_BINARIES) {
|
|
129
|
+
const abs = resolveOnPath(name, path);
|
|
130
|
+
if (abs)
|
|
131
|
+
this.allow.set(name, abs);
|
|
132
|
+
}
|
|
133
|
+
let proc;
|
|
134
|
+
try {
|
|
135
|
+
proc = this.options.spawnHelper
|
|
136
|
+
? this.options.spawnHelper([...this.allow.values()])
|
|
137
|
+
: spawn(process.execPath, ["-e", HELPER_SOURCE, JSON.stringify([...this.allow.values()])], { stdio: ["pipe", "pipe", "ignore"] });
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
this.options.log?.("start_failed", { error: err.message });
|
|
141
|
+
this.scheduleRestart();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
this.proc = proc;
|
|
145
|
+
this.trusted = true;
|
|
146
|
+
proc.unref();
|
|
147
|
+
proc.stdin?.unref?.();
|
|
148
|
+
proc.stdout?.unref?.();
|
|
149
|
+
proc.stdin?.on("error", () => this.retire(proc, "stdin_error"));
|
|
150
|
+
const decoder = new StringDecoder("utf8");
|
|
151
|
+
let buf = "";
|
|
152
|
+
proc.stdout?.on("data", (chunk) => {
|
|
153
|
+
buf += decoder.write(chunk);
|
|
154
|
+
let i;
|
|
155
|
+
while ((i = buf.indexOf("\n")) >= 0) {
|
|
156
|
+
const line = buf.slice(0, i);
|
|
157
|
+
buf = buf.slice(i + 1);
|
|
158
|
+
this.receive(line);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
// `close`: the process is gone and its stdout read to the end — every "started" it wrote has been seen.
|
|
162
|
+
proc.on("close", () => this.gone(proc));
|
|
163
|
+
proc.on("error", () => this.retire(proc, "process_error"));
|
|
164
|
+
}
|
|
165
|
+
/** The fleet's timer: start a helper that is not running, after the backoff. */
|
|
166
|
+
maintain() {
|
|
167
|
+
if (this.stopped || this.proc || this.now() < this.restartAt)
|
|
168
|
+
return;
|
|
169
|
+
this.start();
|
|
170
|
+
}
|
|
171
|
+
/** A fleet started again after a stop may start the helper again. */
|
|
172
|
+
reopen() { this.stopped = false; this.restartAt = 0; this.backoffMs = RESTART_BACKOFF_MIN_MS; }
|
|
173
|
+
/** The fleet is stopping: no more calls go to the helper; its stdin ends, and it exits once its commands have answered. */
|
|
174
|
+
stop() {
|
|
175
|
+
this.stopped = true;
|
|
176
|
+
const proc = this.proc;
|
|
177
|
+
if (proc) {
|
|
178
|
+
this.trusted = false;
|
|
179
|
+
try {
|
|
180
|
+
proc.stdin?.end();
|
|
181
|
+
}
|
|
182
|
+
catch { /* gone */ }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* A command, through the helper when it can be used, else directly — exactly once either way. `direct` is the
|
|
187
|
+
* caller's own call as it has always been made (so a direct call is byte-for-byte the old one); default: directExec.
|
|
188
|
+
*/
|
|
189
|
+
exec(file, args, opts = {}, direct) {
|
|
190
|
+
const goDirect = () => (direct ? direct() : this.options.direct(file, args, opts));
|
|
191
|
+
const abs = this.usable() ? this.absoluteFor(file, opts.env) : null;
|
|
192
|
+
if (!abs)
|
|
193
|
+
return goDirect();
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const p = { id: this.nextId++, started: false, settled: false, file, abs, args, opts, resolve: resolve, reject, startTimer: null, answerTimer: null,
|
|
196
|
+
direct: () => { goDirect().then(resolve, reject); } };
|
|
197
|
+
this.pending.set(p.id, p);
|
|
198
|
+
this.holdOpen();
|
|
199
|
+
const request = { id: p.id, file: abs, args, timeout: opts.timeout ?? 0, maxBuffer: opts.maxBuffer ?? DEFAULT_MAX_BUFFER,
|
|
200
|
+
env: opts.env ?? process.env, ...(opts.input !== undefined ? { input: opts.input } : {}) };
|
|
201
|
+
try {
|
|
202
|
+
this.proc.stdin.write(JSON.stringify(request) + "\n");
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
this.pending.delete(p.id);
|
|
206
|
+
this.retire(this.proc, "write_failed");
|
|
207
|
+
goDirect().then(resolve, reject);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// The fleet's own loop may have been held (a GC, a long sync read, its own spawn) while the "started" sat in the
|
|
211
|
+
// pipe: the timers phase runs before the poll that would read it, so look once that poll has run (setImmediate).
|
|
212
|
+
p.startTimer = setTimeout(() => {
|
|
213
|
+
p.startTimer = null;
|
|
214
|
+
setImmediate(() => { if (!p.started && !p.settled)
|
|
215
|
+
this.retire(this.proc, "no_start"); });
|
|
216
|
+
}, this.options.startTimeoutMs ?? HELPER_START_TIMEOUT_MS);
|
|
217
|
+
p.startTimer.unref?.();
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/** The helper's absolute path for `file` when the call would start the same binary; else null (the call goes direct). */
|
|
221
|
+
absoluteFor(file, env) {
|
|
222
|
+
const name = HELPER_BINARIES.find((n) => n === file);
|
|
223
|
+
const allowed = name ? this.allow.get(name) : [...this.allow.values()].find((v) => v === file);
|
|
224
|
+
if (!allowed)
|
|
225
|
+
return null;
|
|
226
|
+
if (isAbsolute(file))
|
|
227
|
+
return file === allowed ? allowed : null;
|
|
228
|
+
// The call's PATH must resolve the name to the helper's binary (memoized per PATH string).
|
|
229
|
+
const path = (env ?? process.env).PATH;
|
|
230
|
+
if (!this.resolvedPath || this.resolvedPath.path !== path)
|
|
231
|
+
this.resolvedPath = { path, byName: new Map() };
|
|
232
|
+
let resolved = this.resolvedPath.byName.get(file);
|
|
233
|
+
if (resolved === undefined) {
|
|
234
|
+
resolved = resolveOnPath(file, path);
|
|
235
|
+
this.resolvedPath.byName.set(file, resolved);
|
|
236
|
+
}
|
|
237
|
+
return resolved === allowed ? allowed : null;
|
|
238
|
+
}
|
|
239
|
+
receive(line) {
|
|
240
|
+
let m;
|
|
241
|
+
try {
|
|
242
|
+
m = JSON.parse(line);
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const p = typeof m.id === "number" ? this.pending.get(m.id) : undefined;
|
|
248
|
+
if (!p || p.settled)
|
|
249
|
+
return;
|
|
250
|
+
if (m.refused) { // never started: direct is safe
|
|
251
|
+
this.settle(p);
|
|
252
|
+
p.direct();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (m.started) {
|
|
256
|
+
p.started = true;
|
|
257
|
+
if (p.startTimer) {
|
|
258
|
+
clearTimeout(p.startTimer);
|
|
259
|
+
p.startTimer = null;
|
|
260
|
+
}
|
|
261
|
+
const limit = (p.opts.timeout && p.opts.timeout > 0 ? p.opts.timeout : HELPER_NO_TIMEOUT_DEADLINE_MS) + (this.options.answerGraceMs ?? HELPER_ANSWER_GRACE_MS);
|
|
262
|
+
p.answerTimer = setTimeout(() => {
|
|
263
|
+
p.answerTimer = null;
|
|
264
|
+
if (p.settled)
|
|
265
|
+
return;
|
|
266
|
+
this.settle(p);
|
|
267
|
+
p.reject(this.timeoutError(p));
|
|
268
|
+
this.retire(this.proc, "no_answer");
|
|
269
|
+
}, limit);
|
|
270
|
+
p.answerTimer.unref?.();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (m.done) {
|
|
274
|
+
this.settle(p);
|
|
275
|
+
this.backoffMs = RESTART_BACKOFF_MIN_MS;
|
|
276
|
+
const stdout = m.stdout ?? "", stderr = m.stderr ?? "";
|
|
277
|
+
if (!m.error) {
|
|
278
|
+
p.resolve({ stdout, stderr });
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// The message names the command as the direct call would ("tmux …", not the helper's absolute path).
|
|
282
|
+
const message = p.abs !== p.file ? m.error.message.replace(p.abs, p.file) : m.error.message;
|
|
283
|
+
const err = new Error(message);
|
|
284
|
+
err.code = m.error.code;
|
|
285
|
+
err.signal = m.error.signal;
|
|
286
|
+
err.killed = m.error.killed;
|
|
287
|
+
err.cmd = [p.file, ...p.args].join(" ");
|
|
288
|
+
err.stdout = stdout;
|
|
289
|
+
err.stderr = stderr;
|
|
290
|
+
p.reject(err);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* While a call waits on it, the helper keeps the process alive, as a direct call's child would; with none waiting it
|
|
295
|
+
* never does (the fleet's exit is not held up by an idle helper).
|
|
296
|
+
*/
|
|
297
|
+
holdOpen() {
|
|
298
|
+
const proc = this.proc;
|
|
299
|
+
if (!proc)
|
|
300
|
+
return;
|
|
301
|
+
const hold = this.pending.size > 0;
|
|
302
|
+
if (hold)
|
|
303
|
+
proc.ref();
|
|
304
|
+
else
|
|
305
|
+
proc.unref();
|
|
306
|
+
const out = proc.stdout;
|
|
307
|
+
if (hold)
|
|
308
|
+
out?.ref?.();
|
|
309
|
+
else
|
|
310
|
+
out?.unref?.();
|
|
311
|
+
}
|
|
312
|
+
settle(p) {
|
|
313
|
+
p.settled = true;
|
|
314
|
+
this.pending.delete(p.id);
|
|
315
|
+
if (!this.pending.size)
|
|
316
|
+
this.holdOpen();
|
|
317
|
+
if (p.startTimer) {
|
|
318
|
+
clearTimeout(p.startTimer);
|
|
319
|
+
p.startTimer = null;
|
|
320
|
+
}
|
|
321
|
+
if (p.answerTimer) {
|
|
322
|
+
clearTimeout(p.answerTimer);
|
|
323
|
+
p.answerTimer = null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/** What a direct call that timed out would have rejected with. */
|
|
327
|
+
timeoutError(p) {
|
|
328
|
+
const err = new Error(`Command failed: ${[p.file, ...p.args].join(" ")}`);
|
|
329
|
+
err.code = null;
|
|
330
|
+
err.signal = "SIGTERM";
|
|
331
|
+
err.killed = true;
|
|
332
|
+
err.cmd = [p.file, ...p.args].join(" ");
|
|
333
|
+
err.stdout = "";
|
|
334
|
+
err.stderr = "";
|
|
335
|
+
return err;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Stop trusting this helper and end it; the calls it never started go direct once it is gone (gone()). Taken as
|
|
339
|
+
* wedged (no_start) while commands it started still run, it is not killed — killing it would not stop them, and their
|
|
340
|
+
* calls would fail though they ran: its stdin is ended, and it exits once they have answered (or their deadline kills it).
|
|
341
|
+
*/
|
|
342
|
+
retire(proc, why) {
|
|
343
|
+
if (!proc || proc !== this.proc)
|
|
344
|
+
return;
|
|
345
|
+
if (this.trusted)
|
|
346
|
+
this.options.log?.("retired", { why });
|
|
347
|
+
this.trusted = false;
|
|
348
|
+
if (why === "no_start" && [...this.pending.values()].some((p) => p.started)) {
|
|
349
|
+
try {
|
|
350
|
+
proc.stdin?.end();
|
|
351
|
+
}
|
|
352
|
+
catch { /* gone */ }
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
proc.kill("SIGKILL");
|
|
357
|
+
}
|
|
358
|
+
catch { /* gone */ }
|
|
359
|
+
}
|
|
360
|
+
/** The helper is gone and its output has been read to the end. */
|
|
361
|
+
gone(proc) {
|
|
362
|
+
if (proc !== this.proc)
|
|
363
|
+
return;
|
|
364
|
+
this.proc = null;
|
|
365
|
+
this.trusted = false;
|
|
366
|
+
for (const p of [...this.pending.values()]) {
|
|
367
|
+
this.settle(p);
|
|
368
|
+
if (p.started)
|
|
369
|
+
p.reject(this.timeoutError(p)); // it may have run: never again
|
|
370
|
+
else
|
|
371
|
+
p.direct(); // it never started, and now never will
|
|
372
|
+
}
|
|
373
|
+
this.scheduleRestart();
|
|
374
|
+
}
|
|
375
|
+
scheduleRestart() {
|
|
376
|
+
if (this.stopped)
|
|
377
|
+
return;
|
|
378
|
+
this.restartAt = this.now() + this.backoffMs;
|
|
379
|
+
this.backoffMs = Math.min(this.backoffMs * 2, RESTART_BACKOFF_MAX_MS);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/** execFile as a promise, the way the direct call has always run it (with its sync-work attribution done by callers). */
|
|
383
|
+
export function directExec(file, args, opts) {
|
|
384
|
+
return new Promise((resolve, reject) => {
|
|
385
|
+
const child = execFile(file, args, { ...(opts.timeout ? { timeout: opts.timeout } : {}), ...(opts.maxBuffer ? { maxBuffer: opts.maxBuffer } : {}),
|
|
386
|
+
...(opts.env ? { env: opts.env } : {}), encoding: "utf8" }, (err, stdout, stderr) => {
|
|
387
|
+
if (err) {
|
|
388
|
+
Object.assign(err, { stdout, stderr });
|
|
389
|
+
reject(err);
|
|
390
|
+
}
|
|
391
|
+
else
|
|
392
|
+
resolve({ stdout, stderr });
|
|
393
|
+
});
|
|
394
|
+
if (opts.input !== undefined) {
|
|
395
|
+
child.stdin?.on("error", reject);
|
|
396
|
+
child.stdin?.end(opts.input);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* The fleet's one helper. Until the fleet starts it (and whenever it can't be used) every call is the direct one, its
|
|
402
|
+
* synchronous fork attributed as before (#1235's "tmux.spawn").
|
|
403
|
+
*/
|
|
404
|
+
export const tmuxExecHelper = new ExecHelper({
|
|
405
|
+
direct: (file, args, opts) => measureSyncWork("tmux.spawn", () => directExec(file, args, opts)),
|
|
406
|
+
});
|
|
407
|
+
//# sourceMappingURL=tmux-exec-helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux-exec-helper.js","sourceRoot":"","sources":["../src/tmux-exec-helper.ts"],"names":[],"mappings":"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"}
|
package/dist/tmux-manager.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
|
-
import { measureSyncWork } from "./sync-work-attribution.js";
|
|
4
3
|
import { tmuxReadArgs } from "./tmux-read.js";
|
|
4
|
+
import { tmuxExecHelper } from "./tmux-exec-helper.js";
|
|
5
|
+
import { measureSyncWork } from "./sync-work-attribution.js";
|
|
5
6
|
const execAsync = promisify(execFile);
|
|
6
7
|
/**
|
|
7
|
-
* Every `tmux` call here. Starting
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Every `tmux` (and `stty`) call here. Starting a child process is synchronous on the fleet's loop — a fork whose cost
|
|
9
|
+
* grows with the fleet's memory (#1235, #1956) — so the fleet's spawn helper starts it when it is running; otherwise it
|
|
10
|
+
* is the direct call, attributed as "tmux.spawn" as before. Either way: the same command, once; the same result.
|
|
10
11
|
*/
|
|
11
|
-
const exec = ((
|
|
12
|
+
const exec = ((file, args, opts) => tmuxExecHelper.exec(file, args, opts ?? {}, () => measureSyncWork("tmux.spawn", () => opts === undefined ? execAsync(file, args) : execAsync(file, args, opts))));
|
|
12
13
|
/** `exec`, bounded when a timeout is given — and in exactly the old (file, args) form when it is not. */
|
|
13
14
|
const execBounded = (args, timeoutMs) => timeoutMs ? exec("tmux", args, { timeout: timeoutMs }) : exec("tmux", args);
|
|
14
15
|
/** Keys sendKeySequence may send: cursor moves and deletions inside an input line. */
|
|
@@ -44,12 +45,26 @@ function execTmuxWithInput(tmuxArgs, input, timeoutMs) {
|
|
|
44
45
|
error.message = `${error.message}: ${detail}`;
|
|
45
46
|
reject(error);
|
|
46
47
|
};
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
// Through the spawn helper when it runs (#1956); else the direct call as it always was. The input goes to the
|
|
49
|
+
// command's stdin either way.
|
|
50
|
+
const direct = () => new Promise((ok, fail) => {
|
|
51
|
+
const cb = (error, stdout, stderr) => {
|
|
52
|
+
if (error) {
|
|
53
|
+
Object.assign(error, { stdout: String(stdout ?? ""), stderr: String(stderr ?? "") });
|
|
54
|
+
fail(error);
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
ok({ stdout: String(stdout ?? ""), stderr: String(stderr ?? "") });
|
|
58
|
+
};
|
|
59
|
+
// Options only when a bound is asked for: every other caller keeps execFile's (file, args, callback) form.
|
|
60
|
+
const child = measureSyncWork("tmux.spawn", () => timeoutMs
|
|
61
|
+
? execFile("tmux", tmuxArgs, { timeout: timeoutMs }, cb)
|
|
62
|
+
: execFile("tmux", tmuxArgs, cb));
|
|
63
|
+
child?.stdin?.on("error", fail);
|
|
64
|
+
child?.stdin?.end(input);
|
|
65
|
+
});
|
|
66
|
+
tmuxExecHelper.exec("tmux", tmuxArgs, { ...(timeoutMs ? { timeout: timeoutMs } : {}), input }, direct)
|
|
67
|
+
.then(({ stdout, stderr }) => done(null, stdout, stderr), (err) => done(err, err.stdout ?? "", err.stderr ?? ""));
|
|
53
68
|
});
|
|
54
69
|
}
|
|
55
70
|
export const DEFAULT_TMUX_LOGICAL_SIZE = Object.freeze({
|