@clowder-ai/plugin-sdk 0.1.0-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/contract-mirror.d.ts +120 -0
- package/dist/contract-mirror.d.ts.map +1 -0
- package/dist/contract-mirror.js +179 -0
- package/dist/contract-mirror.js.map +1 -0
- package/dist/events-publisher.d.ts +34 -0
- package/dist/events-publisher.d.ts.map +1 -0
- package/dist/events-publisher.js +62 -0
- package/dist/events-publisher.js.map +1 -0
- package/dist/feature-context.d.ts +90 -0
- package/dist/feature-context.d.ts.map +1 -0
- package/dist/feature-context.js +210 -0
- package/dist/feature-context.js.map +1 -0
- package/dist/handshake-client.d.ts +66 -0
- package/dist/handshake-client.d.ts.map +1 -0
- package/dist/handshake-client.js +124 -0
- package/dist/handshake-client.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/standalone-host.d.ts +55 -0
- package/dist/standalone-host.d.ts.map +1 -0
- package/dist/standalone-host.js +249 -0
- package/dist/standalone-host.js.map +1 -0
- package/dist/stdio-runtime.d.ts +50 -0
- package/dist/stdio-runtime.d.ts.map +1 -0
- package/dist/stdio-runtime.js +294 -0
- package/dist/stdio-runtime.js.map +1 -0
- package/dist/wire-dispatch.d.ts +43 -0
- package/dist/wire-dispatch.d.ts.map +1 -0
- package/dist/wire-dispatch.js +983 -0
- package/dist/wire-dispatch.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { DEADLINE_EXPIRED_CODE, DEADLINE_EXPIRED_MESSAGE, DELIVERY_REJECTED_CODE, DELIVERY_REJECTED_MESSAGE, DELIVERY_REJECT_REASONS, METHOD_NOT_FOUND_CODE, METHOD_NOT_FOUND_MESSAGE, PARSE_ERROR_CODE, PARSE_ERROR_MESSAGE, validateManifest, } from '@clowder-ai/plugin-contract';
|
|
3
|
+
import { createStdioChannel, startStdioRuntime, } from './stdio-runtime.js';
|
|
4
|
+
import { classifyFrame } from './wire-dispatch.js';
|
|
5
|
+
export class ManifestStartupError extends Error {
|
|
6
|
+
errors;
|
|
7
|
+
constructor(errors) {
|
|
8
|
+
super('plugin manifest failed contract validation');
|
|
9
|
+
this.name = 'ManifestStartupError';
|
|
10
|
+
this.errors = errors;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class StandaloneProtocolError extends Error {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'StandaloneProtocolError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function requireValidManifest(value) {
|
|
20
|
+
const validation = validateManifest(value);
|
|
21
|
+
if (!validation.valid) {
|
|
22
|
+
throw new ManifestStartupError(validation.errors);
|
|
23
|
+
}
|
|
24
|
+
return validation.manifest;
|
|
25
|
+
}
|
|
26
|
+
function requireStdioManifest(value) {
|
|
27
|
+
const manifest = requireValidManifest(value);
|
|
28
|
+
if (manifest.runtime.transport !== 'stdio') {
|
|
29
|
+
throw new TypeError('standalone stdio host requires a manifest with runtime.transport "stdio"');
|
|
30
|
+
}
|
|
31
|
+
return manifest;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Loads and contract-validates a manifest file for a standalone plugin.
|
|
35
|
+
*
|
|
36
|
+
* File and JSON parsing errors deliberately propagate: callers have not yet
|
|
37
|
+
* started a transport, so no protocol peer can observe a partial startup.
|
|
38
|
+
*/
|
|
39
|
+
export async function loadStandaloneManifest(path) {
|
|
40
|
+
return requireValidManifest(JSON.parse(await readFile(path, 'utf8')));
|
|
41
|
+
}
|
|
42
|
+
function isObject(value) {
|
|
43
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
44
|
+
}
|
|
45
|
+
function requireRequest(value) {
|
|
46
|
+
const { id, method, params } = value;
|
|
47
|
+
if (typeof id !== 'string' || typeof method !== 'string' || !isObject(params) || !isObject(params.input)) {
|
|
48
|
+
throw new StandaloneProtocolError('classifier accepted a frame outside the standalone request boundary');
|
|
49
|
+
}
|
|
50
|
+
return { id, method, input: params.input };
|
|
51
|
+
}
|
|
52
|
+
function deadlineExpiredResponse(id) {
|
|
53
|
+
return {
|
|
54
|
+
jsonrpc: '2.0',
|
|
55
|
+
id,
|
|
56
|
+
error: {
|
|
57
|
+
code: DEADLINE_EXPIRED_CODE,
|
|
58
|
+
message: DEADLINE_EXPIRED_MESSAGE,
|
|
59
|
+
data: {},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function methodNotFoundResponse(id) {
|
|
64
|
+
return {
|
|
65
|
+
jsonrpc: '2.0',
|
|
66
|
+
id,
|
|
67
|
+
error: { code: METHOD_NOT_FOUND_CODE, message: METHOD_NOT_FOUND_MESSAGE },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function deliveryRejectedResponse(id, reason) {
|
|
71
|
+
return {
|
|
72
|
+
jsonrpc: '2.0',
|
|
73
|
+
id,
|
|
74
|
+
error: {
|
|
75
|
+
code: DELIVERY_REJECTED_CODE,
|
|
76
|
+
message: DELIVERY_REJECTED_MESSAGE,
|
|
77
|
+
data: { reason },
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const DELIVERY_REJECT_REASON_SET = new Set(DELIVERY_REJECT_REASONS);
|
|
82
|
+
const HOST_BOUND_REQUEST_METHODS = new Set([
|
|
83
|
+
'broker.hello',
|
|
84
|
+
'broker.ready',
|
|
85
|
+
'events.publish',
|
|
86
|
+
'messaging.send',
|
|
87
|
+
'messaging.appendElements',
|
|
88
|
+
'messaging.subscribe',
|
|
89
|
+
'messaging.read',
|
|
90
|
+
'messaging.ack',
|
|
91
|
+
'messaging.snapshot',
|
|
92
|
+
]);
|
|
93
|
+
function isStandaloneMessageDisposition(value) {
|
|
94
|
+
if (!isObject(value) || typeof value.accepted !== 'boolean') {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if (value.accepted) {
|
|
98
|
+
return Object.keys(value).length === 1;
|
|
99
|
+
}
|
|
100
|
+
return Object.keys(value).length === 2
|
|
101
|
+
&& typeof value.reason === 'string'
|
|
102
|
+
&& DELIVERY_REJECT_REASON_SET.has(value.reason);
|
|
103
|
+
}
|
|
104
|
+
async function dispatchMessage(id, input, onMessage) {
|
|
105
|
+
if (onMessage === undefined) {
|
|
106
|
+
return deliveryRejectedResponse(id, 'NO_HANDLER');
|
|
107
|
+
}
|
|
108
|
+
let disposition;
|
|
109
|
+
try {
|
|
110
|
+
disposition = await onMessage(structuredClone(input));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return deliveryRejectedResponse(id, 'PLUGIN_INTERNAL');
|
|
114
|
+
}
|
|
115
|
+
if (!isStandaloneMessageDisposition(disposition)) {
|
|
116
|
+
return deliveryRejectedResponse(id, 'PLUGIN_INTERNAL');
|
|
117
|
+
}
|
|
118
|
+
if (!disposition.accepted) {
|
|
119
|
+
return deliveryRejectedResponse(id, disposition.reason);
|
|
120
|
+
}
|
|
121
|
+
return { jsonrpc: '2.0', id, result: { deliveryId: input.deliveryId } };
|
|
122
|
+
}
|
|
123
|
+
async function completesBeforeDrainDeadline(onDrain, deadlineUnixMs) {
|
|
124
|
+
if (Date.now() >= deadlineUnixMs) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
const cleanup = Promise.resolve().then(() => onDrain?.({ deadlineUnixMs }));
|
|
128
|
+
while (true) {
|
|
129
|
+
const remaining = deadlineUnixMs - Date.now();
|
|
130
|
+
if (remaining <= 0) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
let deadlineTimer;
|
|
134
|
+
try {
|
|
135
|
+
const completed = await Promise.race([
|
|
136
|
+
cleanup.then(() => true),
|
|
137
|
+
new Promise(resolve => {
|
|
138
|
+
deadlineTimer = setTimeout(resolve, Math.min(remaining, 2 ** 31 - 1), false);
|
|
139
|
+
}),
|
|
140
|
+
]);
|
|
141
|
+
if (completed) {
|
|
142
|
+
return Date.now() < deadlineUnixMs;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
if (deadlineTimer !== undefined) {
|
|
147
|
+
clearTimeout(deadlineTimer);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function createFrameHandler(options) {
|
|
153
|
+
const inFlight = new Map();
|
|
154
|
+
return async (frame) => {
|
|
155
|
+
const dispatch = classifyFrame(frame, inFlight);
|
|
156
|
+
if (dispatch.outcome === 'respond') {
|
|
157
|
+
if (dispatch.response === undefined) {
|
|
158
|
+
throw new StandaloneProtocolError('classifier requested a response without an envelope');
|
|
159
|
+
}
|
|
160
|
+
return dispatch.response;
|
|
161
|
+
}
|
|
162
|
+
if (dispatch.outcome === 'close') {
|
|
163
|
+
throw new StandaloneProtocolError(`classifier rejected frame as ${dispatch.disposition ?? 'unknown'}`);
|
|
164
|
+
}
|
|
165
|
+
if (frame.value.method === 'host.grants.changed') {
|
|
166
|
+
// The notification is closed and legal, but S2 has no grant-gated
|
|
167
|
+
// behavior yet. Accepting it without fabricating a response preserves
|
|
168
|
+
// its notification semantics while keeping authorization fail-closed.
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
const request = requireRequest(frame.value);
|
|
172
|
+
if (HOST_BOUND_REQUEST_METHODS.has(request.method)) {
|
|
173
|
+
// The public contract makes these Host-bound inputs legal at T-M, but
|
|
174
|
+
// this standalone plugin has no Host Broker. Reply conservatively
|
|
175
|
+
// without storing ingress, session state, or activation.
|
|
176
|
+
return methodNotFoundResponse(request.id);
|
|
177
|
+
}
|
|
178
|
+
if (request.method === 'host.lifecycle.ping') {
|
|
179
|
+
const nonce = request.input.nonce;
|
|
180
|
+
if (typeof nonce !== 'string') {
|
|
181
|
+
throw new StandaloneProtocolError('classifier accepted ping without a nonce');
|
|
182
|
+
}
|
|
183
|
+
return { jsonrpc: '2.0', id: request.id, result: { nonce } };
|
|
184
|
+
}
|
|
185
|
+
if (request.method === 'host.messaging.deliver') {
|
|
186
|
+
return dispatchMessage(request.id, request.input, options.onMessage);
|
|
187
|
+
}
|
|
188
|
+
if (request.method === 'host.lifecycle.drain') {
|
|
189
|
+
const deadlineUnixMs = request.input.deadlineUnixMs;
|
|
190
|
+
if (typeof deadlineUnixMs !== 'number') {
|
|
191
|
+
throw new StandaloneProtocolError('classifier accepted drain without a deadline');
|
|
192
|
+
}
|
|
193
|
+
if (!(await completesBeforeDrainDeadline(options.onDrain, deadlineUnixMs))) {
|
|
194
|
+
return deadlineExpiredResponse(request.id);
|
|
195
|
+
}
|
|
196
|
+
return { jsonrpc: '2.0', id: request.id, result: null };
|
|
197
|
+
}
|
|
198
|
+
// No other unsupported method reaches this branch. Keep the assertion so
|
|
199
|
+
// a future ready row cannot silently create standalone-shell behavior.
|
|
200
|
+
throw new StandaloneProtocolError(`unsupported accepted method: ${request.method}`);
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const respondToInvalidJson = error => error.code === 'INVALID_JSON'
|
|
204
|
+
? {
|
|
205
|
+
jsonrpc: '2.0',
|
|
206
|
+
id: null,
|
|
207
|
+
error: { code: PARSE_ERROR_CODE, message: PARSE_ERROR_MESSAGE },
|
|
208
|
+
}
|
|
209
|
+
: undefined;
|
|
210
|
+
function attachManifest(channel, manifest) {
|
|
211
|
+
return {
|
|
212
|
+
manifest,
|
|
213
|
+
send: frame => channel.send(frame),
|
|
214
|
+
close: () => channel.close(),
|
|
215
|
+
get failed() {
|
|
216
|
+
return channel.failed;
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Starts the fail-closed plugin-side standalone shell.
|
|
222
|
+
*
|
|
223
|
+
* A manifest is validated by the published contract runtime before any stdio
|
|
224
|
+
* listener is attached. Lifecycle rows execute locally. beta.8 handshake
|
|
225
|
+
* requests reach the handler only to receive a conservative standard error;
|
|
226
|
+
* no Broker behavior is present in this shell.
|
|
227
|
+
*/
|
|
228
|
+
export function startStandaloneHost(options) {
|
|
229
|
+
const manifest = requireStdioManifest(options.manifest);
|
|
230
|
+
const hasInput = options.input !== undefined;
|
|
231
|
+
const hasOutput = options.output !== undefined;
|
|
232
|
+
if (hasInput !== hasOutput) {
|
|
233
|
+
throw new TypeError('standalone host requires both input and output streams');
|
|
234
|
+
}
|
|
235
|
+
const onFrame = createFrameHandler(options);
|
|
236
|
+
const channel = hasInput
|
|
237
|
+
? createStdioChannel(options.input, options.output, {
|
|
238
|
+
onFrame,
|
|
239
|
+
onFrameError: respondToInvalidJson,
|
|
240
|
+
onFatal: options.onFatal,
|
|
241
|
+
})
|
|
242
|
+
: startStdioRuntime({
|
|
243
|
+
onFrame,
|
|
244
|
+
onFrameError: respondToInvalidJson,
|
|
245
|
+
onFatal: options.onFatal,
|
|
246
|
+
});
|
|
247
|
+
return attachManifest(channel, manifest);
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=standalone-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone-host.js","sourceRoot":"","sources":["../src/standalone-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Readable, Writable } from 'node:stream';
|
|
2
|
+
import { NdjsonFrameError, type DecodedNdjsonFrame, type JsonObject } from '@clowder-ai/plugin-contract/conformance';
|
|
3
|
+
export { NdjsonFrameError } from '@clowder-ai/plugin-contract/conformance';
|
|
4
|
+
export type { DecodedNdjsonFrame as StdioFrame, JsonObject, NdjsonFrameErrorCode as StdioFrameErrorCode, } from '@clowder-ai/plugin-contract/conformance';
|
|
5
|
+
export type StdioRuntimeFatalReason = 'FRAME_ERROR' | 'HANDLER_ERROR' | 'INPUT_ERROR' | 'OUTPUT_ERROR';
|
|
6
|
+
export interface StdioRuntimeFatalErrorOptions {
|
|
7
|
+
readonly reason: StdioRuntimeFatalReason;
|
|
8
|
+
readonly cause: unknown;
|
|
9
|
+
}
|
|
10
|
+
/** A terminal channel failure. It deliberately never becomes a protocol frame. */
|
|
11
|
+
export declare class StdioRuntimeFatalError extends Error {
|
|
12
|
+
readonly reason: StdioRuntimeFatalReason;
|
|
13
|
+
constructor(options: StdioRuntimeFatalErrorOptions);
|
|
14
|
+
}
|
|
15
|
+
export type StdioFrameHandler = (frame: DecodedNdjsonFrame) => JsonObject | undefined | Promise<JsonObject | undefined>;
|
|
16
|
+
export type StdioFrameErrorHandler = (error: NdjsonFrameError) => JsonObject | undefined | Promise<JsonObject | undefined>;
|
|
17
|
+
export interface StdioChannelOptions {
|
|
18
|
+
/** Defaults to the contract-owned hard cap; callers may only choose a stricter cap. */
|
|
19
|
+
readonly maxFrameBytes?: number;
|
|
20
|
+
readonly onFrame: StdioFrameHandler;
|
|
21
|
+
/**
|
|
22
|
+
* Lets a protocol shell map an otherwise terminal decoder error to its
|
|
23
|
+
* contract-defined response before this schema-neutral runtime closes.
|
|
24
|
+
*/
|
|
25
|
+
readonly onFrameError?: StdioFrameErrorHandler;
|
|
26
|
+
readonly onFatal?: (error: StdioRuntimeFatalError) => void;
|
|
27
|
+
}
|
|
28
|
+
export interface StdioRuntimeOptions extends StdioChannelOptions {
|
|
29
|
+
}
|
|
30
|
+
export interface StdioChannel {
|
|
31
|
+
/** Encode and write exactly one object frame followed by LF. */
|
|
32
|
+
send(frame: JsonObject): Promise<void>;
|
|
33
|
+
/** Stop accepting input without closing a stream owned by the caller. */
|
|
34
|
+
close(): void;
|
|
35
|
+
readonly failed: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Connect a schema-neutral NDJSON channel to caller-owned streams.
|
|
39
|
+
*
|
|
40
|
+
* Frame processing is serialized, preserving request order without creating
|
|
41
|
+
* a production request/result vocabulary or a Host Broker substitute.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createStdioChannel(input: Readable, output: Writable, options: StdioChannelOptions): StdioChannel;
|
|
44
|
+
/**
|
|
45
|
+
* Start the public standalone SDK runtime on the process standard streams.
|
|
46
|
+
* Diagnostics remain out of stdout; callers may observe fatal errors through
|
|
47
|
+
* `onFatal` and choose to write their own stderr diagnostics.
|
|
48
|
+
*/
|
|
49
|
+
export declare function startStdioRuntime(options: StdioRuntimeOptions): StdioChannel;
|
|
50
|
+
//# sourceMappingURL=stdio-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio-runtime.d.ts","sourceRoot":"","sources":["../src/stdio-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAIL,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAChB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,YAAY,EACV,kBAAkB,IAAI,UAAU,EAChC,UAAU,EACV,oBAAoB,IAAI,mBAAmB,GAC5C,MAAM,yCAAyC,CAAC;AAEjD,MAAM,MAAM,uBAAuB,GAC/B,aAAa,GACb,eAAe,GACf,aAAa,GACb,cAAc,CAAC;AAEnB,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,kFAAkF;AAClF,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;gBAE7B,OAAO,EAAE,6BAA6B;CAOnD;AAED,MAAM,MAAM,iBAAiB,GAAG,CAC9B,KAAK,EAAE,kBAAkB,KACtB,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;AAC9D,MAAM,MAAM,sBAAsB,GAAG,CACnC,KAAK,EAAE,gBAAgB,KACpB,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;AAC9D,MAAM,WAAW,mBAAmB;IAClC,uFAAuF;IACvF,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,sBAAsB,CAAC;IAC/C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC5D;AAED,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB;CAAG;AACnE,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,yEAAyE;IACzE,KAAK,IAAI,IAAI,CAAC;IACd,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAaD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,mBAAmB,GAC3B,YAAY,CAsQd;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAY5E"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { encodeNdjsonFrame, MAX_NDJSON_FRAME_BYTES, NdjsonFrameDecoder, NdjsonFrameError, } from '@clowder-ai/plugin-contract/conformance';
|
|
2
|
+
export { NdjsonFrameError } from '@clowder-ai/plugin-contract/conformance';
|
|
3
|
+
/** A terminal channel failure. It deliberately never becomes a protocol frame. */
|
|
4
|
+
export class StdioRuntimeFatalError extends Error {
|
|
5
|
+
reason;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
super(`stdio runtime stopped after ${options.reason}`, {
|
|
8
|
+
cause: options.cause,
|
|
9
|
+
});
|
|
10
|
+
this.name = 'StdioRuntimeFatalError';
|
|
11
|
+
this.reason = options.reason;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// Scan a bounded source window at a time. Complete frames are coalesced only
|
|
15
|
+
// once before reaching the contract decoder, so a near-cap partial frame is
|
|
16
|
+
// neither repeatedly copied nor repeatedly scanned by that decoder.
|
|
17
|
+
const MAX_INPUT_DECODE_SLICE_BYTES = 16 * 1024;
|
|
18
|
+
function nextInputSliceEnd(chunk, offset) {
|
|
19
|
+
const boundedEnd = Math.min(offset + MAX_INPUT_DECODE_SLICE_BYTES, chunk.byteLength);
|
|
20
|
+
const newline = chunk.subarray(offset, boundedEnd).indexOf(0x0a);
|
|
21
|
+
return newline === -1 ? boundedEnd : offset + newline + 1;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Connect a schema-neutral NDJSON channel to caller-owned streams.
|
|
25
|
+
*
|
|
26
|
+
* Frame processing is serialized, preserving request order without creating
|
|
27
|
+
* a production request/result vocabulary or a Host Broker substitute.
|
|
28
|
+
*/
|
|
29
|
+
export function createStdioChannel(input, output, options) {
|
|
30
|
+
const readableEncoding = input.readableEncoding;
|
|
31
|
+
if (readableEncoding !== undefined && readableEncoding !== null) {
|
|
32
|
+
throw new RangeError('stdio input must remain in byte mode');
|
|
33
|
+
}
|
|
34
|
+
let decoder = new NdjsonFrameDecoder(options.maxFrameBytes);
|
|
35
|
+
const maxFrameBytes = options.maxFrameBytes ?? MAX_NDJSON_FRAME_BYTES;
|
|
36
|
+
let accepting = true;
|
|
37
|
+
let fatalError;
|
|
38
|
+
let detached = false;
|
|
39
|
+
let processing = false;
|
|
40
|
+
let inputEnded = false;
|
|
41
|
+
let decoderEnded = false;
|
|
42
|
+
let undecodedSegments = [];
|
|
43
|
+
let undecodedBytes = 0;
|
|
44
|
+
let activeWrites = 0;
|
|
45
|
+
let outputDetachmentRequested = false;
|
|
46
|
+
let outputErrorGracePending = false;
|
|
47
|
+
const discardUndecodedFrame = () => {
|
|
48
|
+
undecodedSegments = [];
|
|
49
|
+
undecodedBytes = 0;
|
|
50
|
+
};
|
|
51
|
+
const takeUndecodedFrame = () => {
|
|
52
|
+
if (undecodedSegments.length === 0) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const frame = undecodedSegments.length === 1
|
|
56
|
+
? undecodedSegments[0]
|
|
57
|
+
: Buffer.concat(undecodedSegments, undecodedBytes);
|
|
58
|
+
discardUndecodedFrame();
|
|
59
|
+
return frame;
|
|
60
|
+
};
|
|
61
|
+
const detachInput = () => {
|
|
62
|
+
input.off('data', onData);
|
|
63
|
+
input.off('end', onEnd);
|
|
64
|
+
input.off('error', onInputError);
|
|
65
|
+
input.off('close', onInputClose);
|
|
66
|
+
};
|
|
67
|
+
const detachOutputError = () => {
|
|
68
|
+
output.off('error', onOutputError);
|
|
69
|
+
};
|
|
70
|
+
const maybeDetachOutputError = () => {
|
|
71
|
+
if (outputDetachmentRequested &&
|
|
72
|
+
activeWrites === 0 &&
|
|
73
|
+
!outputErrorGracePending) {
|
|
74
|
+
detachOutputError();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const requestOutputDetachment = () => {
|
|
78
|
+
outputDetachmentRequested = true;
|
|
79
|
+
maybeDetachOutputError();
|
|
80
|
+
};
|
|
81
|
+
const write = (frame) => {
|
|
82
|
+
activeWrites += 1;
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
try {
|
|
85
|
+
output.write(frame, error => {
|
|
86
|
+
if (error === null || error === undefined) {
|
|
87
|
+
resolve();
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
reject(error);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
reject(error);
|
|
96
|
+
}
|
|
97
|
+
}).finally(() => {
|
|
98
|
+
activeWrites -= 1;
|
|
99
|
+
// A Writable may emit its matching error after invoking its write
|
|
100
|
+
// callback. Defer detachment by one turn so that event remains handled.
|
|
101
|
+
outputErrorGracePending = true;
|
|
102
|
+
setImmediate(() => {
|
|
103
|
+
outputErrorGracePending = false;
|
|
104
|
+
maybeDetachOutputError();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
const detach = () => {
|
|
109
|
+
if (detached) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
detached = true;
|
|
113
|
+
detachInput();
|
|
114
|
+
requestOutputDetachment();
|
|
115
|
+
};
|
|
116
|
+
const fail = (reason, cause) => {
|
|
117
|
+
if (fatalError !== undefined) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
accepting = false;
|
|
121
|
+
fatalError = new StdioRuntimeFatalError({ reason, cause });
|
|
122
|
+
discardUndecodedFrame();
|
|
123
|
+
input.pause();
|
|
124
|
+
detach();
|
|
125
|
+
options.onFatal?.(fatalError);
|
|
126
|
+
};
|
|
127
|
+
const send = async (frame) => {
|
|
128
|
+
if (!accepting) {
|
|
129
|
+
throw new NdjsonFrameError('DECODER_CLOSED', 'stdio channel is closed');
|
|
130
|
+
}
|
|
131
|
+
const encoded = encodeNdjsonFrame(frame, options.maxFrameBytes);
|
|
132
|
+
try {
|
|
133
|
+
await write(encoded);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
fail('OUTPUT_ERROR', error);
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const finishDecoder = () => {
|
|
141
|
+
if (!accepting || decoderEnded) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
decoderEnded = true;
|
|
145
|
+
try {
|
|
146
|
+
const pendingFrame = takeUndecodedFrame();
|
|
147
|
+
if (pendingFrame !== undefined) {
|
|
148
|
+
decoder.push(pendingFrame);
|
|
149
|
+
}
|
|
150
|
+
decoder.end();
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
fail('FRAME_ERROR', error);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const onData = (chunk) => {
|
|
157
|
+
if (!accepting) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (processing) {
|
|
161
|
+
fail('INPUT_ERROR', new Error('stdio input emitted data while processing was paused'));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// A data listener puts Readable into flowing mode. Pause before decoding so
|
|
165
|
+
// a slow handler or output write cannot accumulate an unbounded Promise
|
|
166
|
+
// chain from later source chunks.
|
|
167
|
+
processing = true;
|
|
168
|
+
input.pause();
|
|
169
|
+
if (!(chunk instanceof Uint8Array)) {
|
|
170
|
+
fail('INPUT_ERROR', new TypeError('stdio input must emit Uint8Array chunks'));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
void processChunk(chunk);
|
|
174
|
+
};
|
|
175
|
+
const processChunk = async (chunk) => {
|
|
176
|
+
try {
|
|
177
|
+
let offset = 0;
|
|
178
|
+
while (offset < chunk.byteLength && accepting) {
|
|
179
|
+
const nextOffset = nextInputSliceEnd(chunk, offset);
|
|
180
|
+
const segment = chunk.subarray(offset, nextOffset);
|
|
181
|
+
undecodedSegments.push(segment);
|
|
182
|
+
undecodedBytes += segment.byteLength;
|
|
183
|
+
offset = nextOffset;
|
|
184
|
+
if (segment.at(-1) !== 0x0a && undecodedBytes <= maxFrameBytes) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
let frames;
|
|
188
|
+
try {
|
|
189
|
+
const frame = takeUndecodedFrame();
|
|
190
|
+
if (frame === undefined) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
frames = decoder.push(frame);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error instanceof NdjsonFrameError) {
|
|
197
|
+
const response = options.onFrameError === undefined
|
|
198
|
+
? undefined
|
|
199
|
+
: await options.onFrameError(error);
|
|
200
|
+
if (response !== undefined && accepting) {
|
|
201
|
+
await send(response);
|
|
202
|
+
// The decoder deliberately fails closed after any bad frame.
|
|
203
|
+
// A protocol shell that supplied a response has consumed that
|
|
204
|
+
// complete frame, so it may explicitly resume at the next LF.
|
|
205
|
+
decoder = new NdjsonFrameDecoder(options.maxFrameBytes);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
fail('FRAME_ERROR', error);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
for (const frame of frames) {
|
|
213
|
+
if (!accepting) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const response = await options.onFrame(frame);
|
|
217
|
+
if (response !== undefined && accepting) {
|
|
218
|
+
await send(response);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
fail('HANDLER_ERROR', error);
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
processing = false;
|
|
228
|
+
if (inputEnded) {
|
|
229
|
+
finishDecoder();
|
|
230
|
+
}
|
|
231
|
+
else if (accepting) {
|
|
232
|
+
input.resume();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const onEnd = () => {
|
|
237
|
+
inputEnded = true;
|
|
238
|
+
if (!processing) {
|
|
239
|
+
finishDecoder();
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
const onInputError = (error) => fail('INPUT_ERROR', error);
|
|
243
|
+
const onInputClose = () => {
|
|
244
|
+
if (!inputEnded) {
|
|
245
|
+
inputEnded = true;
|
|
246
|
+
if (!processing) {
|
|
247
|
+
finishDecoder();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (fatalError === undefined) {
|
|
251
|
+
detachInput();
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
const onOutputError = (error) => fail('OUTPUT_ERROR', error);
|
|
255
|
+
input.on('data', onData);
|
|
256
|
+
input.once('end', onEnd);
|
|
257
|
+
input.once('error', onInputError);
|
|
258
|
+
input.once('close', onInputClose);
|
|
259
|
+
output.once('error', onOutputError);
|
|
260
|
+
return {
|
|
261
|
+
send,
|
|
262
|
+
close: () => {
|
|
263
|
+
if (detached) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
accepting = false;
|
|
267
|
+
discardUndecodedFrame();
|
|
268
|
+
detach();
|
|
269
|
+
input.pause();
|
|
270
|
+
},
|
|
271
|
+
get failed() {
|
|
272
|
+
return fatalError !== undefined;
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Start the public standalone SDK runtime on the process standard streams.
|
|
278
|
+
* Diagnostics remain out of stdout; callers may observe fatal errors through
|
|
279
|
+
* `onFatal` and choose to write their own stderr diagnostics.
|
|
280
|
+
*/
|
|
281
|
+
export function startStdioRuntime(options) {
|
|
282
|
+
return createStdioChannel(process.stdin, process.stdout, {
|
|
283
|
+
...options,
|
|
284
|
+
onFatal: error => {
|
|
285
|
+
process.exitCode = 1;
|
|
286
|
+
// The standalone runtime owns process.stdin. A framing fatal closes that
|
|
287
|
+
// transport immediately, instead of remaining alive until a hostile or
|
|
288
|
+
// broken parent eventually sends EOF.
|
|
289
|
+
process.stdin.destroy();
|
|
290
|
+
options.onFatal?.(error);
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=stdio-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio-runtime.js","sourceRoot":"","sources":["../src/stdio-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire dispatch classifier — pre-dispatch frame classification per the
|
|
3
|
+
* disposition table (T-A through T-M, §3.8-1 plus beta.8 closure).
|
|
4
|
+
*
|
|
5
|
+
* This module classifies decoded NDJSON frames into one of the 13
|
|
6
|
+
* disposition classes. T-A (transport failure) and T-B (JSON parse error)
|
|
7
|
+
* are handled by the NDJSON frame decoder layer — this classifier covers
|
|
8
|
+
* T-C through T-M.
|
|
9
|
+
*
|
|
10
|
+
* A frame that passes all rejection checks returns disposition=null with
|
|
11
|
+
* outcome='accept', indicating a valid request that should be dispatched
|
|
12
|
+
* to a method handler.
|
|
13
|
+
*
|
|
14
|
+
* This module defines nothing new — every classification rule traces to
|
|
15
|
+
* the frozen disposition table in @clowder-ai/plugin-contract.
|
|
16
|
+
*/
|
|
17
|
+
import type { DecodedNdjsonFrame, JsonObject } from '@clowder-ai/plugin-contract/conformance';
|
|
18
|
+
import { type DispositionClass, type RequestSnapshot, type WireMethodName } from '@clowder-ai/plugin-contract';
|
|
19
|
+
export type { RequestSnapshot } from '@clowder-ai/plugin-contract';
|
|
20
|
+
export interface InFlightEntry {
|
|
21
|
+
readonly method: WireMethodName;
|
|
22
|
+
readonly requestSnapshot?: RequestSnapshot;
|
|
23
|
+
}
|
|
24
|
+
export interface DispatchResult {
|
|
25
|
+
/** T-class from the disposition table, or null for valid requests. */
|
|
26
|
+
readonly disposition: DispositionClass | null;
|
|
27
|
+
/** Required transport action: close, respond (with error), or accept. */
|
|
28
|
+
readonly outcome: 'close' | 'respond' | 'accept';
|
|
29
|
+
/** For 'respond': the error envelope to write back as NDJSON. */
|
|
30
|
+
readonly response?: JsonObject;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Classify a decoded NDJSON frame into a disposition class.
|
|
34
|
+
*
|
|
35
|
+
* Covers T-C through T-M of the disposition table. T-A and T-B
|
|
36
|
+
* are handled by the NDJSON frame decoder layer.
|
|
37
|
+
*
|
|
38
|
+
* @param frame Decoded frame with raw bytes and parsed value.
|
|
39
|
+
* @param inFlight Map of in-flight request IDs to their entry metadata.
|
|
40
|
+
* @returns The disposition result with class, outcome, and optional error response.
|
|
41
|
+
*/
|
|
42
|
+
export declare function classifyFrame(frame: DecodedNdjsonFrame, inFlight: ReadonlyMap<string, InFlightEntry>): DispatchResult;
|
|
43
|
+
//# sourceMappingURL=wire-dispatch.d.ts.map
|