@sentropic/h2a-runtime 0.92.0 → 0.93.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/conv-guard.d.ts +6 -1
- package/dist/conv-guard.d.ts.map +1 -1
- package/dist/conv-guard.js +33 -8
- package/dist/conv-guard.js.map +1 -1
- package/dist/enroll.d.ts.map +1 -1
- package/dist/enroll.js +12 -2
- package/dist/enroll.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +743 -97
- package/dist/index.js.map +1 -1
- package/dist/launch-layout.d.ts +74 -0
- package/dist/launch-layout.d.ts.map +1 -0
- package/dist/launch-layout.js +324 -0
- package/dist/launch-layout.js.map +1 -0
- package/dist/llm-mesh.d.ts +13 -7
- package/dist/llm-mesh.d.ts.map +1 -1
- package/dist/llm-mesh.js +6 -4
- package/dist/llm-mesh.js.map +1 -1
- package/dist/native-host.d.ts +146 -0
- package/dist/native-host.d.ts.map +1 -0
- package/dist/native-host.js +456 -0
- package/dist/native-host.js.map +1 -0
- package/dist/native-terminal/host.d.ts +317 -1
- package/dist/native-terminal/host.d.ts.map +1 -1
- package/dist/native-terminal/host.js +696 -3
- package/dist/native-terminal/host.js.map +1 -1
- package/dist/native-terminal/op.d.ts +5 -0
- package/dist/native-terminal/op.d.ts.map +1 -0
- package/dist/native-terminal/op.js +408 -0
- package/dist/native-terminal/op.js.map +1 -0
- package/dist/native-terminal/process.d.ts +2 -0
- package/dist/native-terminal/process.d.ts.map +1 -1
- package/dist/native-terminal/process.js +16 -2
- package/dist/native-terminal/process.js.map +1 -1
- package/dist/native-terminal/server.d.ts.map +1 -1
- package/dist/native-terminal/server.js +6 -2
- package/dist/native-terminal/server.js.map +1 -1
- package/dist/native-terminal/socket-path.d.ts +8 -0
- package/dist/native-terminal/socket-path.d.ts.map +1 -1
- package/dist/native-terminal/socket-path.js +31 -1
- package/dist/native-terminal/socket-path.js.map +1 -1
- package/dist/native-terminal/supervisor.d.ts +13 -0
- package/dist/native-terminal/supervisor.d.ts.map +1 -1
- package/dist/native-terminal/supervisor.js +73 -1
- package/dist/native-terminal/supervisor.js.map +1 -1
- package/dist/pty.d.ts +23 -0
- package/dist/pty.d.ts.map +1 -1
- package/dist/pty.js +39 -2
- package/dist/pty.js.map +1 -1
- package/dist/readiness.d.ts +7 -0
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +12 -9
- package/dist/readiness.js.map +1 -1
- package/dist/registry.d.ts +317 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +622 -19
- package/dist/registry.js.map +1 -1
- package/dist/restore.d.ts +91 -32
- package/dist/restore.d.ts.map +1 -1
- package/dist/restore.js +226 -186
- package/dist/restore.js.map +1 -1
- package/dist/tmux.d.ts +2 -1
- package/dist/tmux.d.ts.map +1 -1
- package/dist/tmux.js +1 -1
- package/dist/tmux.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,6 +1,125 @@
|
|
|
1
1
|
import type { PtySpawner } from "../pty.js";
|
|
2
|
+
import { type NativeTerminalPgidOwner } from "../registry.js";
|
|
2
3
|
import { type TerminalOutputChunk, type TerminalReplayGap } from "./replay-buffer.js";
|
|
3
4
|
import { type NativeTerminalStopSignal } from "./protocol.js";
|
|
5
|
+
/**
|
|
6
|
+
* Emits the FORCE signal to a whole process group from the PARENT (the host
|
|
7
|
+
* process) and can prove whether the group is actually dead — the two halves
|
|
8
|
+
* of the fix: FORCE_KILL_MUST_NOT_DEPEND_ON_THE_TARGET_EXECUTING_CODE and
|
|
9
|
+
* FORCE_STOP_ALL_MUST_PROVE_THE_PTY_TREE_IS_DEAD_NOT_JUST_SIGNAL_IT. Injectable
|
|
10
|
+
* so tests never send real signals at fabricated pgids (see host.test.ts).
|
|
11
|
+
*/
|
|
12
|
+
export type NativeTerminalProcessGroupReaper = {
|
|
13
|
+
/** Ask the OS to signal the whole group. Must not throw on ESRCH (already gone). */
|
|
14
|
+
killGroup(pgid: number, signal: NativeTerminalStopSignal): void;
|
|
15
|
+
/** True iff the OS still reports at least one process in this group. */
|
|
16
|
+
isGroupAlive(pgid: number): boolean;
|
|
17
|
+
/** Best-effort human-readable list of survivors, for a timeout diagnostic. */
|
|
18
|
+
describeGroup(pgid: number): string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Real POSIX implementation: `process.kill(-pgid, signal)` signals every
|
|
22
|
+
* process in the group in one syscall, regardless of whether any of them is
|
|
23
|
+
* stuck, in D-state, or has overwritten its own trap handlers — it asks
|
|
24
|
+
* nothing of the target. `kill(-pgid, 0)` is the standard "is anything still
|
|
25
|
+
* in this group" probe (ESRCH = empty). Not meaningful on win32 (no POSIX
|
|
26
|
+
* process groups); callers on that platform fall back to per-process kill.
|
|
27
|
+
*/
|
|
28
|
+
export declare const posixProcessGroupReaper: NativeTerminalProcessGroupReaper;
|
|
29
|
+
/**
|
|
30
|
+
* Read a process's start-time — Linux `/proc/<pid>/stat` field 22
|
|
31
|
+
* ("starttime", clock ticks since boot) — the pid-recycling-proof
|
|
32
|
+
* discriminant used for owning-host attribution
|
|
33
|
+
* (`RegistryEntry.ownerHostPid`/`ownerHostStartTime`). An immutable property
|
|
34
|
+
* of a process for its entire lifetime: two DIFFERENT processes that ever
|
|
35
|
+
* held the same pid cannot both report the same start-time (barring a read
|
|
36
|
+
* error), which is what makes "pid matches AND start-time matches" a safe
|
|
37
|
+
* proof of "still the same process", not just "some process now owns this
|
|
38
|
+
* pid".
|
|
39
|
+
*
|
|
40
|
+
* Parsing anchors on the LAST occurrence of the two-character sequence ") "
|
|
41
|
+
* rather than a naive whitespace split: field 2 (`comm`, the executable
|
|
42
|
+
* name) is parenthesized and CAN itself contain spaces and parentheses
|
|
43
|
+
* (e.g. a process named "node (pty host)"), which would shift every
|
|
44
|
+
* subsequent field under a plain split — silently reading the wrong number
|
|
45
|
+
* "most of the time" and breaking on the one process with a weird name. That
|
|
46
|
+
* would be a confident wrong answer, worse than no discriminant.
|
|
47
|
+
*
|
|
48
|
+
* After locating the split point, man(5) proc's field 3 ("state") is
|
|
49
|
+
* `rest[0]`; field 22 ("starttime") is therefore `rest[19]`.
|
|
50
|
+
*
|
|
51
|
+
* A mandatory sanity check follows: `starttime` (in clock ticks, USER_HZ=100
|
|
52
|
+
* on Linux) divided by 100 must not exceed the system's current uptime — a
|
|
53
|
+
* process cannot have started in the future relative to boot. If that check
|
|
54
|
+
* fails, the parse is treated as shifted/malformed and this function returns
|
|
55
|
+
* undefined (never answers from a mis-parsed field) rather than guessing.
|
|
56
|
+
*
|
|
57
|
+
* Returns undefined — never throws — on any read/parse failure or on a
|
|
58
|
+
* non-Linux platform (no /proc); callers must treat "unavailable" the same
|
|
59
|
+
* conservative way `posixProcessGroupReaper.isGroupAlive` treats EPERM: as
|
|
60
|
+
* "cannot prove", never as "prove me dead".
|
|
61
|
+
*/
|
|
62
|
+
export declare function readProcessStartTime(pid: number): number | undefined;
|
|
63
|
+
/** Env var name the spawned tree's session token travels under (see `create()`). */
|
|
64
|
+
export declare const H2A_SESSION_TOKEN_ENV_VAR = "H2A_SESSION_TOKEN";
|
|
65
|
+
/**
|
|
66
|
+
* The group-MEMBERSHIP anchor of last resort: true iff AT LEAST ONE process
|
|
67
|
+
* currently in group `pgid` carries `expectedToken` in its own environment
|
|
68
|
+
* (see `RegistryEntry.pgidGroupToken`'s doc comment for the full rationale).
|
|
69
|
+
* Used ONLY when the group leader itself is unreadable — the rare path —
|
|
70
|
+
* never as a substitute for the fast leader-start-time check above it.
|
|
71
|
+
*
|
|
72
|
+
* DECLARED LIMITS (do not let a reviewer discover these):
|
|
73
|
+
* - `/proc/<pid>/environ` is only readable for processes owned by the SAME
|
|
74
|
+
* uid as this host process — true here (the host and every PTY tree it
|
|
75
|
+
* spawns share a uid).
|
|
76
|
+
* - A process can freely rewrite its own environ after exec; this is
|
|
77
|
+
* therefore a NON-adversarial membership proof. That is sufficient here:
|
|
78
|
+
* the threat this guards against is the CHANCE of the OS recycling a
|
|
79
|
+
* pgid to an unrelated process, not an adversary actively forging a
|
|
80
|
+
* token — an actual attacker with same-uid code execution already has
|
|
81
|
+
* far more direct ways to interfere than faking this one env var.
|
|
82
|
+
* - This enumeration runs ONLY on the leader-absent path (never on every
|
|
83
|
+
* reap), keeping it rare in practice — the fast leader-start-time check
|
|
84
|
+
* above it covers the common case.
|
|
85
|
+
*/
|
|
86
|
+
export declare function groupCarriesSessionToken(pgid: number, expectedToken: string): boolean;
|
|
87
|
+
/** Liveness verdict for a native-terminal PTY session's OWNING HOST. */
|
|
88
|
+
export type NativeTerminalOwnerHostStatus = "alive" | "dead" | "unresolvable";
|
|
89
|
+
/**
|
|
90
|
+
* Injectable probe: given the (pid, start-time) recorded at PTY-creation
|
|
91
|
+
* time, decide whether that owning host is PROVEN dead. Tests inject fakes;
|
|
92
|
+
* `defaultOwnerHostProbe` below is the real /proc-backed implementation used
|
|
93
|
+
* in production.
|
|
94
|
+
*/
|
|
95
|
+
export type NativeTerminalOwnerHostProbe = (owner: NativeTerminalPgidOwner) => NativeTerminalOwnerHostStatus;
|
|
96
|
+
/**
|
|
97
|
+
* The real owner-host liveness probe. Two necessary conditions before
|
|
98
|
+
* declaring a host PROVEN DEAD (mirrors reapOrphan's own "prove death, don't
|
|
99
|
+
* just signal" conservatism one level up — see `reconcileDeadHostOrphans`):
|
|
100
|
+
*
|
|
101
|
+
* - the owner pid no longer exists at all -> DEAD (no ambiguity possible),
|
|
102
|
+
* - OR the pid exists but its CURRENT start-time differs from the one
|
|
103
|
+
* recorded at persistence time -> DEAD (the pid was recycled: a different
|
|
104
|
+
* process now holds it).
|
|
105
|
+
*
|
|
106
|
+
* Every other outcome is conservative-ALIVE: a pid that exists with a
|
|
107
|
+
* matching start-time is alive (possibly just unreachable — NOT proof of
|
|
108
|
+
* death); a pid that exists but whose start-time cannot be read/parsed right
|
|
109
|
+
* now is unresolvable (never guess); a pid that exists with NO recorded
|
|
110
|
+
* start-time (legacy row, or a write-time read failure) cannot be checked
|
|
111
|
+
* for recycling at all, so it stays alive rather than manufacture a "dead"
|
|
112
|
+
* verdict from missing data.
|
|
113
|
+
*
|
|
114
|
+
* This asymmetry is deliberate: the DANGEROUS error (reaping a live host)
|
|
115
|
+
* would require a live process's immutable start-time to change — which
|
|
116
|
+
* cannot happen barring a read error, itself already routed to
|
|
117
|
+
* "unresolvable". The BENIGN error (a recycled pid that coincidentally
|
|
118
|
+
* shares a start-time, or a legacy row with no recorded start-time)
|
|
119
|
+
* concludes "alive" and merely leaves an orphan uncollected — the pre-fix
|
|
120
|
+
* status quo, not a regression.
|
|
121
|
+
*/
|
|
122
|
+
export declare const defaultOwnerHostProbe: NativeTerminalOwnerHostProbe;
|
|
4
123
|
export type NativeTerminalExit = Readonly<{
|
|
5
124
|
exitCode: number;
|
|
6
125
|
signal?: number;
|
|
@@ -15,6 +134,13 @@ export type NativeTerminalSessionState = Readonly<{
|
|
|
15
134
|
latestSeq: number;
|
|
16
135
|
exit: NativeTerminalExit | null;
|
|
17
136
|
stopSignal: string | null;
|
|
137
|
+
/**
|
|
138
|
+
* Whether a controller currently holds this session's exclusive input lease.
|
|
139
|
+
* Deliberately a BOOLEAN: consumers (restore visibility) only need to know
|
|
140
|
+
* that a controlling terminal exists, never who it is — the controller id
|
|
141
|
+
* stays private to the host.
|
|
142
|
+
*/
|
|
143
|
+
controlled: boolean;
|
|
18
144
|
}>;
|
|
19
145
|
export type NativeTerminalReplay = Readonly<{
|
|
20
146
|
generation: string;
|
|
@@ -53,6 +179,52 @@ export type NativeTerminalCreateOptions = Readonly<{
|
|
|
53
179
|
cols: number;
|
|
54
180
|
rows: number;
|
|
55
181
|
}>;
|
|
182
|
+
/**
|
|
183
|
+
* Outcome of asking a host to reap a session BY ID from its persisted pgid —
|
|
184
|
+
* the case where the host asking has no in-memory record of the session at
|
|
185
|
+
* all (a fresh host after the owning host was killed). `"refused"` means
|
|
186
|
+
* either the pgid could not be resolved at all, OR it resolved but the
|
|
187
|
+
* group-leader identity guard (see `#verifyGroupLeaderIdentity`) would not
|
|
188
|
+
* positively re-prove the group at kill-time: NOTHING was signalled (never
|
|
189
|
+
* guess a pgid — killing the wrong process group is irreversible), and a
|
|
190
|
+
* loud diagnostic was logged, because a silent refusal here recreates the
|
|
191
|
+
* invisible-orphan bug. `cause` is present only for the leader-identity
|
|
192
|
+
* refusal and DISCERNS its two distinct roots — `"recycled"` (the leader is
|
|
193
|
+
* readable but its start-time no longer matches: the pgid was reused by an
|
|
194
|
+
* unrelated process, a correct, defensive refusal) from
|
|
195
|
+
* `"membership-unprovable"` (the leader is gone AND no surviving group
|
|
196
|
+
* member carries the persisted session token: possibly our own already-dead
|
|
197
|
+
* orphan, a conservative refuse-and-leak) — collapsing them into one verdict
|
|
198
|
+
* would hide whether this guard is protecting us or making us leak.
|
|
199
|
+
*
|
|
200
|
+
* `verified`, present on `"reaped"` only when the identity guard actually
|
|
201
|
+
* ran, DISCERNS a PROVEN kill (`true` — either the current and persisted
|
|
202
|
+
* leader start-times matched, OR the leader was gone but a surviving member
|
|
203
|
+
* carried the persisted session token) from an UNPROVEN one (`false` — a
|
|
204
|
+
* legacy row with no persisted baseline at all to check against, neither a
|
|
205
|
+
* leader start-time nor a group token: `#verifyGroupLeaderIdentity` fails
|
|
206
|
+
* OPEN there by design — see its doc comment — but that must never look
|
|
207
|
+
* identical to a proven match in logs or outcomes). Absent entirely when no
|
|
208
|
+
* identity check ran at all (e.g. `forceStopAll`, or the group was already
|
|
209
|
+
* confirmed empty before any check).
|
|
210
|
+
*/
|
|
211
|
+
export type NativeTerminalReapOutcome = Readonly<{
|
|
212
|
+
sessionId: string;
|
|
213
|
+
status: "refused";
|
|
214
|
+
reason: string;
|
|
215
|
+
cause?: "recycled" | "membership-unprovable";
|
|
216
|
+
} | {
|
|
217
|
+
sessionId: string;
|
|
218
|
+
status: "reaped";
|
|
219
|
+
pgid: number;
|
|
220
|
+
elapsedMs: number;
|
|
221
|
+
verified?: boolean;
|
|
222
|
+
} | {
|
|
223
|
+
sessionId: string;
|
|
224
|
+
status: "reap-timed-out";
|
|
225
|
+
pgid: number;
|
|
226
|
+
elapsedMs: number;
|
|
227
|
+
}>;
|
|
56
228
|
export declare class NativeTerminalHost {
|
|
57
229
|
#private;
|
|
58
230
|
constructor(options: {
|
|
@@ -60,8 +232,48 @@ export declare class NativeTerminalHost {
|
|
|
60
232
|
replayBytesPerSession: number;
|
|
61
233
|
maxSessions?: number;
|
|
62
234
|
spawner: PtySpawner;
|
|
235
|
+
/** Durable store for pgid persistence; defaults to the real registry path. */
|
|
236
|
+
registryPath?: string;
|
|
237
|
+
/** Injectable so tests never send real signals at fabricated pgids. */
|
|
238
|
+
reaper?: NativeTerminalProcessGroupReaper;
|
|
239
|
+
/** Diagnostic sink for the force-kill chain; defaults to prefixed stderr. */
|
|
240
|
+
log?: (line: string) => void;
|
|
241
|
+
forceKillTimeoutMs?: number;
|
|
242
|
+
forceKillPollIntervalMs?: number;
|
|
243
|
+
/** Injectable so tests never touch a real /proc for fabricated pgids;
|
|
244
|
+
* defaults to the real `readProcessStartTime`. Used BOTH to capture the
|
|
245
|
+
* group-leader's start-time at spawn AND to re-read it at kill-time (see
|
|
246
|
+
* `#verifyGroupLeaderIdentity`) — the same probe, applied twice. */
|
|
247
|
+
readLeaderStartTime?: (pid: number) => number | undefined;
|
|
248
|
+
/** Injectable so tests never touch a real /proc for fabricated pgids;
|
|
249
|
+
* defaults to the real `groupCarriesSessionToken`. Consulted ONLY when
|
|
250
|
+
* the leader is absent — see `#verifyGroupLeaderIdentity`. */
|
|
251
|
+
findGroupMemberToken?: (pgid: number, expectedToken: string) => boolean;
|
|
63
252
|
});
|
|
64
253
|
get generation(): string;
|
|
254
|
+
/**
|
|
255
|
+
* COUNTABLE per arch condition 1/2: the four DISCERNIBLE outcomes of the
|
|
256
|
+
* group-leader-identity guard, so "has this guard ever refused (and why),
|
|
257
|
+
* or ever proceeded WITHOUT the fast leader-start-time proof" is
|
|
258
|
+
* answerable later without re-deriving it from raw logs.
|
|
259
|
+
* `recycled`/`membershipUnprovable` are refusals (the kill was NOT
|
|
260
|
+
* emitted). `tokenVerified` is a PROVEN proceed via the group-membership
|
|
261
|
+
* token (the leader was absent, but a surviving member carried it) —
|
|
262
|
+
* distinct from the ordinary start-time-matched proceed, which is not
|
|
263
|
+
* counted at all (it is the expected common case). `unverifiedLegacy` is
|
|
264
|
+
* an UNPROVEN proceed (the kill WAS emitted) on a row with no baseline —
|
|
265
|
+
* neither a leader-start-time nor a group-token — to check against at
|
|
266
|
+
* all, the fail-open branch of `#verifyGroupLeaderIdentity`. Never
|
|
267
|
+
* collapsed: a proven kill and an unproven one must never look alike
|
|
268
|
+
* here, exactly as a refusal and a proceed must never look alike — see
|
|
269
|
+
* `NativeTerminalReapOutcome`'s `cause`/`verified`.
|
|
270
|
+
*/
|
|
271
|
+
get pgidGuardCounters(): Readonly<{
|
|
272
|
+
recycled: number;
|
|
273
|
+
membershipUnprovable: number;
|
|
274
|
+
unverifiedLegacy: number;
|
|
275
|
+
tokenVerified: number;
|
|
276
|
+
}>;
|
|
65
277
|
create(options: NativeTerminalCreateOptions): NativeTerminalSessionState;
|
|
66
278
|
list(): ReadonlyArray<NativeTerminalSessionState>;
|
|
67
279
|
state(id: string): NativeTerminalSessionState;
|
|
@@ -73,6 +285,110 @@ export declare class NativeTerminalHost {
|
|
|
73
285
|
resize(lease: NativeTerminalControllerLease, cols: number, rows: number): void;
|
|
74
286
|
stop(lease: NativeTerminalControllerLease, signal?: NativeTerminalStopSignal): NativeTerminalSessionState;
|
|
75
287
|
stopAll(signal?: NativeTerminalStopSignal): ReadonlyArray<NativeTerminalSessionState>;
|
|
76
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Force-stop every non-exited session this host currently knows about — the
|
|
290
|
+
* FORCE path. Unlike `stop()`/`stopAll()` (which go through the pty's own
|
|
291
|
+
* kill(), i.e. the SIGUSR1->trap escalation on Linux — still fine for a
|
|
292
|
+
* graceful, cooperative stop), this emits the group SIGKILL itself, from
|
|
293
|
+
* the PARENT, and WAITS to PROVE the whole group is dead before returning —
|
|
294
|
+
* it does not return "done" on the strength of having merely signalled it.
|
|
295
|
+
* A poll timeout is reported as an error (an absence of information, not a
|
|
296
|
+
* claimed success), never silently swallowed.
|
|
297
|
+
*/
|
|
298
|
+
forceStopAll(signal?: NativeTerminalStopSignal): Promise<ReadonlyArray<NativeTerminalSessionState>>;
|
|
299
|
+
/**
|
|
300
|
+
* Reap a session BY ID from its durably persisted pgid, even though THIS
|
|
301
|
+
* host has no in-memory record of it — the case a fresh host faces after
|
|
302
|
+
* the host that created the session was killed. Never guesses a pgid: if
|
|
303
|
+
* it cannot be resolved (registry unreadable, or genuinely never recorded),
|
|
304
|
+
* this signals NOTHING and returns `"refused"` after logging a LOUD
|
|
305
|
+
* diagnostic — a silent no-op here would recreate the exact
|
|
306
|
+
* invisible-orphan bug this mechanism exists to close.
|
|
307
|
+
*/
|
|
308
|
+
reapOrphan(sessionId: string, signal?: NativeTerminalStopSignal): Promise<NativeTerminalReapOutcome>;
|
|
77
309
|
}
|
|
310
|
+
/** Per-entry outcome of a `reconcileDeadHostOrphans` pass. */
|
|
311
|
+
export type NativeTerminalReconcileOutcome = Readonly<{
|
|
312
|
+
sessionId: string;
|
|
313
|
+
status: "reaped";
|
|
314
|
+
ownerPid: number;
|
|
315
|
+
pgid: number;
|
|
316
|
+
} | {
|
|
317
|
+
sessionId: string;
|
|
318
|
+
status: "reap-timed-out";
|
|
319
|
+
ownerPid: number;
|
|
320
|
+
pgid: number;
|
|
321
|
+
} | {
|
|
322
|
+
sessionId: string;
|
|
323
|
+
status: "reap-refused";
|
|
324
|
+
ownerPid: number;
|
|
325
|
+
pgid: number;
|
|
326
|
+
reason: string;
|
|
327
|
+
} | {
|
|
328
|
+
sessionId: string;
|
|
329
|
+
status: "skipped-alive";
|
|
330
|
+
ownerPid: number;
|
|
331
|
+
} | {
|
|
332
|
+
sessionId: string;
|
|
333
|
+
status: "skipped-unresolvable";
|
|
334
|
+
ownerPid: number;
|
|
335
|
+
} | {
|
|
336
|
+
sessionId: string;
|
|
337
|
+
status: "skipped-no-owner";
|
|
338
|
+
}>;
|
|
339
|
+
/** Summary of a `reconcileDeadHostOrphans` pass. */
|
|
340
|
+
export type NativeTerminalReconcileSummary = Readonly<{
|
|
341
|
+
status: "refused";
|
|
342
|
+
reason: string;
|
|
343
|
+
} | {
|
|
344
|
+
status: "completed";
|
|
345
|
+
outcomes: ReadonlyArray<NativeTerminalReconcileOutcome>;
|
|
346
|
+
}>;
|
|
347
|
+
/**
|
|
348
|
+
* Reap orphan PTY groups left behind by a PROVEN-DEAD host — the ONE
|
|
349
|
+
* production trigger for `NativeTerminalHost#reapOrphan`. Called at
|
|
350
|
+
* supervisor takeover (see `NativeTerminalHostSupervisor`), the moment a
|
|
351
|
+
* fresh host is about to be spawned because the previous one could not be
|
|
352
|
+
* reached: exactly the moment a stale registry might exist, and exactly the
|
|
353
|
+
* moment this must NOT be mistaken for "the old host is dead".
|
|
354
|
+
*
|
|
355
|
+
* This mirrors `reapOrphan`'s own conservatism one level up: a lost
|
|
356
|
+
* connection to a host is the UNKNOWN, not proof of death. An
|
|
357
|
+
* unreachable-but-alive host (overloaded, a saturated socket, a paused
|
|
358
|
+
* process) is ALIVE and its sessions are live work — reaping there would be
|
|
359
|
+
* a mass-kill dressed up as cleanup. So this function enumerates EVERY
|
|
360
|
+
* durably-persisted native-terminal-pty row and, for EACH ONE
|
|
361
|
+
* INDEPENDENTLY, proves whether ITS OWNING HOST (not "the host we were just
|
|
362
|
+
* talking to") is dead before touching it:
|
|
363
|
+
*
|
|
364
|
+
* - registry unresolvable (`listNativeTerminalPgidEntries` reports
|
|
365
|
+
* `known:false`) -> reap NOTHING, loud refusal, `status:"refused"`.
|
|
366
|
+
* - an entry with no owner attribution recorded (a legacy row written
|
|
367
|
+
* before this fix, or a write-time start-time-read failure) -> cannot
|
|
368
|
+
* prove death -> skip.
|
|
369
|
+
* - `ownerProbe` reports the owner "alive" or "unresolvable" -> skip
|
|
370
|
+
* (unresolvable is treated exactly like alive: never guess).
|
|
371
|
+
* - `ownerProbe` reports "dead" (pid gone, or recycled — a live pid whose
|
|
372
|
+
* start-time no longer matches) -> reap that entry's orphan process group
|
|
373
|
+
* via `reap`, then prune the entry once the reap is CONFIRMED (`"reaped"`
|
|
374
|
+
* only — a `"reap-timed-out"` leaves the row for a future pass instead of
|
|
375
|
+
* losing the only durable record of an unconfirmed pgid).
|
|
376
|
+
*
|
|
377
|
+
* Best-effort per entry: one entry's failure does not stop the pass over the
|
|
378
|
+
* rest (each entry is an independent host/session; a single problem entry
|
|
379
|
+
* must never mask the others).
|
|
380
|
+
*/
|
|
381
|
+
export declare function reconcileDeadHostOrphans(options?: {
|
|
382
|
+
registryPath?: string;
|
|
383
|
+
signal?: NativeTerminalStopSignal;
|
|
384
|
+
/** Injectable so tests never touch a real /proc; defaults to the real probe. */
|
|
385
|
+
ownerProbe?: NativeTerminalOwnerHostProbe;
|
|
386
|
+
/** Injectable so tests never send real signals; defaults to a throwaway
|
|
387
|
+
* `NativeTerminalHost` (never spawns a pty) calling the real `reapOrphan`.
|
|
388
|
+
* The persisted pgid is passed through for observability/assertions even
|
|
389
|
+
* though the real reap re-resolves it itself from the SAME registry row —
|
|
390
|
+
* they always agree by construction. */
|
|
391
|
+
reap?: (sessionId: string, pgid: number, signal: NativeTerminalStopSignal) => Promise<NativeTerminalReapOutcome>;
|
|
392
|
+
log?: (line: string) => void;
|
|
393
|
+
}): Promise<NativeTerminalReconcileSummary>;
|
|
78
394
|
//# sourceMappingURL=host.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/native-terminal/host.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/native-terminal/host.ts"],"names":[],"mappings":"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"}
|