@mostra/sdk 0.0.0-stage → 0.2.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.js ADDED
@@ -0,0 +1,484 @@
1
+ import { C as safeRelative, S as linkStaysInside, _ as setupBundle, a as sdkVersion, b as describeTree, c as bundleDir, d as browsersDir, f as checkBrowser, g as readSetup, h as readBundle, i as runtimeOf, l as cacheRoot, m as offline, n as readPins, o as bundlePlatform, p as checkFiles, r as resolveRuntime, s as currentPlatform, t as enclosingBundle, u as installPinned, v as setupExtras, w as redactUrl, x as hashFile, y as checkTree } from "./runtime-CUbnK5Za.js";
2
+ import { resolve } from "node:path";
3
+ import { ARTIFACT_PROGRESS, EVENTS_NOTIFY, EventsNotifyParams, HOST_PROTOCOL_REVISIONS, LineSplitter, RpcError, RpcPeer, WAIT_MAX_MS, artifactPublishMethod, artifactVerifyMethod, createDiagnostic, encodeLine, eventsAckMethod, eventsSubscribeMethod, eventsUnsubscribeMethod, livePushMethod, mostraInitializeMethod, portDraftsMirrorMethod, portIdentityResolveMethod, portSecretsGetMethod, portSubmissionsDeliverMethod, portTrustedControlsActivatedMethod, portTrustedControlsPolicyMethod, resultsGetMethod, stateGetMethod, statePatchMethod, viewUrlMethod, waitMethod } from "@mostra/protocol";
4
+ import { spawn } from "node:child_process";
5
+ //#region src/events.ts
6
+ /**
7
+ * Events as an async iterator (IN-08, D101): pages from `events/subscribe`, in cursor order. A
8
+ * consumer that falls behind pauses the subscription instead of buffering without bound or
9
+ * stalling the connection's other calls: past `highWaterMark` pages it unsubscribes, and once it
10
+ * has caught up it subscribes again after the last cursor it received, so nothing is missed or
11
+ * repeated. With a `name`, a page is acknowledged (`events/ack`) when the next one is asked for,
12
+ * which is when the consumer has handled it; a stream of the same name later resumes there.
13
+ */
14
+ /** Opens a stream; nothing is subscribed until the first page is asked for. */
15
+ function openEventStream(session, options) {
16
+ return { [Symbol.asyncIterator]: () => new EventStream(session, options) };
17
+ }
18
+ var EventStream = class {
19
+ #session;
20
+ #options;
21
+ #high;
22
+ #queue = [];
23
+ #subscription;
24
+ /** The cursor of the last page received, where a resumed subscription starts. */
25
+ #received;
26
+ /** The cursor of the last page handed out; handled once the next page is asked for. */
27
+ #handedOut;
28
+ /** The cursor of the last page the consumer handled, and of the last acknowledged. */
29
+ #handled;
30
+ #acked;
31
+ #paused = false;
32
+ /**
33
+ * Pages that arrived while a subscribe was in flight, for a subscription not known yet: the
34
+ * answer and its first page can reach the session in one chunk, and the page is dispatched
35
+ * before the code awaiting the answer resumes. Kept until the answer names its ID.
36
+ */
37
+ #early;
38
+ /** The unsubscribe of a pause; its answer means no page of that subscription follows. */
39
+ #pausing;
40
+ /** A subscribe whose answer is on its way (`#subscribe`). */
41
+ #subscribing;
42
+ #started = false;
43
+ #ended;
44
+ #wake;
45
+ #removeHandler;
46
+ constructor(session, options) {
47
+ this.#session = session;
48
+ this.#options = options;
49
+ this.#high = Math.max(1, options.highWaterMark ?? 16);
50
+ this.#removeHandler = session.onNotification((params) => this.#receive(params));
51
+ session.closed.then(() => this.#end(new RpcError(createDiagnostic("MOSTRA_HOST_CONNECTION_CLOSED", { message: "The Mostra instance exited while its events were being read." }))));
52
+ options.signal?.addEventListener("abort", () => void this.return(), { once: true });
53
+ }
54
+ #receive(params) {
55
+ const parsed = EventsNotifyParams.safeParse(params);
56
+ if (!parsed.success) return;
57
+ if (parsed.data.subscriptionId !== this.#subscription) {
58
+ this.#early?.push(parsed.data);
59
+ return;
60
+ }
61
+ this.#accept(parsed.data);
62
+ }
63
+ #accept({ events, cursor, compacted }) {
64
+ this.#queue.push({
65
+ events,
66
+ cursor,
67
+ ...compacted ? { compacted } : {}
68
+ });
69
+ this.#received = cursor;
70
+ if (this.#queue.length >= this.#high && !this.#paused) this.#pausing = this.#pause();
71
+ this.#wake?.();
72
+ }
73
+ /**
74
+ * Subscribes after `after`. While the answer is on its way the call is `#subscribing`, so
75
+ * `return()` can wait for it and end the subscription it names (GPT review finding 5).
76
+ */
77
+ #subscribe(after) {
78
+ const opening = this.#open(after);
79
+ this.#subscribing = opening;
80
+ return opening.finally(() => {
81
+ if (this.#subscribing === opening) this.#subscribing = void 0;
82
+ });
83
+ }
84
+ async #open(after) {
85
+ this.#early = [];
86
+ let answer;
87
+ let early;
88
+ try {
89
+ answer = await this.#session.call(eventsSubscribeMethod, {
90
+ ...this.#options.filter ? { filter: this.#options.filter } : {},
91
+ ...after !== void 0 ? { after } : {},
92
+ ...this.#options.name ? { name: this.#options.name } : {}
93
+ });
94
+ } finally {
95
+ early = this.#early;
96
+ this.#early = void 0;
97
+ }
98
+ this.#subscription = answer.subscriptionId;
99
+ this.#received ??= answer.cursor;
100
+ for (const page of early) if (page.subscriptionId === answer.subscriptionId) this.#accept(page);
101
+ }
102
+ async #pause() {
103
+ this.#paused = true;
104
+ const id = this.#subscription;
105
+ if (id === void 0) return;
106
+ const acked = this.#sendAck(id);
107
+ await this.#session.call(eventsUnsubscribeMethod, { subscriptionId: id }).catch(() => void 0);
108
+ await acked.catch((error) => this.#end(error));
109
+ if (this.#subscription === id) this.#subscription = void 0;
110
+ }
111
+ async #resume() {
112
+ await this.#pausing;
113
+ this.#pausing = void 0;
114
+ this.#paused = false;
115
+ await this.#subscribe(this.#received);
116
+ }
117
+ /**
118
+ * Acknowledges what the consumer handled under subscription `id`, if a named stream has handled
119
+ * more than it acknowledged. The line is written when this is called (the session writes each
120
+ * call at once), so it is ordered before anything sent after it.
121
+ */
122
+ #sendAck(id) {
123
+ const cursor = this.#handled;
124
+ if (!this.#options.name || cursor === void 0 || cursor === this.#acked) return Promise.resolve();
125
+ this.#acked = cursor;
126
+ return this.#session.call(eventsAckMethod, {
127
+ subscriptionId: id,
128
+ cursor
129
+ }).then(() => void 0);
130
+ }
131
+ #end(error) {
132
+ if (this.#ended) return;
133
+ this.#ended = error === void 0 ? {} : { error };
134
+ this.#removeHandler();
135
+ this.#wake?.();
136
+ }
137
+ async next() {
138
+ if (!this.#started) {
139
+ this.#started = true;
140
+ await this.#subscribe(this.#options.after);
141
+ }
142
+ if (this.#handedOut !== void 0) this.#handled = this.#handedOut;
143
+ if (this.#paused && !this.#ended && this.#queue.length <= this.#high / 2) await this.#resume();
144
+ if (!this.#paused && this.#subscription !== void 0) await this.#sendAck(this.#subscription);
145
+ for (;;) {
146
+ const page = this.#queue.shift();
147
+ if (page !== void 0) {
148
+ this.#handedOut = page.cursor;
149
+ return {
150
+ done: false,
151
+ value: page
152
+ };
153
+ }
154
+ if (this.#ended) {
155
+ if (this.#ended.error !== void 0) throw this.#ended.error;
156
+ return {
157
+ done: true,
158
+ value: void 0
159
+ };
160
+ }
161
+ if (this.#paused) {
162
+ await this.#resume();
163
+ await this.#sendAck(this.#subscription);
164
+ }
165
+ await new Promise((done) => {
166
+ this.#wake = done;
167
+ });
168
+ this.#wake = void 0;
169
+ }
170
+ }
171
+ /**
172
+ * Ends the stream: the subscription ends, and a named stream acknowledges every page the
173
+ * consumer handled, never the last one handed out (the loop may have stopped inside it).
174
+ */
175
+ async return() {
176
+ this.#end();
177
+ this.#queue.length = 0;
178
+ await this.#pausing;
179
+ await this.#subscribing?.catch(() => void 0);
180
+ const id = this.#subscription;
181
+ if (id !== void 0) {
182
+ this.#subscription = void 0;
183
+ const acked = this.#sendAck(id);
184
+ await this.#session.call(eventsUnsubscribeMethod, { subscriptionId: id }).catch(() => void 0);
185
+ await acked.catch(() => void 0);
186
+ } else if (this.#options.name && this.#handled !== void 0 && this.#handled !== this.#acked) {
187
+ await this.#subscribe(this.#received).catch(() => void 0);
188
+ const again = this.#subscription;
189
+ if (again !== void 0) {
190
+ this.#subscription = void 0;
191
+ const acked = this.#sendAck(again);
192
+ await this.#session.call(eventsUnsubscribeMethod, { subscriptionId: again }).catch(() => void 0);
193
+ await acked.catch(() => void 0);
194
+ }
195
+ }
196
+ return {
197
+ done: true,
198
+ value: void 0
199
+ };
200
+ }
201
+ };
202
+ //#endregion
203
+ //#region src/mostra.ts
204
+ /**
205
+ * `createMostra` (HO-04, D25, D58, D59): a JavaScript host's handle on its own Mostra instance.
206
+ * It finds a verified runtime (the pinned bundle, downloaded once), spawns `mostra-runtime host`
207
+ * for the host's home over stdio, opens the protocol session, serves the host's ports and offers
208
+ * the calls every host makes. The instance lives exactly as long as this handle: `close()` ends
209
+ * it, and so does the host's process exiting.
210
+ *
211
+ * ```ts
212
+ * const mostra = await createMostra({
213
+ * home: "/var/lib/inbox/mostra",
214
+ * client: { name: "inbox-app", version: "1.4.0" },
215
+ * ports: { submissions: { deliver: async (envelope) => ({ accepted: true }) } },
216
+ * });
217
+ * const receipt = await mostra.publish("/work/inbox");
218
+ * const outcome = await mostra.wait(receipt.artifactId, "submitted");
219
+ * await mostra.close();
220
+ * ```
221
+ */
222
+ /** The port methods the SDK serves, by port. */
223
+ const PORT_METHODS = {
224
+ identity: [portIdentityResolveMethod],
225
+ submissions: [portSubmissionsDeliverMethod],
226
+ drafts: [portDraftsMirrorMethod],
227
+ secrets: [portSecretsGetMethod],
228
+ trustedControls: [portTrustedControlsPolicyMethod, portTrustedControlsActivatedMethod]
229
+ };
230
+ /** The client name of an SDK host that names none. */
231
+ const SDK_CLIENT_NAME = "mostra-sdk";
232
+ /** `art_…` is an artifact ID; anything else a source path, resolved against the working directory. */
233
+ function artifactRef(input) {
234
+ if (typeof input !== "string") return input;
235
+ return input.startsWith("art_") ? { artifactId: input } : { path: resolve(input) };
236
+ }
237
+ function closedError(message) {
238
+ return new RpcError(createDiagnostic("MOSTRA_HOST_CONNECTION_CLOSED", { message }));
239
+ }
240
+ /** The handler for one port method, its params already checked. */
241
+ function portHandler(ports, method) {
242
+ switch (method) {
243
+ case portIdentityResolveMethod.method: return ports.identity && ((params) => ports.identity?.resolve(params));
244
+ case portSubmissionsDeliverMethod.method: return ports.submissions && ((params) => ports.submissions?.deliver(params));
245
+ case portDraftsMirrorMethod.method: return ports.drafts && ((params) => ports.drafts?.mirror(params));
246
+ case portSecretsGetMethod.method: return ports.secrets && ((params) => ports.secrets?.get(params));
247
+ case portTrustedControlsPolicyMethod.method: return ports.trustedControls && ((params) => ports.trustedControls?.policy(params.artifact));
248
+ case portTrustedControlsActivatedMethod.method: return ports.trustedControls && (async (params) => {
249
+ await ports.trustedControls?.activated?.(params);
250
+ return {};
251
+ });
252
+ default: return;
253
+ }
254
+ }
255
+ /** The port names `ports` registers; anything unknown is a mistake worth failing on. */
256
+ function portNames(ports) {
257
+ const names = [];
258
+ for (const [name, value] of Object.entries(ports)) {
259
+ if (value === void 0) continue;
260
+ if (!(name in PORT_METHODS)) throw new TypeError(`createMostra: no port "${name}" (the SDK serves ${Object.keys(PORT_METHODS).join(", ")}).`);
261
+ names.push(name);
262
+ }
263
+ return names;
264
+ }
265
+ /** Ends the child: stdin closed, then SIGTERM after `graceMs`, then SIGKILL after 5 s more. */
266
+ function stopChild(child, exited, graceMs) {
267
+ child.stdin?.end();
268
+ const term = setTimeout(() => child.kill("SIGTERM"), graceMs);
269
+ const kill = setTimeout(() => child.kill("SIGKILL"), graceMs + 5e3);
270
+ return exited.finally(() => {
271
+ clearTimeout(term);
272
+ clearTimeout(kill);
273
+ });
274
+ }
275
+ /** `callback`, made unable to throw into its caller. */
276
+ function guarded(callback) {
277
+ if (callback === void 0) return void 0;
278
+ return (...args) => {
279
+ try {
280
+ callback(...args);
281
+ } catch {}
282
+ };
283
+ }
284
+ /**
285
+ * Spawns an instance for `home` and opens its session. Rejects with the instance's own diagnostic
286
+ * when it can't start (`MOSTRA_HOME_LOCKED`, `MOSTRA_PLATFORM_UNSUPPORTED` …), or with the
287
+ * runtime's (`MOSTRA_ENV_BUNDLE_*`) when no verified runtime can be found.
288
+ */
289
+ async function createMostra(options) {
290
+ const onLog = guarded(options.onLog);
291
+ const onProgress = guarded(options.onProgress);
292
+ const onPortError = guarded(options.onPortError);
293
+ const ports = options.ports ?? {};
294
+ const names = portNames(ports);
295
+ const env = options.env ?? process.env;
296
+ const home = resolve(options.home);
297
+ const runtime = await resolveRuntime({
298
+ bundle: options.bundle,
299
+ env,
300
+ signal: options.signal,
301
+ ...onProgress ? { onProgress } : {}
302
+ });
303
+ options.signal?.throwIfAborted();
304
+ const args = [
305
+ runtime.runtimeEntry,
306
+ "host",
307
+ "--home",
308
+ home
309
+ ];
310
+ if (options.bind !== void 0) args.push("--bind", options.bind);
311
+ if (options.urlHost !== void 0) args.push("--url-host", options.urlHost);
312
+ const child = spawn(runtime.node, args, {
313
+ stdio: [
314
+ "pipe",
315
+ "pipe",
316
+ "pipe"
317
+ ],
318
+ env
319
+ });
320
+ const logTail = [];
321
+ const exited = new Promise((done) => {
322
+ child.on("close", (code, signal) => done({
323
+ code,
324
+ signal
325
+ }));
326
+ child.on("error", () => done({
327
+ code: null,
328
+ signal: null
329
+ }));
330
+ });
331
+ let spawnError;
332
+ child.on("error", (error) => {
333
+ spawnError = error;
334
+ });
335
+ const errLines = new LineSplitter();
336
+ child.stderr?.setEncoding("utf8");
337
+ child.stderr?.on("data", (chunk) => {
338
+ for (const line of errLines.push(chunk)) {
339
+ logTail.push(line);
340
+ if (logTail.length > 20) logTail.shift();
341
+ onLog?.(line);
342
+ }
343
+ });
344
+ const notificationHandlers = /* @__PURE__ */ new Map();
345
+ const peer = new RpcPeer({
346
+ send: (message) => {
347
+ if (child.stdin?.writable) child.stdin.write(encodeLine(message));
348
+ },
349
+ onRequest: async (method, params) => {
350
+ const definition = Object.values(PORT_METHODS).flat().find((candidate) => candidate.method === method);
351
+ const handler = definition && portHandler(ports, method);
352
+ if (!definition || !handler) throw new RpcError(createDiagnostic("MOSTRA_HOST_METHOD_UNKNOWN", { message: `This host serves no method ${method}.` }), -32601);
353
+ const parsed = definition.params.safeParse(params);
354
+ if (!parsed.success) throw new RpcError(createDiagnostic("MOSTRA_HOST_INVALID_PARAMS", { message: `The params of ${method} don't match its schema.` }), -32602);
355
+ const answer = await handler(parsed.data);
356
+ const checked = definition.result.safeParse(answer ?? null);
357
+ if (!checked.success) throw new RpcError(createDiagnostic("MOSTRA_HOST_INTERNAL_ERROR", { message: `The host's ${method} answered something its schema refuses.` }));
358
+ return checked.data;
359
+ },
360
+ onNotification: (method, params) => {
361
+ for (const handler of notificationHandlers.get(method) ?? []) handler(params);
362
+ },
363
+ onInternalError: (error, method) => onPortError?.(error, method)
364
+ });
365
+ const lines = new LineSplitter();
366
+ child.stdout?.setEncoding("utf8");
367
+ child.stdout?.on("data", (chunk) => {
368
+ for (const line of lines.push(chunk)) peer.receiveLine(line);
369
+ });
370
+ child.stdout?.on("close", () => {
371
+ peer.close(createDiagnostic("MOSTRA_HOST_CONNECTION_CLOSED", { message: `The Mostra instance for ${home} exited${logTail.length > 0 ? `; its last log line: ${logTail.at(-1)}` : ""}.` }));
372
+ });
373
+ child.stdin?.on("error", (error) => {
374
+ peer.close(createDiagnostic("MOSTRA_HOST_CONNECTION_CLOSED", { message: `The Mostra instance for ${home} stopped reading (${error.code ?? error.message}).` }));
375
+ });
376
+ child.stdout?.on("error", () => {});
377
+ child.stderr?.on("error", () => {});
378
+ const abort = () => {
379
+ stopChild(child, exited, 0);
380
+ };
381
+ options.signal?.addEventListener("abort", abort, { once: true });
382
+ const homeConfig = {
383
+ path: home,
384
+ ...options.expectPorts !== false ? { ports: names } : {},
385
+ ...options.settings?.submission ? { submission: options.settings.submission } : {},
386
+ ...options.settings?.submitChannels ? { submitChannels: options.settings.submitChannels } : {},
387
+ ...options.settings?.dependencies ? { dependencies: options.settings.dependencies } : {}
388
+ };
389
+ let session;
390
+ try {
391
+ session = await peer.call(mostraInitializeMethod, {
392
+ client: {
393
+ name: options.client?.name ?? "mostra-sdk",
394
+ version: options.client?.version ?? sdkVersion()
395
+ },
396
+ revisions: [...HOST_PROTOCOL_REVISIONS],
397
+ capabilities: { notifications: [EVENTS_NOTIFY, ARTIFACT_PROGRESS] },
398
+ ports: names,
399
+ home: homeConfig
400
+ });
401
+ } catch (error) {
402
+ await stopChild(child, exited, 2e3);
403
+ if (spawnError) throw closedError(`Couldn't start ${runtime.node}: ${spawnError.message}`);
404
+ throw error;
405
+ } finally {
406
+ options.signal?.removeEventListener("abort", abort);
407
+ }
408
+ const call = (method, params, callOptions) => peer.call(method, params, callOptions?.timeoutMs !== void 0 ? { timeoutMs: callOptions.timeoutMs } : {});
409
+ let closing;
410
+ return {
411
+ instance: session.instance,
412
+ capabilities: session.capabilities,
413
+ revision: session.revision,
414
+ warnings: session.warnings,
415
+ runtime,
416
+ closed: exited,
417
+ call,
418
+ publish: (source, publishOptions = {}) => call(artifactPublishMethod, {
419
+ source: artifactRef(source),
420
+ ...publishOptions
421
+ }),
422
+ verify: (source, verifyOptions = {}) => call(artifactVerifyMethod, {
423
+ source: artifactRef(source),
424
+ ...verifyOptions
425
+ }),
426
+ wait: (artifact, until, waitOptions = {}) => {
427
+ const signal = waitOptions.signal;
428
+ if (signal?.aborted) return Promise.reject(signal.reason);
429
+ const pending = call(waitMethod, {
430
+ artifact: artifactRef(artifact),
431
+ until,
432
+ timeoutMs: waitOptions.timeoutMs ?? WAIT_MAX_MS,
433
+ ...waitOptions.after !== void 0 ? { after: waitOptions.after } : {}
434
+ });
435
+ if (!signal) return pending;
436
+ return new Promise((done, fail) => {
437
+ const onAbort = () => fail(signal.reason);
438
+ signal.addEventListener("abort", onAbort, { once: true });
439
+ pending.then(done, fail).finally(() => signal.removeEventListener("abort", onAbort));
440
+ });
441
+ },
442
+ results: ((artifact, resultsOptions = {}) => call(resultsGetMethod, {
443
+ artifact: artifactRef(artifact),
444
+ ...resultsOptions
445
+ })),
446
+ getState: (artifact, ids) => call(stateGetMethod, {
447
+ artifact: artifactRef(artifact),
448
+ ...ids ? { ids: [...ids] } : {}
449
+ }),
450
+ patchState: (artifact, patch) => call(statePatchMethod, {
451
+ artifact: artifactRef(artifact),
452
+ ...patch
453
+ }),
454
+ pushLive: (artifact, push) => call(livePushMethod, {
455
+ artifact: artifactRef(artifact),
456
+ ...push
457
+ }),
458
+ viewUrl: (artifact, urlOptions = {}) => call(viewUrlMethod, {
459
+ artifact: artifactRef(artifact),
460
+ ...urlOptions
461
+ }),
462
+ events: (streamOptions = {}) => openEventStream({
463
+ call,
464
+ onNotification: (handler) => {
465
+ let set = notificationHandlers.get(EVENTS_NOTIFY);
466
+ if (!set) {
467
+ set = /* @__PURE__ */ new Set();
468
+ notificationHandlers.set(EVENTS_NOTIFY, set);
469
+ }
470
+ set.add(handler);
471
+ return () => set?.delete(handler);
472
+ },
473
+ closed: exited.then(() => void 0)
474
+ }, streamOptions),
475
+ close: () => {
476
+ closing ??= stopChild(child, exited, 1e4);
477
+ return closing;
478
+ }
479
+ };
480
+ }
481
+ //#endregion
482
+ export { SDK_CLIENT_NAME, artifactRef, browsersDir, bundleDir, bundlePlatform, cacheRoot, checkBrowser, checkFiles, checkTree, createMostra, currentPlatform, describeTree, enclosingBundle, hashFile, installPinned, linkStaysInside, offline, readBundle, readPins, readSetup, redactUrl, resolveRuntime, runtimeOf, safeRelative, sdkVersion, setupBundle, setupExtras };
483
+
484
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/events.ts","../src/mostra.ts"],"sourcesContent":["/**\n * Events as an async iterator (IN-08, D101): pages from `events/subscribe`, in cursor order. A\n * consumer that falls behind pauses the subscription instead of buffering without bound or\n * stalling the connection's other calls: past `highWaterMark` pages it unsubscribes, and once it\n * has caught up it subscribes again after the last cursor it received, so nothing is missed or\n * repeated. With a `name`, a page is acknowledged (`events/ack`) when the next one is asked for,\n * which is when the consumer has handled it; a stream of the same name later resumes there.\n */\nimport {\n createDiagnostic,\n type Diagnostic,\n type EventFilter,\n type EventRecord,\n EventsNotifyParams,\n eventsAckMethod,\n eventsSubscribeMethod,\n eventsUnsubscribeMethod,\n type MethodDefinition,\n RpcError,\n} from \"@mostra/protocol\";\n\n/** One page of events. */\nexport interface EventPage {\n /** The events, in cursor order. */\n readonly events: readonly EventRecord[];\n /** Everything up to this cursor was examined: the cursor to resume after. */\n readonly cursor: string;\n /** Set when compaction removed events in this range (`MOSTRA_HOST_CURSOR_COMPACTED`). */\n readonly compacted?: Diagnostic;\n}\n\n/** Options of a stream. */\nexport interface EventStreamOptions {\n /** Which events: artifacts and type patterns. Default: every event. */\n readonly filter?: EventFilter;\n /** Start after this cursor. Default: a named stream's acknowledged cursor, else now. */\n readonly after?: string;\n /** A durable subscriber name: pages are acknowledged under it as they are handled. */\n readonly name?: string;\n /** Pages held before the subscription pauses. Default 16. */\n readonly highWaterMark?: number;\n /** Ends the stream. */\n readonly signal?: AbortSignal;\n}\n\n/** What a stream needs of the session. */\nexport interface EventSession {\n call<P, R>(method: MethodDefinition<P, R>, params: P): Promise<R>;\n /** Registers a handler for `events/notify`; returns its removal. */\n onNotification(handler: (params: unknown) => void): () => void;\n /** Resolves when the session ends. */\n readonly closed: Promise<void>;\n}\n\n/** Opens a stream; nothing is subscribed until the first page is asked for. */\nexport function openEventStream(\n session: EventSession,\n options: EventStreamOptions,\n): AsyncIterable<EventPage> {\n return {\n [Symbol.asyncIterator]: () => new EventStream(session, options),\n };\n}\n\nclass EventStream implements AsyncIterator<EventPage> {\n readonly #session: EventSession;\n readonly #options: EventStreamOptions;\n readonly #high: number;\n readonly #queue: EventPage[] = [];\n #subscription: string | undefined;\n /** The cursor of the last page received, where a resumed subscription starts. */\n #received: string | undefined;\n /** The cursor of the last page handed out; handled once the next page is asked for. */\n #handedOut: string | undefined;\n /** The cursor of the last page the consumer handled, and of the last acknowledged. */\n #handled: string | undefined;\n #acked: string | undefined;\n #paused = false;\n /**\n * Pages that arrived while a subscribe was in flight, for a subscription not known yet: the\n * answer and its first page can reach the session in one chunk, and the page is dispatched\n * before the code awaiting the answer resumes. Kept until the answer names its ID.\n */\n #early: EventsNotifyParams[] | undefined;\n /** The unsubscribe of a pause; its answer means no page of that subscription follows. */\n #pausing: Promise<void> | undefined;\n /** A subscribe whose answer is on its way (`#subscribe`). */\n #subscribing: Promise<void> | undefined;\n #started = false;\n #ended: { error?: unknown } | undefined;\n #wake: (() => void) | undefined;\n readonly #removeHandler: () => void;\n\n constructor(session: EventSession, options: EventStreamOptions) {\n this.#session = session;\n this.#options = options;\n this.#high = Math.max(1, options.highWaterMark ?? 16);\n this.#removeHandler = session.onNotification((params) => this.#receive(params));\n void session.closed.then(() =>\n this.#end(\n new RpcError(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: \"The Mostra instance exited while its events were being read.\",\n }),\n ),\n ),\n );\n options.signal?.addEventListener(\"abort\", () => void this.return(), { once: true });\n }\n\n #receive(params: unknown): void {\n const parsed = EventsNotifyParams.safeParse(params);\n if (!parsed.success) return;\n if (parsed.data.subscriptionId !== this.#subscription) {\n this.#early?.push(parsed.data);\n return;\n }\n this.#accept(parsed.data);\n }\n\n #accept({ events, cursor, compacted }: EventsNotifyParams): void {\n this.#queue.push({ events, cursor, ...(compacted ? { compacted } : {}) });\n this.#received = cursor;\n if (this.#queue.length >= this.#high && !this.#paused) this.#pausing = this.#pause();\n this.#wake?.();\n }\n\n /**\n * Subscribes after `after`. While the answer is on its way the call is `#subscribing`, so\n * `return()` can wait for it and end the subscription it names (GPT review finding 5).\n */\n #subscribe(after: string | undefined): Promise<void> {\n const opening = this.#open(after);\n this.#subscribing = opening;\n return opening.finally(() => {\n if (this.#subscribing === opening) this.#subscribing = undefined;\n });\n }\n\n async #open(after: string | undefined): Promise<void> {\n this.#early = [];\n let answer: { subscriptionId: string; cursor: string };\n let early: EventsNotifyParams[];\n try {\n answer = await this.#session.call(eventsSubscribeMethod, {\n ...(this.#options.filter ? { filter: this.#options.filter } : {}),\n ...(after !== undefined ? { after } : {}),\n ...(this.#options.name ? { name: this.#options.name } : {}),\n });\n } finally {\n early = this.#early;\n this.#early = undefined;\n }\n // Synchronous from here: no page can be dispatched before the ID is known.\n this.#subscription = answer.subscriptionId;\n this.#received ??= answer.cursor;\n // The pages of this subscription that came before its answer was handled, in order.\n for (const page of early) if (page.subscriptionId === answer.subscriptionId) this.#accept(page);\n }\n\n async #pause(): Promise<void> {\n this.#paused = true;\n const id = this.#subscription;\n if (id === undefined) return;\n // What the consumer handled is acknowledged while the subscription still exists: the ack goes\n // out before the unsubscribe on the connection, which keeps their order.\n const acked = this.#sendAck(id);\n // Pages already on their way still arrive under this ID and are kept. The answer comes after\n // them on the connection, so once it is here the subscription is over.\n await this.#session\n .call(eventsUnsubscribeMethod, { subscriptionId: id })\n .catch(() => undefined);\n // A pause runs from a page's arrival, where nobody awaits it, so it never rejects (Opus review\n // finding 7): an ack the instance refused (or a connection gone) ends the stream with that\n // error, which the consumer's next() then throws.\n await acked.catch((error: unknown) => this.#end(error));\n if (this.#subscription === id) this.#subscription = undefined;\n }\n\n async #resume(): Promise<void> {\n await this.#pausing;\n this.#pausing = undefined;\n this.#paused = false;\n await this.#subscribe(this.#received);\n }\n\n /**\n * Acknowledges what the consumer handled under subscription `id`, if a named stream has handled\n * more than it acknowledged. The line is written when this is called (the session writes each\n * call at once), so it is ordered before anything sent after it.\n */\n #sendAck(id: string): Promise<void> {\n const cursor = this.#handled;\n if (!this.#options.name || cursor === undefined || cursor === this.#acked)\n return Promise.resolve();\n this.#acked = cursor;\n return this.#session\n .call(eventsAckMethod, { subscriptionId: id, cursor })\n .then(() => undefined);\n }\n\n #end(error?: unknown): void {\n if (this.#ended) return;\n this.#ended = error === undefined ? {} : { error };\n this.#removeHandler();\n this.#wake?.();\n }\n\n async next(): Promise<IteratorResult<EventPage>> {\n if (!this.#started) {\n this.#started = true;\n await this.#subscribe(this.#options.after);\n }\n // Asking for the next page means the consumer handled the one before it.\n if (this.#handedOut !== undefined) this.#handled = this.#handedOut;\n if (this.#paused && !this.#ended && this.#queue.length <= this.#high / 2) await this.#resume();\n // Never under a subscription being paused: its unsubscribe may already be on its way, and\n // the pause acknowledges what was handled before it.\n if (!this.#paused && this.#subscription !== undefined) await this.#sendAck(this.#subscription);\n for (;;) {\n const page = this.#queue.shift();\n if (page !== undefined) {\n this.#handedOut = page.cursor;\n return { done: false, value: page };\n }\n if (this.#ended) {\n if (this.#ended.error !== undefined) throw this.#ended.error;\n return { done: true, value: undefined };\n }\n if (this.#paused) {\n await this.#resume();\n await this.#sendAck(this.#subscription as string);\n }\n await new Promise<void>((done) => {\n this.#wake = done;\n });\n this.#wake = undefined;\n }\n }\n\n /**\n * Ends the stream: the subscription ends, and a named stream acknowledges every page the\n * consumer handled, never the last one handed out (the loop may have stopped inside it).\n */\n async return(): Promise<IteratorResult<EventPage>> {\n this.#end();\n this.#queue.length = 0;\n await this.#pausing;\n // A subscribe on its way names a subscription that exists on the instance once it is\n // answered: wait for it, so it is ended below like any other.\n await this.#subscribing?.catch(() => undefined);\n const id = this.#subscription;\n if (id !== undefined) {\n this.#subscription = undefined;\n const acked = this.#sendAck(id);\n await this.#session\n .call(eventsUnsubscribeMethod, { subscriptionId: id })\n .catch(() => undefined);\n await acked.catch(() => undefined);\n } else if (this.#options.name && this.#handled !== undefined && this.#handled !== this.#acked) {\n // Paused when it ended, with pages handled since: a subscription of the name, just long\n // enough to acknowledge them (its pages are ignored; the handler is gone).\n await this.#subscribe(this.#received).catch(() => undefined);\n const again = this.#subscription;\n if (again !== undefined) {\n this.#subscription = undefined;\n const acked = this.#sendAck(again);\n await this.#session\n .call(eventsUnsubscribeMethod, { subscriptionId: again })\n .catch(() => undefined);\n await acked.catch(() => undefined);\n }\n }\n return { done: true, value: undefined };\n }\n}\n","/**\n * `createMostra` (HO-04, D25, D58, D59): a JavaScript host's handle on its own Mostra instance.\n * It finds a verified runtime (the pinned bundle, downloaded once), spawns `mostra-runtime host`\n * for the host's home over stdio, opens the protocol session, serves the host's ports and offers\n * the calls every host makes. The instance lives exactly as long as this handle: `close()` ends\n * it, and so does the host's process exiting.\n *\n * ```ts\n * const mostra = await createMostra({\n * home: \"/var/lib/inbox/mostra\",\n * client: { name: \"inbox-app\", version: \"1.4.0\" },\n * ports: { submissions: { deliver: async (envelope) => ({ accepted: true }) } },\n * });\n * const receipt = await mostra.publish(\"/work/inbox\");\n * const outcome = await mostra.wait(receipt.artifactId, \"submitted\");\n * await mostra.close();\n * ```\n */\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { resolve } from \"node:path\";\nimport {\n type AnyMethod,\n ARTIFACT_PROGRESS,\n type ArtifactRef,\n type ArtifactSummary,\n artifactPublishMethod,\n artifactVerifyMethod,\n type CapabilityLink,\n type ControlsPolicy,\n createDiagnostic,\n type DependencyPolicy,\n type Diagnostic,\n type DraftMirror,\n EVENTS_NOTIFY,\n encodeLine,\n HOST_PROTOCOL_REVISIONS,\n type HomeConfig,\n type InstanceCapabilities,\n type InstanceInfo,\n LineSplitter,\n type LivePushParams,\n type LivePushResult,\n livePushMethod,\n type MethodDefinition,\n type MostraInitializeResult,\n mostraInitializeMethod,\n type PortIdentityResolveParams,\n type PortName,\n type PortSecretsGetParams,\n type PortTrustedControlsActivatedParams,\n type PublishReceipt,\n portDraftsMirrorMethod,\n portIdentityResolveMethod,\n portSecretsGetMethod,\n portSubmissionsDeliverMethod,\n portTrustedControlsActivatedMethod,\n portTrustedControlsPolicyMethod,\n type Results,\n type ResultsMarkdown,\n RpcError,\n RpcPeer,\n resultsGetMethod,\n type SourceRef,\n type StateGetResult,\n type StatePatchParams,\n type StatePatchResult,\n type SubmissionEnvelope,\n type SubmissionPolicy,\n type SubmissionsDeliverResult,\n type SubmitChannel,\n stateGetMethod,\n statePatchMethod,\n type VerifyVerdict,\n type ViewerSpec,\n type ViewUrlParams,\n viewUrlMethod,\n WAIT_MAX_MS,\n type WaitCondition,\n type WaitResult,\n waitMethod,\n} from \"@mostra/protocol\";\nimport type { InstallProgress } from \"./bundle/cache.ts\";\nimport type { Environment } from \"./bundle/setup.ts\";\nimport { type EventPage, type EventStreamOptions, openEventStream } from \"./events.ts\";\nimport { type Runtime, resolveRuntime } from \"./runtime.ts\";\nimport { sdkVersion } from \"./version.ts\";\n\n/**\n * The ports a host may serve (INTERFACES §7). Each is optional and has a standalone default;\n * Mostra calls a port only on the instance this handle spawned, and every call is idempotent by\n * its stable ID, so a port may see the same call again after a crash.\n */\nexport interface Ports {\n /**\n * Names the person behind a link minted without a viewer, from the request's headers (D143):\n * `{ ref, name? }` (one viewer per `ref`), or `null` for the home's local user. Answer within\n * 5 s; a failure refuses the link rather than guess.\n */\n readonly identity?: {\n resolve(request: PortIdentityResolveParams): ViewerSpec | null | Promise<ViewerSpec | null>;\n };\n /** Receives each submission once it is durable (D58, D105). Answer within 30 s. */\n readonly submissions?: {\n deliver(\n envelope: SubmissionEnvelope,\n ): SubmissionsDeliverResult | Promise<SubmissionsDeliverResult>;\n };\n /** Receives each viewer draft's latest revision, best effort, never retried (IN-06). */\n readonly drafts?: { mirror(draft: DraftMirror): void | Promise<void> };\n /** Answers `secret()` first, before the home's store (D131); `null` falls back. */\n readonly secrets?: {\n get(request: PortSecretsGetParams): string | null | Promise<string | null>;\n };\n /** Decides each view's submit channels and trusted controls (D108). Answer within 5 s. */\n readonly trustedControls?: {\n policy(artifact: ArtifactSummary): ControlsPolicy | Promise<ControlsPolicy>;\n activated?(event: PortTrustedControlsActivatedParams): void | Promise<void>;\n };\n}\n\n/** Home settings a host chooses (INTERFACES §6 `HomeConfig`), kept in the home's `config.json`. */\nexport interface HomeSettings {\n /** The submission policy for artifacts whose publish names none (D102). */\n readonly submission?: SubmissionPolicy;\n /** The channels that may submit (D40, D108); absent, both. */\n readonly submitChannels?: readonly SubmitChannel[];\n /** How packages enter an artifact's lockfile (D43, D95). */\n readonly dependencies?: DependencyPolicy;\n}\n\n/** Options of {@link createMostra}. */\nexport interface MostraOptions {\n /** The home this host owns (D45). Created 0700 when missing. */\n readonly home: string;\n /**\n * Who the host is. The name owns the host's deliveries across restarts (D105), so keep it\n * stable; it defaults to `mostra-sdk`.\n */\n readonly client?: { readonly name: string; readonly version?: string };\n /** The ports this host serves. */\n readonly ports?: Ports;\n /**\n * Whether submissions made while this host is away wait for it (D105): the registered ports\n * are recorded as ports this host always provides. Default `true`; `false` leaves the home's\n * record as it is.\n */\n readonly expectPorts?: boolean;\n /** Home settings, kept in the home's `config.json` for every later client. */\n readonly settings?: HomeSettings;\n /** An extracted runtime bundle to use instead of the pinned one. */\n readonly bundle?: string;\n /** The gateway's bind address and URL host (D99); loopback by default. */\n readonly bind?: string;\n /** The host name URLs use, which a wildcard `bind` needs (D99). */\n readonly urlHost?: string;\n /** The instance's environment. Default: this process's. */\n readonly env?: Environment;\n /** Each line the instance logs (JSON, on its stderr). A callback that throws is ignored. */\n readonly onLog?: (line: string) => void;\n /** Progress of the first download or setup of the runtime bundle. A throw is ignored. */\n readonly onProgress?: (progress: InstallProgress) => void;\n /**\n * An exception a port threw; the instance got `MOSTRA_HOST_INTERNAL_ERROR` and will retry. A\n * callback that throws is ignored, and the instance is answered all the same.\n */\n readonly onPortError?: (error: unknown, method: string) => void;\n /** Gives up finding the runtime and starting the instance. */\n readonly signal?: AbortSignal;\n}\n\n/** How the instance ended. */\nexport interface InstanceExit {\n /** Its exit code: 0 after a clean stop. */\n readonly code: number | null;\n /** The signal that ended it, if one did. */\n readonly signal: NodeJS.Signals | null;\n}\n\n/** An artifact by ID (`art_…`), by source path, or as the protocol's reference. */\nexport type ArtifactInput = string | ArtifactRef;\n\n/** A host's handle on its instance. */\nexport interface Mostra {\n /** The instance: release, home, platform, PID and mode. */\n readonly instance: InstanceInfo;\n /** What the instance serves. */\n readonly capabilities: InstanceCapabilities;\n /** The negotiated host protocol revision. */\n readonly revision: string;\n /** The session's warnings (an unsupported port, for example). */\n readonly warnings: readonly Diagnostic[];\n /** The runtime it runs on. */\n readonly runtime: Runtime;\n /** Resolves when the instance has exited, for whatever reason. */\n readonly closed: Promise<InstanceExit>;\n /** Calls any host protocol method by its definition from `@mostra/protocol`. */\n call<P, R>(method: MethodDefinition<P, R>, params: P, options?: CallOptions): Promise<R>;\n /** `artifact/publish`: verifies (D41) and publishes; durable before it resolves (H-07). */\n publish(\n source: ArtifactInput,\n options?: {\n readonly force?: boolean;\n readonly policy?: SubmissionPolicy;\n readonly requestId?: string;\n },\n ): Promise<PublishReceipt>;\n /** `artifact/verify`: one verdict, never a publish (D138). */\n verify(\n source: ArtifactInput,\n options?: { readonly screenshots?: \"on-failure\" | \"always\" | \"never\" },\n ): Promise<VerifyVerdict>;\n /**\n * `wait` (IN-10, D103): until the condition holds or `timeoutMs` passes (default: the longest\n * timer, about 24.8 days). `still_waiting` carries the cursor to wait from next; an aborted wait\n * rejects with the signal's reason and loses nothing.\n */\n wait(\n artifact: ArtifactInput,\n until: WaitCondition,\n options?: {\n readonly after?: string;\n readonly timeoutMs?: number;\n readonly signal?: AbortSignal;\n },\n ): Promise<WaitResult>;\n /** `results/get`: a submission as JSON (the default) or Markdown (INTERFACES §8). */\n results(\n artifact: ArtifactInput,\n options?: { readonly submission?: string; readonly format?: \"json\" },\n ): Promise<Results>;\n /** `results/get` as Markdown, for an agent to read. */\n results(\n artifact: ArtifactInput,\n options: { readonly submission?: string; readonly format: \"md\" },\n ): Promise<ResultsMarkdown>;\n /** `state/get`: artifact-scoped fields (D63). */\n getState(artifact: ArtifactInput, ids?: readonly string[]): Promise<StateGetResult>;\n /** `state/patch`: writes artifact-scoped fields through compare-and-set (D63). */\n patchState(\n artifact: ArtifactInput,\n patch: Omit<StatePatchParams, \"artifact\">,\n ): Promise<StatePatchResult>;\n /** `live/push`: a live source's next value (D56). */\n pushLive(\n artifact: ArtifactInput,\n push: Omit<LivePushParams, \"artifact\">,\n ): Promise<LivePushResult>;\n /** `view/url`: a new capability link (D97). */\n viewUrl(\n artifact: ArtifactInput,\n options?: Omit<ViewUrlParams, \"artifact\">,\n ): Promise<CapabilityLink>;\n /**\n * The events matching `options.filter`, page by page, in cursor order, with no gap across a\n * pause (`events/subscribe`). With a `name`, each page is acknowledged when the next one is\n * asked for, so a later stream of that name resumes after the last page handled.\n */\n events(options?: EventStreamOptions): AsyncIterable<EventPage>;\n /** Ends the instance: its stdio closes, and it is stopped if it doesn't exit within 10 s. */\n close(): Promise<InstanceExit>;\n}\n\n/** Options of one call. */\nexport interface CallOptions {\n /** Gives up after this many milliseconds with `MOSTRA_HOST_TIMEOUT`. Default: none. */\n readonly timeoutMs?: number;\n}\n\n/** The port methods the SDK serves, by port. */\nconst PORT_METHODS = {\n identity: [portIdentityResolveMethod],\n submissions: [portSubmissionsDeliverMethod],\n drafts: [portDraftsMirrorMethod],\n secrets: [portSecretsGetMethod],\n trustedControls: [portTrustedControlsPolicyMethod, portTrustedControlsActivatedMethod],\n} as const satisfies Record<keyof Ports, readonly AnyMethod[]>;\n\n/** The client name of an SDK host that names none. */\nexport const SDK_CLIENT_NAME = \"mostra-sdk\";\n\n/** `art_…` is an artifact ID; anything else a source path, resolved against the working directory. */\nexport function artifactRef(input: ArtifactInput): ArtifactRef & SourceRef {\n if (typeof input !== \"string\") return input as ArtifactRef & SourceRef;\n return input.startsWith(\"art_\") ? { artifactId: input } : { path: resolve(input) };\n}\n\nfunction closedError(message: string): RpcError {\n return new RpcError(createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", { message }));\n}\n\n/** The handler for one port method, its params already checked. */\nfunction portHandler(ports: Ports, method: string): ((params: unknown) => unknown) | undefined {\n switch (method) {\n case portIdentityResolveMethod.method:\n return (\n ports.identity && ((params) => ports.identity?.resolve(params as PortIdentityResolveParams))\n );\n case portSubmissionsDeliverMethod.method:\n return (\n ports.submissions && ((params) => ports.submissions?.deliver(params as SubmissionEnvelope))\n );\n case portDraftsMirrorMethod.method:\n return ports.drafts && ((params) => ports.drafts?.mirror(params as DraftMirror));\n case portSecretsGetMethod.method:\n return ports.secrets && ((params) => ports.secrets?.get(params as PortSecretsGetParams));\n case portTrustedControlsPolicyMethod.method:\n return (\n ports.trustedControls &&\n ((params) =>\n ports.trustedControls?.policy((params as { artifact: ArtifactSummary }).artifact))\n );\n case portTrustedControlsActivatedMethod.method:\n return (\n ports.trustedControls &&\n (async (params) => {\n await ports.trustedControls?.activated?.(params as PortTrustedControlsActivatedParams);\n return {};\n })\n );\n default:\n return undefined;\n }\n}\n\n/** The port names `ports` registers; anything unknown is a mistake worth failing on. */\nfunction portNames(ports: Ports): PortName[] {\n const names: PortName[] = [];\n for (const [name, value] of Object.entries(ports)) {\n if (value === undefined) continue;\n if (!(name in PORT_METHODS)) {\n throw new TypeError(\n `createMostra: no port \"${name}\" (the SDK serves ${Object.keys(PORT_METHODS).join(\", \")}).`,\n );\n }\n names.push(name as PortName);\n }\n return names;\n}\n\n/** Ends the child: stdin closed, then SIGTERM after `graceMs`, then SIGKILL after 5 s more. */\nfunction stopChild(\n child: ChildProcess,\n exited: Promise<InstanceExit>,\n graceMs: number,\n): Promise<InstanceExit> {\n child.stdin?.end();\n const term = setTimeout(() => child.kill(\"SIGTERM\"), graceMs);\n const kill = setTimeout(() => child.kill(\"SIGKILL\"), graceMs + 5000);\n return exited.finally(() => {\n clearTimeout(term);\n clearTimeout(kill);\n });\n}\n\n/** `callback`, made unable to throw into its caller. */\nfunction guarded<A extends unknown[]>(\n callback: ((...args: A) => void) | undefined,\n): ((...args: A) => void) | undefined {\n if (callback === undefined) return undefined;\n return (...args: A) => {\n try {\n callback(...args);\n } catch {\n // The host's own code failed; Mostra's work goes on.\n }\n };\n}\n\n/**\n * Spawns an instance for `home` and opens its session. Rejects with the instance's own diagnostic\n * when it can't start (`MOSTRA_HOME_LOCKED`, `MOSTRA_PLATFORM_UNSUPPORTED` …), or with the\n * runtime's (`MOSTRA_ENV_BUNDLE_*`) when no verified runtime can be found.\n */\nexport async function createMostra(options: MostraOptions): Promise<Mostra> {\n // The host's callbacks run inside Mostra's own handlers (a pipe's data listener, the RPC\n // peer's error path, a download's progress): one that throws must never escape into the host's\n // process as an uncaught exception or unhandled rejection, nor stop an answer (Opus review\n // finding 6). A callback that throws is ignored.\n const onLog = guarded(options.onLog);\n const onProgress = guarded(options.onProgress);\n const onPortError = guarded(options.onPortError);\n const ports = options.ports ?? {};\n const names = portNames(ports);\n const env = options.env ?? process.env;\n const home = resolve(options.home);\n const runtime = await resolveRuntime({\n bundle: options.bundle,\n env,\n signal: options.signal,\n ...(onProgress ? { onProgress } : {}),\n });\n options.signal?.throwIfAborted();\n const args = [runtime.runtimeEntry, \"host\", \"--home\", home];\n if (options.bind !== undefined) args.push(\"--bind\", options.bind);\n if (options.urlHost !== undefined) args.push(\"--url-host\", options.urlHost);\n const child = spawn(runtime.node, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: env as NodeJS.ProcessEnv,\n });\n const logTail: string[] = [];\n const exited = new Promise<InstanceExit>((done) => {\n child.on(\"close\", (code, signal) => done({ code, signal }));\n child.on(\"error\", () => done({ code: null, signal: null }));\n });\n let spawnError: Error | undefined;\n child.on(\"error\", (error) => {\n spawnError = error;\n });\n const errLines = new LineSplitter();\n child.stderr?.setEncoding(\"utf8\");\n child.stderr?.on(\"data\", (chunk: string) => {\n for (const line of errLines.push(chunk)) {\n logTail.push(line);\n if (logTail.length > 20) logTail.shift();\n onLog?.(line);\n }\n });\n\n const notificationHandlers = new Map<string, Set<(params: unknown) => void>>();\n const peer = new RpcPeer({\n send: (message) => {\n if (child.stdin?.writable) child.stdin.write(encodeLine(message));\n },\n onRequest: async (method, params) => {\n const definition = Object.values(PORT_METHODS)\n .flat()\n .find((candidate) => candidate.method === method) as\n | MethodDefinition<unknown, unknown>\n | undefined;\n const handler = definition && portHandler(ports, method);\n if (!definition || !handler) {\n throw new RpcError(\n createDiagnostic(\"MOSTRA_HOST_METHOD_UNKNOWN\", {\n message: `This host serves no method ${method}.`,\n }),\n -32601,\n );\n }\n const parsed = definition.params.safeParse(params);\n if (!parsed.success) {\n throw new RpcError(\n createDiagnostic(\"MOSTRA_HOST_INVALID_PARAMS\", {\n message: `The params of ${method} don't match its schema.`,\n }),\n -32602,\n );\n }\n const answer = await handler(parsed.data);\n const checked = definition.result.safeParse(answer ?? null);\n if (!checked.success) {\n throw new RpcError(\n createDiagnostic(\"MOSTRA_HOST_INTERNAL_ERROR\", {\n message: `The host's ${method} answered something its schema refuses.`,\n }),\n );\n }\n return checked.data;\n },\n onNotification: (method, params) => {\n for (const handler of notificationHandlers.get(method) ?? []) handler(params);\n },\n onInternalError: (error, method) => onPortError?.(error, method),\n });\n const lines = new LineSplitter();\n child.stdout?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (chunk: string) => {\n for (const line of lines.push(chunk)) peer.receiveLine(line);\n });\n child.stdout?.on(\"close\", () => {\n peer.close(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: `The Mostra instance for ${home} exited${logTail.length > 0 ? `; its last log line: ${logTail.at(-1)}` : \"\"}.`,\n }),\n );\n });\n // A pipe to an instance that died fails its next write (EPIPE) on its own schedule. Without a\n // listener that error is an uncaught exception in the host's own process: every pipe has one,\n // and a write that failed ends the session at once, as the instance's exit does.\n child.stdin?.on(\"error\", (error: NodeJS.ErrnoException) => {\n peer.close(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: `The Mostra instance for ${home} stopped reading (${error.code ?? error.message}).`,\n }),\n );\n });\n child.stdout?.on(\"error\", () => {});\n child.stderr?.on(\"error\", () => {});\n\n const abort = (): void => {\n void stopChild(child, exited, 0);\n };\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n const homeConfig: HomeConfig = {\n path: home,\n // What this host always provides, an empty list included: a host that stopped serving a port\n // clears the record, so its absence is never awaited (Opus review of WP-12, finding 7).\n ...(options.expectPorts !== false ? { ports: names } : {}),\n ...(options.settings?.submission ? { submission: options.settings.submission } : {}),\n ...(options.settings?.submitChannels\n ? { submitChannels: options.settings.submitChannels }\n : {}),\n ...(options.settings?.dependencies ? { dependencies: options.settings.dependencies } : {}),\n };\n let session: MostraInitializeResult;\n try {\n session = await peer.call(mostraInitializeMethod, {\n client: {\n name: options.client?.name ?? SDK_CLIENT_NAME,\n version: options.client?.version ?? sdkVersion(),\n },\n revisions: [...HOST_PROTOCOL_REVISIONS],\n capabilities: { notifications: [EVENTS_NOTIFY, ARTIFACT_PROGRESS] },\n ports: names,\n home: homeConfig,\n });\n } catch (error) {\n await stopChild(child, exited, 2000);\n if (spawnError) throw closedError(`Couldn't start ${runtime.node}: ${spawnError.message}`);\n throw error;\n } finally {\n options.signal?.removeEventListener(\"abort\", abort);\n }\n\n const call = <P, R>(\n method: MethodDefinition<P, R>,\n params: P,\n callOptions?: CallOptions,\n ): Promise<R> =>\n peer.call(\n method,\n params,\n callOptions?.timeoutMs !== undefined ? { timeoutMs: callOptions.timeoutMs } : {},\n );\n let closing: Promise<InstanceExit> | undefined;\n\n const mostra: Mostra = {\n instance: session.instance,\n capabilities: session.capabilities,\n revision: session.revision,\n warnings: session.warnings,\n runtime,\n closed: exited,\n call,\n publish: (source, publishOptions = {}) =>\n call(artifactPublishMethod, { source: artifactRef(source), ...publishOptions }),\n verify: (source, verifyOptions = {}) =>\n call(artifactVerifyMethod, { source: artifactRef(source), ...verifyOptions }),\n wait: (artifact, until, waitOptions = {}) => {\n const signal = waitOptions.signal;\n // Checked before the request goes out: nothing is sent for a wait already given up, so no\n // answer is left without a handler (GPT review finding 4).\n if (signal?.aborted) return Promise.reject(signal.reason);\n const pending = call(waitMethod, {\n artifact: artifactRef(artifact),\n until,\n timeoutMs: waitOptions.timeoutMs ?? WAIT_MAX_MS,\n ...(waitOptions.after !== undefined ? { after: waitOptions.after } : {}),\n });\n if (!signal) return pending;\n // The instance's wait runs on until its own timeout; its late answer, or the connection's\n // end, is handled here and dropped.\n return new Promise<WaitResult>((done, fail) => {\n const onAbort = (): void => fail(signal.reason);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n pending.then(done, fail).finally(() => signal.removeEventListener(\"abort\", onAbort));\n });\n },\n results: ((\n artifact: ArtifactInput,\n resultsOptions: { submission?: string; format?: \"json\" | \"md\" } = {},\n ) =>\n call(resultsGetMethod, {\n artifact: artifactRef(artifact),\n ...resultsOptions,\n })) as Mostra[\"results\"],\n getState: (artifact, ids) =>\n call(stateGetMethod, { artifact: artifactRef(artifact), ...(ids ? { ids: [...ids] } : {}) }),\n patchState: (artifact, patch) =>\n call(statePatchMethod, { artifact: artifactRef(artifact), ...patch }),\n pushLive: (artifact, push) =>\n call(livePushMethod, { artifact: artifactRef(artifact), ...push }),\n viewUrl: (artifact, urlOptions = {}) =>\n call(viewUrlMethod, { artifact: artifactRef(artifact), ...urlOptions }),\n events: (streamOptions = {}) =>\n openEventStream(\n {\n call,\n onNotification: (handler) => {\n let set = notificationHandlers.get(EVENTS_NOTIFY);\n if (!set) {\n set = new Set();\n notificationHandlers.set(EVENTS_NOTIFY, set);\n }\n set.add(handler);\n return () => set?.delete(handler);\n },\n closed: exited.then(() => undefined),\n },\n streamOptions,\n ),\n close: () => {\n closing ??= stopChild(child, exited, 10_000);\n return closing;\n },\n };\n return mostra;\n}\n"],"mappings":";;;;;;;;;;;;;;AAuDA,SAAgB,gBACd,SACA,SAC0B;CAC1B,OAAO,GACJ,OAAO,sBAAsB,IAAI,YAAY,SAAS,OAAO,EAChE;AACF;AAEA,IAAM,cAAN,MAAsD;CACpD;CACA;CACA;CACA,SAA+B,CAAC;CAChC;;CAEA;;CAEA;;CAEA;CACA;CACA,UAAU;;;;;;CAMV;;CAEA;;CAEA;CACA,WAAW;CACX;CACA;CACA;CAEA,YAAY,SAAuB,SAA6B;EAC9D,KAAK,WAAW;EAChB,KAAK,WAAW;EAChB,KAAK,QAAQ,KAAK,IAAI,GAAG,QAAQ,iBAAiB,EAAE;EACpD,KAAK,iBAAiB,QAAQ,gBAAgB,WAAW,KAAK,SAAS,MAAM,CAAC;EAC9E,QAAa,OAAO,WAClB,KAAK,KACH,IAAI,SACF,iBAAiB,iCAAiC,EAChD,SAAS,+DACX,CAAC,CACH,CACF,CACF;EACA,QAAQ,QAAQ,iBAAiB,eAAe,KAAK,KAAK,OAAO,GAAG,EAAE,MAAM,KAAK,CAAC;CACpF;CAEA,SAAS,QAAuB;EAC9B,MAAM,SAAS,mBAAmB,UAAU,MAAM;EAClD,IAAI,CAAC,OAAO,SAAS;EACrB,IAAI,OAAO,KAAK,mBAAmB,KAAK,eAAe;GACrD,KAAK,QAAQ,KAAK,OAAO,IAAI;GAC7B;EACF;EACA,KAAK,QAAQ,OAAO,IAAI;CAC1B;CAEA,QAAQ,EAAE,QAAQ,QAAQ,aAAuC;EAC/D,KAAK,OAAO,KAAK;GAAE;GAAQ;GAAQ,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EAAG,CAAC;EACxE,KAAK,YAAY;EACjB,IAAI,KAAK,OAAO,UAAU,KAAK,SAAS,CAAC,KAAK,SAAS,KAAK,WAAW,KAAK,OAAO;EACnF,KAAK,QAAQ;CACf;;;;;CAMA,WAAW,OAA0C;EACnD,MAAM,UAAU,KAAK,MAAM,KAAK;EAChC,KAAK,eAAe;EACpB,OAAO,QAAQ,cAAc;GAC3B,IAAI,KAAK,iBAAiB,SAAS,KAAK,eAAe,KAAA;EACzD,CAAC;CACH;CAEA,MAAM,MAAM,OAA0C;EACpD,KAAK,SAAS,CAAC;EACf,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,KAAK,SAAS,KAAK,uBAAuB;IACvD,GAAI,KAAK,SAAS,SAAS,EAAE,QAAQ,KAAK,SAAS,OAAO,IAAI,CAAC;IAC/D,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;IACvC,GAAI,KAAK,SAAS,OAAO,EAAE,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC;GAC3D,CAAC;EACH,UAAU;GACR,QAAQ,KAAK;GACb,KAAK,SAAS,KAAA;EAChB;EAEA,KAAK,gBAAgB,OAAO;EAC5B,KAAK,cAAc,OAAO;EAE1B,KAAK,MAAM,QAAQ,OAAO,IAAI,KAAK,mBAAmB,OAAO,gBAAgB,KAAK,QAAQ,IAAI;CAChG;CAEA,MAAM,SAAwB;EAC5B,KAAK,UAAU;EACf,MAAM,KAAK,KAAK;EAChB,IAAI,OAAO,KAAA,GAAW;EAGtB,MAAM,QAAQ,KAAK,SAAS,EAAE;EAG9B,MAAM,KAAK,SACR,KAAK,yBAAyB,EAAE,gBAAgB,GAAG,CAAC,CAAC,CACrD,YAAY,KAAA,CAAS;EAIxB,MAAM,MAAM,OAAO,UAAmB,KAAK,KAAK,KAAK,CAAC;EACtD,IAAI,KAAK,kBAAkB,IAAI,KAAK,gBAAgB,KAAA;CACtD;CAEA,MAAM,UAAyB;EAC7B,MAAM,KAAK;EACX,KAAK,WAAW,KAAA;EAChB,KAAK,UAAU;EACf,MAAM,KAAK,WAAW,KAAK,SAAS;CACtC;;;;;;CAOA,SAAS,IAA2B;EAClC,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,KAAK,SAAS,QAAQ,WAAW,KAAA,KAAa,WAAW,KAAK,QACjE,OAAO,QAAQ,QAAQ;EACzB,KAAK,SAAS;EACd,OAAO,KAAK,SACT,KAAK,iBAAiB;GAAE,gBAAgB;GAAI;EAAO,CAAC,CAAC,CACrD,WAAW,KAAA,CAAS;CACzB;CAEA,KAAK,OAAuB;EAC1B,IAAI,KAAK,QAAQ;EACjB,KAAK,SAAS,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACjD,KAAK,eAAe;EACpB,KAAK,QAAQ;CACf;CAEA,MAAM,OAA2C;EAC/C,IAAI,CAAC,KAAK,UAAU;GAClB,KAAK,WAAW;GAChB,MAAM,KAAK,WAAW,KAAK,SAAS,KAAK;EAC3C;EAEA,IAAI,KAAK,eAAe,KAAA,GAAW,KAAK,WAAW,KAAK;EACxD,IAAI,KAAK,WAAW,CAAC,KAAK,UAAU,KAAK,OAAO,UAAU,KAAK,QAAQ,GAAG,MAAM,KAAK,QAAQ;EAG7F,IAAI,CAAC,KAAK,WAAW,KAAK,kBAAkB,KAAA,GAAW,MAAM,KAAK,SAAS,KAAK,aAAa;EAC7F,SAAS;GACP,MAAM,OAAO,KAAK,OAAO,MAAM;GAC/B,IAAI,SAAS,KAAA,GAAW;IACtB,KAAK,aAAa,KAAK;IACvB,OAAO;KAAE,MAAM;KAAO,OAAO;IAAK;GACpC;GACA,IAAI,KAAK,QAAQ;IACf,IAAI,KAAK,OAAO,UAAU,KAAA,GAAW,MAAM,KAAK,OAAO;IACvD,OAAO;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU;GACxC;GACA,IAAI,KAAK,SAAS;IAChB,MAAM,KAAK,QAAQ;IACnB,MAAM,KAAK,SAAS,KAAK,aAAuB;GAClD;GACA,MAAM,IAAI,SAAe,SAAS;IAChC,KAAK,QAAQ;GACf,CAAC;GACD,KAAK,QAAQ,KAAA;EACf;CACF;;;;;CAMA,MAAM,SAA6C;EACjD,KAAK,KAAK;EACV,KAAK,OAAO,SAAS;EACrB,MAAM,KAAK;EAGX,MAAM,KAAK,cAAc,YAAY,KAAA,CAAS;EAC9C,MAAM,KAAK,KAAK;EAChB,IAAI,OAAO,KAAA,GAAW;GACpB,KAAK,gBAAgB,KAAA;GACrB,MAAM,QAAQ,KAAK,SAAS,EAAE;GAC9B,MAAM,KAAK,SACR,KAAK,yBAAyB,EAAE,gBAAgB,GAAG,CAAC,CAAC,CACrD,YAAY,KAAA,CAAS;GACxB,MAAM,MAAM,YAAY,KAAA,CAAS;EACnC,OAAO,IAAI,KAAK,SAAS,QAAQ,KAAK,aAAa,KAAA,KAAa,KAAK,aAAa,KAAK,QAAQ;GAG7F,MAAM,KAAK,WAAW,KAAK,SAAS,CAAC,CAAC,YAAY,KAAA,CAAS;GAC3D,MAAM,QAAQ,KAAK;GACnB,IAAI,UAAU,KAAA,GAAW;IACvB,KAAK,gBAAgB,KAAA;IACrB,MAAM,QAAQ,KAAK,SAAS,KAAK;IACjC,MAAM,KAAK,SACR,KAAK,yBAAyB,EAAE,gBAAgB,MAAM,CAAC,CAAC,CACxD,YAAY,KAAA,CAAS;IACxB,MAAM,MAAM,YAAY,KAAA,CAAS;GACnC;EACF;EACA,OAAO;GAAE,MAAM;GAAM,OAAO,KAAA;EAAU;CACxC;AACF;;;;;;;;;;;;;;;;;;;;;;ACNA,MAAM,eAAe;CACnB,UAAU,CAAC,yBAAyB;CACpC,aAAa,CAAC,4BAA4B;CAC1C,QAAQ,CAAC,sBAAsB;CAC/B,SAAS,CAAC,oBAAoB;CAC9B,iBAAiB,CAAC,iCAAiC,kCAAkC;AACvF;;AAGA,MAAa,kBAAkB;;AAG/B,SAAgB,YAAY,OAA+C;CACzE,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,OAAO,MAAM,WAAW,MAAM,IAAI,EAAE,YAAY,MAAM,IAAI,EAAE,MAAM,QAAQ,KAAK,EAAE;AACnF;AAEA,SAAS,YAAY,SAA2B;CAC9C,OAAO,IAAI,SAAS,iBAAiB,iCAAiC,EAAE,QAAQ,CAAC,CAAC;AACpF;;AAGA,SAAS,YAAY,OAAc,QAA4D;CAC7F,QAAQ,QAAR;EACE,KAAK,0BAA0B,QAC7B,OACE,MAAM,cAAc,WAAW,MAAM,UAAU,QAAQ,MAAmC;EAE9F,KAAK,6BAA6B,QAChC,OACE,MAAM,iBAAiB,WAAW,MAAM,aAAa,QAAQ,MAA4B;EAE7F,KAAK,uBAAuB,QAC1B,OAAO,MAAM,YAAY,WAAW,MAAM,QAAQ,OAAO,MAAqB;EAChF,KAAK,qBAAqB,QACxB,OAAO,MAAM,aAAa,WAAW,MAAM,SAAS,IAAI,MAA8B;EACxF,KAAK,gCAAgC,QACnC,OACE,MAAM,qBACJ,WACA,MAAM,iBAAiB,OAAQ,OAAyC,QAAQ;EAEtF,KAAK,mCAAmC,QACtC,OACE,MAAM,oBACL,OAAO,WAAW;GACjB,MAAM,MAAM,iBAAiB,YAAY,MAA4C;GACrF,OAAO,CAAC;EACV;EAEJ,SACE;CACJ;AACF;;AAGA,SAAS,UAAU,OAA0B;CAC3C,MAAM,QAAoB,CAAC;CAC3B,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,GAAG;EACjD,IAAI,UAAU,KAAA,GAAW;EACzB,IAAI,EAAE,QAAQ,eACZ,MAAM,IAAI,UACR,0BAA0B,KAAK,oBAAoB,OAAO,KAAK,YAAY,CAAC,CAAC,KAAK,IAAI,EAAE,GAC1F;EAEF,MAAM,KAAK,IAAgB;CAC7B;CACA,OAAO;AACT;;AAGA,SAAS,UACP,OACA,QACA,SACuB;CACvB,MAAM,OAAO,IAAI;CACjB,MAAM,OAAO,iBAAiB,MAAM,KAAK,SAAS,GAAG,OAAO;CAC5D,MAAM,OAAO,iBAAiB,MAAM,KAAK,SAAS,GAAG,UAAU,GAAI;CACnE,OAAO,OAAO,cAAc;EAC1B,aAAa,IAAI;EACjB,aAAa,IAAI;CACnB,CAAC;AACH;;AAGA,SAAS,QACP,UACoC;CACpC,IAAI,aAAa,KAAA,GAAW,OAAO,KAAA;CACnC,QAAQ,GAAG,SAAY;EACrB,IAAI;GACF,SAAS,GAAG,IAAI;EAClB,QAAQ,CAER;CACF;AACF;;;;;;AAOA,eAAsB,aAAa,SAAyC;CAK1E,MAAM,QAAQ,QAAQ,QAAQ,KAAK;CACnC,MAAM,aAAa,QAAQ,QAAQ,UAAU;CAC7C,MAAM,cAAc,QAAQ,QAAQ,WAAW;CAC/C,MAAM,QAAQ,QAAQ,SAAS,CAAC;CAChC,MAAM,QAAQ,UAAU,KAAK;CAC7B,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,OAAO,QAAQ,QAAQ,IAAI;CACjC,MAAM,UAAU,MAAM,eAAe;EACnC,QAAQ,QAAQ;EAChB;EACA,QAAQ,QAAQ;EAChB,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;CACrC,CAAC;CACD,QAAQ,QAAQ,eAAe;CAC/B,MAAM,OAAO;EAAC,QAAQ;EAAc;EAAQ;EAAU;CAAI;CAC1D,IAAI,QAAQ,SAAS,KAAA,GAAW,KAAK,KAAK,UAAU,QAAQ,IAAI;CAChE,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,KAAK,cAAc,QAAQ,OAAO;CAC1E,MAAM,QAAQ,MAAM,QAAQ,MAAM,MAAM;EACtC,OAAO;GAAC;GAAQ;GAAQ;EAAM;EACzB;CACP,CAAC;CACD,MAAM,UAAoB,CAAC;CAC3B,MAAM,SAAS,IAAI,SAAuB,SAAS;EACjD,MAAM,GAAG,UAAU,MAAM,WAAW,KAAK;GAAE;GAAM;EAAO,CAAC,CAAC;EAC1D,MAAM,GAAG,eAAe,KAAK;GAAE,MAAM;GAAM,QAAQ;EAAK,CAAC,CAAC;CAC5D,CAAC;CACD,IAAI;CACJ,MAAM,GAAG,UAAU,UAAU;EAC3B,aAAa;CACf,CAAC;CACD,MAAM,WAAW,IAAI,aAAa;CAClC,MAAM,QAAQ,YAAY,MAAM;CAChC,MAAM,QAAQ,GAAG,SAAS,UAAkB;EAC1C,KAAK,MAAM,QAAQ,SAAS,KAAK,KAAK,GAAG;GACvC,QAAQ,KAAK,IAAI;GACjB,IAAI,QAAQ,SAAS,IAAI,QAAQ,MAAM;GACvC,QAAQ,IAAI;EACd;CACF,CAAC;CAED,MAAM,uCAAuB,IAAI,IAA4C;CAC7E,MAAM,OAAO,IAAI,QAAQ;EACvB,OAAO,YAAY;GACjB,IAAI,MAAM,OAAO,UAAU,MAAM,MAAM,MAAM,WAAW,OAAO,CAAC;EAClE;EACA,WAAW,OAAO,QAAQ,WAAW;GACnC,MAAM,aAAa,OAAO,OAAO,YAAY,CAAC,CAC3C,KAAK,CAAC,CACN,MAAM,cAAc,UAAU,WAAW,MAAM;GAGlD,MAAM,UAAU,cAAc,YAAY,OAAO,MAAM;GACvD,IAAI,CAAC,cAAc,CAAC,SAClB,MAAM,IAAI,SACR,iBAAiB,8BAA8B,EAC7C,SAAS,8BAA8B,OAAO,GAChD,CAAC,GACD,MACF;GAEF,MAAM,SAAS,WAAW,OAAO,UAAU,MAAM;GACjD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,SACR,iBAAiB,8BAA8B,EAC7C,SAAS,iBAAiB,OAAO,0BACnC,CAAC,GACD,MACF;GAEF,MAAM,SAAS,MAAM,QAAQ,OAAO,IAAI;GACxC,MAAM,UAAU,WAAW,OAAO,UAAU,UAAU,IAAI;GAC1D,IAAI,CAAC,QAAQ,SACX,MAAM,IAAI,SACR,iBAAiB,8BAA8B,EAC7C,SAAS,cAAc,OAAO,yCAChC,CAAC,CACH;GAEF,OAAO,QAAQ;EACjB;EACA,iBAAiB,QAAQ,WAAW;GAClC,KAAK,MAAM,WAAW,qBAAqB,IAAI,MAAM,KAAK,CAAC,GAAG,QAAQ,MAAM;EAC9E;EACA,kBAAkB,OAAO,WAAW,cAAc,OAAO,MAAM;CACjE,CAAC;CACD,MAAM,QAAQ,IAAI,aAAa;CAC/B,MAAM,QAAQ,YAAY,MAAM;CAChC,MAAM,QAAQ,GAAG,SAAS,UAAkB;EAC1C,KAAK,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG,KAAK,YAAY,IAAI;CAC7D,CAAC;CACD,MAAM,QAAQ,GAAG,eAAe;EAC9B,KAAK,MACH,iBAAiB,iCAAiC,EAChD,SAAS,2BAA2B,KAAK,SAAS,QAAQ,SAAS,IAAI,wBAAwB,QAAQ,GAAG,EAAE,MAAM,GAAG,GACvH,CAAC,CACH;CACF,CAAC;CAID,MAAM,OAAO,GAAG,UAAU,UAAiC;EACzD,KAAK,MACH,iBAAiB,iCAAiC,EAChD,SAAS,2BAA2B,KAAK,oBAAoB,MAAM,QAAQ,MAAM,QAAQ,IAC3F,CAAC,CACH;CACF,CAAC;CACD,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC;CAClC,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC;CAElC,MAAM,cAAoB;EACxB,UAAe,OAAO,QAAQ,CAAC;CACjC;CACA,QAAQ,QAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;CAC/D,MAAM,aAAyB;EAC7B,MAAM;EAGN,GAAI,QAAQ,gBAAgB,QAAQ,EAAE,OAAO,MAAM,IAAI,CAAC;EACxD,GAAI,QAAQ,UAAU,aAAa,EAAE,YAAY,QAAQ,SAAS,WAAW,IAAI,CAAC;EAClF,GAAI,QAAQ,UAAU,iBAClB,EAAE,gBAAgB,QAAQ,SAAS,eAAe,IAClD,CAAC;EACL,GAAI,QAAQ,UAAU,eAAe,EAAE,cAAc,QAAQ,SAAS,aAAa,IAAI,CAAC;CAC1F;CACA,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,KAAK,KAAK,wBAAwB;GAChD,QAAQ;IACN,MAAM,QAAQ,QAAQ,QAAA;IACtB,SAAS,QAAQ,QAAQ,WAAW,WAAW;GACjD;GACA,WAAW,CAAC,GAAG,uBAAuB;GACtC,cAAc,EAAE,eAAe,CAAC,eAAe,iBAAiB,EAAE;GAClE,OAAO;GACP,MAAM;EACR,CAAC;CACH,SAAS,OAAO;EACd,MAAM,UAAU,OAAO,QAAQ,GAAI;EACnC,IAAI,YAAY,MAAM,YAAY,kBAAkB,QAAQ,KAAK,IAAI,WAAW,SAAS;EACzF,MAAM;CACR,UAAU;EACR,QAAQ,QAAQ,oBAAoB,SAAS,KAAK;CACpD;CAEA,MAAM,QACJ,QACA,QACA,gBAEA,KAAK,KACH,QACA,QACA,aAAa,cAAc,KAAA,IAAY,EAAE,WAAW,YAAY,UAAU,IAAI,CAAC,CACjF;CACF,IAAI;CAwEJ,OAAO;EArEL,UAAU,QAAQ;EAClB,cAAc,QAAQ;EACtB,UAAU,QAAQ;EAClB,UAAU,QAAQ;EAClB;EACA,QAAQ;EACR;EACA,UAAU,QAAQ,iBAAiB,CAAC,MAClC,KAAK,uBAAuB;GAAE,QAAQ,YAAY,MAAM;GAAG,GAAG;EAAe,CAAC;EAChF,SAAS,QAAQ,gBAAgB,CAAC,MAChC,KAAK,sBAAsB;GAAE,QAAQ,YAAY,MAAM;GAAG,GAAG;EAAc,CAAC;EAC9E,OAAO,UAAU,OAAO,cAAc,CAAC,MAAM;GAC3C,MAAM,SAAS,YAAY;GAG3B,IAAI,QAAQ,SAAS,OAAO,QAAQ,OAAO,OAAO,MAAM;GACxD,MAAM,UAAU,KAAK,YAAY;IAC/B,UAAU,YAAY,QAAQ;IAC9B;IACA,WAAW,YAAY,aAAa;IACpC,GAAI,YAAY,UAAU,KAAA,IAAY,EAAE,OAAO,YAAY,MAAM,IAAI,CAAC;GACxE,CAAC;GACD,IAAI,CAAC,QAAQ,OAAO;GAGpB,OAAO,IAAI,SAAqB,MAAM,SAAS;IAC7C,MAAM,gBAAsB,KAAK,OAAO,MAAM;IAC9C,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;IACxD,QAAQ,KAAK,MAAM,IAAI,CAAC,CAAC,cAAc,OAAO,oBAAoB,SAAS,OAAO,CAAC;GACrF,CAAC;EACH;EACA,WACE,UACA,iBAAkE,CAAC,MAEnE,KAAK,kBAAkB;GACrB,UAAU,YAAY,QAAQ;GAC9B,GAAG;EACL,CAAC;EACH,WAAW,UAAU,QACnB,KAAK,gBAAgB;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAI,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC;EAAG,CAAC;EAC7F,aAAa,UAAU,UACrB,KAAK,kBAAkB;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAG;EAAM,CAAC;EACtE,WAAW,UAAU,SACnB,KAAK,gBAAgB;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAG;EAAK,CAAC;EACnE,UAAU,UAAU,aAAa,CAAC,MAChC,KAAK,eAAe;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAG;EAAW,CAAC;EACxE,SAAS,gBAAgB,CAAC,MACxB,gBACE;GACE;GACA,iBAAiB,YAAY;IAC3B,IAAI,MAAM,qBAAqB,IAAI,aAAa;IAChD,IAAI,CAAC,KAAK;KACR,sBAAM,IAAI,IAAI;KACd,qBAAqB,IAAI,eAAe,GAAG;IAC7C;IACA,IAAI,IAAI,OAAO;IACf,aAAa,KAAK,OAAO,OAAO;GAClC;GACA,QAAQ,OAAO,WAAW,KAAA,CAAS;EACrC,GACA,aACF;EACF,aAAa;GACX,YAAY,UAAU,OAAO,QAAQ,GAAM;GAC3C,OAAO;EACT;CAEU;AACd"}