@byok-sdk/client 0.4.0 → 0.4.2

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.
@@ -25,7 +25,7 @@ export interface ResolvedBin {
25
25
  * only ever constructs `new PiAdapter()` with no options (see `createDaemon`),
26
26
  * so an out-of-process substitution (e.g. examples/basic's e2e run swapping
27
27
  * in the fake-pi fixture, or a single-file product injecting its required
28
- * Node 22.19+ pi sidecar) has no other seam to use.
28
+ * Node 22.22+ pi sidecar) has no other seam to use.
29
29
  *
30
30
  * Deliberately does NOT use `createRequire(...).resolve()`: this package is
31
31
  * pure ESM with no `require` export condition (`exports["."]` only offers
@@ -66,7 +66,13 @@ export declare class PiRpcClient {
66
66
  * a post-mortem on a failed/hung task has it without separate log scraping.
67
67
  */
68
68
  recordUnmappedFrame(type: string): void;
69
- /** Immediate process-tree termination request. `dispose()` is the settlement receipt. */
69
+ /**
70
+ * Immediate process-tree termination request. `dispose()` is the settlement
71
+ * receipt, so this stays fire-and-forget: an interrupt must not block on a
72
+ * terminator. A request that could not be spawned is left unrecorded, so
73
+ * `dispose()` re-issues it and raises the typed `stage:'signal'` failure —
74
+ * swallowing it here loses nothing.
75
+ */
70
76
  kill(): void;
71
77
  waitClosed(): Promise<void>;
72
78
  dispose(): Promise<void>;
@@ -1,4 +1,5 @@
1
- import { type ChildProcess, type SpawnOptions } from 'node:child_process';
1
+ import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';
2
+ type KillFn = (pid: number, signal: NodeJS.Signals | number) => void;
2
3
  export interface OwnedProcessTreeOptions {
3
4
  child: ChildProcess;
4
5
  waitClosed: () => Promise<void>;
@@ -6,6 +7,12 @@ export interface OwnedProcessTreeOptions {
6
7
  label: string;
7
8
  termGraceMs?: number;
8
9
  killGraceMs?: number;
10
+ /** DI seam — defaults to `process.platform`. Lets the win32 branch be exercised from POSIX CI, mirroring `util/secure-dir.ts`'s identical convention. */
11
+ platform?: NodeJS.Platform;
12
+ /** DI seam for the win32 `taskkill` sweep — defaults to `node:child_process`'s `spawn`. */
13
+ spawnFn?: typeof spawn;
14
+ /** DI seam for liveness probing — defaults to `process.kill`. */
15
+ killFn?: KillFn;
9
16
  }
10
17
  /**
11
18
  * Every bundled runtime root is an owned process-group leader on POSIX. Pipes
@@ -13,7 +20,41 @@ export interface OwnedProcessTreeOptions {
13
20
  * the runtime outlive the daemon. Windows uses taskkill's `/T` tree authority.
14
21
  */
15
22
  export declare function withOwnedProcessTree<T extends SpawnOptions>(options: T): T;
16
- /** Immediate termination request used by interrupt paths; close remains the receipt. */
17
- export declare function requestOwnedProcessTreeTermination(options: OwnedProcessTreeOptions): void;
18
- /** Resolve only after the adapter-owned root and descendants are quiescent. */
23
+ /**
24
+ * Immediate termination request used by interrupt paths; close remains the
25
+ * receipt. On win32 the request also RECORDS the pid set taskkill walked,
26
+ * which is what {@link disposeOwnedProcessTree} later measures — so an
27
+ * interrupt that is fired and forgotten still leaves disposal a measurable
28
+ * tree. A request that could not be spawned records nothing, which makes
29
+ * disposal re-issue it and surface `stage:'signal'` itself.
30
+ */
31
+ export declare function requestOwnedProcessTreeTermination(options: OwnedProcessTreeOptions): Promise<void>;
32
+ /**
33
+ * Resolve only after the adapter-owned root and descendants are quiescent.
34
+ *
35
+ * POSIX measures the owned process GROUP; win32 measures the pid set taskkill
36
+ * reported walking (`stage:'quiescence'` names how many of those were still
37
+ * alive at the deadline). Neither platform reads a terminator's exit status:
38
+ * on win32 `stage:'signal'` now means only that taskkill could not be spawned.
39
+ * `close` stays the final receipt on both — it is the stdio-flush guarantee,
40
+ * not the liveness proof.
41
+ *
42
+ * Grace budget: each phase carries its own full grace on both platforms. On
43
+ * win32 the quiescence poll gets `killGraceMs` and the close wait that follows
44
+ * gets a fresh `killGraceMs`; POSIX likewise gives the SIGTERM wait
45
+ * `termGraceMs`, the SIGKILL wait `killGraceMs`, and the close wait another
46
+ * `killGraceMs`. Worst-case disposal is therefore bounded by the sum, never by
47
+ * one shared deadline that could starve the close wait after a slow drain.
48
+ *
49
+ * Residual boundary, stated honestly. Three cases this mechanism cannot cover:
50
+ * a descendant whose intermediate parent died before any sweep observed it is
51
+ * unreachable, because Windows does not re-parent orphans — nothing in the
52
+ * surviving tree links back to it and `taskkill /T` cannot find it. The same
53
+ * window means a recycled pid could read as alive. And if taskkill's output is
54
+ * empty or unparseable, the walked set collapses to `{root}`: disposal then
55
+ * measures the root alone and reports quiescence on that basis, which is a
56
+ * narrower claim than the tree, not a false one. Preventing orphans left behind
57
+ * by a DAEMON crash is a separate job-object concern, explicitly out of scope.
58
+ */
19
59
  export declare function disposeOwnedProcessTree(options: OwnedProcessTreeOptions): Promise<void>;
60
+ export {};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Windows-only: recovers the process-tree PID set that `taskkill /T /F`
3
+ * reports it walked, so `process-tree.ts` can MEASURE quiescence instead of
4
+ * trusting taskkill's exit status.
5
+ *
6
+ * Two constraints shape this parser:
7
+ *
8
+ * - taskkill's messages are LOCALIZED (the same run prints
9
+ * `SUCCESS: The process with PID ...` on en-US and a translated sentence on
10
+ * de-DE/zh-CN/ja-JP), so no word, prefix, or field label may be matched.
11
+ * Only the integers and their CO-OCCURRENCE on one line are stable: every
12
+ * line taskkill emits for a walked process names that process and its
13
+ * parent together. Seeding with the root pid and closing over co-occurrence
14
+ * therefore reaches exactly the walked tree, in any locale.
15
+ * - the text arrives in the console OEM codepage, not UTF-8. It is decoded as
16
+ * latin1 (byte-preserving) rather than guessed: every OEM codepage taskkill
17
+ * can use encodes ASCII digits as single bytes 0x30-0x39, and no DBCS trail
18
+ * byte (CP932/CP936/CP949/CP950 all start their trail range at 0x40) can
19
+ * land in that range. Undecodable non-ASCII bytes become mojibake, which is
20
+ * irrelevant: they can never manufacture a digit.
21
+ *
22
+ * Line ORDER is deliberately not relied on (taskkill emits children before
23
+ * parents today); the walk iterates to a fixpoint instead.
24
+ */
25
+ /**
26
+ * Returns the PID set reachable from `rootPid` by co-occurrence over `text`.
27
+ *
28
+ * `excludedPids` removes integers that must never enter the set even when
29
+ * they share a line with an accepted pid — taskkill names the ROOT's own
30
+ * parent (this daemon process) on the root's line, and accepting it would
31
+ * make quiescence unreachable by construction. `rootPid` is always seeded and
32
+ * is never subject to exclusion.
33
+ */
34
+ export declare function walkTaskkillPidSet(text: string, rootPid: number, excludedPids?: Iterable<number>): Set<number>;