@secure-exec/core 0.0.0-nathan-docs-sdk-expansion.c9c2e4e
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/README.md +7 -0
- package/dist/binary.d.ts +4 -0
- package/dist/binary.js +25 -0
- package/dist/bytes.d.ts +2 -0
- package/dist/bytes.js +6 -0
- package/dist/callbacks.d.ts +41 -0
- package/dist/callbacks.js +94 -0
- package/dist/cargo.d.ts +2 -0
- package/dist/cargo.js +142 -0
- package/dist/correlation.d.ts +10 -0
- package/dist/correlation.js +49 -0
- package/dist/descriptors.d.ts +34 -0
- package/dist/descriptors.js +37 -0
- package/dist/event-buffer.d.ts +90 -0
- package/dist/event-buffer.js +313 -0
- package/dist/ext.d.ts +7 -0
- package/dist/ext.js +13 -0
- package/dist/filesystem.d.ts +41 -0
- package/dist/filesystem.js +70 -0
- package/dist/frame-payload-codec.d.ts +8 -0
- package/dist/frame-payload-codec.js +14 -0
- package/dist/frame-rpc.d.ts +38 -0
- package/dist/frame-rpc.js +73 -0
- package/dist/frame-stream.d.ts +27 -0
- package/dist/frame-stream.js +99 -0
- package/dist/framing.d.ts +7 -0
- package/dist/framing.js +22 -0
- package/dist/generated/AcpLimitsConfig.d.ts +4 -0
- package/dist/generated/AcpLimitsConfig.js +2 -0
- package/dist/generated/CreateVmConfig.d.ts +19 -0
- package/dist/generated/CreateVmConfig.js +1 -0
- package/dist/generated/FsPermissionRule.d.ts +6 -0
- package/dist/generated/FsPermissionRule.js +1 -0
- package/dist/generated/FsPermissionRuleSet.d.ts +6 -0
- package/dist/generated/FsPermissionRuleSet.js +1 -0
- package/dist/generated/FsPermissionScope.d.ts +3 -0
- package/dist/generated/FsPermissionScope.js +1 -0
- package/dist/generated/HttpLimitsConfig.d.ts +3 -0
- package/dist/generated/HttpLimitsConfig.js +2 -0
- package/dist/generated/JsModuleResolution.d.ts +1 -0
- package/dist/generated/JsModuleResolution.js +2 -0
- package/dist/generated/JsRuntimeConfig.d.ts +26 -0
- package/dist/generated/JsRuntimeConfig.js +1 -0
- package/dist/generated/JsRuntimeLimitsConfig.d.ts +7 -0
- package/dist/generated/JsRuntimeLimitsConfig.js +2 -0
- package/dist/generated/JsRuntimePlatform.d.ts +1 -0
- package/dist/generated/JsRuntimePlatform.js +2 -0
- package/dist/generated/MountPluginDescriptor.d.ts +4 -0
- package/dist/generated/MountPluginDescriptor.js +2 -0
- package/dist/generated/NativeRootFilesystemConfig.d.ts +5 -0
- package/dist/generated/NativeRootFilesystemConfig.js +1 -0
- package/dist/generated/PatternPermissionRule.d.ts +6 -0
- package/dist/generated/PatternPermissionRule.js +1 -0
- package/dist/generated/PatternPermissionRuleSet.d.ts +6 -0
- package/dist/generated/PatternPermissionRuleSet.js +1 -0
- package/dist/generated/PatternPermissionScope.d.ts +3 -0
- package/dist/generated/PatternPermissionScope.js +1 -0
- package/dist/generated/PermissionMode.d.ts +1 -0
- package/dist/generated/PermissionMode.js +2 -0
- package/dist/generated/PermissionsPolicy.d.ts +10 -0
- package/dist/generated/PermissionsPolicy.js +1 -0
- package/dist/generated/PluginLimitsConfig.d.ts +4 -0
- package/dist/generated/PluginLimitsConfig.js +2 -0
- package/dist/generated/PythonLimitsConfig.d.ts +5 -0
- package/dist/generated/PythonLimitsConfig.js +2 -0
- package/dist/generated/ResourceLimitsConfig.d.ts +22 -0
- package/dist/generated/ResourceLimitsConfig.js +2 -0
- package/dist/generated/RootFilesystemConfig.d.ts +9 -0
- package/dist/generated/RootFilesystemConfig.js +1 -0
- package/dist/generated/RootFilesystemEntry.d.ts +13 -0
- package/dist/generated/RootFilesystemEntry.js +1 -0
- package/dist/generated/RootFilesystemEntryEncoding.d.ts +1 -0
- package/dist/generated/RootFilesystemEntryEncoding.js +2 -0
- package/dist/generated/RootFilesystemEntryKind.d.ts +1 -0
- package/dist/generated/RootFilesystemEntryKind.js +2 -0
- package/dist/generated/RootFilesystemLowerDescriptor.d.ts +7 -0
- package/dist/generated/RootFilesystemLowerDescriptor.js +1 -0
- package/dist/generated/RootFilesystemMode.d.ts +1 -0
- package/dist/generated/RootFilesystemMode.js +2 -0
- package/dist/generated/ToolLimitsConfig.d.ts +10 -0
- package/dist/generated/ToolLimitsConfig.js +2 -0
- package/dist/generated/VmDnsConfig.d.ts +6 -0
- package/dist/generated/VmDnsConfig.js +2 -0
- package/dist/generated/VmLimitsConfig.d.ts +18 -0
- package/dist/generated/VmLimitsConfig.js +1 -0
- package/dist/generated/VmListenPolicyConfig.d.ts +5 -0
- package/dist/generated/VmListenPolicyConfig.js +2 -0
- package/dist/generated/WasmLimitsConfig.d.ts +5 -0
- package/dist/generated/WasmLimitsConfig.js +2 -0
- package/dist/generated-protocol.d.ts +1037 -0
- package/dist/generated-protocol.js +2887 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +24 -0
- package/dist/json.d.ts +2 -0
- package/dist/json.js +20 -0
- package/dist/kernel-proxy.d.ts +149 -0
- package/dist/kernel-proxy.js +1733 -0
- package/dist/native-client.d.ts +41 -0
- package/dist/native-client.js +124 -0
- package/dist/node-runtime.d.ts +443 -0
- package/dist/node-runtime.js +569 -0
- package/dist/numbers.d.ts +1 -0
- package/dist/numbers.js +8 -0
- package/dist/ownership.d.ts +18 -0
- package/dist/ownership.js +77 -0
- package/dist/permissions.d.ts +29 -0
- package/dist/permissions.js +68 -0
- package/dist/process.d.ts +35 -0
- package/dist/process.js +125 -0
- package/dist/protocol-client.d.ts +46 -0
- package/dist/protocol-client.js +180 -0
- package/dist/protocol-frames.d.ts +68 -0
- package/dist/protocol-frames.js +139 -0
- package/dist/protocol-maps.d.ts +28 -0
- package/dist/protocol-maps.js +217 -0
- package/dist/protocol-schema.d.ts +10 -0
- package/dist/protocol-schema.js +11 -0
- package/dist/request-payloads.d.ts +137 -0
- package/dist/request-payloads.js +210 -0
- package/dist/response-payloads.d.ts +107 -0
- package/dist/response-payloads.js +161 -0
- package/dist/sidecar-client.d.ts +242 -0
- package/dist/sidecar-client.js +797 -0
- package/dist/state.d.ts +40 -0
- package/dist/state.js +44 -0
- package/dist/test-runtime.d.ts +526 -0
- package/dist/test-runtime.js +2119 -0
- package/dist/vm-config.d.ts +31 -0
- package/dist/vm-config.js +1 -0
- package/fixtures/alpine-defaults.json +520 -0
- package/fixtures/base-filesystem.json +528 -0
- package/package.json +194 -0
|
@@ -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
|
+
}
|
package/dist/process.js
ADDED
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { SidecarEventBuffer, SidecarEventBufferOverflow, normalizeSidecarEventMatcher, sidecarEventWaitAbortError, } from "./event-buffer.js";
|
|
2
|
+
import { FrameRpcTransport } from "./frame-rpc.js";
|
|
3
|
+
import { HostProtocolFrameFactory, classifySidecarWrittenProtocolFrame, decodeProtocolFramePayload, encodeProtocolFramePayload, resolveSidecarRequestFramePayload, } from "./protocol-frames.js";
|
|
4
|
+
export class SidecarProtocolClient {
|
|
5
|
+
eventBuffer;
|
|
6
|
+
eventListeners = new Set();
|
|
7
|
+
frameTimeoutMs;
|
|
8
|
+
payloadCodec;
|
|
9
|
+
stderrText;
|
|
10
|
+
hostFrameFactory = new HostProtocolFrameFactory();
|
|
11
|
+
frameTransport;
|
|
12
|
+
closedError = null;
|
|
13
|
+
eventWaiters = new Set();
|
|
14
|
+
sidecarRequestHandler = null;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.frameTimeoutMs = options.frameTimeoutMs;
|
|
17
|
+
this.eventBuffer = new SidecarEventBuffer(options.eventBufferCapacity);
|
|
18
|
+
this.payloadCodec = options.payloadCodec ?? "bare";
|
|
19
|
+
this.stderrText = options.stderrText ?? (() => "");
|
|
20
|
+
this.frameTransport = new FrameRpcTransport({
|
|
21
|
+
stdin: options.stdin,
|
|
22
|
+
stdout: options.stdout,
|
|
23
|
+
encodeFrame: (frame) => encodeProtocolFramePayload(frame, this.payloadCodec),
|
|
24
|
+
decodeFrame: (payload) => decodeProtocolFramePayload(payload, this.payloadCodec),
|
|
25
|
+
classifyFrame: classifySidecarWrittenProtocolFrame,
|
|
26
|
+
});
|
|
27
|
+
this.frameTransport.onEvent((event) => {
|
|
28
|
+
this.dispatchEvent(event);
|
|
29
|
+
});
|
|
30
|
+
this.frameTransport.onSidecarRequest((request) => {
|
|
31
|
+
void this.dispatchSidecarRequest(request);
|
|
32
|
+
});
|
|
33
|
+
this.frameTransport.onError((error) => {
|
|
34
|
+
this.failPermanently(options.frameError?.(error) ?? error);
|
|
35
|
+
});
|
|
36
|
+
this.frameTransport.onEnd(() => {
|
|
37
|
+
this.failPermanently(options.streamEndedError?.() ??
|
|
38
|
+
new Error("sidecar protocol stream ended"));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
setSidecarRequestHandler(handler) {
|
|
42
|
+
this.sidecarRequestHandler = handler;
|
|
43
|
+
}
|
|
44
|
+
onEvent(handler) {
|
|
45
|
+
this.eventListeners.add(handler);
|
|
46
|
+
return () => {
|
|
47
|
+
this.eventListeners.delete(handler);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async sendRequest(input) {
|
|
51
|
+
if (this.closedError) {
|
|
52
|
+
throw this.closedError;
|
|
53
|
+
}
|
|
54
|
+
const request = this.hostFrameFactory.createRequestFrame(input);
|
|
55
|
+
const response = await this.frameTransport.sendFrame(request.request_id, request, {
|
|
56
|
+
timeoutMs: this.frameTimeoutMs,
|
|
57
|
+
timeoutMessage: () => `timed out waiting for sidecar protocol frame for ${input.payload.type}\nstderr:\n${this.stderrText()}`,
|
|
58
|
+
});
|
|
59
|
+
if (response.payload.type === "rejected") {
|
|
60
|
+
throw new Error(`sidecar rejected request ${request.request_id}: ${response.payload.code}: ${response.payload.message}`);
|
|
61
|
+
}
|
|
62
|
+
return response;
|
|
63
|
+
}
|
|
64
|
+
async waitForEvent(matcher, timeoutMs, options) {
|
|
65
|
+
if (this.closedError instanceof SidecarEventBufferOverflow) {
|
|
66
|
+
throw this.closedError;
|
|
67
|
+
}
|
|
68
|
+
const normalizedMatcher = normalizeSidecarEventMatcher(matcher);
|
|
69
|
+
const bufferedEvent = this.eventBuffer.take(normalizedMatcher);
|
|
70
|
+
if (bufferedEvent) {
|
|
71
|
+
return bufferedEvent;
|
|
72
|
+
}
|
|
73
|
+
if (this.closedError) {
|
|
74
|
+
throw this.closedError;
|
|
75
|
+
}
|
|
76
|
+
if (options?.signal?.aborted) {
|
|
77
|
+
throw sidecarEventWaitAbortError(options.signal.reason);
|
|
78
|
+
}
|
|
79
|
+
return await new Promise((resolve, reject) => {
|
|
80
|
+
let abortListener = null;
|
|
81
|
+
const waiter = {
|
|
82
|
+
matches: normalizedMatcher.matches,
|
|
83
|
+
resolve: (event) => {
|
|
84
|
+
if (waiter.timer !== null) {
|
|
85
|
+
clearTimeout(waiter.timer);
|
|
86
|
+
}
|
|
87
|
+
if (abortListener) {
|
|
88
|
+
options?.signal?.removeEventListener("abort", abortListener);
|
|
89
|
+
abortListener = null;
|
|
90
|
+
}
|
|
91
|
+
this.eventWaiters.delete(waiter);
|
|
92
|
+
resolve(event);
|
|
93
|
+
},
|
|
94
|
+
reject: (error) => {
|
|
95
|
+
if (waiter.timer !== null) {
|
|
96
|
+
clearTimeout(waiter.timer);
|
|
97
|
+
}
|
|
98
|
+
if (abortListener) {
|
|
99
|
+
options?.signal?.removeEventListener("abort", abortListener);
|
|
100
|
+
abortListener = null;
|
|
101
|
+
}
|
|
102
|
+
this.eventWaiters.delete(waiter);
|
|
103
|
+
reject(error);
|
|
104
|
+
},
|
|
105
|
+
timer: timeoutMs === undefined
|
|
106
|
+
? null
|
|
107
|
+
: setTimeout(() => {
|
|
108
|
+
this.eventWaiters.delete(waiter);
|
|
109
|
+
reject(new Error(`timed out waiting for sidecar event\nstderr:\n${this.stderrText()}`));
|
|
110
|
+
}, timeoutMs),
|
|
111
|
+
};
|
|
112
|
+
if (options?.signal) {
|
|
113
|
+
abortListener = () => {
|
|
114
|
+
waiter.reject(sidecarEventWaitAbortError(options.signal?.reason));
|
|
115
|
+
};
|
|
116
|
+
options.signal.addEventListener("abort", abortListener, { once: true });
|
|
117
|
+
}
|
|
118
|
+
this.eventWaiters.add(waiter);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
failPermanently(error, options) {
|
|
122
|
+
if (this.closedError) {
|
|
123
|
+
if (!options?.replaceExisting?.(this.closedError, error)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
this.closedError = error;
|
|
128
|
+
this.rejectPending(error);
|
|
129
|
+
}
|
|
130
|
+
dispose() {
|
|
131
|
+
this.frameTransport.dispose();
|
|
132
|
+
}
|
|
133
|
+
async writeFrame(frame) {
|
|
134
|
+
await this.frameTransport.writeFrame(frame);
|
|
135
|
+
}
|
|
136
|
+
async dispatchSidecarRequest(request) {
|
|
137
|
+
const payload = await resolveSidecarRequestFramePayload(request, this.sidecarRequestHandler);
|
|
138
|
+
try {
|
|
139
|
+
await this.writeFrame(this.hostFrameFactory.createSidecarResponseFrame({
|
|
140
|
+
request,
|
|
141
|
+
payload,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
146
|
+
this.failPermanently(normalized);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
dispatchEvent(event) {
|
|
150
|
+
for (const listener of this.eventListeners) {
|
|
151
|
+
try {
|
|
152
|
+
listener(event);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// Event listeners are best-effort observers and must not break framing.
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
for (const waiter of this.eventWaiters) {
|
|
159
|
+
if (!waiter.matches(event)) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
waiter.resolve(event);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.bufferEvent(event);
|
|
166
|
+
}
|
|
167
|
+
bufferEvent(event) {
|
|
168
|
+
const overflow = this.eventBuffer.buffer(event);
|
|
169
|
+
if (overflow) {
|
|
170
|
+
this.failPermanently(overflow);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
rejectPending(error) {
|
|
174
|
+
this.frameTransport.rejectAll(error);
|
|
175
|
+
for (const waiter of this.eventWaiters) {
|
|
176
|
+
waiter.reject(error);
|
|
177
|
+
}
|
|
178
|
+
this.eventWaiters.clear();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type TransportPayloadCodec } from "./frame-payload-codec.js";
|
|
2
|
+
import type { ClassifiedFrame } from "./frame-rpc.js";
|
|
3
|
+
import { type LiveSidecarRequestPayload, type LiveSidecarResponsePayload } from "./callbacks.js";
|
|
4
|
+
import { type LiveSidecarEventPayload } from "./event-buffer.js";
|
|
5
|
+
import * as protocol from "./generated-protocol.js";
|
|
6
|
+
import { type LiveOwnershipScope } from "./ownership.js";
|
|
7
|
+
import { SIDECAR_PROTOCOL_SCHEMA } from "./protocol-schema.js";
|
|
8
|
+
import { type LiveRequestPayload } from "./request-payloads.js";
|
|
9
|
+
import { type LiveResponsePayload } from "./response-payloads.js";
|
|
10
|
+
export interface LiveRequestFrame {
|
|
11
|
+
frame_type: "request";
|
|
12
|
+
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
|
|
13
|
+
request_id: number;
|
|
14
|
+
ownership: LiveOwnershipScope;
|
|
15
|
+
payload: LiveRequestPayload;
|
|
16
|
+
}
|
|
17
|
+
export interface LiveEventFrame {
|
|
18
|
+
frame_type: "event";
|
|
19
|
+
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
|
|
20
|
+
ownership: LiveOwnershipScope;
|
|
21
|
+
payload: LiveSidecarEventPayload;
|
|
22
|
+
}
|
|
23
|
+
export interface LiveSidecarRequestFrame {
|
|
24
|
+
frame_type: "sidecar_request";
|
|
25
|
+
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
|
|
26
|
+
request_id: number;
|
|
27
|
+
ownership: LiveOwnershipScope;
|
|
28
|
+
payload: LiveSidecarRequestPayload;
|
|
29
|
+
}
|
|
30
|
+
export interface LiveResponseFrame {
|
|
31
|
+
frame_type: "response";
|
|
32
|
+
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
|
|
33
|
+
request_id: number;
|
|
34
|
+
ownership: LiveOwnershipScope;
|
|
35
|
+
payload: LiveResponsePayload;
|
|
36
|
+
}
|
|
37
|
+
export interface LiveSidecarResponseFrame {
|
|
38
|
+
frame_type: "sidecar_response";
|
|
39
|
+
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
|
|
40
|
+
request_id: number;
|
|
41
|
+
ownership: LiveOwnershipScope;
|
|
42
|
+
payload: LiveSidecarResponsePayload;
|
|
43
|
+
}
|
|
44
|
+
export type LiveProtocolFrame = LiveRequestFrame | LiveResponseFrame | LiveEventFrame | LiveSidecarRequestFrame | LiveSidecarResponseFrame;
|
|
45
|
+
export type LiveSidecarWrittenProtocolFrame = LiveResponseFrame | LiveEventFrame | LiveSidecarRequestFrame;
|
|
46
|
+
export type ProtocolFramePayloadCodec = TransportPayloadCodec;
|
|
47
|
+
export type ClassifiedSidecarWrittenProtocolFrame = ClassifiedFrame<LiveResponseFrame, LiveEventFrame, LiveSidecarRequestFrame>;
|
|
48
|
+
export type LiveSidecarRequestHandler = (request: LiveSidecarRequestFrame) => Promise<LiveSidecarResponsePayload> | LiveSidecarResponsePayload;
|
|
49
|
+
export declare class HostProtocolFrameFactory {
|
|
50
|
+
private nextRequestId;
|
|
51
|
+
createRequestFrame(input: {
|
|
52
|
+
ownership: LiveOwnershipScope;
|
|
53
|
+
payload: LiveRequestPayload;
|
|
54
|
+
}): LiveRequestFrame;
|
|
55
|
+
createSidecarResponseFrame(input: {
|
|
56
|
+
request: LiveSidecarRequestFrame;
|
|
57
|
+
payload: LiveSidecarResponsePayload;
|
|
58
|
+
}): LiveSidecarResponseFrame;
|
|
59
|
+
}
|
|
60
|
+
export declare function resolveSidecarRequestFramePayload(request: LiveSidecarRequestFrame, handler: LiveSidecarRequestHandler | null): Promise<LiveSidecarResponsePayload>;
|
|
61
|
+
export declare function toGeneratedProtocolFrame(frame: LiveProtocolFrame): protocol.ProtocolFrame;
|
|
62
|
+
export declare function encodeBareProtocolFrame(frame: LiveProtocolFrame): Buffer;
|
|
63
|
+
export declare function decodeBareProtocolFrame(payload: Uint8Array): LiveSidecarWrittenProtocolFrame;
|
|
64
|
+
export declare function encodeProtocolFramePayload(frame: LiveProtocolFrame, codec: ProtocolFramePayloadCodec): Buffer;
|
|
65
|
+
export declare function decodeProtocolFramePayload(payload: Uint8Array, codec: ProtocolFramePayloadCodec): LiveSidecarWrittenProtocolFrame;
|
|
66
|
+
export declare function classifySidecarWrittenProtocolFrame(frame: LiveSidecarWrittenProtocolFrame): ClassifiedSidecarWrittenProtocolFrame;
|
|
67
|
+
export declare function fromGeneratedSidecarWrittenProtocolFrame(frame: protocol.ProtocolFrame): LiveSidecarWrittenProtocolFrame;
|
|
68
|
+
export declare function toLiveProtocolSchema(schema: protocol.ProtocolSchema): typeof SIDECAR_PROTOCOL_SCHEMA;
|