@secure-exec/core 0.0.0-split-runtime-preview.7b0dded

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.
Files changed (66) hide show
  1. package/README.md +7 -0
  2. package/dist/binary.d.ts +4 -0
  3. package/dist/binary.js +25 -0
  4. package/dist/bytes.d.ts +2 -0
  5. package/dist/bytes.js +6 -0
  6. package/dist/callbacks.d.ts +41 -0
  7. package/dist/callbacks.js +94 -0
  8. package/dist/cargo.d.ts +2 -0
  9. package/dist/cargo.js +142 -0
  10. package/dist/correlation.d.ts +10 -0
  11. package/dist/correlation.js +49 -0
  12. package/dist/descriptors.d.ts +34 -0
  13. package/dist/descriptors.js +37 -0
  14. package/dist/event-buffer.d.ts +90 -0
  15. package/dist/event-buffer.js +313 -0
  16. package/dist/ext.d.ts +7 -0
  17. package/dist/ext.js +13 -0
  18. package/dist/filesystem.d.ts +41 -0
  19. package/dist/filesystem.js +70 -0
  20. package/dist/frame-payload-codec.d.ts +8 -0
  21. package/dist/frame-payload-codec.js +14 -0
  22. package/dist/frame-rpc.d.ts +38 -0
  23. package/dist/frame-rpc.js +73 -0
  24. package/dist/frame-stream.d.ts +27 -0
  25. package/dist/frame-stream.js +99 -0
  26. package/dist/framing.d.ts +7 -0
  27. package/dist/framing.js +22 -0
  28. package/dist/generated-protocol.d.ts +1038 -0
  29. package/dist/generated-protocol.js +2879 -0
  30. package/dist/index.d.ts +23 -0
  31. package/dist/index.js +23 -0
  32. package/dist/json.d.ts +2 -0
  33. package/dist/json.js +20 -0
  34. package/dist/kernel-proxy.d.ts +137 -0
  35. package/dist/kernel-proxy.js +1725 -0
  36. package/dist/native-client.d.ts +41 -0
  37. package/dist/native-client.js +124 -0
  38. package/dist/numbers.d.ts +1 -0
  39. package/dist/numbers.js +8 -0
  40. package/dist/ownership.d.ts +18 -0
  41. package/dist/ownership.js +77 -0
  42. package/dist/permissions.d.ts +29 -0
  43. package/dist/permissions.js +68 -0
  44. package/dist/process.d.ts +35 -0
  45. package/dist/process.js +125 -0
  46. package/dist/protocol-client.d.ts +46 -0
  47. package/dist/protocol-client.js +180 -0
  48. package/dist/protocol-frames.d.ts +68 -0
  49. package/dist/protocol-frames.js +139 -0
  50. package/dist/protocol-maps.d.ts +28 -0
  51. package/dist/protocol-maps.js +217 -0
  52. package/dist/protocol-schema.d.ts +10 -0
  53. package/dist/protocol-schema.js +11 -0
  54. package/dist/request-payloads.d.ts +139 -0
  55. package/dist/request-payloads.js +213 -0
  56. package/dist/response-payloads.d.ts +107 -0
  57. package/dist/response-payloads.js +161 -0
  58. package/dist/sidecar-client.d.ts +250 -0
  59. package/dist/sidecar-client.js +802 -0
  60. package/dist/state.d.ts +40 -0
  61. package/dist/state.js +44 -0
  62. package/dist/test-runtime.d.ts +483 -0
  63. package/dist/test-runtime.js +1985 -0
  64. package/fixtures/alpine-defaults.json +520 -0
  65. package/fixtures/base-filesystem.json +528 -0
  66. package/package.json +188 -0
@@ -0,0 +1,41 @@
1
+ import { StdioSidecarProcess } from "./process.js";
2
+ import type { LiveEventFrame, LiveResponseFrame, LiveSidecarRequestHandler, ProtocolFramePayloadCodec } from "./protocol-frames.js";
3
+ import type { LiveOwnershipScope } from "./ownership.js";
4
+ import type { LiveRequestPayload } from "./request-payloads.js";
5
+ import type { LiveSidecarEventSelector } from "./event-buffer.js";
6
+ export declare const DEFAULT_SIDECAR_FRAME_TIMEOUT_MS = 120000;
7
+ export declare const DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY = 4096;
8
+ export declare const DEFAULT_SIDECAR_GRACEFUL_EXIT_MS = 5000;
9
+ export declare const DEFAULT_SIDECAR_FORCE_EXIT_MS = 2000;
10
+ export interface StdioSidecarProtocolClientSpawnOptions {
11
+ cwd?: string;
12
+ command?: string;
13
+ args?: string[];
14
+ frameTimeoutMs?: number;
15
+ eventBufferCapacity?: number;
16
+ gracefulExitMs?: number;
17
+ forceExitMs?: number;
18
+ disposedErrorMessage?: string;
19
+ payloadCodec?: ProtocolFramePayloadCodec;
20
+ }
21
+ export declare class StdioSidecarProtocolClient {
22
+ readonly child: StdioSidecarProcess["child"];
23
+ private readonly sidecarProcess;
24
+ private readonly protocolClient;
25
+ private readonly gracefulExitMs;
26
+ private readonly forceExitMs;
27
+ private readonly disposedErrorMessage;
28
+ private constructor();
29
+ static spawn(options?: StdioSidecarProtocolClientSpawnOptions): StdioSidecarProtocolClient;
30
+ setSidecarRequestHandler(handler: LiveSidecarRequestHandler | null): void;
31
+ onEvent(handler: (event: LiveEventFrame) => void): () => void;
32
+ sendRequest(input: {
33
+ ownership: LiveOwnershipScope;
34
+ payload: LiveRequestPayload;
35
+ }): Promise<LiveResponseFrame>;
36
+ waitForEvent(matcher: LiveSidecarEventSelector | ((event: LiveEventFrame) => boolean), timeoutMs?: number, options?: {
37
+ signal?: AbortSignal;
38
+ }): Promise<LiveEventFrame>;
39
+ dispose(): Promise<void>;
40
+ failPermanently(error: Error): void;
41
+ }
@@ -0,0 +1,124 @@
1
+ import { resolvePublishedSidecarBinary } from "./binary.js";
2
+ import { SidecarProcessExited, StdioSidecarProcess, } from "./process.js";
3
+ import { SidecarProtocolClient } from "./protocol-client.js";
4
+ export const DEFAULT_SIDECAR_FRAME_TIMEOUT_MS = 120_000;
5
+ export const DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY = 4_096;
6
+ export const DEFAULT_SIDECAR_GRACEFUL_EXIT_MS = 5_000;
7
+ export const DEFAULT_SIDECAR_FORCE_EXIT_MS = 2_000;
8
+ export class StdioSidecarProtocolClient {
9
+ child;
10
+ sidecarProcess;
11
+ protocolClient;
12
+ gracefulExitMs;
13
+ forceExitMs;
14
+ disposedErrorMessage;
15
+ constructor(sidecarProcess, options) {
16
+ this.sidecarProcess = sidecarProcess;
17
+ this.child = sidecarProcess.child;
18
+ this.gracefulExitMs = options.gracefulExitMs;
19
+ this.forceExitMs = options.forceExitMs;
20
+ this.disposedErrorMessage = options.disposedErrorMessage;
21
+ this.protocolClient = new SidecarProtocolClient({
22
+ stdin: this.child.stdin,
23
+ stdout: this.child.stdout,
24
+ frameTimeoutMs: options.frameTimeoutMs,
25
+ eventBufferCapacity: options.eventBufferCapacity,
26
+ payloadCodec: options.payloadCodec,
27
+ stderrText: () => this.sidecarProcess.stderrText(),
28
+ streamEndedError: () => this.sidecarProcess.currentExitError() ??
29
+ new SidecarProcessExited({
30
+ exitCode: this.child.exitCode,
31
+ signal: this.child.signalCode,
32
+ stderr: this.sidecarProcess.stderrText(),
33
+ }),
34
+ frameError: (error) => this.sidecarProcess.currentExitError() ?? error,
35
+ });
36
+ this.sidecarProcess.onExit((error) => {
37
+ this.failPermanently(error);
38
+ });
39
+ this.sidecarProcess.onError((error) => {
40
+ this.failPermanently(error);
41
+ });
42
+ }
43
+ static spawn(options = {}) {
44
+ return new StdioSidecarProtocolClient(StdioSidecarProcess.spawn({
45
+ command: options.command ?? resolvePublishedSidecarBinary(),
46
+ args: options.args ?? [],
47
+ cwd: options.cwd,
48
+ }), {
49
+ frameTimeoutMs: options.frameTimeoutMs ?? DEFAULT_SIDECAR_FRAME_TIMEOUT_MS,
50
+ eventBufferCapacity: options.eventBufferCapacity ??
51
+ DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY,
52
+ gracefulExitMs: options.gracefulExitMs ?? DEFAULT_SIDECAR_GRACEFUL_EXIT_MS,
53
+ forceExitMs: options.forceExitMs ?? DEFAULT_SIDECAR_FORCE_EXIT_MS,
54
+ disposedErrorMessage: options.disposedErrorMessage ?? "sidecar client disposed",
55
+ payloadCodec: options.payloadCodec ?? "bare",
56
+ });
57
+ }
58
+ setSidecarRequestHandler(handler) {
59
+ this.protocolClient.setSidecarRequestHandler(handler);
60
+ }
61
+ onEvent(handler) {
62
+ return this.protocolClient.onEvent(handler);
63
+ }
64
+ async sendRequest(input) {
65
+ return await this.protocolClient.sendRequest(input);
66
+ }
67
+ async waitForEvent(matcher, timeoutMs, options) {
68
+ return await this.protocolClient.waitForEvent(matcher, timeoutMs, options);
69
+ }
70
+ async dispose() {
71
+ this.protocolClient.failPermanently(new Error(this.disposedErrorMessage));
72
+ if (!this.child.stdin.destroyed) {
73
+ try {
74
+ this.child.stdin.end();
75
+ }
76
+ catch {
77
+ // Stdin may already be closing. The child exit watcher will catch up.
78
+ }
79
+ }
80
+ const exitCode = await this.sidecarProcess.waitForExit(this.gracefulExitMs);
81
+ if (exitCode === null) {
82
+ try {
83
+ this.child.kill("SIGKILL");
84
+ }
85
+ catch {
86
+ // The child may have exited between the timeout and the kill attempt.
87
+ }
88
+ await this.sidecarProcess.waitForExit(this.forceExitMs);
89
+ }
90
+ this.protocolClient.dispose();
91
+ try {
92
+ this.child.stdin.destroy();
93
+ }
94
+ catch {
95
+ // Best effort. The child is gone so the descriptor will close on its own.
96
+ }
97
+ try {
98
+ this.child.stdout.destroy();
99
+ }
100
+ catch {
101
+ // Best effort. The child is gone so the descriptor will close on its own.
102
+ }
103
+ try {
104
+ this.child.stderr.destroy();
105
+ }
106
+ catch {
107
+ // Best effort. The child is gone so the descriptor will close on its own.
108
+ }
109
+ if (exitCode !== null &&
110
+ exitCode !== 0 &&
111
+ this.child.signalCode === null) {
112
+ throw new Error(`sidecar exited with code ${exitCode}\nstderr:\n${this.sidecarProcess.stderrText()}`);
113
+ }
114
+ }
115
+ failPermanently(error) {
116
+ this.protocolClient.failPermanently(error, {
117
+ replaceExisting: (current, next) => current instanceof SidecarProcessExited &&
118
+ current.exitCode === null &&
119
+ current.signal === null &&
120
+ next instanceof SidecarProcessExited &&
121
+ (next.exitCode !== null || next.signal !== null),
122
+ });
123
+ }
124
+ }
@@ -0,0 +1 @@
1
+ export declare function bigIntToSafeNumber(value: bigint, context: string): number;
@@ -0,0 +1,8 @@
1
+ export function bigIntToSafeNumber(value, context) {
2
+ const max = BigInt(Number.MAX_SAFE_INTEGER);
3
+ const min = BigInt(Number.MIN_SAFE_INTEGER);
4
+ if (value > max || value < min) {
5
+ throw new Error(`${context} exceeds JavaScript safe integer range`);
6
+ }
7
+ return Number(value);
8
+ }
@@ -0,0 +1,18 @@
1
+ import * as protocol from "./generated-protocol.js";
2
+ export type LiveOwnershipScope = {
3
+ scope: "connection";
4
+ connection_id: string;
5
+ } | {
6
+ scope: "session";
7
+ connection_id: string;
8
+ session_id: string;
9
+ } | {
10
+ scope: "vm";
11
+ connection_id: string;
12
+ session_id: string;
13
+ vm_id: string;
14
+ };
15
+ export declare function ownershipSelectorKey(ownership: LiveOwnershipScope): string;
16
+ export declare function ownershipMatchesSelector(selector: LiveOwnershipScope | undefined, ownership: LiveOwnershipScope): boolean;
17
+ export declare function toGeneratedOwnershipScope(ownership: LiveOwnershipScope): protocol.OwnershipScope;
18
+ export declare function fromGeneratedOwnershipScope(ownership: protocol.OwnershipScope): LiveOwnershipScope;
@@ -0,0 +1,77 @@
1
+ export function ownershipSelectorKey(ownership) {
2
+ switch (ownership.scope) {
3
+ case "connection":
4
+ return `connection:${ownership.connection_id}`;
5
+ case "session":
6
+ return `session:${ownership.connection_id}:${ownership.session_id}`;
7
+ case "vm":
8
+ return `vm:${ownership.connection_id}:${ownership.session_id}:${ownership.vm_id}`;
9
+ }
10
+ }
11
+ export function ownershipMatchesSelector(selector, ownership) {
12
+ if (!selector) {
13
+ return true;
14
+ }
15
+ switch (selector.scope) {
16
+ case "connection":
17
+ return (ownership.scope === "connection" &&
18
+ selector.connection_id === ownership.connection_id);
19
+ case "session":
20
+ return (ownership.scope === "session" &&
21
+ selector.connection_id === ownership.connection_id &&
22
+ selector.session_id === ownership.session_id);
23
+ case "vm":
24
+ return (ownership.scope === "vm" &&
25
+ selector.connection_id === ownership.connection_id &&
26
+ selector.session_id === ownership.session_id &&
27
+ selector.vm_id === ownership.vm_id);
28
+ }
29
+ }
30
+ export function toGeneratedOwnershipScope(ownership) {
31
+ switch (ownership.scope) {
32
+ case "connection":
33
+ return {
34
+ tag: "ConnectionOwnership",
35
+ val: { connectionId: ownership.connection_id },
36
+ };
37
+ case "session":
38
+ return {
39
+ tag: "SessionOwnership",
40
+ val: {
41
+ connectionId: ownership.connection_id,
42
+ sessionId: ownership.session_id,
43
+ },
44
+ };
45
+ case "vm":
46
+ return {
47
+ tag: "VmOwnership",
48
+ val: {
49
+ connectionId: ownership.connection_id,
50
+ sessionId: ownership.session_id,
51
+ vmId: ownership.vm_id,
52
+ },
53
+ };
54
+ }
55
+ }
56
+ export function fromGeneratedOwnershipScope(ownership) {
57
+ switch (ownership.tag) {
58
+ case "ConnectionOwnership":
59
+ return {
60
+ scope: "connection",
61
+ connection_id: ownership.val.connectionId,
62
+ };
63
+ case "SessionOwnership":
64
+ return {
65
+ scope: "session",
66
+ connection_id: ownership.val.connectionId,
67
+ session_id: ownership.val.sessionId,
68
+ };
69
+ case "VmOwnership":
70
+ return {
71
+ scope: "vm",
72
+ connection_id: ownership.val.connectionId,
73
+ session_id: ownership.val.sessionId,
74
+ vm_id: ownership.val.vmId,
75
+ };
76
+ }
77
+ }
@@ -0,0 +1,29 @@
1
+ import * as protocol from "./generated-protocol.js";
2
+ import { type LivePermissionMode } from "./protocol-maps.js";
3
+ export type { LivePermissionMode } from "./protocol-maps.js";
4
+ export interface LiveFsPermissionRule {
5
+ mode: LivePermissionMode;
6
+ operations?: string[];
7
+ paths?: string[];
8
+ }
9
+ export interface LivePatternPermissionRule {
10
+ mode: LivePermissionMode;
11
+ operations?: string[];
12
+ patterns?: string[];
13
+ }
14
+ export interface LiveRulePermissions<TRule> {
15
+ default?: LivePermissionMode;
16
+ rules: TRule[];
17
+ }
18
+ export type LivePermissionScope<TRule> = LivePermissionMode | LiveRulePermissions<TRule>;
19
+ export interface LivePermissionsPolicy {
20
+ fs?: LivePermissionScope<LiveFsPermissionRule>;
21
+ network?: LivePermissionScope<LivePatternPermissionRule>;
22
+ child_process?: LivePermissionScope<LivePatternPermissionRule>;
23
+ process?: LivePermissionScope<LivePatternPermissionRule>;
24
+ env?: LivePermissionScope<LivePatternPermissionRule>;
25
+ tool?: LivePermissionScope<LivePatternPermissionRule>;
26
+ }
27
+ export declare function toGeneratedPermissionsPolicy(policy: LivePermissionsPolicy | undefined): protocol.PermissionsPolicy | null;
28
+ export declare function toGeneratedFilesystemPermissionScope(scope: LivePermissionScope<LiveFsPermissionRule>): protocol.FsPermissionScope;
29
+ export declare function toGeneratedPatternPermissionScope(scope: LivePermissionScope<LivePatternPermissionRule>): protocol.PatternPermissionScope;
@@ -0,0 +1,68 @@
1
+ import { toGeneratedPermissionMode, } from "./protocol-maps.js";
2
+ export function toGeneratedPermissionsPolicy(policy) {
3
+ if (policy === undefined) {
4
+ return null;
5
+ }
6
+ return {
7
+ fs: policy.fs === undefined
8
+ ? null
9
+ : toGeneratedFilesystemPermissionScope(policy.fs),
10
+ network: policy.network === undefined
11
+ ? null
12
+ : toGeneratedPatternPermissionScope(policy.network),
13
+ childProcess: policy.child_process === undefined
14
+ ? null
15
+ : toGeneratedPatternPermissionScope(policy.child_process),
16
+ process: policy.process === undefined
17
+ ? null
18
+ : toGeneratedPatternPermissionScope(policy.process),
19
+ env: policy.env === undefined
20
+ ? null
21
+ : toGeneratedPatternPermissionScope(policy.env),
22
+ tool: policy.tool === undefined
23
+ ? null
24
+ : toGeneratedPatternPermissionScope(policy.tool),
25
+ };
26
+ }
27
+ export function toGeneratedFilesystemPermissionScope(scope) {
28
+ if (typeof scope === "string") {
29
+ return {
30
+ tag: "PermissionMode",
31
+ val: toGeneratedPermissionMode(scope),
32
+ };
33
+ }
34
+ return {
35
+ tag: "FsPermissionRuleSet",
36
+ val: {
37
+ default: scope.default === undefined
38
+ ? null
39
+ : toGeneratedPermissionMode(scope.default),
40
+ rules: scope.rules.map((rule) => ({
41
+ mode: toGeneratedPermissionMode(rule.mode),
42
+ operations: rule.operations ?? [],
43
+ paths: rule.paths ?? [],
44
+ })),
45
+ },
46
+ };
47
+ }
48
+ export function toGeneratedPatternPermissionScope(scope) {
49
+ if (typeof scope === "string") {
50
+ return {
51
+ tag: "PermissionMode",
52
+ val: toGeneratedPermissionMode(scope),
53
+ };
54
+ }
55
+ return {
56
+ tag: "PatternPermissionRuleSet",
57
+ val: {
58
+ default: scope.default === undefined
59
+ ? null
60
+ : toGeneratedPermissionMode(scope.default),
61
+ rules: scope.rules.map((rule) => ({
62
+ mode: toGeneratedPermissionMode(rule.mode),
63
+ operations: rule.operations ?? [],
64
+ patterns: rule.patterns ?? [],
65
+ })),
66
+ },
67
+ };
68
+ }
@@ -0,0 +1,35 @@
1
+ import { type ChildProcessWithoutNullStreams } from "node:child_process";
2
+ export interface StdioSidecarProcessSpawnOptions {
3
+ command: string;
4
+ args?: string[];
5
+ cwd?: string;
6
+ }
7
+ export declare class SidecarProcessExited extends Error {
8
+ readonly exitCode: number | null;
9
+ readonly signal: NodeJS.Signals | null;
10
+ readonly stderr: string;
11
+ constructor(options: {
12
+ exitCode: number | null;
13
+ signal: NodeJS.Signals | null;
14
+ stderr: string;
15
+ });
16
+ }
17
+ export declare class SidecarProcessError extends Error {
18
+ readonly childError: Error;
19
+ readonly stderr: string;
20
+ constructor(error: Error, stderr: string);
21
+ }
22
+ export declare class StdioSidecarProcess {
23
+ readonly child: ChildProcessWithoutNullStreams;
24
+ private readonly stderrChunks;
25
+ private readonly exitListeners;
26
+ private readonly errorListeners;
27
+ private constructor();
28
+ static spawn(options: StdioSidecarProcessSpawnOptions): StdioSidecarProcess;
29
+ static fromChild(child: ChildProcessWithoutNullStreams): StdioSidecarProcess;
30
+ onExit(handler: (error: SidecarProcessExited) => void): () => void;
31
+ onError(handler: (error: SidecarProcessError) => void): () => void;
32
+ stderrText(): string;
33
+ currentExitError(): SidecarProcessExited | null;
34
+ waitForExit(timeoutMs: number): Promise<number | null>;
35
+ }
@@ -0,0 +1,125 @@
1
+ import { spawn, } from "node:child_process";
2
+ function formatSidecarStderrSuffix(stderr) {
3
+ return stderr ? `\nstderr:\n${stderr}` : "";
4
+ }
5
+ export class SidecarProcessExited extends Error {
6
+ exitCode;
7
+ signal;
8
+ stderr;
9
+ constructor(options) {
10
+ const reason = options.signal !== null
11
+ ? `signal ${options.signal}`
12
+ : options.exitCode !== null
13
+ ? `code ${options.exitCode}`
14
+ : "disconnect";
15
+ super(`sidecar process exited with ${reason}${formatSidecarStderrSuffix(options.stderr)}`);
16
+ this.name = "SidecarProcessExited";
17
+ this.exitCode = options.exitCode;
18
+ this.signal = options.signal;
19
+ this.stderr = options.stderr;
20
+ }
21
+ }
22
+ export class SidecarProcessError extends Error {
23
+ childError;
24
+ stderr;
25
+ constructor(error, stderr) {
26
+ super(`sidecar process error: ${error.message}${formatSidecarStderrSuffix(stderr)}`);
27
+ this.name = "SidecarProcessError";
28
+ this.childError = error;
29
+ this.stderr = stderr;
30
+ }
31
+ }
32
+ export class StdioSidecarProcess {
33
+ child;
34
+ stderrChunks = [];
35
+ exitListeners = new Set();
36
+ errorListeners = new Set();
37
+ constructor(child) {
38
+ this.child = child;
39
+ this.child.stderr.on("data", (chunk) => {
40
+ this.stderrChunks.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk));
41
+ });
42
+ this.child.on("exit", (code, signal) => {
43
+ const error = new SidecarProcessExited({
44
+ exitCode: code,
45
+ signal,
46
+ stderr: this.stderrText(),
47
+ });
48
+ for (const listener of this.exitListeners) {
49
+ listener(error);
50
+ }
51
+ });
52
+ this.child.on("error", (error) => {
53
+ const normalized = error instanceof Error ? error : new Error(String(error));
54
+ const sidecarError = new SidecarProcessError(normalized, this.stderrText());
55
+ for (const listener of this.errorListeners) {
56
+ listener(sidecarError);
57
+ }
58
+ });
59
+ }
60
+ static spawn(options) {
61
+ return new StdioSidecarProcess(spawn(options.command, options.args ?? [], {
62
+ cwd: options.cwd,
63
+ stdio: ["pipe", "pipe", "pipe"],
64
+ }));
65
+ }
66
+ static fromChild(child) {
67
+ return new StdioSidecarProcess(child);
68
+ }
69
+ onExit(handler) {
70
+ this.exitListeners.add(handler);
71
+ return () => {
72
+ this.exitListeners.delete(handler);
73
+ };
74
+ }
75
+ onError(handler) {
76
+ this.errorListeners.add(handler);
77
+ return () => {
78
+ this.errorListeners.delete(handler);
79
+ };
80
+ }
81
+ stderrText() {
82
+ return Buffer.concat(this.stderrChunks).toString("utf8").trim();
83
+ }
84
+ currentExitError() {
85
+ if (this.child.exitCode === null && this.child.signalCode === null) {
86
+ return null;
87
+ }
88
+ return new SidecarProcessExited({
89
+ exitCode: this.child.exitCode,
90
+ signal: this.child.signalCode,
91
+ stderr: this.stderrText(),
92
+ });
93
+ }
94
+ waitForExit(timeoutMs) {
95
+ return new Promise((resolve) => {
96
+ let timer = null;
97
+ const cleanup = () => {
98
+ this.child.off("exit", onExit);
99
+ this.child.off("close", onClose);
100
+ if (timer !== null) {
101
+ clearTimeout(timer);
102
+ timer = null;
103
+ }
104
+ };
105
+ const onExit = (code) => {
106
+ cleanup();
107
+ resolve(code);
108
+ };
109
+ const onClose = (code) => {
110
+ cleanup();
111
+ resolve(code);
112
+ };
113
+ if (this.child.exitCode !== null || this.child.signalCode !== null) {
114
+ resolve(this.child.exitCode);
115
+ return;
116
+ }
117
+ this.child.on("exit", onExit);
118
+ this.child.on("close", onClose);
119
+ timer = setTimeout(() => {
120
+ cleanup();
121
+ resolve(null);
122
+ }, timeoutMs);
123
+ });
124
+ }
125
+ }
@@ -0,0 +1,46 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+ import { type LiveSidecarEventSelector } from "./event-buffer.js";
3
+ import { type LiveEventFrame, type LiveResponseFrame, type LiveSidecarRequestHandler, type ProtocolFramePayloadCodec } from "./protocol-frames.js";
4
+ import type { LiveOwnershipScope } from "./ownership.js";
5
+ import type { LiveRequestPayload } from "./request-payloads.js";
6
+ export interface SidecarProtocolClientOptions {
7
+ stdin: Writable;
8
+ stdout: Readable;
9
+ frameTimeoutMs: number;
10
+ eventBufferCapacity: number;
11
+ payloadCodec?: ProtocolFramePayloadCodec;
12
+ stderrText?: () => string;
13
+ frameError?: (error: Error) => Error;
14
+ streamEndedError?: () => Error;
15
+ }
16
+ export declare class SidecarProtocolClient {
17
+ private readonly eventBuffer;
18
+ private readonly eventListeners;
19
+ private readonly frameTimeoutMs;
20
+ private readonly payloadCodec;
21
+ private readonly stderrText;
22
+ private readonly hostFrameFactory;
23
+ private readonly frameTransport;
24
+ private closedError;
25
+ private readonly eventWaiters;
26
+ private sidecarRequestHandler;
27
+ constructor(options: SidecarProtocolClientOptions);
28
+ setSidecarRequestHandler(handler: LiveSidecarRequestHandler | null): void;
29
+ onEvent(handler: (event: LiveEventFrame) => void): () => void;
30
+ sendRequest(input: {
31
+ ownership: LiveOwnershipScope;
32
+ payload: LiveRequestPayload;
33
+ }): Promise<LiveResponseFrame>;
34
+ waitForEvent(matcher: LiveSidecarEventSelector | ((event: LiveEventFrame) => boolean), timeoutMs?: number, options?: {
35
+ signal?: AbortSignal;
36
+ }): Promise<LiveEventFrame>;
37
+ failPermanently(error: Error, options?: {
38
+ replaceExisting?: (current: Error, next: Error) => boolean;
39
+ }): void;
40
+ dispose(): void;
41
+ private writeFrame;
42
+ private dispatchSidecarRequest;
43
+ private dispatchEvent;
44
+ private bufferEvent;
45
+ private rejectPending;
46
+ }