@songsid/agend 2.1.4 → 2.1.5-beta.10
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.js +2 -2
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/types.d.ts +22 -0
- package/dist/backend/types.js +23 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +2 -0
- package/dist/channel/adapters/discord.js +25 -3
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +5 -0
- package/dist/channel/adapters/telegram.js +11 -0
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/types.d.ts +7 -0
- package/dist/cli.js +37 -37
- package/dist/cli.js.map +1 -1
- package/dist/config.js +38 -0
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +38 -0
- package/dist/daemon.js +95 -43
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-context.d.ts +5 -0
- package/dist/fleet-manager.d.ts +126 -0
- package/dist/fleet-manager.js +688 -95
- package/dist/fleet-manager.js.map +1 -1
- package/dist/full-restart.d.ts +20 -0
- package/dist/full-restart.js +39 -0
- package/dist/full-restart.js.map +1 -0
- package/dist/locale.js +72 -2
- package/dist/locale.js.map +1 -1
- package/dist/login-controller.d.ts +127 -0
- package/dist/login-controller.js +450 -0
- package/dist/login-controller.js.map +1 -0
- package/dist/login-flows.d.ts +41 -3
- package/dist/login-flows.js +37 -5
- package/dist/login-flows.js.map +1 -1
- package/dist/login-manager.d.ts +12 -1
- package/dist/login-manager.js +64 -7
- package/dist/login-manager.js.map +1 -1
- package/dist/login-window-lock.d.ts +26 -0
- package/dist/login-window-lock.js +64 -0
- package/dist/login-window-lock.js.map +1 -0
- package/dist/restart-progress.d.ts +35 -5
- package/dist/restart-progress.js +195 -34
- package/dist/restart-progress.js.map +1 -1
- package/dist/service-restart-selection.d.ts +27 -0
- package/dist/service-restart-selection.js +25 -0
- package/dist/service-restart-selection.js.map +1 -0
- package/dist/tmux-manager.d.ts +19 -0
- package/dist/tmux-manager.js +79 -12
- package/dist/tmux-manager.js.map +1 -1
- package/dist/topic-commands.js +23 -7
- package/dist/topic-commands.js.map +1 -1
- package/dist/types.d.ts +18 -0
- package/dist/ui/web-terminal/terminal.css +17 -0
- package/dist/ui/web-terminal/terminal.html +44 -0
- package/dist/ui/web-terminal/terminal.js +148 -0
- package/dist/ui/web-terminal/vendor/LICENSE.addon-fit +19 -0
- package/dist/ui/web-terminal/vendor/LICENSE.addon-web-links +19 -0
- package/dist/ui/web-terminal/vendor/LICENSE.xterm +21 -0
- package/dist/ui/web-terminal/vendor/VENDORED.md +22 -0
- package/dist/ui/web-terminal/vendor/addon-fit.js +1 -0
- package/dist/ui/web-terminal/vendor/addon-web-links.js +1 -0
- package/dist/ui/web-terminal/vendor/xterm.css +285 -0
- package/dist/ui/web-terminal/vendor/xterm.js +1 -0
- package/dist/update-marker.d.ts +11 -1
- package/dist/update-marker.js +19 -4
- package/dist/update-marker.js.map +1 -1
- package/dist/update-progress.d.ts +1 -1
- package/dist/update-progress.js +12 -0
- package/dist/update-progress.js.map +1 -1
- package/dist/usage/providers.d.ts +39 -0
- package/dist/usage/providers.js +69 -3
- package/dist/usage/providers.js.map +1 -1
- package/dist/web-terminal-http.d.ts +61 -0
- package/dist/web-terminal-http.js +422 -0
- package/dist/web-terminal-http.js.map +1 -0
- package/dist/web-terminal.d.ts +336 -0
- package/dist/web-terminal.js +888 -0
- package/dist/web-terminal.js.map +1 -0
- package/dist/ws-server.d.ts +69 -0
- package/dist/ws-server.js +392 -0
- package/dist/ws-server.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web terminal session: one command, one tmux pane, one browser, a few minutes.
|
|
3
|
+
*
|
|
4
|
+
* This is the core of remote `/login` and `/install-cli` (v2.1.5). The fleet
|
|
5
|
+
* starts exactly one command inside a *dedicated* tmux server and hands the
|
|
6
|
+
* admin a browser terminal onto that single pane. Nothing here interprets the
|
|
7
|
+
* CLI's screens or presses keys on the user's behalf — the human is the TUI's
|
|
8
|
+
* interpreter, so every CLI's every login flow works without per-CLI modelling.
|
|
9
|
+
*
|
|
10
|
+
* Security shape (design doc §3):
|
|
11
|
+
* - scope: the browser never gets a tmux client. Input goes through
|
|
12
|
+
* `send-keys -H` to this one pane; the pane runs `sh -c "<command>"` and
|
|
13
|
+
* dies when the command exits. No shell is reachable.
|
|
14
|
+
* - token gate: the URL carries no secret (sid is a path, not a credential).
|
|
15
|
+
* A separate one-time access token — delivered over the authenticated chat
|
|
16
|
+
* channel — is verified in constant time; three failures destroy the
|
|
17
|
+
* session. Success yields a session-bound cookie.
|
|
18
|
+
* - TTL: the session ends when the process exits, when the TTL lapses, on
|
|
19
|
+
* lockout, or on cancel. Ending always kills the tmux server.
|
|
20
|
+
* - observation: the fleet reads the pane (capture-pane) to post the device
|
|
21
|
+
* URL/code into chat and to judge success/failure — read-only.
|
|
22
|
+
*
|
|
23
|
+
* Output streaming uses `pipe-pane` into a FIFO we hold open O_RDWR (never
|
|
24
|
+
* EOF, nothing on disk). The pane is created running a placeholder and the
|
|
25
|
+
* real command is started with `respawn-pane -k` *after* the pipe is attached,
|
|
26
|
+
* so the very first bytes are captured (verified live: new-session + pipe-pane
|
|
27
|
+
* loses the first line; placeholder + respawn does not).
|
|
28
|
+
*/
|
|
29
|
+
import { EventEmitter } from "node:events";
|
|
30
|
+
export interface WebTerminalObserve {
|
|
31
|
+
/** Overrides the generic https matcher when the CLI prints several URLs. */
|
|
32
|
+
urlPattern?: RegExp;
|
|
33
|
+
/** One-time device code shown next to the URL. First capture group wins. */
|
|
34
|
+
codePattern?: RegExp;
|
|
35
|
+
/** Pane text that proves the command achieved its purpose (login done). */
|
|
36
|
+
successPattern?: RegExp;
|
|
37
|
+
/** Known failure strings → human wording + suggested next step. */
|
|
38
|
+
failures?: Array<{
|
|
39
|
+
pattern: RegExp;
|
|
40
|
+
message: string;
|
|
41
|
+
suggest?: "relogin" | "check-args" | "retry";
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
export interface WebTerminalSpec {
|
|
45
|
+
kind: "login" | "install";
|
|
46
|
+
backend: string;
|
|
47
|
+
/** The one shell command the pane will run (`sh -c`). */
|
|
48
|
+
command: string;
|
|
49
|
+
cwd: string;
|
|
50
|
+
ttlMs: number;
|
|
51
|
+
cols?: number;
|
|
52
|
+
rows?: number;
|
|
53
|
+
observe?: WebTerminalObserve;
|
|
54
|
+
requester: {
|
|
55
|
+
adapterId: string;
|
|
56
|
+
userId: string;
|
|
57
|
+
chatId: string;
|
|
58
|
+
threadId?: string;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export type WebTerminalEndReason = "exit" | "ttl" | "cancel" | "token_lockout" | "error";
|
|
62
|
+
export interface WebTerminalResult {
|
|
63
|
+
ok: boolean;
|
|
64
|
+
reason: WebTerminalEndReason;
|
|
65
|
+
exitCode?: number;
|
|
66
|
+
/** Last non-empty pane lines (evidence), or the failure mapping's message. */
|
|
67
|
+
detail: string;
|
|
68
|
+
suggest?: "relogin" | "check-args" | "retry";
|
|
69
|
+
/** The dedicated tmux server could NOT be confirmed dead — operator attention needed. */
|
|
70
|
+
cleanupFailed?: boolean;
|
|
71
|
+
}
|
|
72
|
+
export interface WebTerminalEvents {
|
|
73
|
+
/** Device URL (+ code) appeared in the pane — post to chat (spoiler). Once per distinct URL. */
|
|
74
|
+
onHint?(url: string, code: string | null): void | Promise<void>;
|
|
75
|
+
/** Terminal state, exactly once. */
|
|
76
|
+
onDone(result: WebTerminalResult): void | Promise<void>;
|
|
77
|
+
/** Audit trail (eventLog). Never receives the token or cookie. */
|
|
78
|
+
onAudit?(event: string, fields: Record<string, unknown>): void;
|
|
79
|
+
}
|
|
80
|
+
/** A browser attached over WebSocket, as the session sees it. */
|
|
81
|
+
export interface TerminalClient {
|
|
82
|
+
send(data: Buffer | string): void;
|
|
83
|
+
close(code: number, reason: string): void;
|
|
84
|
+
}
|
|
85
|
+
export interface TerminalLogger {
|
|
86
|
+
debug(obj: unknown, msg?: string): void;
|
|
87
|
+
info(obj: unknown, msg?: string): void;
|
|
88
|
+
warn(obj: unknown, msg?: string): void;
|
|
89
|
+
}
|
|
90
|
+
/** The tmux operations a session needs — injectable for unit tests. */
|
|
91
|
+
export interface TerminalBackend {
|
|
92
|
+
start(opts: {
|
|
93
|
+
socket: string;
|
|
94
|
+
command: string;
|
|
95
|
+
cwd: string;
|
|
96
|
+
cols: number;
|
|
97
|
+
rows: number;
|
|
98
|
+
onOutput: (chunk: Buffer) => void;
|
|
99
|
+
/** Aborted by finish(): no stage may leave a resource behind once this fires. */
|
|
100
|
+
signal?: AbortSignal;
|
|
101
|
+
}): Promise<void>;
|
|
102
|
+
sendInput(socket: string, bytes: Buffer): Promise<void>;
|
|
103
|
+
resize(socket: string, cols: number, rows: number): Promise<void>;
|
|
104
|
+
/** Plain-text pane content (joined wrapped lines), for observation and evidence. */
|
|
105
|
+
capture(socket: string): Promise<string>;
|
|
106
|
+
paneStatus(socket: string): Promise<{
|
|
107
|
+
alive: boolean;
|
|
108
|
+
exitCode?: number;
|
|
109
|
+
} | null>;
|
|
110
|
+
/** Resolves only when the server is confirmed gone; rejects when it may still be alive. */
|
|
111
|
+
kill(socket: string): Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
export declare const ACCESS_TOKEN_LENGTH = 20;
|
|
114
|
+
export declare const MAX_TOKEN_ATTEMPTS = 3;
|
|
115
|
+
export declare const MAX_TTL_MS: number;
|
|
116
|
+
export declare const DEFAULT_COLS = 120;
|
|
117
|
+
export declare const DEFAULT_ROWS = 36;
|
|
118
|
+
export declare const MIN_COLS = 20, MAX_COLS = 250, MIN_ROWS = 5, MAX_ROWS = 100;
|
|
119
|
+
/** Bytes of browser input allowed to wait for tmux before the session is ended as wedged (B4). */
|
|
120
|
+
export declare const MAX_PENDING_INPUT_BYTES: number;
|
|
121
|
+
/** Queued tmux operations allowed to wait (input batches + at most one resize); more means tmux is stuck. */
|
|
122
|
+
export declare const MAX_PENDING_JOBS = 64;
|
|
123
|
+
/** Consecutive failed pane probes before the session is ended as unreachable. */
|
|
124
|
+
export declare const MAX_PROBE_FAILURES = 3;
|
|
125
|
+
/** 20 base32 chars = 100 bits of entropy (13 random bytes, the last 4 bits truncated). */
|
|
126
|
+
export declare function generateAccessToken(bytes?: Buffer): string;
|
|
127
|
+
/** Evidence for a failure report: the last `n` non-empty lines, bounded. */
|
|
128
|
+
export declare function nonEmptyTail(text: string, n?: number, maxLen?: number): string;
|
|
129
|
+
export declare class WebTerminalSession extends EventEmitter {
|
|
130
|
+
readonly spec: WebTerminalSpec;
|
|
131
|
+
private readonly events;
|
|
132
|
+
private readonly backend;
|
|
133
|
+
private readonly logger;
|
|
134
|
+
private readonly now;
|
|
135
|
+
readonly sid: string;
|
|
136
|
+
readonly socketName: string;
|
|
137
|
+
readonly createdAt: number;
|
|
138
|
+
expiresAt: number;
|
|
139
|
+
state: "created" | "running" | "finished";
|
|
140
|
+
private accessToken;
|
|
141
|
+
private tokenAttempts;
|
|
142
|
+
private cookieValue;
|
|
143
|
+
private readonly replay;
|
|
144
|
+
private replayBytes;
|
|
145
|
+
private replayTruncated;
|
|
146
|
+
private client;
|
|
147
|
+
private ttlTimer;
|
|
148
|
+
private pollTimer;
|
|
149
|
+
private polling;
|
|
150
|
+
private finishing;
|
|
151
|
+
/** The backend.start() in flight, so finish() can wait for it to settle before its one confirmed kill. */
|
|
152
|
+
private startInFlight;
|
|
153
|
+
/** Aborted by finish(): the backend checks it after every stage and tears down anything it created. */
|
|
154
|
+
private readonly startAbort;
|
|
155
|
+
private sentUrls;
|
|
156
|
+
private successSeenAt;
|
|
157
|
+
/** Consecutive polls where tmux could not even be asked — a vanished server must not idle until TTL. */
|
|
158
|
+
private probeFailures;
|
|
159
|
+
/** Geometry most recently requested by the browser (queued, frozen or applied) — the only dedupe key. */
|
|
160
|
+
private lastRequested;
|
|
161
|
+
/** Single FIFO for input + resize: browser order is pane order (B4). */
|
|
162
|
+
private ioQueue;
|
|
163
|
+
private pendingInputBytes;
|
|
164
|
+
private pendingJobCount;
|
|
165
|
+
/** Input bytes not yet handed to a running job: consecutive frames coalesce into one tmux paste. */
|
|
166
|
+
private pendingBatch;
|
|
167
|
+
/**
|
|
168
|
+
* The resize job at the TAIL of the queue, still open for coalescing. Only
|
|
169
|
+
* while no input has been queued after it may a newer resize update it;
|
|
170
|
+
* input freezes it (a resize is a barrier and must stay in its FIFO slot).
|
|
171
|
+
*/
|
|
172
|
+
private tailResize;
|
|
173
|
+
private cols;
|
|
174
|
+
private rows;
|
|
175
|
+
constructor(spec: WebTerminalSpec, events: WebTerminalEvents, backend: TerminalBackend, logger: TerminalLogger, now?: () => number);
|
|
176
|
+
/** The one-time access token, readable only until it is redeemed or the session ends. */
|
|
177
|
+
peekAccessToken(): string | null;
|
|
178
|
+
get ttlRemainingMs(): number;
|
|
179
|
+
start(): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Redeem the one-time access token. Constant-time compare; three failures
|
|
182
|
+
* destroy the session (URL + attempts = suspected leak). Success returns the
|
|
183
|
+
* cookie value the HTTP layer sets; the token is gone from memory afterwards.
|
|
184
|
+
*/
|
|
185
|
+
redeemToken(candidate: string): {
|
|
186
|
+
result: "ok";
|
|
187
|
+
cookie: string;
|
|
188
|
+
} | {
|
|
189
|
+
result: "bad";
|
|
190
|
+
remaining: number;
|
|
191
|
+
} | {
|
|
192
|
+
result: "used" | "locked" | "finished";
|
|
193
|
+
};
|
|
194
|
+
checkCookie(value: string | undefined): boolean;
|
|
195
|
+
/** Attach the (single) browser; replays buffered output first. Returns a detach function. */
|
|
196
|
+
attachClient(client: TerminalClient): () => void;
|
|
197
|
+
/** Bytes queued for the pane but not yet delivered. */
|
|
198
|
+
get pendingInput(): number;
|
|
199
|
+
/** Queued tmux operations not yet started (tests: must stay small under any input pattern). */
|
|
200
|
+
get pendingJobs(): number;
|
|
201
|
+
/**
|
|
202
|
+
* Queue browser input for the pane. Strictly ordered with resize.
|
|
203
|
+
* Consecutive input frames coalesce into ONE paste (a resize is a barrier),
|
|
204
|
+
* so the number of tmux operations is bounded by the number of barriers,
|
|
205
|
+
* not by the number of keystrokes. Returns false when the session is not
|
|
206
|
+
* running. A backlog beyond MAX_PENDING_INPUT_BYTES / MAX_PENDING_JOBS
|
|
207
|
+
* means tmux is wedged: the session ENDS (fail closed) rather than letting
|
|
208
|
+
* queued keystrokes reach a credential prompt unattended.
|
|
209
|
+
*/
|
|
210
|
+
input(bytes: Buffer): boolean;
|
|
211
|
+
resize(cols: number, rows: number): void;
|
|
212
|
+
/** Everything queued before the returned promise settles has reached tmux (tests). */
|
|
213
|
+
drain(): Promise<void>;
|
|
214
|
+
private enqueue;
|
|
215
|
+
cancel(detail?: string): Promise<void>;
|
|
216
|
+
private onOutput;
|
|
217
|
+
private schedulePoll;
|
|
218
|
+
/** Exposed for tests; the timer calls this every second. */
|
|
219
|
+
poll(): Promise<void>;
|
|
220
|
+
private observe;
|
|
221
|
+
private finishFromExit;
|
|
222
|
+
private finish;
|
|
223
|
+
private audit;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Drives a dedicated tmux server (`-L <socket>`, `-f /dev/null` so the user's
|
|
227
|
+
* tmux.conf cannot alter behaviour). Output: pipe-pane → FIFO held O_RDWR.
|
|
228
|
+
*/
|
|
229
|
+
export declare class TmuxTerminalBackend implements TerminalBackend {
|
|
230
|
+
private readonly tmuxBin;
|
|
231
|
+
private readonly streams;
|
|
232
|
+
/**
|
|
233
|
+
* Identity of each dedicated server, captured right after new-session: the
|
|
234
|
+
* PID plus an immutable process-generation fingerprint (Linux /proc start
|
|
235
|
+
* time). There is no weaker fallback: on platforms without /proc no
|
|
236
|
+
* identity is recorded and no signal is ever sent. A bare PID may be reused
|
|
237
|
+
* by the OS once the server dies outside our control; a signal must NEVER
|
|
238
|
+
* be sent unless the fingerprint still matches — otherwise the "one
|
|
239
|
+
* command" scope would be violated against an unrelated process.
|
|
240
|
+
*/
|
|
241
|
+
private readonly servers;
|
|
242
|
+
private readonly probe;
|
|
243
|
+
constructor(tmuxBin?: string, opts?: {
|
|
244
|
+
probeProcess?: (pid: number) => ProcessProbe;
|
|
245
|
+
});
|
|
246
|
+
/** Test seam: the recorded server identity, if a signal fallback was registered. */
|
|
247
|
+
serverRecordForTests(socket: string): {
|
|
248
|
+
pid: number;
|
|
249
|
+
identity: string;
|
|
250
|
+
} | undefined;
|
|
251
|
+
/**
|
|
252
|
+
* Run one tmux command. Errors are re-thrown SANITIZED: operation name,
|
|
253
|
+
* socket and exit code only — never the argv, which for input would be the
|
|
254
|
+
* user's keystrokes (B2), and never tmux's stderr, which echoes the command.
|
|
255
|
+
*/
|
|
256
|
+
private tmux;
|
|
257
|
+
start(opts: {
|
|
258
|
+
socket: string;
|
|
259
|
+
command: string;
|
|
260
|
+
cwd: string;
|
|
261
|
+
cols: number;
|
|
262
|
+
rows: number;
|
|
263
|
+
onOutput: (chunk: Buffer) => void;
|
|
264
|
+
signal?: AbortSignal;
|
|
265
|
+
}): Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Deliver bytes to the pane WITHOUT putting typed text in any argv (B2).
|
|
268
|
+
*
|
|
269
|
+
* Two transports, chosen per run of bytes (see segmentInput):
|
|
270
|
+
* - text runs (anything a user could be typing as a secret) go to tmux
|
|
271
|
+
* over stdin into a named buffer and are pasted with `paste-buffer -r`
|
|
272
|
+
* (raw: bytes unchanged), `-d` deleting the buffer;
|
|
273
|
+
* - control runs (0x00–0x1f, 0x7f and complete ESC sequences: arrows,
|
|
274
|
+
* Enter, Tab, Ctrl-C…) go through `send-keys -H`. They carry no
|
|
275
|
+
* secret, and this is the only path on which the pane's tty performs
|
|
276
|
+
* signal handling: a pasted 0x03 is echoed but does NOT raise SIGINT
|
|
277
|
+
* (verified live), a sent one does.
|
|
278
|
+
*/
|
|
279
|
+
sendInput(socket: string, bytes: Buffer): Promise<void>;
|
|
280
|
+
resize(socket: string, cols: number, rows: number): Promise<void>;
|
|
281
|
+
capture(socket: string): Promise<string>;
|
|
282
|
+
paneStatus(socket: string): Promise<{
|
|
283
|
+
alive: boolean;
|
|
284
|
+
exitCode?: number;
|
|
285
|
+
} | null>;
|
|
286
|
+
/**
|
|
287
|
+
* Tri-state liveness of the dedicated server. "dead" is asserted only on
|
|
288
|
+
* POSITIVE evidence of absence (tmux's own no-server answer, and — when a
|
|
289
|
+
* PID is known — the process gone); a probe that could not execute
|
|
290
|
+
* (spawn failure, timeout, unexpected exit) is "unknown", never "dead".
|
|
291
|
+
* stderr is inspected in memory only and never logged.
|
|
292
|
+
*/
|
|
293
|
+
serverState(socket: string): Promise<"alive" | "dead" | "unknown">;
|
|
294
|
+
/**
|
|
295
|
+
* Kill the dedicated server and CONFIRM it is gone (B2). `kill-server` is
|
|
296
|
+
* tried twice; if the server is not positively dead, the PID captured at
|
|
297
|
+
* start is sent SIGTERM then SIGKILL. The FINAL probe decides: resolves
|
|
298
|
+
* only on "dead"; "alive" and "unknown" both reject so the caller reports
|
|
299
|
+
* a cleanup failure instead of claiming the boundary held. "Already gone"
|
|
300
|
+
* (positively) is success.
|
|
301
|
+
*/
|
|
302
|
+
kill(socket: string): Promise<void>;
|
|
303
|
+
/** Test seam: adopt a server identity as if captured at start. */
|
|
304
|
+
rememberServerForTests(socket: string, pid: number, identity: string): void;
|
|
305
|
+
}
|
|
306
|
+
export type ProcessProbe = {
|
|
307
|
+
kind: "identified";
|
|
308
|
+
identity: string;
|
|
309
|
+
comm?: string;
|
|
310
|
+
} | {
|
|
311
|
+
kind: "gone";
|
|
312
|
+
} | {
|
|
313
|
+
kind: "unknown";
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* Tri-state process probe. "gone" requires positive ESRCH evidence. The
|
|
317
|
+
* identity is the process start time in clock ticks since boot (Linux
|
|
318
|
+
* /proc/<pid>/stat field 22) and NOTHING else: it is fixed for the life of
|
|
319
|
+
* the process, so a mismatch is proof of PID reuse. The command name is
|
|
320
|
+
* returned separately for diagnostics only — a live process can rename
|
|
321
|
+
* itself (prctl / /proc/self/comm), so it must never take part in equality.
|
|
322
|
+
* On platforms without /proc there is NO fingerprint: the probe can only say
|
|
323
|
+
* gone/unknown, and the backend registers no signal fallback.
|
|
324
|
+
*/
|
|
325
|
+
export declare function probeProcess(pid: number): ProcessProbe;
|
|
326
|
+
/**
|
|
327
|
+
* Split browser input into runs: "control" (C0 bytes, DEL, and complete ESC
|
|
328
|
+
* sequences — never secrets) vs "text" (everything else — possibly a
|
|
329
|
+
* password). Control runs may travel in argv; text runs must not.
|
|
330
|
+
*/
|
|
331
|
+
export declare function segmentInput(bytes: Buffer): Array<{
|
|
332
|
+
kind: "control" | "text";
|
|
333
|
+
bytes: Buffer;
|
|
334
|
+
}>;
|
|
335
|
+
/** Single-quote for `sh`: the only quoting that survives any content. */
|
|
336
|
+
export declare function shellQuote(s: string): string;
|