posipaki 0.32.1 → 0.33.0
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/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/remote/node.d.ts +205 -7
- package/dist/remote/node.d.ts.map +1 -1
- package/dist/remote/node.js +604 -41
- package/dist/remote/node.js.map +1 -1
- package/dist/version-W7guWJeR.js +7 -0
- package/dist/version-W7guWJeR.js.map +1 -0
- package/package.json +7 -1
package/dist/index.d.ts
CHANGED
|
@@ -18,5 +18,9 @@ declare function asyncify<A, S, IM extends Message, OM extends Message>(fn: Proc
|
|
|
18
18
|
declare function defineMessages<OutMsg extends Message = Message>(): ActorMessages<OutMsg>;
|
|
19
19
|
declare function defineActor<Args, InternalState, InMsg extends Message, OutMsg extends Message, Methods extends MethodOptions, Handlers extends HandlerOptions<InMsg>, ReflectionMethods extends ReflectionOptions>(config: ActorConfig<Args, InternalState, InMsg, OutMsg, Methods, Handlers, ReflectionMethods>): ActorDefinition<Args, InternalState, InMsg, OutMsg, ReflectionMethods>;
|
|
20
20
|
//#endregion
|
|
21
|
-
|
|
21
|
+
//#region src/version.d.ts
|
|
22
|
+
/** posipaki's release version. Must equal package.json's `version`. */
|
|
23
|
+
declare const LIB_VERSION = "0.33.0";
|
|
24
|
+
//#endregion
|
|
25
|
+
export { type ActorConfig, type ActorContext, type ActorDecorated, type ActorDefinition, type ActorPlugin, type ActorReflection, type AnyProcess, AsyncProcess, type AsyncProcessFn, type ExitMessage, type HandlerFn, type HandlerOptions, type HookResult, LIB_VERSION, type Message, type MethodOptions, type PluginTransform, Process, type ProcessCtx, type ProcessFn, type SenderInfo, type SenderOrigin, type SpawnedFrom, type StopMessage, type WithSender, type WithoutSender, asyncify, callHook, chainHook, defineActor, defineMessages, mergeConfigs, propagateError, runDispatch, runDispatchAsync, spawn, spawnAsync, stopPropagation };
|
|
22
26
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/util.ts","../src/adapters.ts","../src/define-actor.ts"],"mappings":";;;;KAOK,eAAe,MAAM,KAAK;KAC1B;;;;;;iBASK,YAAY,GACpB,cACA,IAAI,eAAe,IACnB,UAAS,SACT,uBACC,sBAAsB;;;iBCpBT,SAAS,GAAG,GAAG,WAAW,SAAS,WAAW,SAC5D,IAAI,UAAU,GAAG,GAAG,IAAI,MACvB,eAAe,GAAG,GAAG,IAAI;;;iBC+BZ,eAAe,eAAe,UAAU,YAAY,cAAc;iBA2DlE,YACd,MACA,eACA,cAAc,SACd,eAAe,SACf,gBAAgB,eAChB,iBAAiB,eAAe,QAChC,0BAA0B,mBAE1B,QAAQ,YAAY,MAAM,eAAe,OAAO,QAAQ,SAAS,UAAU,qBAC1E,gBAAgB,MAAM,eAAe,OAAO,QAAQ"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/util.ts","../src/adapters.ts","../src/define-actor.ts","../src/version.ts"],"mappings":";;;;KAOK,eAAe,MAAM,KAAK;KAC1B;;;;;;iBASK,YAAY,GACpB,cACA,IAAI,eAAe,IACnB,UAAS,SACT,uBACC,sBAAsB;;;iBCpBT,SAAS,GAAG,GAAG,WAAW,SAAS,WAAW,SAC5D,IAAI,UAAU,GAAG,GAAG,IAAI,MACvB,eAAe,GAAG,GAAG,IAAI;;;iBC+BZ,eAAe,eAAe,UAAU,YAAY,cAAc;iBA2DlE,YACd,MACA,eACA,cAAc,SACd,eAAe,SACf,gBAAgB,eAChB,iBAAiB,eAAe,QAChC,0BAA0B,mBAE1B,QAAQ,YAAY,MAAM,eAAe,OAAO,QAAQ,SAAS,UAAU,qBAC1E,gBAAgB,MAAM,eAAe,OAAO,QAAQ;;;;cC1F1C"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { i as runDispatch } from "./util-Brtfgv68.js";
|
|
2
2
|
import { a as spawnAsync, i as runDispatchAsync, n as defineMessages, o as asyncify, r as AsyncProcess, t as defineActor } from "./define-actor-DqOW2StX.js";
|
|
3
3
|
import { n as chainHook, o as propagateError, r as mergeConfigs, s as stopPropagation, t as callHook } from "./hooks-CgXhmY4r.js";
|
|
4
|
+
import { t as LIB_VERSION } from "./version-W7guWJeR.js";
|
|
4
5
|
//#region src/process.ts
|
|
5
6
|
/** @deprecated Use {@link spawnAsync} instead. */
|
|
6
7
|
function spawn(fn, pname, tp) {
|
|
@@ -30,6 +31,6 @@ var Process = class extends AsyncProcess {
|
|
|
30
31
|
* @module
|
|
31
32
|
*/
|
|
32
33
|
//#endregion
|
|
33
|
-
export { AsyncProcess, Process, asyncify, callHook, chainHook, defineActor, defineMessages, mergeConfigs, propagateError, runDispatch, runDispatchAsync, spawn, spawnAsync, stopPropagation };
|
|
34
|
+
export { AsyncProcess, LIB_VERSION, Process, asyncify, callHook, chainHook, defineActor, defineMessages, mergeConfigs, propagateError, runDispatch, runDispatchAsync, spawn, spawnAsync, stopPropagation };
|
|
34
35
|
|
|
35
36
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/process.ts","../src/index.ts"],"sourcesContent":["import { AsyncProcess } from \"./process.async.js\";\nimport { asyncify } from \"./adapters.js\";\nimport type { Message, SenderInfo, ExitMessage, ProcessFn } from \"./types.js\";\n\nexport type { Message, ProcessFn, ProcessCtx } from \"./types.js\";\nexport { runDispatch } from \"./util.js\";\n\n/** @deprecated Use {@link spawnAsync} instead. */\nexport function spawn<A, S, IM extends Message = Message, OM extends Message = ExitMessage>(\n fn: ProcessFn<A, S, IM, OM>,\n pname: string,\n tp?: (m: OM, from: SenderInfo) => void,\n): (a: A) => Process<A, S, IM, OM> {\n return (a: A) => new Process(fn, pname, tp).start(a);\n}\n\n/** @deprecated Use {@link AsyncProcess} instead. Sync processes are\n * wrapped via asyncify internally; spawnAsync handles both. */\nclass Process<A, S, IM extends Message, OM extends Message> extends AsyncProcess<A, S, IM, OM, {}> {\n constructor(fn: ProcessFn<A, S, IM, OM>, pname: string, tp?: (m: OM, from: SenderInfo) => void) {\n super(asyncify(fn), pname, tp);\n }\n\n start(a: A) {\n super.start(a);\n return this;\n }\n tick(): Promise<void> {\n return super.pvtTick();\n }\n}\n\nexport { Process };\n","/**\n * Posipaki — Erlang-inspired lightweight actor processes built on\n * generator functions. Processes communicate via message-passing,\n * can fork children, and expose their state reactively.\n *\n * @module\n */\n\nimport { Process, spawn } from \"./process.js\";\nimport { runDispatch } from \"./util.js\";\nimport { AsyncProcess, spawnAsync, runDispatchAsync } from \"./process.async.js\";\nimport { asyncify } from \"./adapters.js\";\n\nexport { Process, spawn, runDispatch };\nexport { AsyncProcess, spawnAsync, runDispatchAsync, asyncify };\nexport type { AnyProcess } from \"./process.async.js\";\n\nexport type {\n Message,\n WithSender,\n WithoutSender,\n ExitMessage,\n StopMessage,\n ProcessFn,\n ProcessCtx,\n AsyncProcessFn,\n SenderOrigin,\n SenderInfo,\n} from \"./types.js\";\n\nexport { defineActor, defineMessages } from \"./define-actor.js\";\nexport type {\n ActorDefinition,\n SpawnedFrom,\n ActorConfig,\n ActorContext,\n MethodOptions,\n HandlerOptions,\n HandlerFn,\n} from \"./actor-types.js\";\n\n// ── hooks ─────────────────────────────────────────────────────────────────\n\nexport { stopPropagation, propagateError, mergeConfigs, chainHook, callHook } from \"./hooks.js\";\nexport type {\n HookResult,\n ActorPlugin,\n ActorDecorated,\n ActorReflection,\n PluginTransform,\n} from \"./hooks.js\";\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/process.ts","../src/index.ts"],"sourcesContent":["import { AsyncProcess } from \"./process.async.js\";\nimport { asyncify } from \"./adapters.js\";\nimport type { Message, SenderInfo, ExitMessage, ProcessFn } from \"./types.js\";\n\nexport type { Message, ProcessFn, ProcessCtx } from \"./types.js\";\nexport { runDispatch } from \"./util.js\";\n\n/** @deprecated Use {@link spawnAsync} instead. */\nexport function spawn<A, S, IM extends Message = Message, OM extends Message = ExitMessage>(\n fn: ProcessFn<A, S, IM, OM>,\n pname: string,\n tp?: (m: OM, from: SenderInfo) => void,\n): (a: A) => Process<A, S, IM, OM> {\n return (a: A) => new Process(fn, pname, tp).start(a);\n}\n\n/** @deprecated Use {@link AsyncProcess} instead. Sync processes are\n * wrapped via asyncify internally; spawnAsync handles both. */\nclass Process<A, S, IM extends Message, OM extends Message> extends AsyncProcess<A, S, IM, OM, {}> {\n constructor(fn: ProcessFn<A, S, IM, OM>, pname: string, tp?: (m: OM, from: SenderInfo) => void) {\n super(asyncify(fn), pname, tp);\n }\n\n start(a: A) {\n super.start(a);\n return this;\n }\n tick(): Promise<void> {\n return super.pvtTick();\n }\n}\n\nexport { Process };\n","/**\n * Posipaki — Erlang-inspired lightweight actor processes built on\n * generator functions. Processes communicate via message-passing,\n * can fork children, and expose their state reactively.\n *\n * @module\n */\n\nimport { Process, spawn } from \"./process.js\";\nimport { runDispatch } from \"./util.js\";\nimport { AsyncProcess, spawnAsync, runDispatchAsync } from \"./process.async.js\";\nimport { asyncify } from \"./adapters.js\";\n\nexport { Process, spawn, runDispatch };\nexport { AsyncProcess, spawnAsync, runDispatchAsync, asyncify };\nexport type { AnyProcess } from \"./process.async.js\";\n\nexport type {\n Message,\n WithSender,\n WithoutSender,\n ExitMessage,\n StopMessage,\n ProcessFn,\n ProcessCtx,\n AsyncProcessFn,\n SenderOrigin,\n SenderInfo,\n} from \"./types.js\";\n\nexport { defineActor, defineMessages } from \"./define-actor.js\";\nexport type {\n ActorDefinition,\n SpawnedFrom,\n ActorConfig,\n ActorContext,\n MethodOptions,\n HandlerOptions,\n HandlerFn,\n} from \"./actor-types.js\";\n\n// ── hooks ─────────────────────────────────────────────────────────────────\n\nexport { stopPropagation, propagateError, mergeConfigs, chainHook, callHook } from \"./hooks.js\";\nexport type {\n HookResult,\n ActorPlugin,\n ActorDecorated,\n ActorReflection,\n PluginTransform,\n} from \"./hooks.js\";\nexport { LIB_VERSION } from \"./version.js\";\n"],"mappings":";;;;;;AAQA,SAAgB,MACd,IACA,OACA,IACiC;CACjC,QAAQ,MAAS,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;AACrD;;;AAIA,IAAM,UAAN,cAAoE,aAA+B;CACjG,YAAY,IAA6B,OAAe,IAAwC;EAC9F,MAAM,SAAS,EAAE,GAAG,OAAO,EAAE;CAC/B;CAEA,MAAM,GAAM;EACV,MAAM,MAAM,CAAC;EACb,OAAO;CACT;CACA,OAAsB;EACpB,OAAO,MAAM,QAAQ;CACvB;AACF"}
|
package/dist/remote/node.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { r as Message } from "../types-CsE8xK3a.js";
|
|
|
2
2
|
import { h as MethodOptions, l as ActorDefinition, p as HandlerOptions, v as ReflectionOptions } from "../hooks-CfQ8EhKW.js";
|
|
3
3
|
import { n as StringTransport, t as Channel } from "../channel-HCjngMq_.js";
|
|
4
4
|
import { FileHandle } from "node:fs/promises";
|
|
5
|
+
import { Readable, Writable } from "node:stream";
|
|
5
6
|
//#region src/remote/transports/fifo.d.ts
|
|
6
7
|
declare class FifoUtf8NlineTransport implements StringTransport {
|
|
7
8
|
private readFd;
|
|
@@ -14,21 +15,20 @@ declare class FifoUtf8NlineTransport implements StringTransport {
|
|
|
14
15
|
private closed;
|
|
15
16
|
private closingPromise;
|
|
16
17
|
private pvtError;
|
|
17
|
-
private pvtWriter;
|
|
18
18
|
private constructor();
|
|
19
|
-
private static openReader;
|
|
20
|
-
private static openWriter;
|
|
21
19
|
static openReaderFd(readFd: FileHandle): FifoUtf8NlineTransport;
|
|
22
20
|
static openWriterFd(writeFd: FileHandle): FifoUtf8NlineTransport;
|
|
23
21
|
static fromFds(readFd: FileHandle, writeFd: FileHandle): FifoUtf8NlineTransport;
|
|
24
|
-
private attachWriter;
|
|
25
22
|
/**
|
|
26
23
|
* Start opening a bidirectional connection. Opens readPath for reading
|
|
27
24
|
* in the background, returns a promise. The caller should do whatever
|
|
28
25
|
* setup is needed to unblock the read (e.g. spawn a process that opens
|
|
29
26
|
* readPath for writing), then await the returned transport.
|
|
30
27
|
*
|
|
31
|
-
*
|
|
28
|
+
* The transport itself is built only once both directions are open. A read
|
|
29
|
+
* stream created while no writer exists on its fifo is handed an immediate
|
|
30
|
+
* end-of-stream by some runtimes (bun), which closes a channel that has not
|
|
31
|
+
* carried a byte yet — so the writes side is opened before the reader exists.
|
|
32
32
|
*/
|
|
33
33
|
static beginConnect(readPath: string, writePath: string): {
|
|
34
34
|
transport: Promise<FifoUtf8NlineTransport>;
|
|
@@ -40,7 +40,8 @@ declare class FifoUtf8NlineTransport implements StringTransport {
|
|
|
40
40
|
*
|
|
41
41
|
* Use this when you are the side that responds to the peer's beginConnect
|
|
42
42
|
* (i.e. you don't need to interleave any setup between the read and write
|
|
43
|
-
* opens).
|
|
43
|
+
* opens). Built like `beginConnect`: the transport exists only after both
|
|
44
|
+
* directions do.
|
|
44
45
|
*/
|
|
45
46
|
static connect(readPath: string, writePath: string): Promise<FifoUtf8NlineTransport>;
|
|
46
47
|
get canSend(): boolean;
|
|
@@ -71,5 +72,202 @@ interface SubprocessActorBundle<Args, State, InMsg extends Message, OutMsg exten
|
|
|
71
72
|
}
|
|
72
73
|
declare function defineSubprocessActor<Args, State, InMsg extends Message, OutMsg extends Message, Methods extends MethodOptions, Handlers extends HandlerOptions<InMsg>, R extends ReflectionOptions>(actor: ActorDefinition<Args, State, InMsg, OutMsg, R>, url: string, opts?: SubprocessActorOptions): SubprocessActorBundle<Args, State, InMsg, OutMsg, R>;
|
|
73
74
|
//#endregion
|
|
74
|
-
|
|
75
|
+
//#region src/remote/stdio.d.ts
|
|
76
|
+
/** The stream pair a line transport talks over. */
|
|
77
|
+
interface LineStreams {
|
|
78
|
+
read: Readable;
|
|
79
|
+
write: Writable;
|
|
80
|
+
}
|
|
81
|
+
/** A stream of the far end's process: its stdout or its stderr. */
|
|
82
|
+
type OutputFd = 1 | 2;
|
|
83
|
+
/** Where the far end's own output goes. */
|
|
84
|
+
type OutputSink = (fd: OutputFd, data: string) => void;
|
|
85
|
+
/** How long to wait for the far end's first protocol frame. */
|
|
86
|
+
declare const HANDSHAKE_TIMEOUT_MS = 15000;
|
|
87
|
+
/** Raised when the wire itself fails: no frame, the wrong protocol, a peer that went away. */
|
|
88
|
+
declare class StdioWireError extends Error {
|
|
89
|
+
constructor(message: string);
|
|
90
|
+
}
|
|
91
|
+
/** One frame of the far end's own output. */
|
|
92
|
+
interface OutputFrame {
|
|
93
|
+
fd: OutputFd;
|
|
94
|
+
data: string;
|
|
95
|
+
}
|
|
96
|
+
declare function outputFrame(fd: OutputFd, data: string): string;
|
|
97
|
+
/** The output frame a line carries, or null when it carries something else. */
|
|
98
|
+
declare function parseOutputFrame(line: string): OutputFrame | null;
|
|
99
|
+
declare function errorFrame(reason: string): string;
|
|
100
|
+
/** The reason an `$error` frame carries, or null when it is not one. */
|
|
101
|
+
declare function errorReason(frame: Record<string, unknown>): string | null;
|
|
102
|
+
/**
|
|
103
|
+
* A `StringTransport` over a read/write stream pair, one JSON frame per line.
|
|
104
|
+
*
|
|
105
|
+
* `close()` is idempotent and never throws: teardown races are normal here —
|
|
106
|
+
* the peer exits, the read side ends, and a write arrives a moment later.
|
|
107
|
+
*/
|
|
108
|
+
declare class LineTransport implements StringTransport {
|
|
109
|
+
private readonly streams;
|
|
110
|
+
private pvtOnMessage;
|
|
111
|
+
private pvtOnClose;
|
|
112
|
+
private pvtClosed;
|
|
113
|
+
private pvtClosing;
|
|
114
|
+
constructor(streams: LineStreams);
|
|
115
|
+
get closed(): boolean;
|
|
116
|
+
get hasHandler(): boolean;
|
|
117
|
+
onMessage(handler: (line: string) => void): void;
|
|
118
|
+
removeHandler(): ((line: string) => void) | null;
|
|
119
|
+
onClose(handler: () => void): void;
|
|
120
|
+
send(frame: string): Promise<void>;
|
|
121
|
+
close(): Promise<void>;
|
|
122
|
+
private pvtClose;
|
|
123
|
+
}
|
|
124
|
+
/** Streams over two fds of *our own* process — the child side of an fd wire. */
|
|
125
|
+
declare function fdStreams(readFd: number, writeFd: number): LineStreams;
|
|
126
|
+
/**
|
|
127
|
+
* A transport that lifts output frames out of the stream: everything else is
|
|
128
|
+
* passed through untouched, so the protocol sees frames and only frames.
|
|
129
|
+
*/
|
|
130
|
+
declare class OutputFilter implements StringTransport {
|
|
131
|
+
private readonly inner;
|
|
132
|
+
private readonly sink;
|
|
133
|
+
constructor(inner: StringTransport, sink: OutputSink);
|
|
134
|
+
send(frame: string): void | Promise<void>;
|
|
135
|
+
onMessage(handler: (frame: string) => void): void;
|
|
136
|
+
removeHandler(): void;
|
|
137
|
+
onClose(handler: () => void): void;
|
|
138
|
+
close(): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
/** Write the far end's output to our stderr, tagged with its name. */
|
|
141
|
+
declare function stderrSink(name?: string): OutputSink;
|
|
142
|
+
interface ClientChannelOptions {
|
|
143
|
+
/** Where the far end's own output goes. Defaults to our stderr. */
|
|
144
|
+
onOutput?: OutputSink;
|
|
145
|
+
/** How long to wait for the peer's protocol frame. Defaults to {@link HANDSHAKE_TIMEOUT_MS}. */
|
|
146
|
+
timeoutMs?: number;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The client end of the wire: hand the streams to the seam (minus output
|
|
150
|
+
* frames) and wait for the peer's `$proto`. A first stage reports a failed
|
|
151
|
+
* start as an `$error` frame instead.
|
|
152
|
+
*/
|
|
153
|
+
declare function clientChannel(streams: LineStreams, opts?: ClientChannelOptions): Promise<Channel>;
|
|
154
|
+
/** The server end of the wire — a payload speaking stdio itself. */
|
|
155
|
+
declare function serverChannel(streams: LineStreams): Promise<Channel>;
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/remote/kit.d.ts
|
|
158
|
+
/** Runtimes a kit is happy to be run with, best first. A caller may override. */
|
|
159
|
+
declare const DEFAULT_RUNTIMES: string[];
|
|
160
|
+
/** Layout of a staged kit — the contract's own version, recorded in `version.json`. */
|
|
161
|
+
declare const KIT_LAYOUT = 1;
|
|
162
|
+
/** Where kits live on a host, relative to `$HOME`, unless the caller says otherwise. */
|
|
163
|
+
declare const DEFAULT_KIT_PARENT = "bin/posipaki";
|
|
164
|
+
interface KitFile {
|
|
165
|
+
/** Name inside the kit directory, e.g. `payload.js`. */
|
|
166
|
+
name: string;
|
|
167
|
+
/** The file's bytes, base64 — what the script writes. */
|
|
168
|
+
base64: string;
|
|
169
|
+
/** Byte count, for the cheap presence check. */
|
|
170
|
+
bytes: number;
|
|
171
|
+
/** sha256 of the file, recorded in `version.json`. */
|
|
172
|
+
sha256: string;
|
|
173
|
+
}
|
|
174
|
+
/** Who is staging this kit: the consumer's own name and build version. */
|
|
175
|
+
interface KitApp {
|
|
176
|
+
name: string;
|
|
177
|
+
version: string;
|
|
178
|
+
}
|
|
179
|
+
interface Kit {
|
|
180
|
+
/** Directory name: `<app>-<app version>-posipaki-<posipaki version>-<manifest8>`. */
|
|
181
|
+
name: string;
|
|
182
|
+
/** sha256 over the manifest of the kit's own files. */
|
|
183
|
+
manifestHash: string;
|
|
184
|
+
/** Everything the kit installs, `version.json` last. */
|
|
185
|
+
files: KitFile[];
|
|
186
|
+
/** Runtime candidates, in order; the first one found wins. */
|
|
187
|
+
runtimes: string[];
|
|
188
|
+
/** Directory the kit lands in, relative to the host's `$HOME`. */
|
|
189
|
+
parent: string;
|
|
190
|
+
/** The consumer this kit belongs to. */
|
|
191
|
+
app: KitApp;
|
|
192
|
+
}
|
|
193
|
+
interface MakeKitOptions {
|
|
194
|
+
/** Required: a kit with no owner cannot be told apart from another's. */
|
|
195
|
+
app: KitApp;
|
|
196
|
+
/** Defaults to {@link DEFAULT_RUNTIMES}. */
|
|
197
|
+
runtimes?: string[];
|
|
198
|
+
/** Defaults to {@link DEFAULT_KIT_PARENT}. */
|
|
199
|
+
parent?: string;
|
|
200
|
+
}
|
|
201
|
+
type BootstrapReport = {
|
|
202
|
+
kind: "ready";
|
|
203
|
+
kitDir: string;
|
|
204
|
+
runtime: string;
|
|
205
|
+
staged: boolean;
|
|
206
|
+
} | {
|
|
207
|
+
kind: "error";
|
|
208
|
+
reason: string;
|
|
209
|
+
};
|
|
210
|
+
/** The part an artifact plays when it names itself. */
|
|
211
|
+
type ArtifactRole = "gateway" | "payload";
|
|
212
|
+
/** Hex sha256 of some content. */
|
|
213
|
+
declare function sha256Hex(content: string | Uint8Array): string;
|
|
214
|
+
/** The directory name a kit with these contents, this owner and this posipaki gets. */
|
|
215
|
+
declare function kitName(app: KitApp, manifestHash: string): string;
|
|
216
|
+
/**
|
|
217
|
+
* One line naming a staged artifact: what it is, which posipaki it speaks, which
|
|
218
|
+
* kit layout it was built for. A client that staged it can judge compatibility
|
|
219
|
+
* from this alone — nothing else is claimed.
|
|
220
|
+
*/
|
|
221
|
+
declare function versionLine(app: KitApp, role: ArtifactRole, proto: string): string;
|
|
222
|
+
/**
|
|
223
|
+
* Describe a kit around its files. The manifest hash covers the files only, so
|
|
224
|
+
* `version.json` — which records that hash — can be one of them without chasing
|
|
225
|
+
* its own tail.
|
|
226
|
+
*/
|
|
227
|
+
declare function makeKit(files: {
|
|
228
|
+
name: string;
|
|
229
|
+
content: string | Uint8Array;
|
|
230
|
+
}[], options: MakeKitOptions): Kit;
|
|
231
|
+
/** What `version.json` says: identity, versions, and the exact content hashes. */
|
|
232
|
+
declare function kitVersion(kit: Kit): Record<string, unknown>;
|
|
233
|
+
/**
|
|
234
|
+
* The bootstrap, as one POSIX sh script.
|
|
235
|
+
*
|
|
236
|
+
* Staging is the whole job. The script always arrives on stdin, never in argv,
|
|
237
|
+
* and that is also why it cannot carry the wire: the caller runs the payload on a
|
|
238
|
+
* second channel into the same environment once this one has reported.
|
|
239
|
+
*/
|
|
240
|
+
declare function bootstrapScript(kit: Kit): string;
|
|
241
|
+
/** Read the script's report back. Missing or unknown lines are an error. */
|
|
242
|
+
declare function parseBootstrapReport(stdout: string): BootstrapReport;
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/remote/gateway.d.ts
|
|
245
|
+
/** Exit code for "this environment could not be set up". */
|
|
246
|
+
declare const GATEWAY_FAILED = 1;
|
|
247
|
+
/** Absolute path to this entry point, for whoever runs it as a script. */
|
|
248
|
+
declare const GATEWAY_SCRIPT: string;
|
|
249
|
+
interface GatewayBoot {
|
|
250
|
+
/** Who staged this payload. Names the artifact in its `--version` line. */
|
|
251
|
+
app: KitApp;
|
|
252
|
+
/** A label for logs and the tree name; the payload may have its own default. */
|
|
253
|
+
env: string;
|
|
254
|
+
/** Forwarded to the payload when given. */
|
|
255
|
+
poolSize?: number;
|
|
256
|
+
/** The payload to relay to. Required — the gateway has no opinion of its own. */
|
|
257
|
+
worker: string;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* The argv that boots this gateway — built by the client, read by
|
|
261
|
+
* {@link gatewayBoot}, so the two halves cannot drift apart.
|
|
262
|
+
*/
|
|
263
|
+
declare function gatewayArgs(boot: GatewayBoot): string[];
|
|
264
|
+
/** The boot a gateway was started with, or the reason it cannot start. */
|
|
265
|
+
declare function gatewayBoot(argv: string[]): GatewayBoot;
|
|
266
|
+
/**
|
|
267
|
+
* Serve one environment over stdin/stdout. Returns the exit code; the caller
|
|
268
|
+
* turns it into `process.exitCode`.
|
|
269
|
+
*/
|
|
270
|
+
declare function runGateway(argv?: string[]): Promise<number>;
|
|
271
|
+
//#endregion
|
|
272
|
+
export { type ArtifactRole, type BootstrapReport, type ClientChannelOptions, DEFAULT_KIT_PARENT, DEFAULT_RUNTIMES, FifoUtf8NlineTransport, GATEWAY_FAILED, GATEWAY_SCRIPT, type GatewayBoot, HANDSHAKE_TIMEOUT_MS, KIT_LAYOUT, type Kit, type KitApp, type KitFile, type LineStreams, LineTransport, type MakeKitOptions, type OutputFd, OutputFilter, type OutputFrame, type OutputSink, StdioWireError, type SubprocessActorBundle, type SubprocessActorOptions, bootstrapScript, clientChannel, commandSpawner, defineSubprocessActor, errorFrame, errorReason, fdStreams, fifoArgvSpawner, gatewayArgs, gatewayBoot, kitName, kitVersion, makeKit, outputFrame, parseBootstrapReport, parseOutputFrame, runGateway, serverChannel, sha256Hex, stderrSink, versionLine };
|
|
75
273
|
//# sourceMappingURL=node.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","names":[],"sources":["../../src/remote/transports/fifo.ts","../../src/remote/spawners/fifo-command.ts","../../src/remote/spawners/fifo-argv.ts","../../src/remote/define-subprocess.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"node.d.ts","names":[],"sources":["../../src/remote/transports/fifo.ts","../../src/remote/spawners/fifo-command.ts","../../src/remote/spawners/fifo-argv.ts","../../src/remote/define-subprocess.ts","../../src/remote/stdio.ts","../../src/remote/kit.ts","../../src/remote/gateway.ts"],"mappings":";;;;;;cAUa,kCAAkC;UACrC;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UAED;SAuCA,aAAa,QAAQ,aAAa;SAIlC,aAAa,SAAS,aAAa;SAInC,QAAQ,QAAQ,YAAY,SAAS,aAAa;;;;;;;;;;;;SAiBlD,aACL,kBACA;IACG,WAAW,QAAQ;;;;;;;;;;;;SAqBX,QAAQ,kBAAkB,oBAAoB,QAAQ;MAS/D;EAIJ,UAAU,UAAU;EASpB,mBAAmB;EAMnB,QAAQ;MAIJ;MAIA,aAAa;EAIX,KAAK,eAAe;EAOpB,SAAS;UAMD;;;;iBCpJA,eAAe,0BAA0B,QAAQ;;;iBCN3C,mBAAmB,QAAQ;;;UCOhC;EACf;;UAGe,sBACf,MACA,OACA,cAAc,SACd,eAAe,SACf,UAAU;EAEV,OAAO,gBAAgB,MAAM,OAAO,OAAO,QAAQ;EACnD,iBAAiB;EACjB;;iBASc,sBACd,MACA,OACA,cAAc,SACd,eAAe,SACf,gBAAgB,eAChB,iBAAiB,eAAe,QAChC,UAAU,mBAEV,OAAO,gBAAgB,MAAM,OAAO,OAAO,QAAQ,IACnD,aACA,OAAM,yBACL,sBAAsB,MAAM,OAAO,OAAO,QAAQ;;;;UCvBpC;EACf,MAAM;EACN,OAAO;;;KAIG;;KAGA,cAAc,IAAI,UAAU;;cAG3B;;cAOA,uBAAuB;EAClC,YAAY;;;UASG;EACf,IAAI;EACJ;;iBAGc,YAAY,IAAI,UAAU;;iBAK1B,iBAAiB,eAAe;iBAiBhC,WAAW;;iBAKX,YAAY,OAAO;;;;;;;cA2BtB,yBAAyB;mBACnB;UACT;UACA;UACA;UACA;EAER,YAAY,SAAS;MAoBjB;MAIA;EAIJ,UAAU,UAAU;EAQpB,mBAAmB;EAMnB,QAAQ;EAIR,KAAK,gBAAgB;EAuBf,SAAS;UAKD;;;iBAoBA,UAAU,gBAAgB,kBAAkB;;;;;cAU/C,wBAAwB;mBAClB;mBACA;EAEjB,YAAY,OAAO,iBAAiB,MAAM;EAK1C,KAAK,uBAAuB;EAI5B,UAAU,UAAU;EAQpB;EAIA,QAAQ;EAIR,SAAS;;;iBAQK,WAAW,gBAAY;UA2BtB;;EAEf,WAAW;;EAEX;;;;;;;iBAQoB,cACpB,SAAS,aACT,OAAM,uBACL,QAAQ;;iBAkBW,cAAc,SAAS,cAAc,QAAQ;;;;cC/StD;;cAGA;;cAGA;UAEI;;EAEf;;EAEA;;EAEA;;EAEA;;;UAIe;EACf;EACA;;UAGe;;EAEf;;EAEA;;EAEA,OAAO;;EAEP;;EAEA;;EAEA,KAAK;;UAGU;;EAEf,KAAK;;EAEL;;EAEA;;KAGU;EACN;EAAe;EAAgB;EAAiB;;EAChD;EAAe;;;KAGT;;iBAGI,UAAU,kBAAkB;;iBAK5B,QAAQ,KAAK,QAAQ;;;;;;iBASrB,YAAY,KAAK,QAAQ,MAAM,cAAc;;;;;;iBAgB7C,QACd;EAAS;EAAc,kBAAkB;KACzC,SAAS,iBACR;;iBAgCa,WAAW,KAAK,MAAM;;;;;;;;iBAiCtB,gBAAgB,KAAK;;iBAgCrB,qBAAqB,iBAAiB;;;;cCtKzC;;cAGA;UAEI;;EAEf,KAAK;;EAEL;;EAEA;;EAEA;;;;;;iBAsBc,YAAY,MAAM;;iBAWlB,YAAY,iBAAiB;;;;;iBAkDvB,WAAW,kBAAgC"}
|
package/dist/remote/node.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { t as LIB_VERSION } from "../version-W7guWJeR.js";
|
|
1
2
|
import { a as remoteClient, d as isProto, i as json1Channel, s as serveRemoteActor, t as VERSION } from "../json1-DzPx9oX3.js";
|
|
2
|
-
import { open, unlink } from "node:fs/promises";
|
|
3
|
-
import { createReadStream, createWriteStream } from "node:fs";
|
|
3
|
+
import { mkdtemp, open, rm, unlink } from "node:fs/promises";
|
|
4
4
|
import * as readline from "node:readline";
|
|
5
|
-
import { execSync, spawn } from "node:child_process";
|
|
5
|
+
import { execFile, execSync, spawn } from "node:child_process";
|
|
6
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
|
-
import { join } from "node:path";
|
|
8
|
+
import { join, resolve } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
11
|
+
import { promisify } from "node:util";
|
|
10
12
|
//#region src/remote/transports/fifo.ts
|
|
11
13
|
var FifoUtf8NlineTransport = class FifoUtf8NlineTransport {
|
|
12
14
|
constructor(opts) {
|
|
@@ -15,15 +17,10 @@ var FifoUtf8NlineTransport = class FifoUtf8NlineTransport {
|
|
|
15
17
|
this.closed = false;
|
|
16
18
|
this.closingPromise = null;
|
|
17
19
|
this.pvtError = null;
|
|
18
|
-
this.pvtWriter = null;
|
|
19
20
|
this.readFd = opts.readFd ?? null;
|
|
20
21
|
this.writeFd = opts.writeFd ?? null;
|
|
21
22
|
if (this.readFd) {
|
|
22
|
-
this.rs = createReadStream(""
|
|
23
|
-
fd: this.readFd.fd,
|
|
24
|
-
encoding: "utf-8",
|
|
25
|
-
autoClose: false
|
|
26
|
-
});
|
|
23
|
+
this.rs = this.readFd.createReadStream({ encoding: "utf-8" });
|
|
27
24
|
this.rl = readline.createInterface({ input: this.rs });
|
|
28
25
|
this.rl.on("line", (line) => {
|
|
29
26
|
if (this.pvtOnMessage && !this.closed) this.pvtOnMessage(line);
|
|
@@ -41,25 +38,13 @@ var FifoUtf8NlineTransport = class FifoUtf8NlineTransport {
|
|
|
41
38
|
this.rl = null;
|
|
42
39
|
}
|
|
43
40
|
if (this.writeFd) {
|
|
44
|
-
this.ws = createWriteStream(""
|
|
45
|
-
fd: this.writeFd.fd,
|
|
46
|
-
encoding: "utf-8",
|
|
47
|
-
autoClose: false
|
|
48
|
-
});
|
|
41
|
+
this.ws = this.writeFd.createWriteStream({ encoding: "utf-8" });
|
|
49
42
|
this.ws.on("error", (err) => {
|
|
50
43
|
this.pvtError = err;
|
|
51
44
|
this.close();
|
|
52
45
|
});
|
|
53
46
|
} else this.ws = null;
|
|
54
47
|
}
|
|
55
|
-
static async openReader(path) {
|
|
56
|
-
const readFd = await open(path, "r");
|
|
57
|
-
return new FifoUtf8NlineTransport({ readFd });
|
|
58
|
-
}
|
|
59
|
-
static async openWriter(path) {
|
|
60
|
-
const writeFd = await open(path, "w");
|
|
61
|
-
return new FifoUtf8NlineTransport({ writeFd });
|
|
62
|
-
}
|
|
63
48
|
static openReaderFd(readFd) {
|
|
64
49
|
return new FifoUtf8NlineTransport({ readFd });
|
|
65
50
|
}
|
|
@@ -72,22 +57,21 @@ var FifoUtf8NlineTransport = class FifoUtf8NlineTransport {
|
|
|
72
57
|
writeFd
|
|
73
58
|
});
|
|
74
59
|
}
|
|
75
|
-
attachWriter(w) {
|
|
76
|
-
this.pvtWriter = w;
|
|
77
|
-
}
|
|
78
60
|
/**
|
|
79
61
|
* Start opening a bidirectional connection. Opens readPath for reading
|
|
80
62
|
* in the background, returns a promise. The caller should do whatever
|
|
81
63
|
* setup is needed to unblock the read (e.g. spawn a process that opens
|
|
82
64
|
* readPath for writing), then await the returned transport.
|
|
83
65
|
*
|
|
84
|
-
*
|
|
66
|
+
* The transport itself is built only once both directions are open. A read
|
|
67
|
+
* stream created while no writer exists on its fifo is handed an immediate
|
|
68
|
+
* end-of-stream by some runtimes (bun), which closes a channel that has not
|
|
69
|
+
* carried a byte yet — so the writes side is opened before the reader exists.
|
|
85
70
|
*/
|
|
86
71
|
static beginConnect(readPath, writePath) {
|
|
87
|
-
return { transport:
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
return reader;
|
|
72
|
+
return { transport: open(readPath, "r").then(async (readFd) => {
|
|
73
|
+
const writeFd = await open(writePath, "w");
|
|
74
|
+
return FifoUtf8NlineTransport.fromFds(readFd, writeFd);
|
|
91
75
|
}) };
|
|
92
76
|
}
|
|
93
77
|
/**
|
|
@@ -97,17 +81,16 @@ var FifoUtf8NlineTransport = class FifoUtf8NlineTransport {
|
|
|
97
81
|
*
|
|
98
82
|
* Use this when you are the side that responds to the peer's beginConnect
|
|
99
83
|
* (i.e. you don't need to interleave any setup between the read and write
|
|
100
|
-
* opens).
|
|
84
|
+
* opens). Built like `beginConnect`: the transport exists only after both
|
|
85
|
+
* directions do.
|
|
101
86
|
*/
|
|
102
87
|
static async connect(readPath, writePath) {
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
return reader;
|
|
88
|
+
const readFdPromise = open(readPath, "r");
|
|
89
|
+
const writeFd = await open(writePath, "w");
|
|
90
|
+
const readFd = await readFdPromise;
|
|
91
|
+
return FifoUtf8NlineTransport.fromFds(readFd, writeFd);
|
|
108
92
|
}
|
|
109
93
|
get canSend() {
|
|
110
|
-
if (this.pvtWriter) return this.pvtWriter.canSend;
|
|
111
94
|
return this.writeFd !== null && !this.closed;
|
|
112
95
|
}
|
|
113
96
|
onMessage(handler) {
|
|
@@ -131,7 +114,6 @@ var FifoUtf8NlineTransport = class FifoUtf8NlineTransport {
|
|
|
131
114
|
return this.pvtError;
|
|
132
115
|
}
|
|
133
116
|
async send(line) {
|
|
134
|
-
if (this.pvtWriter) return this.pvtWriter.send(line);
|
|
135
117
|
if (this.closed) throw new Error("FifoUtf8NlineTransport: closed");
|
|
136
118
|
if (this.writeFd === null) throw new Error("FifoUtf8NlineTransport: not a writer");
|
|
137
119
|
if (!line.endsWith("\n")) line += "\n";
|
|
@@ -174,7 +156,6 @@ var FifoUtf8NlineTransport = class FifoUtf8NlineTransport {
|
|
|
174
156
|
} catch {}
|
|
175
157
|
this.writeFd = null;
|
|
176
158
|
}
|
|
177
|
-
if (this.pvtWriter) await this.pvtWriter.close();
|
|
178
159
|
}
|
|
179
160
|
};
|
|
180
161
|
//#endregion
|
|
@@ -255,6 +236,588 @@ function defineSubprocessActor(actor, url, opts = {}) {
|
|
|
255
236
|
};
|
|
256
237
|
}
|
|
257
238
|
//#endregion
|
|
258
|
-
|
|
239
|
+
//#region src/remote/stdio.ts
|
|
240
|
+
/** How long to wait for the far end's first protocol frame. */
|
|
241
|
+
const HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
242
|
+
const OUTPUT_KEY = "$fd";
|
|
243
|
+
const ERROR_KEY = "$error";
|
|
244
|
+
/** Raised when the wire itself fails: no frame, the wrong protocol, a peer that went away. */
|
|
245
|
+
var StdioWireError = class extends Error {
|
|
246
|
+
constructor(message) {
|
|
247
|
+
super(message);
|
|
248
|
+
this.name = "StdioWireError";
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
function outputFrame(fd, data) {
|
|
252
|
+
return JSON.stringify({
|
|
253
|
+
[OUTPUT_KEY]: fd,
|
|
254
|
+
data
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/** The output frame a line carries, or null when it carries something else. */
|
|
258
|
+
function parseOutputFrame(line) {
|
|
259
|
+
if (!line.startsWith("{\"$fd\"")) return null;
|
|
260
|
+
let parsed;
|
|
261
|
+
try {
|
|
262
|
+
parsed = JSON.parse(line);
|
|
263
|
+
} catch {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
267
|
+
const frame = parsed;
|
|
268
|
+
const fd = frame[OUTPUT_KEY];
|
|
269
|
+
const data = frame.data;
|
|
270
|
+
if (fd !== 1 && fd !== 2) return null;
|
|
271
|
+
return {
|
|
272
|
+
fd,
|
|
273
|
+
data: typeof data === "string" ? data : JSON.stringify(data)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function errorFrame(reason) {
|
|
277
|
+
return JSON.stringify({ [ERROR_KEY]: reason });
|
|
278
|
+
}
|
|
279
|
+
/** The reason an `$error` frame carries, or null when it is not one. */
|
|
280
|
+
function errorReason(frame) {
|
|
281
|
+
const reason = frame[ERROR_KEY];
|
|
282
|
+
return typeof reason === "string" ? reason : null;
|
|
283
|
+
}
|
|
284
|
+
/** Is this write error just a peer that already left? */
|
|
285
|
+
function peerGone(code) {
|
|
286
|
+
return code === "EPIPE" || code === "ERR_STREAM_DESTROYED" || code === "ERR_STREAM_WRITE_AFTER_END" || code === "ERR_STREAM_ALREADY_FINISHED";
|
|
287
|
+
}
|
|
288
|
+
function errorText$1(err) {
|
|
289
|
+
return err instanceof Error ? err.message : String(err);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* A `StringTransport` over a read/write stream pair, one JSON frame per line.
|
|
293
|
+
*
|
|
294
|
+
* `close()` is idempotent and never throws: teardown races are normal here —
|
|
295
|
+
* the peer exits, the read side ends, and a write arrives a moment later.
|
|
296
|
+
*/
|
|
297
|
+
var LineTransport = class {
|
|
298
|
+
constructor(streams) {
|
|
299
|
+
this.pvtOnMessage = null;
|
|
300
|
+
this.pvtOnClose = null;
|
|
301
|
+
this.pvtClosed = false;
|
|
302
|
+
this.pvtClosing = null;
|
|
303
|
+
this.streams = streams;
|
|
304
|
+
const lines = readline.createInterface({ input: streams.read });
|
|
305
|
+
lines.on("line", (line) => {
|
|
306
|
+
if (this.pvtOnMessage && !this.pvtClosed) this.pvtOnMessage(line);
|
|
307
|
+
});
|
|
308
|
+
lines.on("close", () => {
|
|
309
|
+
this.pvtOnClose?.();
|
|
310
|
+
this.close();
|
|
311
|
+
});
|
|
312
|
+
streams.read.on("error", () => {
|
|
313
|
+
this.close();
|
|
314
|
+
});
|
|
315
|
+
streams.write.on("error", () => {
|
|
316
|
+
this.close();
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
get closed() {
|
|
320
|
+
return this.pvtClosed;
|
|
321
|
+
}
|
|
322
|
+
get hasHandler() {
|
|
323
|
+
return this.pvtOnMessage !== null;
|
|
324
|
+
}
|
|
325
|
+
onMessage(handler) {
|
|
326
|
+
if (this.pvtClosed) throw new StdioWireError("stdio transport: closed");
|
|
327
|
+
if (this.pvtOnMessage !== null) throw new StdioWireError("stdio transport: handler already set — call removeHandler()");
|
|
328
|
+
this.pvtOnMessage = handler;
|
|
329
|
+
}
|
|
330
|
+
removeHandler() {
|
|
331
|
+
const previous = this.pvtOnMessage;
|
|
332
|
+
this.pvtOnMessage = null;
|
|
333
|
+
return previous;
|
|
334
|
+
}
|
|
335
|
+
onClose(handler) {
|
|
336
|
+
this.pvtOnClose = handler;
|
|
337
|
+
}
|
|
338
|
+
send(frame) {
|
|
339
|
+
if (this.pvtClosed) return Promise.resolve();
|
|
340
|
+
const line = frame.endsWith("\n") ? frame : `${frame}\n`;
|
|
341
|
+
return new Promise((resolve, reject) => {
|
|
342
|
+
try {
|
|
343
|
+
this.streams.write.write(line, (err) => {
|
|
344
|
+
if (!err) return resolve();
|
|
345
|
+
const code = err.code;
|
|
346
|
+
if (peerGone(code)) return resolve();
|
|
347
|
+
reject(new StdioWireError(`stdio transport: ${err.message}`));
|
|
348
|
+
});
|
|
349
|
+
} catch (err) {
|
|
350
|
+
reject(new StdioWireError(`stdio transport: ${errorText$1(err)}`));
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
async close() {
|
|
355
|
+
this.pvtClosing ??= this.pvtClose();
|
|
356
|
+
await this.pvtClosing;
|
|
357
|
+
}
|
|
358
|
+
async pvtClose() {
|
|
359
|
+
await Promise.resolve();
|
|
360
|
+
this.pvtClosed = true;
|
|
361
|
+
try {
|
|
362
|
+
this.streams.read.destroy?.();
|
|
363
|
+
} catch {}
|
|
364
|
+
try {
|
|
365
|
+
if (!this.streams.write.writableEnded) this.streams.write.end();
|
|
366
|
+
} catch {}
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
/** Streams over two fds of *our own* process — the child side of an fd wire. */
|
|
370
|
+
function fdStreams(readFd, writeFd) {
|
|
371
|
+
return {
|
|
372
|
+
read: createReadStream("", {
|
|
373
|
+
fd: readFd,
|
|
374
|
+
encoding: "utf-8",
|
|
375
|
+
autoClose: false
|
|
376
|
+
}),
|
|
377
|
+
write: createWriteStream("", {
|
|
378
|
+
fd: writeFd,
|
|
379
|
+
encoding: "utf-8",
|
|
380
|
+
autoClose: false
|
|
381
|
+
})
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* A transport that lifts output frames out of the stream: everything else is
|
|
386
|
+
* passed through untouched, so the protocol sees frames and only frames.
|
|
387
|
+
*/
|
|
388
|
+
var OutputFilter = class {
|
|
389
|
+
constructor(inner, sink) {
|
|
390
|
+
this.inner = inner;
|
|
391
|
+
this.sink = sink;
|
|
392
|
+
}
|
|
393
|
+
send(frame) {
|
|
394
|
+
return this.inner.send(frame);
|
|
395
|
+
}
|
|
396
|
+
onMessage(handler) {
|
|
397
|
+
this.inner.onMessage((line) => {
|
|
398
|
+
const output = parseOutputFrame(line);
|
|
399
|
+
if (output) this.sink(output.fd, output.data);
|
|
400
|
+
else handler(line);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
removeHandler() {
|
|
404
|
+
this.inner.removeHandler();
|
|
405
|
+
}
|
|
406
|
+
onClose(handler) {
|
|
407
|
+
this.inner.onClose(handler);
|
|
408
|
+
}
|
|
409
|
+
close() {
|
|
410
|
+
return this.inner.close();
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
/** Write the far end's output to our stderr, tagged with its name. */
|
|
414
|
+
function stderrSink(name = "") {
|
|
415
|
+
const tag = name ? `[${name}] ` : "";
|
|
416
|
+
return (fd, data) => {
|
|
417
|
+
const lead = fd === 1 ? tag : `${tag}err: `;
|
|
418
|
+
for (const line of data.split("\n")) if (line.length > 0) process.stderr.write(`${lead}${line}\n`);
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
/** The next frame that arrives, or a timeout. */
|
|
422
|
+
async function nextFrame(channel, timeoutMs) {
|
|
423
|
+
return await new Promise((resolve, reject) => {
|
|
424
|
+
const timer = setTimeout(() => reject(new StdioWireError(`no protocol frame within ${timeoutMs}ms`)), timeoutMs);
|
|
425
|
+
timer.unref?.();
|
|
426
|
+
channel.onMessage((frame) => {
|
|
427
|
+
clearTimeout(timer);
|
|
428
|
+
resolve(frame);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* The client end of the wire: hand the streams to the seam (minus output
|
|
434
|
+
* frames) and wait for the peer's `$proto`. A first stage reports a failed
|
|
435
|
+
* start as an `$error` frame instead.
|
|
436
|
+
*/
|
|
437
|
+
async function clientChannel(streams, opts = {}) {
|
|
438
|
+
const transport = new LineTransport(streams);
|
|
439
|
+
const channel = json1Channel(new OutputFilter(transport, opts.onOutput ?? stderrSink()));
|
|
440
|
+
const frame = await nextFrame(channel, opts.timeoutMs ?? 15e3);
|
|
441
|
+
channel.removeHandler();
|
|
442
|
+
const reason = errorReason(frame);
|
|
443
|
+
if (reason) throw new StdioWireError(reason);
|
|
444
|
+
if (!isProto(frame)) throw new StdioWireError(`unexpected first frame: ${JSON.stringify(frame).slice(0, 120)}`);
|
|
445
|
+
if (frame.$proto !== "json.v1") throw new StdioWireError(`unsupported protocol ${String(frame.$proto)}, expected ${VERSION}`);
|
|
446
|
+
return channel;
|
|
447
|
+
}
|
|
448
|
+
/** The server end of the wire — a payload speaking stdio itself. */
|
|
449
|
+
async function serverChannel(streams) {
|
|
450
|
+
const channel = json1Channel(new LineTransport(streams));
|
|
451
|
+
await channel.send({ $proto: VERSION });
|
|
452
|
+
return channel;
|
|
453
|
+
}
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region src/remote/kit.ts
|
|
456
|
+
/** Runtimes a kit is happy to be run with, best first. A caller may override. */
|
|
457
|
+
const DEFAULT_RUNTIMES = [
|
|
458
|
+
"node",
|
|
459
|
+
"nodejs",
|
|
460
|
+
"bun"
|
|
461
|
+
];
|
|
462
|
+
/** Layout of a staged kit — the contract's own version, recorded in `version.json`. */
|
|
463
|
+
const KIT_LAYOUT = 1;
|
|
464
|
+
/** Where kits live on a host, relative to `$HOME`, unless the caller says otherwise. */
|
|
465
|
+
const DEFAULT_KIT_PARENT = "bin/posipaki";
|
|
466
|
+
/** Hex sha256 of some content. */
|
|
467
|
+
function sha256Hex(content) {
|
|
468
|
+
return createHash("sha256").update(content).digest("hex");
|
|
469
|
+
}
|
|
470
|
+
/** The directory name a kit with these contents, this owner and this posipaki gets. */
|
|
471
|
+
function kitName(app, manifestHash) {
|
|
472
|
+
return `${app.name}-${app.version}-posipaki-${LIB_VERSION}-${manifestHash.slice(0, 8)}`;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* One line naming a staged artifact: what it is, which posipaki it speaks, which
|
|
476
|
+
* kit layout it was built for. A client that staged it can judge compatibility
|
|
477
|
+
* from this alone — nothing else is claimed.
|
|
478
|
+
*/
|
|
479
|
+
function versionLine(app, role, proto) {
|
|
480
|
+
return `${app.name}-${role} ${app.version} posipaki ${LIB_VERSION} proto ${proto} layout 1`;
|
|
481
|
+
}
|
|
482
|
+
/** Wrap base64 at a comfortable width; `base64 -d` ignores the newlines. */
|
|
483
|
+
function wrapped(base64, width = 76) {
|
|
484
|
+
const lines = [];
|
|
485
|
+
for (let at = 0; at < base64.length; at += width) lines.push(base64.slice(at, at + width));
|
|
486
|
+
return lines.join("\n");
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Describe a kit around its files. The manifest hash covers the files only, so
|
|
490
|
+
* `version.json` — which records that hash — can be one of them without chasing
|
|
491
|
+
* its own tail.
|
|
492
|
+
*/
|
|
493
|
+
function makeKit(files, options) {
|
|
494
|
+
const raw = files.map((file) => ({
|
|
495
|
+
name: file.name,
|
|
496
|
+
bytes: Buffer.byteLength(file.content),
|
|
497
|
+
sha256: sha256Hex(file.content),
|
|
498
|
+
base64: Buffer.from(file.content).toString("base64")
|
|
499
|
+
}));
|
|
500
|
+
const manifestHash = sha256Hex(raw.map((f) => `${f.name} ${f.sha256} ${f.bytes}`).join("\n"));
|
|
501
|
+
const kit = {
|
|
502
|
+
name: kitName(options.app, manifestHash),
|
|
503
|
+
manifestHash,
|
|
504
|
+
files: raw,
|
|
505
|
+
runtimes: [...options.runtimes ?? DEFAULT_RUNTIMES],
|
|
506
|
+
parent: options.parent ?? "bin/posipaki",
|
|
507
|
+
app: options.app
|
|
508
|
+
};
|
|
509
|
+
const version = Buffer.from(`${JSON.stringify(kitVersion(kit), null, 2)}\n`);
|
|
510
|
+
return {
|
|
511
|
+
...kit,
|
|
512
|
+
files: [...kit.files, {
|
|
513
|
+
name: "version.json",
|
|
514
|
+
bytes: version.byteLength,
|
|
515
|
+
sha256: sha256Hex(version),
|
|
516
|
+
base64: version.toString("base64")
|
|
517
|
+
}]
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
/** What `version.json` says: identity, versions, and the exact content hashes. */
|
|
521
|
+
function kitVersion(kit) {
|
|
522
|
+
return {
|
|
523
|
+
kit: kit.name,
|
|
524
|
+
app: kit.app,
|
|
525
|
+
posipaki: LIB_VERSION,
|
|
526
|
+
layout: 1,
|
|
527
|
+
manifestHash: kit.manifestHash,
|
|
528
|
+
files: kit.files.map((file) => ({
|
|
529
|
+
name: file.name,
|
|
530
|
+
sha256: file.sha256,
|
|
531
|
+
bytes: file.bytes
|
|
532
|
+
}))
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
/** The `[ -f … ] && [ "$(wc -c < …)" -eq … ]` test for one file. */
|
|
536
|
+
function presentTest(file) {
|
|
537
|
+
return `[ -f "$kit_dir/${file.name}" ] && [ "$(wc -c < "$kit_dir/${file.name}")" -eq ${file.bytes} ]`;
|
|
538
|
+
}
|
|
539
|
+
/** One file: write it to a temp name, then move it into place. */
|
|
540
|
+
function writeFile(file) {
|
|
541
|
+
return [
|
|
542
|
+
` base64 -d > "$tmp" <<'KIT_FILE'`,
|
|
543
|
+
wrapped(file.base64),
|
|
544
|
+
"KIT_FILE",
|
|
545
|
+
` mv "$tmp" "$kit_dir/${file.name}" || { echo "error cannot install ${file.name}"; exit 73; }`
|
|
546
|
+
].join("\n");
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* The bootstrap, as one POSIX sh script.
|
|
550
|
+
*
|
|
551
|
+
* Staging is the whole job. The script always arrives on stdin, never in argv,
|
|
552
|
+
* and that is also why it cannot carry the wire: the caller runs the payload on a
|
|
553
|
+
* second channel into the same environment once this one has reported.
|
|
554
|
+
*/
|
|
555
|
+
function bootstrapScript(kit) {
|
|
556
|
+
const checks = kit.files.map(presentTest).join(" && \\\n ");
|
|
557
|
+
return `${[
|
|
558
|
+
"# ── kit bootstrap ─────────────────────────────────────────────────────────",
|
|
559
|
+
"# probe, stage what is missing, report every step as one machine-readable line.",
|
|
560
|
+
"set -u",
|
|
561
|
+
"",
|
|
562
|
+
"if [ -z \"${HOME:-}\" ]; then echo \"error HOME is not set\"; exit 74; fi",
|
|
563
|
+
`kit_dir="$HOME/${kit.parent}/${kit.name}"`,
|
|
564
|
+
"",
|
|
565
|
+
"runtime=\"\"",
|
|
566
|
+
`for candidate in ${kit.runtimes.join(" ")}; do`,
|
|
567
|
+
" if command -v \"$candidate\" >/dev/null 2>&1; then runtime=\"$(command -v \"$candidate\")\"; break; fi",
|
|
568
|
+
"done",
|
|
569
|
+
`if [ -z "$runtime" ]; then echo "error no runtime among: ${kit.runtimes.join(" ")}"; exit 75; fi`,
|
|
570
|
+
"if ! command -v base64 >/dev/null 2>&1; then echo \"error base64 is missing\"; exit 76; fi",
|
|
571
|
+
"",
|
|
572
|
+
`if ${checks}; then`,
|
|
573
|
+
" echo \"present\"",
|
|
574
|
+
"else",
|
|
575
|
+
` mkdir -p "$kit_dir" || { echo "error cannot create $kit_dir"; exit 73; }`,
|
|
576
|
+
" tmp=\"$kit_dir/.staging.$$\"",
|
|
577
|
+
...kit.files.map(writeFile),
|
|
578
|
+
" echo \"staged\"",
|
|
579
|
+
"fi",
|
|
580
|
+
"echo \"kit $kit_dir\"",
|
|
581
|
+
"echo \"runtime $runtime\""
|
|
582
|
+
].join("\n")}\n`;
|
|
583
|
+
}
|
|
584
|
+
/** Read the script's report back. Missing or unknown lines are an error. */
|
|
585
|
+
function parseBootstrapReport(stdout) {
|
|
586
|
+
let kitDir = "";
|
|
587
|
+
let runtime = "";
|
|
588
|
+
let staged = null;
|
|
589
|
+
for (const raw of stdout.split("\n")) {
|
|
590
|
+
const line = raw.trim();
|
|
591
|
+
if (line === "") continue;
|
|
592
|
+
if (line.startsWith("error ")) return {
|
|
593
|
+
kind: "error",
|
|
594
|
+
reason: line.slice(6)
|
|
595
|
+
};
|
|
596
|
+
if (line === "staged") {
|
|
597
|
+
staged = true;
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
if (line === "present") {
|
|
601
|
+
staged = false;
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
if (line.startsWith("kit ")) {
|
|
605
|
+
kitDir = line.slice(4);
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
if (line.startsWith("runtime ")) {
|
|
609
|
+
runtime = line.slice(8);
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (kitDir === "" || runtime === "" || staged === null) {
|
|
614
|
+
const seen = stdout.trim();
|
|
615
|
+
return {
|
|
616
|
+
kind: "error",
|
|
617
|
+
reason: seen === "" ? "no report at all" : `incomplete report: ${seen}`
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
kind: "ready",
|
|
622
|
+
kitDir,
|
|
623
|
+
runtime,
|
|
624
|
+
staged
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
//#endregion
|
|
628
|
+
//#region src/remote/gateway.ts
|
|
629
|
+
const run = promisify(execFile);
|
|
630
|
+
/** Exit code for "this environment could not be set up". */
|
|
631
|
+
const GATEWAY_FAILED = 1;
|
|
632
|
+
/** Absolute path to this entry point, for whoever runs it as a script. */
|
|
633
|
+
const GATEWAY_SCRIPT = fileURLToPath(import.meta.url);
|
|
634
|
+
function errorText(err) {
|
|
635
|
+
return err instanceof Error ? err.message : String(err);
|
|
636
|
+
}
|
|
637
|
+
function flagValue(argv, name) {
|
|
638
|
+
const prefix = `--${name}=`;
|
|
639
|
+
return argv.find((a) => a.startsWith(prefix))?.slice(prefix.length);
|
|
640
|
+
}
|
|
641
|
+
/** A `--flag=<positive integer>` argument, or undefined. */
|
|
642
|
+
function positiveInt(value) {
|
|
643
|
+
const n = Number(value);
|
|
644
|
+
return value !== void 0 && Number.isFinite(n) && n >= 1 ? Math.floor(n) : void 0;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* The argv that boots this gateway — built by the client, read by
|
|
648
|
+
* {@link gatewayBoot}, so the two halves cannot drift apart.
|
|
649
|
+
*/
|
|
650
|
+
function gatewayArgs(boot) {
|
|
651
|
+
return [
|
|
652
|
+
`--worker=${boot.worker}`,
|
|
653
|
+
`--env=${boot.env}`,
|
|
654
|
+
`--app-name=${boot.app.name}`,
|
|
655
|
+
`--app-version=${boot.app.version}`,
|
|
656
|
+
...boot.poolSize === void 0 ? [] : [`--pool-size=${boot.poolSize}`]
|
|
657
|
+
];
|
|
658
|
+
}
|
|
659
|
+
/** The boot a gateway was started with, or the reason it cannot start. */
|
|
660
|
+
function gatewayBoot(argv) {
|
|
661
|
+
const worker = flagValue(argv, "worker");
|
|
662
|
+
if (!worker) throw new Error("no --worker=<script>: the gateway needs a payload to relay to");
|
|
663
|
+
const name = flagValue(argv, "app-name");
|
|
664
|
+
const version = flagValue(argv, "app-version");
|
|
665
|
+
if (!name || !version) throw new Error("no --app-name=<name> and --app-version=<version>: the gateway names its artifact");
|
|
666
|
+
return {
|
|
667
|
+
app: {
|
|
668
|
+
name,
|
|
669
|
+
version
|
|
670
|
+
},
|
|
671
|
+
env: flagValue(argv, "env") ?? "unnamed",
|
|
672
|
+
poolSize: positiveInt(flagValue(argv, "pool-size")),
|
|
673
|
+
worker
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* End a start that failed. The reason has already gone out on the wire, and
|
|
678
|
+
* returning is not enough: the `open()` on a fifo no payload will ever write sits
|
|
679
|
+
* in the threadpool forever, so the loop never drains and the gateway would linger
|
|
680
|
+
* with a dead payload on its books.
|
|
681
|
+
*/
|
|
682
|
+
function abandon() {
|
|
683
|
+
process.exit(1);
|
|
684
|
+
}
|
|
685
|
+
/** Carry a failed start to the client, then let the caller exit non-zero. */
|
|
686
|
+
async function fail(wire, reason) {
|
|
687
|
+
process.stderr.write(`gateway: ${reason}\n`);
|
|
688
|
+
try {
|
|
689
|
+
await wire.send(errorFrame(reason));
|
|
690
|
+
} catch {}
|
|
691
|
+
}
|
|
692
|
+
/** Copy everything the payload prints onto the wire as output frames. */
|
|
693
|
+
function forwardOutput(stream, wire, fd) {
|
|
694
|
+
stream.setEncoding("utf-8");
|
|
695
|
+
stream.on("data", (chunk) => {
|
|
696
|
+
wire.send(outputFrame(fd, chunk)).catch(() => {});
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Serve one environment over stdin/stdout. Returns the exit code; the caller
|
|
701
|
+
* turns it into `process.exitCode`.
|
|
702
|
+
*/
|
|
703
|
+
async function runGateway(argv = process.argv) {
|
|
704
|
+
if (argv.includes("--version")) {
|
|
705
|
+
const name = flagValue(argv, "app-name") ?? "posipaki";
|
|
706
|
+
const version = flagValue(argv, "app-version") ?? "-";
|
|
707
|
+
process.stdout.write(`${versionLine({
|
|
708
|
+
name,
|
|
709
|
+
version
|
|
710
|
+
}, "gateway", VERSION)}\n`);
|
|
711
|
+
return 0;
|
|
712
|
+
}
|
|
713
|
+
const wire = new LineTransport({
|
|
714
|
+
read: process.stdin,
|
|
715
|
+
write: process.stdout
|
|
716
|
+
});
|
|
717
|
+
let booted;
|
|
718
|
+
try {
|
|
719
|
+
booted = gatewayBoot(argv);
|
|
720
|
+
} catch (err) {
|
|
721
|
+
await fail(wire, errorText(err));
|
|
722
|
+
abandon();
|
|
723
|
+
}
|
|
724
|
+
const { env, poolSize, worker } = booted;
|
|
725
|
+
const dir = await mkdtemp(join(tmpdir(), `posipaki-${env.replace(/\//g, "-")}-`));
|
|
726
|
+
const fifoIn = join(dir, "in");
|
|
727
|
+
const fifoOut = join(dir, "out");
|
|
728
|
+
let child = null;
|
|
729
|
+
let fifo = null;
|
|
730
|
+
/**
|
|
731
|
+
* Everything this gateway owns, undone. All of it is idempotent, because the
|
|
732
|
+
* exit path and the signal path can both arrive.
|
|
733
|
+
*/
|
|
734
|
+
const teardown = async () => {
|
|
735
|
+
child?.kill();
|
|
736
|
+
await fifo?.close().catch(() => {});
|
|
737
|
+
await rm(dir, {
|
|
738
|
+
recursive: true,
|
|
739
|
+
force: true
|
|
740
|
+
}).catch(() => {});
|
|
741
|
+
};
|
|
742
|
+
/**
|
|
743
|
+
* A client that gives up stops us with a signal, and a gateway that died on the
|
|
744
|
+
* spot would leave its payload behind in the environment — a process holding a
|
|
745
|
+
* fifo nobody will ever write, which is a leak the environment cannot clean up
|
|
746
|
+
* for us. So the signals do what the exit path does.
|
|
747
|
+
*/
|
|
748
|
+
const onTerminate = () => {
|
|
749
|
+
teardown().catch(() => {}).then(() => process.exit(1));
|
|
750
|
+
};
|
|
751
|
+
for (const signal of [
|
|
752
|
+
"SIGTERM",
|
|
753
|
+
"SIGINT",
|
|
754
|
+
"SIGHUP"
|
|
755
|
+
]) process.on(signal, onTerminate);
|
|
756
|
+
try {
|
|
757
|
+
await run("mkfifo", [
|
|
758
|
+
"-m",
|
|
759
|
+
"600",
|
|
760
|
+
fifoIn,
|
|
761
|
+
fifoOut
|
|
762
|
+
]);
|
|
763
|
+
} catch (err) {
|
|
764
|
+
await fail(wire, `cannot create the environment's fifos: ${errorText(err)}`);
|
|
765
|
+
await teardown();
|
|
766
|
+
abandon();
|
|
767
|
+
}
|
|
768
|
+
const connection = FifoUtf8NlineTransport.beginConnect(fifoIn, fifoOut);
|
|
769
|
+
const workerArgs = [
|
|
770
|
+
worker,
|
|
771
|
+
`--env=${env}`,
|
|
772
|
+
...poolSize === void 0 ? [] : [`--pool-size=${poolSize}`],
|
|
773
|
+
`--fifo-in=${fifoIn}`,
|
|
774
|
+
`--fifo-out=${fifoOut}`
|
|
775
|
+
];
|
|
776
|
+
const workerProc = spawn(process.execPath, workerArgs, { stdio: [
|
|
777
|
+
"ignore",
|
|
778
|
+
"pipe",
|
|
779
|
+
"pipe"
|
|
780
|
+
] });
|
|
781
|
+
child = workerProc;
|
|
782
|
+
forwardOutput(workerProc.stdout, wire, 1);
|
|
783
|
+
forwardOutput(workerProc.stderr, wire, 2);
|
|
784
|
+
const exited = new Promise((settle) => {
|
|
785
|
+
workerProc.once("exit", (code) => settle(code ?? 0));
|
|
786
|
+
});
|
|
787
|
+
try {
|
|
788
|
+
fifo = await Promise.race([connection.transport, exited.then((code) => {
|
|
789
|
+
throw new Error(`the payload exited with code ${code} before it opened its channel`);
|
|
790
|
+
})]);
|
|
791
|
+
} catch (err) {
|
|
792
|
+
await fail(wire, errorText(err));
|
|
793
|
+
await teardown();
|
|
794
|
+
abandon();
|
|
795
|
+
}
|
|
796
|
+
const relay = fifo;
|
|
797
|
+
relay.onMessage((line) => {
|
|
798
|
+
wire.send(line).catch(() => {});
|
|
799
|
+
});
|
|
800
|
+
wire.onMessage((line) => {
|
|
801
|
+
relay.send(line).catch(() => {});
|
|
802
|
+
});
|
|
803
|
+
wire.onClose(() => {
|
|
804
|
+
child?.kill();
|
|
805
|
+
});
|
|
806
|
+
const code = await exited;
|
|
807
|
+
await teardown();
|
|
808
|
+
await wire.close();
|
|
809
|
+
return code;
|
|
810
|
+
}
|
|
811
|
+
const entry = process.argv[1];
|
|
812
|
+
if (entry && resolve(entry) === fileURLToPath(import.meta.url)) (async () => {
|
|
813
|
+
try {
|
|
814
|
+
process.exitCode = await runGateway();
|
|
815
|
+
} catch (err) {
|
|
816
|
+
process.stderr.write(`gateway: ${errorText(err)}\n`);
|
|
817
|
+
process.exitCode = 1;
|
|
818
|
+
}
|
|
819
|
+
})();
|
|
820
|
+
//#endregion
|
|
821
|
+
export { DEFAULT_KIT_PARENT, DEFAULT_RUNTIMES, FifoUtf8NlineTransport, GATEWAY_FAILED, GATEWAY_SCRIPT, HANDSHAKE_TIMEOUT_MS, KIT_LAYOUT, LineTransport, OutputFilter, StdioWireError, bootstrapScript, clientChannel, commandSpawner, defineSubprocessActor, errorFrame, errorReason, fdStreams, fifoArgvSpawner, gatewayArgs, gatewayBoot, kitName, kitVersion, makeKit, outputFrame, parseBootstrapReport, parseOutputFrame, runGateway, serverChannel, sha256Hex, stderrSink, versionLine };
|
|
259
822
|
|
|
260
823
|
//# sourceMappingURL=node.js.map
|
package/dist/remote/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.js","names":[],"sources":["../../src/remote/transports/fifo.ts","../../src/remote/spawners/fifo-command.ts","../../src/remote/spawners/fifo-argv.ts","../../src/remote/define-subprocess.ts"],"sourcesContent":["// ── FIFO transport ─────────────────────────────────────────────────────────\n//\n// Newline-delimited UTF-8 over named fifos. Strict handler lifecycle:\n// onMessage() throws if a handler is already set; call removeHandler() first.\n\nimport { open, type FileHandle } from \"node:fs/promises\";\nimport { createReadStream, createWriteStream, type ReadStream, type WriteStream } from \"node:fs\";\nimport * as readline from \"node:readline\";\nimport type { StringTransport } from \"../channel.js\";\n\nexport class FifoUtf8NlineTransport implements StringTransport {\n private readFd: FileHandle | null;\n private writeFd: FileHandle | null;\n private rl: readline.Interface | null;\n private rs: ReadStream | null;\n private ws: WriteStream | null;\n private pvtOnMessage: ((line: string) => void) | null = null;\n private pvtOnClose: (() => void) | null = null;\n private closed = false;\n private closingPromise: Promise<void> | null = null;\n private pvtError: Error | null = null;\n private pvtWriter: FifoUtf8NlineTransport | null = null;\n\n private constructor(opts: { readFd?: FileHandle; writeFd?: FileHandle }) {\n this.readFd = opts.readFd ?? null;\n this.writeFd = opts.writeFd ?? null;\n\n if (this.readFd) {\n this.rs = createReadStream(\"\", {\n fd: this.readFd.fd,\n encoding: \"utf-8\",\n autoClose: false,\n });\n this.rl = readline.createInterface({ input: this.rs });\n\n this.rl.on(\"line\", (line) => {\n if (this.pvtOnMessage && !this.closed) this.pvtOnMessage(line);\n });\n\n this.rl.on(\"close\", () => {\n this.pvtOnClose?.();\n this.close();\n });\n\n this.rs.on(\"error\", (err: Error) => {\n this.pvtError = err;\n this.close();\n });\n } else {\n this.rs = null;\n this.rl = null;\n }\n\n if (this.writeFd) {\n this.ws = createWriteStream(\"\", {\n fd: this.writeFd.fd,\n encoding: \"utf-8\",\n autoClose: false,\n });\n this.ws.on(\"error\", (err: Error) => {\n this.pvtError = err;\n this.close();\n });\n } else {\n this.ws = null;\n }\n }\n\n // ── factories ──────────────────────────────────────────────────────────\n\n private static async openReader(path: string): Promise<FifoUtf8NlineTransport> {\n const readFd = await open(path, \"r\");\n return new FifoUtf8NlineTransport({ readFd });\n }\n\n private static async openWriter(path: string): Promise<FifoUtf8NlineTransport> {\n const writeFd = await open(path, \"w\");\n return new FifoUtf8NlineTransport({ writeFd });\n }\n\n static openReaderFd(readFd: FileHandle): FifoUtf8NlineTransport {\n return new FifoUtf8NlineTransport({ readFd });\n }\n\n static openWriterFd(writeFd: FileHandle): FifoUtf8NlineTransport {\n return new FifoUtf8NlineTransport({ writeFd });\n }\n\n static fromFds(readFd: FileHandle, writeFd: FileHandle): FifoUtf8NlineTransport {\n return new FifoUtf8NlineTransport({ readFd, writeFd });\n }\n\n // ── bidirectional connection ───────────────────────────────────────────\n\n private attachWriter(w: FifoUtf8NlineTransport): void {\n this.pvtWriter = w;\n }\n\n /**\n * Start opening a bidirectional connection. Opens readPath for reading\n * in the background, returns a promise. The caller should do whatever\n * setup is needed to unblock the read (e.g. spawn a process that opens\n * readPath for writing), then await the returned transport.\n *\n * Once the read completes, writePath is opened for writing automatically.\n */\n static beginConnect(\n readPath: string,\n writePath: string,\n ): { transport: Promise<FifoUtf8NlineTransport> } {\n const readerPromise = FifoUtf8NlineTransport.openReader(readPath);\n\n const transport = readerPromise.then(async (reader) => {\n const writer = await FifoUtf8NlineTransport.openWriter(writePath);\n reader.attachWriter(writer);\n return reader;\n });\n\n return { transport };\n }\n\n /**\n * Open a bidirectional connection. Opens writePath for writing first\n * (unblocking the peer's read), starts reading readPath in the\n * background, then awaits both.\n *\n * Use this when you are the side that responds to the peer's beginConnect\n * (i.e. you don't need to interleave any setup between the read and write\n * opens).\n */\n static async connect(readPath: string, writePath: string): Promise<FifoUtf8NlineTransport> {\n const readerPromise = FifoUtf8NlineTransport.openReader(readPath);\n const writer = await FifoUtf8NlineTransport.openWriter(writePath);\n const reader = await readerPromise;\n reader.attachWriter(writer);\n return reader;\n }\n\n // ── public API ─────────────────────────────────────────────────────────\n\n get canSend(): boolean {\n if (this.pvtWriter) return this.pvtWriter.canSend;\n return this.writeFd !== null && !this.closed;\n }\n\n onMessage(handler: (line: string) => void): void {\n if (this.closed) throw new Error(\"FifoUtf8NlineTransport: closed\");\n if (this.readFd === null) throw new Error(\"FifoUtf8NlineTransport: not a reader\");\n if (this.pvtOnMessage !== null) {\n throw new Error(\"FifoUtf8NlineTransport: handler already set — call removeHandler() first\");\n }\n this.pvtOnMessage = handler;\n }\n\n removeHandler(): ((line: string) => void) | null {\n const prev = this.pvtOnMessage;\n this.pvtOnMessage = null;\n return prev;\n }\n\n onClose(handler: () => void): void {\n this.pvtOnClose = handler;\n }\n\n get hasHandler(): boolean {\n return this.pvtOnMessage !== null;\n }\n\n get lastError(): Error | null {\n return this.pvtError;\n }\n\n async send(line: string): Promise<void> {\n if (this.pvtWriter) return this.pvtWriter.send(line);\n if (this.closed) throw new Error(\"FifoUtf8NlineTransport: closed\");\n if (this.writeFd === null) throw new Error(\"FifoUtf8NlineTransport: not a writer\");\n if (!line.endsWith(\"\\n\")) line += \"\\n\";\n await this.ws?.write(line);\n }\n\n async close(): Promise<void> {\n this.closingPromise = this.closingPromise || this.pvtClose();\n await this.closingPromise;\n this.closed = true;\n }\n\n private async pvtClose(): Promise<void> {\n // tranfser control back to caller\n // so they can capture the promise reference\n // and prevent reentry\n await Promise.resolve();\n\n if (this.rl) {\n this.rl.close();\n this.rl = null;\n }\n if (this.rs) {\n this.rs.destroy();\n this.rs = null;\n }\n if (this.ws) {\n await new Promise<void>((resolve) => {\n try {\n this.ws?.end(\"\", resolve);\n } catch {\n resolve();\n }\n });\n this.ws = null;\n }\n\n if (this.readFd) {\n try {\n await this.readFd.close();\n } catch {}\n this.readFd = null;\n }\n if (this.writeFd) {\n try {\n await this.writeFd.close();\n } catch {}\n this.writeFd = null;\n }\n if (this.pvtWriter) {\n await this.pvtWriter.close();\n }\n }\n}\n","// ── FIFO client spawner (command) ──────────────────────────────────────────\n//\n// Environment-specific: spawns a command, creates the two named fifos, and\n// returns a frame Channel with the $proto handshake already validated.\n\nimport { spawn, execSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { unlink } from \"node:fs/promises\";\nimport { FifoUtf8NlineTransport } from \"../transports/fifo.js\";\nimport { json1Channel, VERSION } from \"../protocols/json1.js\";\nimport { isProto } from \"../channel.js\";\nimport type { Channel } from \"../channel.js\";\n\nexport function commandSpawner(command: string[]): () => Promise<Channel> {\n return async () => {\n const basePath = join(tmpdir(), `posipaki-${randomUUID()}`);\n\n execSync(`mkfifo \"${basePath}.in\"`);\n execSync(`mkfifo \"${basePath}.out\"`);\n\n const setup = FifoUtf8NlineTransport.beginConnect(basePath + \".in\", basePath + \".out\");\n\n const childCmd = [\n ...command,\n `--fifo-in=${basePath + \".in\"}`,\n `--fifo-out=${basePath + \".out\"}`,\n ];\n const childProc = spawn(childCmd[0], childCmd.slice(1), {\n cwd: process.cwd(),\n stdio: [\"inherit\", \"inherit\", \"inherit\"],\n });\n\n const transport = await setup.transport;\n const channel = json1Channel(transport);\n\n const protoFrame = await new Promise<Record<string, unknown>>((resolve) => {\n channel.onMessage((frame) => resolve(frame));\n });\n channel.removeHandler();\n if (!isProto(protoFrame) || protoFrame.$proto !== VERSION) {\n throw new Error(`unsupported protocol: ${JSON.stringify(protoFrame).slice(0, 50)}`);\n }\n\n let cleaned = false;\n const cleanup = () => {\n if (cleaned) return;\n cleaned = true;\n transport.close();\n unlink(basePath + \".in\").catch(() => {});\n unlink(basePath + \".out\").catch(() => {});\n };\n childProc.on(\"exit\", cleanup);\n\n return channel;\n };\n}\n","// ── FIFO server spawner (argv) ─────────────────────────────────────────────\n//\n// Environment-specific: discovers the two named fifos from process argv,\n// connects, and returns a frame Channel with the $proto handshake already sent.\n\nimport { FifoUtf8NlineTransport } from \"../transports/fifo.js\";\nimport { json1Channel, VERSION } from \"../protocols/json1.js\";\nimport type { Channel } from \"../channel.js\";\n\nexport async function fifoArgvSpawner(): Promise<Channel> {\n const fifoIn = process.argv.find((a) => a.startsWith(\"--fifo-in=\"))?.slice(\"--fifo-in=\".length);\n const fifoOut = process.argv\n .find((a) => a.startsWith(\"--fifo-out=\"))\n ?.slice(\"--fifo-out=\".length);\n\n if (!fifoIn || !fifoOut) {\n console.error(\"server: --fifo-in=<path> and --fifo-out=<path> required\");\n process.exit(1);\n }\n\n const transport = await FifoUtf8NlineTransport.connect(fifoOut, fifoIn);\n const channel = json1Channel(transport);\n await channel.send({ $proto: VERSION });\n return channel;\n}\n","// ── defineSubprocessActor ───────────────────────────────────────────────────\n//\n// Environment-specific glue: wraps an actor definition so that spawning it runs\n// it in a subprocess over two named fifos. It does the guesswork — argv marker\n// detection and runner auto-detection — and prepares the client spawner. This\n// is the only place in the subprocess path that imports transports/protocols.\n\nimport { fileURLToPath } from \"node:url\";\nimport { createHash } from \"node:crypto\";\nimport type { Message } from \"../types.js\";\nimport type { ActorDefinition, HandlerOptions, MethodOptions, ReflectionOptions } from \"../actor-types.js\";\nimport { remoteClient } from \"./client.js\";\nimport { serveRemoteActor } from \"./server.js\";\nimport { commandSpawner } from \"./spawners/fifo-command.js\";\nimport { fifoArgvSpawner } from \"./spawners/fifo-argv.js\";\n\nexport interface SubprocessActorOptions {\n manual?: boolean;\n}\n\nexport interface SubprocessActorBundle<\n Args,\n State,\n InMsg extends Message,\n OutMsg extends Message,\n R extends ReflectionOptions,\n> {\n actor: ActorDefinition<Args, State, InMsg, OutMsg, R>;\n runRemoteRoot(): Promise<void>;\n isRemoteRoot: boolean;\n}\n\nfunction pathHash(path: string): string {\n return createHash(\"sha256\").update(path).digest(\"hex\").slice(0, 12);\n}\n\nconst MARKER_PREFIX = \"--remote=\";\n\nexport function defineSubprocessActor<\n Args,\n State,\n InMsg extends Message,\n OutMsg extends Message,\n Methods extends MethodOptions,\n Handlers extends HandlerOptions<InMsg>,\n R extends ReflectionOptions,\n>(\n actor: ActorDefinition<Args, State, InMsg, OutMsg, R>,\n url: string,\n opts: SubprocessActorOptions = {},\n): SubprocessActorBundle<Args, State, InMsg, OutMsg, R> {\n const scriptPath = fileURLToPath(url);\n const marker = `${MARKER_PREFIX}${pathHash(scriptPath)}`;\n const isRemoteRoot = !opts.manual && process.argv.includes(marker);\n\n const serve = () => serveRemoteActor(actor, fifoArgvSpawner);\n\n if (isRemoteRoot) {\n void serve();\n }\n\n const isBun = typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\";\n const runner = isBun ? \"bun\" : \"node\";\n const spawner = commandSpawner([runner, scriptPath, marker]);\n\n const proxyDef = remoteClient<Args, State, InMsg, OutMsg, Methods, Handlers, R>(\n actor.name ?? \"actor\",\n spawner,\n );\n\n return {\n actor: proxyDef,\n runRemoteRoot: serve,\n isRemoteRoot,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"node.js","names":["errorText","Lines"],"sources":["../../src/remote/transports/fifo.ts","../../src/remote/spawners/fifo-command.ts","../../src/remote/spawners/fifo-argv.ts","../../src/remote/define-subprocess.ts","../../src/remote/stdio.ts","../../src/remote/kit.ts","../../src/remote/gateway.ts"],"sourcesContent":["// ── FIFO transport ─────────────────────────────────────────────────────────\n//\n// Newline-delimited UTF-8 over named fifos. Strict handler lifecycle:\n// onMessage() throws if a handler is already set; call removeHandler() first.\n\nimport { open, type FileHandle } from \"node:fs/promises\";\nimport type { ReadStream, WriteStream } from \"node:fs\";\nimport * as readline from \"node:readline\";\nimport type { StringTransport } from \"../channel.js\";\n\nexport class FifoUtf8NlineTransport implements StringTransport {\n private readFd: FileHandle | null;\n private writeFd: FileHandle | null;\n private rl: readline.Interface | null;\n private rs: ReadStream | null;\n private ws: WriteStream | null;\n private pvtOnMessage: ((line: string) => void) | null = null;\n private pvtOnClose: (() => void) | null = null;\n private closed = false;\n private closingPromise: Promise<void> | null = null;\n private pvtError: Error | null = null;\n\n private constructor(opts: { readFd?: FileHandle; writeFd?: FileHandle }) {\n this.readFd = opts.readFd ?? null;\n this.writeFd = opts.writeFd ?? null;\n\n if (this.readFd) {\n this.rs = this.readFd.createReadStream({ encoding: \"utf-8\" });\n this.rl = readline.createInterface({ input: this.rs });\n\n this.rl.on(\"line\", (line) => {\n if (this.pvtOnMessage && !this.closed) this.pvtOnMessage(line);\n });\n\n this.rl.on(\"close\", () => {\n this.pvtOnClose?.();\n this.close();\n });\n\n this.rs.on(\"error\", (err: Error) => {\n this.pvtError = err;\n this.close();\n });\n } else {\n this.rs = null;\n this.rl = null;\n }\n\n if (this.writeFd) {\n this.ws = this.writeFd.createWriteStream({ encoding: \"utf-8\" });\n this.ws.on(\"error\", (err: Error) => {\n this.pvtError = err;\n this.close();\n });\n } else {\n this.ws = null;\n }\n }\n\n // ── factories ──────────────────────────────────────────────────────────\n\n static openReaderFd(readFd: FileHandle): FifoUtf8NlineTransport {\n return new FifoUtf8NlineTransport({ readFd });\n }\n\n static openWriterFd(writeFd: FileHandle): FifoUtf8NlineTransport {\n return new FifoUtf8NlineTransport({ writeFd });\n }\n\n static fromFds(readFd: FileHandle, writeFd: FileHandle): FifoUtf8NlineTransport {\n return new FifoUtf8NlineTransport({ readFd, writeFd });\n }\n\n // ── bidirectional connection ───────────────────────────────────────────\n\n /**\n * Start opening a bidirectional connection. Opens readPath for reading\n * in the background, returns a promise. The caller should do whatever\n * setup is needed to unblock the read (e.g. spawn a process that opens\n * readPath for writing), then await the returned transport.\n *\n * The transport itself is built only once both directions are open. A read\n * stream created while no writer exists on its fifo is handed an immediate\n * end-of-stream by some runtimes (bun), which closes a channel that has not\n * carried a byte yet — so the writes side is opened before the reader exists.\n */\n static beginConnect(\n readPath: string,\n writePath: string,\n ): { transport: Promise<FifoUtf8NlineTransport> } {\n const readFdPromise = open(readPath, \"r\");\n\n const transport = readFdPromise.then(async (readFd) => {\n const writeFd = await open(writePath, \"w\");\n return FifoUtf8NlineTransport.fromFds(readFd, writeFd);\n });\n\n return { transport };\n }\n\n /**\n * Open a bidirectional connection. Opens writePath for writing first\n * (unblocking the peer's read), starts reading readPath in the\n * background, then awaits both.\n *\n * Use this when you are the side that responds to the peer's beginConnect\n * (i.e. you don't need to interleave any setup between the read and write\n * opens). Built like `beginConnect`: the transport exists only after both\n * directions do.\n */\n static async connect(readPath: string, writePath: string): Promise<FifoUtf8NlineTransport> {\n const readFdPromise = open(readPath, \"r\");\n const writeFd = await open(writePath, \"w\");\n const readFd = await readFdPromise;\n return FifoUtf8NlineTransport.fromFds(readFd, writeFd);\n }\n\n // ── public API ─────────────────────────────────────────────────────────\n\n get canSend(): boolean {\n return this.writeFd !== null && !this.closed;\n }\n\n onMessage(handler: (line: string) => void): void {\n if (this.closed) throw new Error(\"FifoUtf8NlineTransport: closed\");\n if (this.readFd === null) throw new Error(\"FifoUtf8NlineTransport: not a reader\");\n if (this.pvtOnMessage !== null) {\n throw new Error(\"FifoUtf8NlineTransport: handler already set — call removeHandler() first\");\n }\n this.pvtOnMessage = handler;\n }\n\n removeHandler(): ((line: string) => void) | null {\n const prev = this.pvtOnMessage;\n this.pvtOnMessage = null;\n return prev;\n }\n\n onClose(handler: () => void): void {\n this.pvtOnClose = handler;\n }\n\n get hasHandler(): boolean {\n return this.pvtOnMessage !== null;\n }\n\n get lastError(): Error | null {\n return this.pvtError;\n }\n\n async send(line: string): Promise<void> {\n if (this.closed) throw new Error(\"FifoUtf8NlineTransport: closed\");\n if (this.writeFd === null) throw new Error(\"FifoUtf8NlineTransport: not a writer\");\n if (!line.endsWith(\"\\n\")) line += \"\\n\";\n await this.ws?.write(line);\n }\n\n async close(): Promise<void> {\n this.closingPromise = this.closingPromise || this.pvtClose();\n await this.closingPromise;\n this.closed = true;\n }\n\n private async pvtClose(): Promise<void> {\n // tranfser control back to caller\n // so they can capture the promise reference\n // and prevent reentry\n await Promise.resolve();\n\n if (this.rl) {\n this.rl.close();\n this.rl = null;\n }\n if (this.rs) {\n this.rs.destroy();\n this.rs = null;\n }\n if (this.ws) {\n await new Promise<void>((resolve) => {\n try {\n this.ws?.end(\"\", resolve);\n } catch {\n resolve();\n }\n });\n this.ws = null;\n }\n\n if (this.readFd) {\n try {\n await this.readFd.close();\n } catch {}\n this.readFd = null;\n }\n if (this.writeFd) {\n try {\n await this.writeFd.close();\n } catch {}\n this.writeFd = null;\n }\n }\n}\n","// ── FIFO client spawner (command) ──────────────────────────────────────────\n//\n// Environment-specific: spawns a command, creates the two named fifos, and\n// returns a frame Channel with the $proto handshake already validated.\n\nimport { spawn, execSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { unlink } from \"node:fs/promises\";\nimport { FifoUtf8NlineTransport } from \"../transports/fifo.js\";\nimport { json1Channel, VERSION } from \"../protocols/json1.js\";\nimport { isProto } from \"../channel.js\";\nimport type { Channel } from \"../channel.js\";\n\nexport function commandSpawner(command: string[]): () => Promise<Channel> {\n return async () => {\n const basePath = join(tmpdir(), `posipaki-${randomUUID()}`);\n\n execSync(`mkfifo \"${basePath}.in\"`);\n execSync(`mkfifo \"${basePath}.out\"`);\n\n const setup = FifoUtf8NlineTransport.beginConnect(basePath + \".in\", basePath + \".out\");\n\n const childCmd = [\n ...command,\n `--fifo-in=${basePath + \".in\"}`,\n `--fifo-out=${basePath + \".out\"}`,\n ];\n const childProc = spawn(childCmd[0], childCmd.slice(1), {\n cwd: process.cwd(),\n stdio: [\"inherit\", \"inherit\", \"inherit\"],\n });\n\n const transport = await setup.transport;\n const channel = json1Channel(transport);\n\n const protoFrame = await new Promise<Record<string, unknown>>((resolve) => {\n channel.onMessage((frame) => resolve(frame));\n });\n channel.removeHandler();\n if (!isProto(protoFrame) || protoFrame.$proto !== VERSION) {\n throw new Error(`unsupported protocol: ${JSON.stringify(protoFrame).slice(0, 50)}`);\n }\n\n let cleaned = false;\n const cleanup = () => {\n if (cleaned) return;\n cleaned = true;\n transport.close();\n unlink(basePath + \".in\").catch(() => {});\n unlink(basePath + \".out\").catch(() => {});\n };\n childProc.on(\"exit\", cleanup);\n\n return channel;\n };\n}\n","// ── FIFO server spawner (argv) ─────────────────────────────────────────────\n//\n// Environment-specific: discovers the two named fifos from process argv,\n// connects, and returns a frame Channel with the $proto handshake already sent.\n\nimport { FifoUtf8NlineTransport } from \"../transports/fifo.js\";\nimport { json1Channel, VERSION } from \"../protocols/json1.js\";\nimport type { Channel } from \"../channel.js\";\n\nexport async function fifoArgvSpawner(): Promise<Channel> {\n const fifoIn = process.argv.find((a) => a.startsWith(\"--fifo-in=\"))?.slice(\"--fifo-in=\".length);\n const fifoOut = process.argv\n .find((a) => a.startsWith(\"--fifo-out=\"))\n ?.slice(\"--fifo-out=\".length);\n\n if (!fifoIn || !fifoOut) {\n console.error(\"server: --fifo-in=<path> and --fifo-out=<path> required\");\n process.exit(1);\n }\n\n const transport = await FifoUtf8NlineTransport.connect(fifoOut, fifoIn);\n const channel = json1Channel(transport);\n await channel.send({ $proto: VERSION });\n return channel;\n}\n","// ── defineSubprocessActor ───────────────────────────────────────────────────\n//\n// Environment-specific glue: wraps an actor definition so that spawning it runs\n// it in a subprocess over two named fifos. It does the guesswork — argv marker\n// detection and runner auto-detection — and prepares the client spawner. This\n// is the only place in the subprocess path that imports transports/protocols.\n\nimport { fileURLToPath } from \"node:url\";\nimport { createHash } from \"node:crypto\";\nimport type { Message } from \"../types.js\";\nimport type { ActorDefinition, HandlerOptions, MethodOptions, ReflectionOptions } from \"../actor-types.js\";\nimport { remoteClient } from \"./client.js\";\nimport { serveRemoteActor } from \"./server.js\";\nimport { commandSpawner } from \"./spawners/fifo-command.js\";\nimport { fifoArgvSpawner } from \"./spawners/fifo-argv.js\";\n\nexport interface SubprocessActorOptions {\n manual?: boolean;\n}\n\nexport interface SubprocessActorBundle<\n Args,\n State,\n InMsg extends Message,\n OutMsg extends Message,\n R extends ReflectionOptions,\n> {\n actor: ActorDefinition<Args, State, InMsg, OutMsg, R>;\n runRemoteRoot(): Promise<void>;\n isRemoteRoot: boolean;\n}\n\nfunction pathHash(path: string): string {\n return createHash(\"sha256\").update(path).digest(\"hex\").slice(0, 12);\n}\n\nconst MARKER_PREFIX = \"--remote=\";\n\nexport function defineSubprocessActor<\n Args,\n State,\n InMsg extends Message,\n OutMsg extends Message,\n Methods extends MethodOptions,\n Handlers extends HandlerOptions<InMsg>,\n R extends ReflectionOptions,\n>(\n actor: ActorDefinition<Args, State, InMsg, OutMsg, R>,\n url: string,\n opts: SubprocessActorOptions = {},\n): SubprocessActorBundle<Args, State, InMsg, OutMsg, R> {\n const scriptPath = fileURLToPath(url);\n const marker = `${MARKER_PREFIX}${pathHash(scriptPath)}`;\n const isRemoteRoot = !opts.manual && process.argv.includes(marker);\n\n const serve = () => serveRemoteActor(actor, fifoArgvSpawner);\n\n if (isRemoteRoot) {\n void serve();\n }\n\n const isBun = typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\";\n const runner = isBun ? \"bun\" : \"node\";\n const spawner = commandSpawner([runner, scriptPath, marker]);\n\n const proxyDef = remoteClient<Args, State, InMsg, OutMsg, Methods, Handlers, R>(\n actor.name ?? \"actor\",\n spawner,\n );\n\n return {\n actor: proxyDef,\n runRemoteRoot: serve,\n isRemoteRoot,\n };\n}\n","// ── stdio wire ─────────────────────────────────────────────────────────────\n//\n// A newline-delimited JSON channel over a pair of streams: the one wire every\n// way into a foreign execution context can carry. Two callers rely on it, and\n// they are two halves of the same thing:\n//\n// * the client of a spawn we make ourselves, on fds handed to the child (see\n// `fdStreams`) — private fds, so the child's own stdout stays free;\n// * a first-stage command on its own stdin/stdout, over ssh, under `sudo`, in\n// a container — where stdio is the only channel there is.\n//\n// Frames are one JSON object per line. Whatever the far end prints is carried\n// as an *output frame* (`$fd`), so it can never be mistaken for a frame of the\n// protocol itself; a failed start is carried as an `$error` frame.\n//\n// Why the client filters output frames before handing the transport to the\n// seam: `json1Channel` decodes every line it sees, and the first frame of a\n// session is the peer's `$proto`. Stripping output frames in the transport\n// keeps the protocol view clean without the seam knowing anything about them.\n\nimport { createReadStream, createWriteStream } from \"node:fs\";\nimport * as readline from \"node:readline\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { isProto, type Channel, type StringTransport } from \"./channel.js\";\nimport { VERSION, json1Channel } from \"./protocols/json1.js\";\n\n/** The stream pair a line transport talks over. */\nexport interface LineStreams {\n read: Readable;\n write: Writable;\n}\n\n/** A stream of the far end's process: its stdout or its stderr. */\nexport type OutputFd = 1 | 2;\n\n/** Where the far end's own output goes. */\nexport type OutputSink = (fd: OutputFd, data: string) => void;\n\n/** How long to wait for the far end's first protocol frame. */\nexport const HANDSHAKE_TIMEOUT_MS = 15_000;\n\nconst OUTPUT_KEY = \"$fd\";\n\nconst ERROR_KEY = \"$error\";\n\n/** Raised when the wire itself fails: no frame, the wrong protocol, a peer that went away. */\nexport class StdioWireError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"StdioWireError\";\n }\n}\n\n// ── output frames ──────────────────────────────────────────────────────────\n\n/** One frame of the far end's own output. */\nexport interface OutputFrame {\n fd: OutputFd;\n data: string;\n}\n\nexport function outputFrame(fd: OutputFd, data: string): string {\n return JSON.stringify({ [OUTPUT_KEY]: fd, data });\n}\n\n/** The output frame a line carries, or null when it carries something else. */\nexport function parseOutputFrame(line: string): OutputFrame | null {\n // Cheap reject first: almost every line of a session is protocol traffic.\n if (!line.startsWith('{\"$fd\"')) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const frame = parsed as Record<string, unknown>;\n const fd = frame[OUTPUT_KEY];\n const data = frame.data;\n if (fd !== 1 && fd !== 2) return null;\n return { fd, data: typeof data === \"string\" ? data : JSON.stringify(data) };\n}\n\nexport function errorFrame(reason: string): string {\n return JSON.stringify({ [ERROR_KEY]: reason });\n}\n\n/** The reason an `$error` frame carries, or null when it is not one. */\nexport function errorReason(frame: Record<string, unknown>): string | null {\n const reason = frame[ERROR_KEY];\n return typeof reason === \"string\" ? reason : null;\n}\n\n// ── transports ─────────────────────────────────────────────────────────────\n\n/** Is this write error just a peer that already left? */\nfunction peerGone(code: unknown): boolean {\n return (\n code === \"EPIPE\" ||\n code === \"ERR_STREAM_DESTROYED\" ||\n code === \"ERR_STREAM_WRITE_AFTER_END\" ||\n code === \"ERR_STREAM_ALREADY_FINISHED\"\n );\n}\n\nfunction errorText(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * A `StringTransport` over a read/write stream pair, one JSON frame per line.\n *\n * `close()` is idempotent and never throws: teardown races are normal here —\n * the peer exits, the read side ends, and a write arrives a moment later.\n */\nexport class LineTransport implements StringTransport {\n private readonly streams: LineStreams;\n private pvtOnMessage: ((line: string) => void) | null = null;\n private pvtOnClose: (() => void) | null = null;\n private pvtClosed = false;\n private pvtClosing: Promise<void> | null = null;\n\n constructor(streams: LineStreams) {\n this.streams = streams;\n const lines = readline.createInterface({ input: streams.read });\n lines.on(\"line\", (line) => {\n if (this.pvtOnMessage && !this.pvtClosed) this.pvtOnMessage(line);\n });\n lines.on(\"close\", () => {\n this.pvtOnClose?.();\n void this.close();\n });\n // A stream error is a peer that went away, not a reason to take the process\n // down with an unhandled 'error' event.\n streams.read.on(\"error\", () => {\n void this.close();\n });\n streams.write.on(\"error\", () => {\n void this.close();\n });\n }\n\n get closed(): boolean {\n return this.pvtClosed;\n }\n\n get hasHandler(): boolean {\n return this.pvtOnMessage !== null;\n }\n\n onMessage(handler: (line: string) => void): void {\n if (this.pvtClosed) throw new StdioWireError(\"stdio transport: closed\");\n if (this.pvtOnMessage !== null) {\n throw new StdioWireError(\"stdio transport: handler already set — call removeHandler()\");\n }\n this.pvtOnMessage = handler;\n }\n\n removeHandler(): ((line: string) => void) | null {\n const previous = this.pvtOnMessage;\n this.pvtOnMessage = null;\n return previous;\n }\n\n onClose(handler: () => void): void {\n this.pvtOnClose = handler;\n }\n\n send(frame: string): Promise<void> {\n if (this.pvtClosed) {\n return Promise.resolve();\n }\n const line = frame.endsWith(\"\\n\") ? frame : `${frame}\\n`;\n return new Promise<void>((resolve, reject) => {\n try {\n this.streams.write.write(line, (err?: Error | null) => {\n if (!err) return resolve();\n const code = (err as { code?: unknown }).code;\n // A peer that has hung up cannot be told anything; the write that\n // races its exit is not an error in this transport.\n if (peerGone(code)) {\n return resolve();\n }\n reject(new StdioWireError(`stdio transport: ${err.message}`));\n });\n } catch (err) {\n reject(new StdioWireError(`stdio transport: ${errorText(err)}`));\n }\n });\n }\n\n async close(): Promise<void> {\n this.pvtClosing ??= this.pvtClose();\n await this.pvtClosing;\n }\n\n private async pvtClose(): Promise<void> {\n await Promise.resolve();\n\n this.pvtClosed = true;\n // Best effort: a half-open stream (the peer is already gone) must not turn\n // teardown into an exception.\n try {\n this.streams.read.destroy?.();\n } catch {\n /* already gone */\n }\n try {\n if (!this.streams.write.writableEnded) this.streams.write.end();\n } catch {\n /* already gone */\n }\n }\n}\n\n/** Streams over two fds of *our own* process — the child side of an fd wire. */\nexport function fdStreams(readFd: number, writeFd: number): LineStreams {\n const read = createReadStream(\"\", { fd: readFd, encoding: \"utf-8\", autoClose: false });\n const write = createWriteStream(\"\", { fd: writeFd, encoding: \"utf-8\", autoClose: false });\n return { read, write };\n}\n\n/**\n * A transport that lifts output frames out of the stream: everything else is\n * passed through untouched, so the protocol sees frames and only frames.\n */\nexport class OutputFilter implements StringTransport {\n private readonly inner: StringTransport;\n private readonly sink: OutputSink;\n\n constructor(inner: StringTransport, sink: OutputSink) {\n this.inner = inner;\n this.sink = sink;\n }\n\n send(frame: string): void | Promise<void> {\n return this.inner.send(frame);\n }\n\n onMessage(handler: (frame: string) => void): void {\n this.inner.onMessage((line) => {\n const output = parseOutputFrame(line);\n if (output) this.sink(output.fd, output.data);\n else handler(line);\n });\n }\n\n removeHandler(): void {\n this.inner.removeHandler();\n }\n\n onClose(handler: () => void): void {\n this.inner.onClose(handler);\n }\n\n close(): Promise<void> {\n return this.inner.close();\n }\n}\n\n// ── sinks ──────────────────────────────────────────────────────────────────\n\n/** Write the far end's output to our stderr, tagged with its name. */\nexport function stderrSink(name = \"\"): OutputSink {\n const tag = name ? `[${name}] ` : \"\";\n return (fd, data) => {\n const lead = fd === 1 ? tag : `${tag}err: `;\n for (const line of data.split(\"\\n\")) {\n if (line.length > 0) process.stderr.write(`${lead}${line}\\n`);\n }\n };\n}\n\n// ── handshakes ─────────────────────────────────────────────────────────────\n\n/** The next frame that arrives, or a timeout. */\nasync function nextFrame(channel: Channel, timeoutMs: number): Promise<Record<string, unknown>> {\n return await new Promise((resolve, reject) => {\n const timer = setTimeout(\n () => reject(new StdioWireError(`no protocol frame within ${timeoutMs}ms`)),\n timeoutMs,\n );\n timer.unref?.();\n channel.onMessage((frame) => {\n clearTimeout(timer);\n resolve(frame);\n });\n });\n}\n\nexport interface ClientChannelOptions {\n /** Where the far end's own output goes. Defaults to our stderr. */\n onOutput?: OutputSink;\n /** How long to wait for the peer's protocol frame. Defaults to {@link HANDSHAKE_TIMEOUT_MS}. */\n timeoutMs?: number;\n}\n\n/**\n * The client end of the wire: hand the streams to the seam (minus output\n * frames) and wait for the peer's `$proto`. A first stage reports a failed\n * start as an `$error` frame instead.\n */\nexport async function clientChannel(\n streams: LineStreams,\n opts: ClientChannelOptions = {},\n): Promise<Channel> {\n const transport = new LineTransport(streams);\n const channel = json1Channel(new OutputFilter(transport, opts.onOutput ?? stderrSink()));\n const frame = await nextFrame(channel, opts.timeoutMs ?? HANDSHAKE_TIMEOUT_MS);\n channel.removeHandler();\n\n const reason = errorReason(frame);\n if (reason) throw new StdioWireError(reason);\n if (!isProto(frame)) {\n throw new StdioWireError(`unexpected first frame: ${JSON.stringify(frame).slice(0, 120)}`);\n }\n if (frame.$proto !== VERSION) {\n throw new StdioWireError(`unsupported protocol ${String(frame.$proto)}, expected ${VERSION}`);\n }\n return channel;\n}\n\n/** The server end of the wire — a payload speaking stdio itself. */\nexport async function serverChannel(streams: LineStreams): Promise<Channel> {\n const channel = json1Channel(new LineTransport(streams));\n await channel.send({ $proto: VERSION });\n return channel;\n}\n","// ── Kit ────────────────────────────────────────────────────────────────────\n//\n// Getting a payload onto a host we know nothing about. A way in gives us a\n// shell; from there one script runs everywhere: probe what is really there, write\n// the kit only if it is not, and report every step as one machine-readable line.\n// The script always arrives on stdin, never in argv — and since stdin *is* the\n// script, the payload gets its own channel into the same environment (a second\n// `ssh`, an `exec` on a container that is already running).\n//\n// The script assumes POSIX sh and nothing else: `command -v`, `mkdir -p`,\n// `wc -c`, `base64 -d`. A broken environment therefore reaches the client as a\n// readable reason instead of a channel that just closes.\n//\n// A kit is named after the build that made it and the posipaki it speaks, so two\n// consumers, two builds or two releases cannot land on each other's files.\n\nimport { createHash } from \"node:crypto\";\nimport { LIB_VERSION } from \"../version.js\";\n\n/** Runtimes a kit is happy to be run with, best first. A caller may override. */\nexport const DEFAULT_RUNTIMES = [\"node\", \"nodejs\", \"bun\"];\n\n/** Layout of a staged kit — the contract's own version, recorded in `version.json`. */\nexport const KIT_LAYOUT = 1;\n\n/** Where kits live on a host, relative to `$HOME`, unless the caller says otherwise. */\nexport const DEFAULT_KIT_PARENT = \"bin/posipaki\";\n\nexport interface KitFile {\n /** Name inside the kit directory, e.g. `payload.js`. */\n name: string;\n /** The file's bytes, base64 — what the script writes. */\n base64: string;\n /** Byte count, for the cheap presence check. */\n bytes: number;\n /** sha256 of the file, recorded in `version.json`. */\n sha256: string;\n}\n\n/** Who is staging this kit: the consumer's own name and build version. */\nexport interface KitApp {\n name: string;\n version: string;\n}\n\nexport interface Kit {\n /** Directory name: `<app>-<app version>-posipaki-<posipaki version>-<manifest8>`. */\n name: string;\n /** sha256 over the manifest of the kit's own files. */\n manifestHash: string;\n /** Everything the kit installs, `version.json` last. */\n files: KitFile[];\n /** Runtime candidates, in order; the first one found wins. */\n runtimes: string[];\n /** Directory the kit lands in, relative to the host's `$HOME`. */\n parent: string;\n /** The consumer this kit belongs to. */\n app: KitApp;\n}\n\nexport interface MakeKitOptions {\n /** Required: a kit with no owner cannot be told apart from another's. */\n app: KitApp;\n /** Defaults to {@link DEFAULT_RUNTIMES}. */\n runtimes?: string[];\n /** Defaults to {@link DEFAULT_KIT_PARENT}. */\n parent?: string;\n}\n\nexport type BootstrapReport =\n | { kind: \"ready\"; kitDir: string; runtime: string; staged: boolean }\n | { kind: \"error\"; reason: string };\n\n/** The part an artifact plays when it names itself. */\nexport type ArtifactRole = \"gateway\" | \"payload\";\n\n/** Hex sha256 of some content. */\nexport function sha256Hex(content: string | Uint8Array): string {\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\n/** The directory name a kit with these contents, this owner and this posipaki gets. */\nexport function kitName(app: KitApp, manifestHash: string): string {\n return `${app.name}-${app.version}-posipaki-${LIB_VERSION}-${manifestHash.slice(0, 8)}`;\n}\n\n/**\n * One line naming a staged artifact: what it is, which posipaki it speaks, which\n * kit layout it was built for. A client that staged it can judge compatibility\n * from this alone — nothing else is claimed.\n */\nexport function versionLine(app: KitApp, role: ArtifactRole, proto: string): string {\n return `${app.name}-${role} ${app.version} posipaki ${LIB_VERSION} proto ${proto} layout ${KIT_LAYOUT}`;\n}\n\n/** Wrap base64 at a comfortable width; `base64 -d` ignores the newlines. */\nfunction wrapped(base64: string, width = 76): string {\n const lines: string[] = [];\n for (let at = 0; at < base64.length; at += width) lines.push(base64.slice(at, at + width));\n return lines.join(\"\\n\");\n}\n\n/**\n * Describe a kit around its files. The manifest hash covers the files only, so\n * `version.json` — which records that hash — can be one of them without chasing\n * its own tail.\n */\nexport function makeKit(\n files: { name: string; content: string | Uint8Array }[],\n options: MakeKitOptions,\n): Kit {\n const raw = files.map((file) => ({\n name: file.name,\n bytes: Buffer.byteLength(file.content),\n sha256: sha256Hex(file.content),\n base64: Buffer.from(file.content).toString(\"base64\"),\n }));\n const manifestHash = sha256Hex(raw.map((f) => `${f.name} ${f.sha256} ${f.bytes}`).join(\"\\n\"));\n const kit: Kit = {\n name: kitName(options.app, manifestHash),\n manifestHash,\n files: raw,\n runtimes: [...(options.runtimes ?? DEFAULT_RUNTIMES)],\n parent: options.parent ?? DEFAULT_KIT_PARENT,\n app: options.app,\n };\n const version = Buffer.from(`${JSON.stringify(kitVersion(kit), null, 2)}\\n`);\n return {\n ...kit,\n files: [\n ...kit.files,\n {\n name: \"version.json\",\n bytes: version.byteLength,\n sha256: sha256Hex(version),\n base64: version.toString(\"base64\"),\n },\n ],\n };\n}\n\n/** What `version.json` says: identity, versions, and the exact content hashes. */\nexport function kitVersion(kit: Kit): Record<string, unknown> {\n return {\n kit: kit.name,\n app: kit.app,\n posipaki: LIB_VERSION,\n layout: KIT_LAYOUT,\n manifestHash: kit.manifestHash,\n files: kit.files.map((file) => ({ name: file.name, sha256: file.sha256, bytes: file.bytes })),\n };\n}\n\n/** The `[ -f … ] && [ \"$(wc -c < …)\" -eq … ]` test for one file. */\nfunction presentTest(file: KitFile): string {\n return `[ -f \"$kit_dir/${file.name}\" ] && [ \"$(wc -c < \"$kit_dir/${file.name}\")\" -eq ${file.bytes} ]`;\n}\n\n/** One file: write it to a temp name, then move it into place. */\nfunction writeFile(file: KitFile): string {\n return [\n ` base64 -d > \"$tmp\" <<'KIT_FILE'`,\n wrapped(file.base64),\n \"KIT_FILE\",\n ` mv \"$tmp\" \"$kit_dir/${file.name}\" || { echo \"error cannot install ${file.name}\"; exit 73; }`,\n ].join(\"\\n\");\n}\n\n/**\n * The bootstrap, as one POSIX sh script.\n *\n * Staging is the whole job. The script always arrives on stdin, never in argv,\n * and that is also why it cannot carry the wire: the caller runs the payload on a\n * second channel into the same environment once this one has reported.\n */\nexport function bootstrapScript(kit: Kit): string {\n const checks = kit.files.map(presentTest).join(\" && \\\\\\n \");\n const lines = [\n \"# ── kit bootstrap ─────────────────────────────────────────────────────────\",\n \"# probe, stage what is missing, report every step as one machine-readable line.\",\n \"set -u\",\n \"\",\n 'if [ -z \"${HOME:-}\" ]; then echo \"error HOME is not set\"; exit 74; fi',\n `kit_dir=\"$HOME/${kit.parent}/${kit.name}\"`,\n \"\",\n 'runtime=\"\"',\n `for candidate in ${kit.runtimes.join(\" \")}; do`,\n ' if command -v \"$candidate\" >/dev/null 2>&1; then runtime=\"$(command -v \"$candidate\")\"; break; fi',\n \"done\",\n `if [ -z \"$runtime\" ]; then echo \"error no runtime among: ${kit.runtimes.join(\" \")}\"; exit 75; fi`,\n 'if ! command -v base64 >/dev/null 2>&1; then echo \"error base64 is missing\"; exit 76; fi',\n \"\",\n `if ${checks}; then`,\n ' echo \"present\"',\n \"else\",\n ` mkdir -p \"$kit_dir\" || { echo \"error cannot create $kit_dir\"; exit 73; }`,\n ' tmp=\"$kit_dir/.staging.$$\"',\n ...kit.files.map(writeFile),\n ' echo \"staged\"',\n \"fi\",\n 'echo \"kit $kit_dir\"',\n 'echo \"runtime $runtime\"',\n ];\n return `${lines.join(\"\\n\")}\\n`;\n}\n\n/** Read the script's report back. Missing or unknown lines are an error. */\nexport function parseBootstrapReport(stdout: string): BootstrapReport {\n let kitDir = \"\";\n let runtime = \"\";\n let staged: boolean | null = null;\n for (const raw of stdout.split(\"\\n\")) {\n const line = raw.trim();\n if (line === \"\") continue;\n if (line.startsWith(\"error \")) return { kind: \"error\", reason: line.slice(\"error \".length) };\n if (line === \"staged\") {\n staged = true;\n continue;\n }\n if (line === \"present\") {\n staged = false;\n continue;\n }\n if (line.startsWith(\"kit \")) {\n kitDir = line.slice(\"kit \".length);\n continue;\n }\n if (line.startsWith(\"runtime \")) {\n runtime = line.slice(\"runtime \".length);\n continue;\n }\n }\n if (kitDir === \"\" || runtime === \"\" || staged === null) {\n const seen = stdout.trim();\n return {\n kind: \"error\",\n reason: seen === \"\" ? \"no report at all\" : `incomplete report: ${seen}`,\n };\n }\n return { kind: \"ready\", kitDir, runtime, staged };\n}\n","// ── Gateway ────────────────────────────────────────────────────────────────\n//\n// The first-stage command. Run this *inside* a foreign environment — directly,\n// under `sudo`, over ssh, in a container — and it:\n//\n// 1. creates the channel to the payload (fifo paths it makes itself, in a\n// private temp dir, so they belong to the uid that will open them),\n// 2. starts the payload (`--worker=<script>`, required) and relays frames\n// between stdin/stdout,\n// 3. carries whatever the payload prints to the client as output frames\n// (`$fd`), so the payload's own stdout can never be mistaken for protocol.\n//\n// Why a gateway at all: a fifo path created on the client side is useless when the\n// payload runs as another uid (it cannot open it) or on another host (the path is\n// not there). stdio is the one channel every way into an environment gives us, so\n// the wire is stdio and the fifo stays inside the environment.\n//\n// Directories and processes are cleaned up on every exit path, which is also why\n// the client never sees a transport being closed twice.\n//\n// It imports nothing but node builtins and this package, so the payload behind it\n// can be anything the runtime there can start.\n\nimport { execFile, spawn } from \"node:child_process\";\nimport type { ChildProcess } from \"node:child_process\";\nimport { mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { promisify } from \"node:util\";\nimport type { KitApp } from \"./kit.js\";\nimport { versionLine } from \"./kit.js\";\nimport { VERSION } from \"./protocols/json1.js\";\nimport { errorFrame, outputFrame } from \"./stdio.js\";\nimport type { LineTransport, OutputFd } from \"./stdio.js\";\nimport { LineTransport as Lines } from \"./stdio.js\";\nimport { FifoUtf8NlineTransport } from \"./transports/fifo.js\";\n\nconst run = promisify(execFile);\n\n/** Exit code for \"this environment could not be set up\". */\nexport const GATEWAY_FAILED = 1;\n\n/** Absolute path to this entry point, for whoever runs it as a script. */\nexport const GATEWAY_SCRIPT = fileURLToPath(import.meta.url);\n\nexport interface GatewayBoot {\n /** Who staged this payload. Names the artifact in its `--version` line. */\n app: KitApp;\n /** A label for logs and the tree name; the payload may have its own default. */\n env: string;\n /** Forwarded to the payload when given. */\n poolSize?: number;\n /** The payload to relay to. Required — the gateway has no opinion of its own. */\n worker: string;\n}\n\nfunction errorText(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction flagValue(argv: string[], name: string): string | undefined {\n const prefix = `--${name}=`;\n return argv.find((a) => a.startsWith(prefix))?.slice(prefix.length);\n}\n\n/** A `--flag=<positive integer>` argument, or undefined. */\nfunction positiveInt(value: string | undefined): number | undefined {\n const n = Number(value);\n return value !== undefined && Number.isFinite(n) && n >= 1 ? Math.floor(n) : undefined;\n}\n\n/**\n * The argv that boots this gateway — built by the client, read by\n * {@link gatewayBoot}, so the two halves cannot drift apart.\n */\nexport function gatewayArgs(boot: GatewayBoot): string[] {\n return [\n `--worker=${boot.worker}`,\n `--env=${boot.env}`,\n `--app-name=${boot.app.name}`,\n `--app-version=${boot.app.version}`,\n ...(boot.poolSize === undefined ? [] : [`--pool-size=${boot.poolSize}`]),\n ];\n}\n\n/** The boot a gateway was started with, or the reason it cannot start. */\nexport function gatewayBoot(argv: string[]): GatewayBoot {\n const worker = flagValue(argv, \"worker\");\n if (!worker) throw new Error(\"no --worker=<script>: the gateway needs a payload to relay to\");\n const name = flagValue(argv, \"app-name\");\n const version = flagValue(argv, \"app-version\");\n if (!name || !version) {\n throw new Error(\"no --app-name=<name> and --app-version=<version>: the gateway names its artifact\");\n }\n return {\n app: { name, version },\n env: flagValue(argv, \"env\") ?? \"unnamed\",\n poolSize: positiveInt(flagValue(argv, \"pool-size\")),\n worker,\n };\n}\n\n/**\n * End a start that failed. The reason has already gone out on the wire, and\n * returning is not enough: the `open()` on a fifo no payload will ever write sits\n * in the threadpool forever, so the loop never drains and the gateway would linger\n * with a dead payload on its books.\n */\nfunction abandon(): never {\n process.exit(GATEWAY_FAILED);\n}\n\n/** Carry a failed start to the client, then let the caller exit non-zero. */\nasync function fail(wire: LineTransport, reason: string): Promise<void> {\n process.stderr.write(`gateway: ${reason}\\n`);\n try {\n await wire.send(errorFrame(reason));\n } catch {\n /* the client is gone too — the exit code still says what happened */\n }\n}\n\n/** Copy everything the payload prints onto the wire as output frames. */\nfunction forwardOutput(stream: NodeJS.ReadableStream, wire: LineTransport, fd: OutputFd): void {\n stream.setEncoding(\"utf-8\");\n stream.on(\"data\", (chunk: string) => {\n void wire.send(outputFrame(fd, chunk)).catch(() => {\n /* the client hung up: the payload's exit is what matters now */\n });\n });\n}\n\n/**\n * Serve one environment over stdin/stdout. Returns the exit code; the caller\n * turns it into `process.exitCode`.\n */\nexport async function runGateway(argv: string[] = process.argv): Promise<number> {\n if (argv.includes(\"--version\")) {\n const name = flagValue(argv, \"app-name\") ?? \"posipaki\";\n const version = flagValue(argv, \"app-version\") ?? \"-\";\n process.stdout.write(`${versionLine({ name, version }, \"gateway\", VERSION)}\\n`);\n return 0;\n }\n const wire = new Lines({ read: process.stdin, write: process.stdout });\n\n let booted: GatewayBoot;\n try {\n booted = gatewayBoot(argv);\n } catch (err) {\n // Nothing has been started yet, so there is nothing to tear down — but the\n // client still deserves the reason rather than a channel that closes on it,\n // and the loop can outlive us: an open stdin nobody will close keeps the\n // process alive, so this is an exit, not a return.\n await fail(wire, errorText(err));\n abandon();\n }\n const { env, poolSize, worker } = booted;\n\n const dir = await mkdtemp(join(tmpdir(), `posipaki-${env.replace(/\\//g, \"-\")}-`));\n const fifoIn = join(dir, \"in\"); // the payload writes, we read\n const fifoOut = join(dir, \"out\"); // we write, the payload reads\n\n let child: ChildProcess | null = null;\n let fifo: FifoUtf8NlineTransport | null = null;\n\n /**\n * Everything this gateway owns, undone. All of it is idempotent, because the\n * exit path and the signal path can both arrive.\n */\n const teardown = async (): Promise<void> => {\n child?.kill();\n await fifo?.close().catch(() => {});\n await rm(dir, { recursive: true, force: true }).catch(() => {});\n };\n\n /**\n * A client that gives up stops us with a signal, and a gateway that died on the\n * spot would leave its payload behind in the environment — a process holding a\n * fifo nobody will ever write, which is a leak the environment cannot clean up\n * for us. So the signals do what the exit path does.\n */\n const onTerminate = () => {\n void teardown()\n .catch(() => {})\n .then(() => process.exit(GATEWAY_FAILED));\n };\n for (const signal of [\"SIGTERM\", \"SIGINT\", \"SIGHUP\"] as const) process.on(signal, onTerminate);\n\n try {\n await run(\"mkfifo\", [\"-m\", \"600\", fifoIn, fifoOut]);\n } catch (err) {\n await fail(wire, `cannot create the environment's fifos: ${errorText(err)}`);\n await teardown();\n abandon();\n }\n\n // Open the read side in the background, then start the payload that unblocks\n // it — the order the fifo handshake requires.\n const connection = FifoUtf8NlineTransport.beginConnect(fifoIn, fifoOut);\n const workerArgs = [\n worker,\n `--env=${env}`,\n ...(poolSize === undefined ? [] : [`--pool-size=${poolSize}`]),\n `--fifo-in=${fifoIn}`,\n `--fifo-out=${fifoOut}`,\n ];\n const workerProc = spawn(process.execPath, workerArgs, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n child = workerProc;\n forwardOutput(workerProc.stdout, wire, 1);\n forwardOutput(workerProc.stderr, wire, 2);\n\n const exited = new Promise<number>((settle) => {\n // A payload killed by a signal (the client hung up, and we killed it) has no\n // code — for us that is a clean end, not a failure.\n workerProc.once(\"exit\", (code) => settle(code ?? 0));\n });\n\n try {\n fifo = await Promise.race([\n connection.transport,\n exited.then((code) => {\n throw new Error(`the payload exited with code ${code} before it opened its channel`);\n }),\n ]);\n } catch (err) {\n await fail(wire, errorText(err));\n await teardown();\n abandon();\n }\n\n // Relay: the protocol is the client's and the payload's business, not ours.\n const relay = fifo;\n relay.onMessage((line) => {\n void wire.send(line).catch(() => {\n /* client gone */\n });\n });\n wire.onMessage((line) => {\n void relay.send(line).catch(() => {\n /* the payload is gone; its exit path cleans up */\n });\n });\n wire.onClose(() => {\n child?.kill();\n });\n\n const code = await exited;\n await teardown();\n await wire.close();\n return code;\n}\n\n// ── entry point ────────────────────────────────────────────────────────────\n\n// Only when this file is the program being run, not when it is imported.\nconst entry = process.argv[1];\nif (entry && resolve(entry) === fileURLToPath(import.meta.url)) {\n void (async () => {\n try {\n process.exitCode = await runGateway();\n } catch (err) {\n process.stderr.write(`gateway: ${errorText(err)}\\n`);\n process.exitCode = GATEWAY_FAILED;\n }\n })();\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-W7guWJeR.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["// ── Library identity ───────────────────────────────────────────────────────\n//\n// posipaki's own release version, as a plain inlined string. A consumer that\n// stages artifacts onto a host it knows nothing about has to name those\n// artifacts after which posipaki they speak, and the bundle that runs there has\n// no node_modules and no posipaki on disk — it can never read a file to find out.\n//\n// It is not the protocol version: that is `VERSION` in ./remote (`json.v1`), and\n// it does not move between releases. The two answer different questions.\n//\n// version.test.ts pins this to package.json, so a release that forgets to bump it\n// fails the suite rather than shipping a lie.\n\n/** posipaki's release version. Must equal package.json's `version`. */\nexport const LIB_VERSION = \"0.33.0\";\n"],"mappings":";;AAcA,MAAa,cAAc"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posipaki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"homepage": "https://posipaki-docs.muromec.nl/",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
"dist"
|
|
11
11
|
],
|
|
12
12
|
"type": "module",
|
|
13
|
+
"workspaces": [
|
|
14
|
+
"packages/*"
|
|
15
|
+
],
|
|
16
|
+
"overrides": {
|
|
17
|
+
"posipaki": "file:."
|
|
18
|
+
},
|
|
13
19
|
"main": "./dist/index.js",
|
|
14
20
|
"types": "./dist/index.d.ts",
|
|
15
21
|
"exports": {
|