@canonmsg/agent-sdk 12.4.1 → 12.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +37 -6
  2. package/dist/chunks/{chunk-6PKXASSK.js → chunk-5J4YPLNW.js} +394 -1687
  3. package/dist/chunks/{chunk-NXL4C2R6.js → chunk-DAJ7L7TN.js} +954 -429
  4. package/dist/chunks/chunk-FVYKLH6I.js +1475 -0
  5. package/dist/chunks/chunk-HH7KR22P.js +406 -0
  6. package/dist/chunks/chunk-QK37HAHC.js +118 -0
  7. package/dist/chunks/{chunk-GWQF4WFT.js → chunk-S7BV74DN.js} +734 -812
  8. package/dist/chunks/{mls-crypto-LGPCXM3K.js → mls-crypto-4DZT2UFH.js} +2 -1
  9. package/dist/cli.js +70 -32
  10. package/dist/drivers/acp.d.ts +56 -0
  11. package/dist/drivers/codex.d.ts +1 -2
  12. package/dist/drivers/connector.d.ts +5 -4
  13. package/dist/drivers/subprocess.d.ts +35 -0
  14. package/dist/index.d.ts +3 -1
  15. package/dist/index.js +12 -3
  16. package/dist/internal/backend-contracts/connectorRelay.d.ts +34 -0
  17. package/dist/internal/backend-contracts/endpoint.d.ts +1 -1
  18. package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +2 -2
  19. package/dist/internal/backend-contracts/index.d.ts +1 -0
  20. package/dist/internal/backend-contracts/loggedOutSessions.d.ts +7 -0
  21. package/dist/internal/backend-contracts/mlsDevCredential.d.ts +3 -0
  22. package/dist/internal/endpoint/engine.d.ts +8 -1
  23. package/dist/internal/endpoint/mls-dev-client.d.ts +11 -0
  24. package/dist/internal/endpoint/mls-dev-text.d.ts +3 -0
  25. package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +5 -0
  26. package/dist/internal/endpoint/node.d.ts +1 -0
  27. package/dist/media.js +4 -2
  28. package/dist/mls/darwin-arm64/canon-mls-custody +0 -0
  29. package/dist/mls/linux-arm64/canon-mls-custody +0 -0
  30. package/dist/mls/linux-x64/canon-mls-custody +0 -0
  31. package/dist/mls/wasm/canon_mls_wasm.js +363 -0
  32. package/dist/mls/wasm/canon_mls_wasm_bg.wasm +0 -0
  33. package/dist/mls-wasm-host.js +258 -0
  34. package/dist/runner/mls-bundled.d.ts +15 -2
  35. package/dist/runner/mls-managed.d.ts +10 -2
  36. package/dist/runner/mls.d.ts +7 -1
  37. package/dist/runner/relay.d.ts +3 -3
  38. package/dist/runner/runner.d.ts +18 -7
  39. package/dist/runner/types.d.ts +12 -4
  40. package/package.json +1 -1
@@ -0,0 +1,1475 @@
1
+ import {
2
+ CANON_CLIENT_HEADER,
3
+ CANON_ENDPOINT_PROTOCOL,
4
+ CLIENT_UPDATE_REQUIRED,
5
+ MLS_DEV_CREDENTIAL_MAX_BYTES,
6
+ MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES,
7
+ MLS_DEV_ENROLLMENT_MAX_BYTES,
8
+ MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW,
9
+ MLS_DEV_KEY_PACKAGES_MAX_BYTES,
10
+ __name,
11
+ canonicalEndpointJson,
12
+ classifyTransferHttpFailure,
13
+ classifyTransferReceipt,
14
+ endpointContentBoundary,
15
+ formatCanonClientHeader,
16
+ isEndpointOperationReceipt,
17
+ isMlsDevReceiptClockSkew,
18
+ isMlsEnvironmentId,
19
+ parseMlsDevCredentialBegin,
20
+ parseMlsDevCredentialChallenge,
21
+ parseMlsDevCredentialComplete,
22
+ parseMlsDevCredentialReceipt,
23
+ parseMlsDevCredentialReconciliationBegin,
24
+ parseMlsDevCredentialReconciliationChallenge,
25
+ parseMlsDevCredentialReconciliationComplete,
26
+ parseMlsDevCredentialReconciliationReceipt,
27
+ parseMlsDevCredentialRevocationReceipt,
28
+ parseMlsDevCredentialRevoke,
29
+ parseMlsDevCredentialToken,
30
+ parseMlsDevDeviceCommand,
31
+ parseMlsDevDeviceContext,
32
+ parseMlsDevDevicePreparation,
33
+ parseMlsDevEnrollmentBegin,
34
+ parseMlsDevEnrollmentChallenge,
35
+ parseMlsDevEnrollmentComplete,
36
+ parseMlsDevEnrollmentResult,
37
+ parseMlsDevGroupCommand,
38
+ parseMlsDevGroupContext,
39
+ parseMlsDevGroupPreparation,
40
+ parseMlsDevKeyPackageClaim,
41
+ parseMlsDevKeyPackageClaimReceipt,
42
+ parseMlsDevKeyPackageCommand,
43
+ parseMlsDevKeyPackagePreparation,
44
+ parseMlsDevKeyPackagePublicationReceipt,
45
+ parseMlsDevKeyPackagePublish
46
+ } from "./chunk-S7BV74DN.js";
47
+
48
+ // ../endpoint/src/events.ts
49
+ function createEndpointEventHub(input) {
50
+ const observers = /* @__PURE__ */ new Set();
51
+ let controller;
52
+ let running;
53
+ let closed = false;
54
+ let connected = false;
55
+ let terminalError = false;
56
+ let lastEventId;
57
+ let subscribedScopes = "";
58
+ const principalScopes = /* @__PURE__ */ __name(() => [...new Set([...observers].flatMap(({ hooks }) => hooks?.principalScopes ?? []))].sort(), "principalScopes");
59
+ const families = /* @__PURE__ */ __name(() => [...new Set([...observers].flatMap(({ hooks }) => hooks?.families ?? ["messages", "contacts"]))].sort(), "families");
60
+ const subscriptionKey = /* @__PURE__ */ __name(() => JSON.stringify([families(), principalScopes()]), "subscriptionKey");
61
+ const safe = /* @__PURE__ */ __name((callback) => {
62
+ try {
63
+ callback?.();
64
+ } catch {
65
+ }
66
+ }, "safe");
67
+ async function loop(signal) {
68
+ let attempt = 0;
69
+ while (!signal.aborted && observers.size) {
70
+ let retryAfterMs = 0;
71
+ let handling = false;
72
+ try {
73
+ if (!input.transport.events) return;
74
+ for await (const event of input.transport.events({ lastEventId, signal, families: families(), principalScopes: principalScopes(), onConnected: /* @__PURE__ */ __name(() => {
75
+ connected = true;
76
+ for (const { hooks } of observers) safe(hooks?.onConnected);
77
+ input.invalidate();
78
+ }, "onConnected") })) {
79
+ if (signal.aborted) break;
80
+ validateEndpointContentBoundary("event", event.kind, event.data);
81
+ if (event.kind === "error") throw decodeEndpointError(event.data, { stream: true });
82
+ handling = true;
83
+ await input.onEvent?.(event);
84
+ handling = false;
85
+ attempt = 0;
86
+ if (event.kind === "replay.expired") lastEventId = void 0;
87
+ else if (event.id) lastEventId = event.id;
88
+ for (const observer of observers) safe(() => observer.listener(event));
89
+ if (/^(artifact\.|message\.|conversations?\.|contacts?\.|agent\.context$|replay\.|voice\.session\.)/.test(event.kind)) input.invalidate(event);
90
+ }
91
+ } catch (error) {
92
+ if (!signal.aborted) {
93
+ terminalError = !handling && error instanceof EndpointError && !error.retryable;
94
+ if (error instanceof EndpointError && typeof error.retryAfterMs === "number" && Number.isFinite(error.retryAfterMs)) retryAfterMs = Math.max(0, Math.min(3e5, error.retryAfterMs));
95
+ for (const { hooks } of observers) safe(() => hooks?.onError?.(error instanceof Error ? error : new EndpointError("Stream connection failed")));
96
+ }
97
+ } finally {
98
+ if (connected) {
99
+ connected = false;
100
+ safe(input.onDisconnected);
101
+ for (const { hooks } of observers) safe(hooks?.onDisconnected);
102
+ }
103
+ }
104
+ if (signal.aborted || !observers.size || terminalError) break;
105
+ const delay = Math.max(retryAfterMs, Math.min(3e4, 500 * 2 ** Math.min(attempt++, 6)) * (0.8 + Math.random() * 0.4));
106
+ await new Promise((resolve4) => {
107
+ const finish = /* @__PURE__ */ __name(() => {
108
+ clearTimeout(timer);
109
+ signal.removeEventListener("abort", finish);
110
+ resolve4();
111
+ }, "finish");
112
+ const timer = setTimeout(finish, delay);
113
+ timer.unref?.();
114
+ signal.addEventListener("abort", finish, { once: true });
115
+ });
116
+ }
117
+ }
118
+ __name(loop, "loop");
119
+ function start() {
120
+ if (running || closed || terminalError || !observers.size || !input.transport.events) return;
121
+ controller = new AbortController();
122
+ subscribedScopes = subscriptionKey();
123
+ running = loop(controller.signal).finally(() => {
124
+ running = void 0;
125
+ controller = void 0;
126
+ if (!closed && observers.size) start();
127
+ });
128
+ }
129
+ __name(start, "start");
130
+ return {
131
+ isConnected: /* @__PURE__ */ __name(() => connected, "isConnected"),
132
+ observe(listener, hooks) {
133
+ if (closed) throw new EndpointError("Endpoint is closed", { code: "ENDPOINT_CLOSED", retryable: false });
134
+ const observer = { listener, hooks };
135
+ observers.add(observer);
136
+ if (connected) safe(hooks?.onConnected);
137
+ if (running && subscribedScopes !== subscriptionKey()) controller?.abort();
138
+ start();
139
+ return () => {
140
+ observers.delete(observer);
141
+ if (!observers.size || subscribedScopes !== subscriptionKey()) controller?.abort();
142
+ };
143
+ },
144
+ async close() {
145
+ closed = true;
146
+ controller?.abort();
147
+ observers.clear();
148
+ await running;
149
+ }
150
+ };
151
+ }
152
+ __name(createEndpointEventHub, "createEndpointEventHub");
153
+ var MAX_EVENT_FRAME_CHARS = 2 * 1024 * 1024;
154
+ var DECODE_SLICE_BYTES = 64 * 1024;
155
+ async function* readEndpointEvents(response, options) {
156
+ if (!response.body) throw new EndpointError("Canon returned an empty event stream", { code: "INVALID_EVENT_STREAM" });
157
+ const reader = response.body.getReader();
158
+ const decoder = new TextDecoder();
159
+ let buffer = "";
160
+ let kind = "message";
161
+ let id;
162
+ let data = [];
163
+ let dataSize = 0;
164
+ let frameSize = 0;
165
+ const abort = /* @__PURE__ */ __name(() => {
166
+ void reader.cancel().catch(() => void 0);
167
+ }, "abort");
168
+ options.signal.addEventListener("abort", abort, { once: true });
169
+ try {
170
+ while (!options.signal.aborted) {
171
+ let timer;
172
+ const chunk = await Promise.race([
173
+ reader.read(),
174
+ new Promise((_, reject) => {
175
+ timer = setTimeout(() => {
176
+ void reader.cancel().catch(() => void 0);
177
+ reject(new EndpointError("Canon event stream stalled", { code: "STREAM_IDLE" }));
178
+ }, options.idleTimeoutMs ?? 6e4);
179
+ })
180
+ ]).finally(() => {
181
+ if (timer) clearTimeout(timer);
182
+ });
183
+ if (chunk.done) break;
184
+ for (let offset = 0; offset < chunk.value.length; offset += DECODE_SLICE_BYTES) {
185
+ buffer += decoder.decode(chunk.value.subarray(offset, offset + DECODE_SLICE_BYTES), { stream: true });
186
+ let newline;
187
+ while ((newline = buffer.indexOf("\n")) !== -1) {
188
+ const line = buffer.slice(0, newline).replace(/\r$/, "");
189
+ buffer = buffer.slice(newline + 1);
190
+ frameSize += line.length + 1;
191
+ if (frameSize > MAX_EVENT_FRAME_CHARS) throw new EndpointError("Canon event frame is too large", { code: "INVALID_EVENT_STREAM" });
192
+ if (!line) {
193
+ if (data.length) {
194
+ const value2 = JSON.parse(data.join("\n"));
195
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new EndpointError("Invalid Canon event payload", { code: "INVALID_EVENT_STREAM" });
196
+ validateEndpointContentBoundary("event", kind, value2);
197
+ if (kind === "error") throw decodeEndpointError(value2, { stream: true, ...!response.ok ? { status: response.status } : {}, retryAfter: response.headers.get("Retry-After") });
198
+ yield { ...id ? { id } : {}, kind, data: value2 };
199
+ }
200
+ kind = "message";
201
+ id = void 0;
202
+ data = [];
203
+ dataSize = 0;
204
+ frameSize = 0;
205
+ continue;
206
+ }
207
+ if (line.startsWith(":")) continue;
208
+ const separator = line.indexOf(":");
209
+ const field = separator < 0 ? line : line.slice(0, separator);
210
+ const value = separator < 0 ? "" : line.slice(separator + 1).replace(/^ /, "");
211
+ if (field === "event") kind = value;
212
+ else if (field === "id" && !value.includes("\0")) id = value;
213
+ else if (field === "data") {
214
+ data.push(value);
215
+ dataSize += value.length + 1;
216
+ if (dataSize > MAX_EVENT_FRAME_CHARS) throw new EndpointError("Canon event frame is too large", { code: "INVALID_EVENT_STREAM" });
217
+ }
218
+ }
219
+ if (frameSize + buffer.length > MAX_EVENT_FRAME_CHARS) throw new EndpointError("Canon event frame is too large", { code: "INVALID_EVENT_STREAM" });
220
+ }
221
+ }
222
+ } finally {
223
+ options.signal.removeEventListener("abort", abort);
224
+ await reader.cancel().catch(() => void 0);
225
+ reader.releaseLock();
226
+ }
227
+ }
228
+ __name(readEndpointEvents, "readEndpointEvents");
229
+
230
+ // ../endpoint/src/mls-dev-transport.ts
231
+ var utf8 = new TextEncoder();
232
+ function failure(code, status = 0, retryable = false, retryAfterMs) {
233
+ return new EndpointError("MLS Dev request failed", { code, status, retryable, ...retryAfterMs === void 0 ? {} : { retryAfterMs } });
234
+ }
235
+ __name(failure, "failure");
236
+ function discard(response) {
237
+ try {
238
+ void response.body?.cancel().catch(() => {
239
+ });
240
+ } catch {
241
+ }
242
+ }
243
+ __name(discard, "discard");
244
+ function createMlsDevDeviceHttpTransport(config, dependencies) {
245
+ const invalidConfig = /* @__PURE__ */ __name(() => failure("INVALID_MLS_DEV_TRANSPORT_CONFIG"), "invalidConfig");
246
+ const send = dependencies.fetch;
247
+ const timeoutMs = dependencies.timeoutMs ?? 3e4;
248
+ const environment = dependencies.environmentId;
249
+ if (!isMlsEnvironmentId(environment) || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 12e4 || !config || !Array.isArray(config.trustedOrigins) || !config.trustedOrigins.length || config.trustedOrigins.length > 16 || config.allowSyntheticLoopbackHttp !== void 0 && typeof config.allowSyntheticLoopbackHttp !== "boolean") throw invalidConfig();
250
+ function url(value) {
251
+ try {
252
+ if (typeof value !== "string" || value.length > 2048) throw invalidConfig();
253
+ const parsed = new URL(value);
254
+ const localHttp = config.allowSyntheticLoopbackHttp === true && parsed.protocol === "http:" && (parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]");
255
+ if (parsed.href !== value || parsed.username || parsed.password || parsed.search || parsed.hash || parsed.protocol !== "https:" && !localHttp) throw invalidConfig();
256
+ return parsed;
257
+ } catch {
258
+ throw invalidConfig();
259
+ }
260
+ }
261
+ __name(url, "url");
262
+ const origins = new Set(config.trustedOrigins.map((value) => {
263
+ const parsed = url(value + "/");
264
+ if (parsed.origin !== value || parsed.pathname !== "/") throw invalidConfig();
265
+ return parsed.origin;
266
+ }));
267
+ function route(value) {
268
+ const parsed = url(value);
269
+ if (!origins.has(parsed.origin) || !/^\/(?:[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*\/?)?$/.test(parsed.pathname)) throw invalidConfig();
270
+ return parsed.href.replace(/\/$/, "");
271
+ }
272
+ __name(route, "route");
273
+ const enrollmentUrl = route(config.enrollmentUrl);
274
+ const credentialUrl = route(config.credentialUrl);
275
+ const reconciliationUrl = route(config.reconciliationUrl);
276
+ const keyPackagesUrl = config.keyPackagesUrl === void 0 ? void 0 : route(config.keyPackagesUrl);
277
+ const observations = /* @__PURE__ */ new Map();
278
+ async function call(base, action, input, parseInput, parseOutput, maxBytes, signal) {
279
+ let request;
280
+ let body;
281
+ try {
282
+ request = parseInput(input);
283
+ body = JSON.stringify(request);
284
+ if (utf8.encode(body).length > maxBytes) throw new Error();
285
+ } catch {
286
+ throw failure("INVALID_MLS_DEV_REQUEST");
287
+ }
288
+ const controller = new AbortController();
289
+ let stopError;
290
+ let rejectStopped;
291
+ const stopped = new Promise((_resolve, reject) => {
292
+ rejectStopped = reject;
293
+ });
294
+ void stopped.catch(() => {
295
+ });
296
+ const stop = /* @__PURE__ */ __name((error) => {
297
+ if (stopError) return;
298
+ stopError = error;
299
+ controller.abort();
300
+ rejectStopped(error);
301
+ }, "stop");
302
+ const abort = /* @__PURE__ */ __name(() => stop(failure("MLS_DEV_REQUEST_ABORTED")), "abort");
303
+ if (signal?.aborted) abort();
304
+ else signal?.addEventListener("abort", abort, { once: true });
305
+ const timeout = setTimeout(() => stop(failure("MLS_DEV_REQUEST_TIMEOUT", 0, true)), timeoutMs);
306
+ async function bounded(work) {
307
+ if (stopError) throw stopError;
308
+ const result = await Promise.race([Promise.resolve().then(() => {
309
+ if (stopError) throw stopError;
310
+ return work();
311
+ }), stopped]);
312
+ if (stopError) throw stopError;
313
+ return result;
314
+ }
315
+ __name(bounded, "bounded");
316
+ const target = base + "/" + action;
317
+ let response;
318
+ try {
319
+ for (let attempt = 0; attempt < 2; attempt++) {
320
+ let token;
321
+ try {
322
+ token = await bounded(() => dependencies.credentials.getToken(attempt > 0));
323
+ } catch {
324
+ throw stopError ?? failure("MLS_DEV_AUTHENTICATION_UNAVAILABLE", 0, true);
325
+ }
326
+ if (typeof token !== "string" || !token || token.length > 8185 || /\s/.test(token)) throw failure("UNAUTHENTICATED", 401);
327
+ let ownership;
328
+ try {
329
+ ownership = dependencies.ownershipHeaders();
330
+ } catch {
331
+ throw failure("INVALID_OWNERSHIP_VERSION");
332
+ }
333
+ response = await bounded(async () => {
334
+ let value;
335
+ try {
336
+ value = await send(target, {
337
+ method: "POST",
338
+ redirect: "error",
339
+ cache: "no-store",
340
+ credentials: "omit",
341
+ referrerPolicy: "no-referrer",
342
+ signal: controller.signal,
343
+ headers: {
344
+ Authorization: "Bearer " + token,
345
+ "Content-Type": "application/json",
346
+ Accept: "application/json",
347
+ "X-Canon-Endpoint-Protocol": CANON_ENDPOINT_PROTOCOL,
348
+ ...ownership
349
+ },
350
+ body
351
+ });
352
+ } catch {
353
+ throw stopError ?? failure("MLS_DEV_REQUEST_UNAVAILABLE", 0, true);
354
+ }
355
+ if (stopError) discard(value);
356
+ return value;
357
+ });
358
+ if (response.redirected || response.url !== target || response.status >= 300 && response.status < 400) {
359
+ throw failure("MLS_DEV_REDIRECT_REJECTED");
360
+ }
361
+ const responseEnvironment = response.headers.get("X-Canon-Environment-Id");
362
+ const gateway = response.status === 429 || response.status >= 500;
363
+ if (responseEnvironment !== environment && !(responseEnvironment === null && gateway)) throw failure("ENVIRONMENT_MISMATCH", 409);
364
+ if (response.status === 401 && attempt === 0) {
365
+ discard(response);
366
+ response = void 0;
367
+ continue;
368
+ }
369
+ if (!response.ok) {
370
+ const retry = response.headers.get("Retry-After");
371
+ const seconds = retry && /^\d{1,6}$/.test(retry) ? Number(retry) : 0;
372
+ throw failure("HTTP_" + response.status, response.status, gateway, seconds > 0 ? seconds * 1e3 : void 0);
373
+ }
374
+ if (!/^application\/json(?:\s*;|$)/i.test(response.headers.get("Content-Type") ?? "")) throw failure("INVALID_MLS_DEV_RESPONSE", 502);
375
+ const length = response.headers.get("Content-Length");
376
+ if (length !== null && (!/^\d+$/.test(length) || Number(length) > maxBytes)) throw failure("INVALID_MLS_DEV_RESPONSE", 502);
377
+ const reader = response.body?.getReader();
378
+ if (!reader) throw failure("INVALID_MLS_DEV_RESPONSE", 502);
379
+ let count = 0;
380
+ let text = "";
381
+ const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
382
+ try {
383
+ for (; ; ) {
384
+ const chunk = await bounded(() => reader.read());
385
+ if (chunk.done) break;
386
+ count += chunk.value.byteLength;
387
+ if (count > maxBytes) throw failure("INVALID_MLS_DEV_RESPONSE", 502);
388
+ text += decoder.decode(chunk.value, { stream: true });
389
+ }
390
+ text += decoder.decode();
391
+ const result = parseOutput(JSON.parse(text));
392
+ if (stopError) throw stopError;
393
+ return { request, result };
394
+ } catch (error) {
395
+ if (error === stopError) throw error;
396
+ throw failure("INVALID_MLS_DEV_RESPONSE", 502);
397
+ } finally {
398
+ try {
399
+ void reader.cancel().catch(() => {
400
+ });
401
+ } catch {
402
+ }
403
+ reader.releaseLock();
404
+ }
405
+ }
406
+ throw failure("UNAUTHENTICATED", 401);
407
+ } catch (error) {
408
+ if (error instanceof EndpointError) throw error;
409
+ throw stopError ?? failure("MLS_DEV_REQUEST_UNAVAILABLE", 0, true);
410
+ } finally {
411
+ if (response) discard(response);
412
+ clearTimeout(timeout);
413
+ signal?.removeEventListener("abort", abort);
414
+ }
415
+ }
416
+ __name(call, "call");
417
+ const invalidResponse = /* @__PURE__ */ __name(() => failure("INVALID_MLS_DEV_RESPONSE", 502), "invalidResponse");
418
+ return {
419
+ ...keyPackagesUrl ? { keyPackages: {
420
+ async publish(input, signal) {
421
+ const { request, result } = await call(
422
+ keyPackagesUrl,
423
+ "publish",
424
+ input,
425
+ parseMlsDevKeyPackagePublish,
426
+ parseMlsDevKeyPackagePublicationReceipt,
427
+ MLS_DEV_KEY_PACKAGES_MAX_BYTES,
428
+ signal
429
+ );
430
+ if (result.requestId !== request.requestId) throw invalidResponse();
431
+ return result;
432
+ },
433
+ async claim(input, signal) {
434
+ const { request, result } = await call(
435
+ keyPackagesUrl,
436
+ "claim",
437
+ input,
438
+ parseMlsDevKeyPackageClaim,
439
+ parseMlsDevKeyPackageClaimReceipt,
440
+ MLS_DEV_KEY_PACKAGES_MAX_BYTES,
441
+ signal
442
+ );
443
+ if (result.requestId !== request.requestId || result.recipientDeviceId !== request.recipientDeviceId || result.claimantDeviceId !== parseMlsDevCredentialToken(request.claimantCredential).unsigned.claims.device.deviceId || canonicalEndpointJson(result.preconditions) !== canonicalEndpointJson(request.preconditions)) throw invalidResponse();
444
+ return result;
445
+ }
446
+ } } : {},
447
+ async beginEnrollment(input, signal) {
448
+ const { request, result } = await call(enrollmentUrl, "begin", input, parseMlsDevEnrollmentBegin, parseMlsDevEnrollmentChallenge, MLS_DEV_ENROLLMENT_MAX_BYTES, signal);
449
+ if (result.installationId !== request.installationId || result.signaturePublicKey !== request.signaturePublicKey) throw invalidResponse();
450
+ return result;
451
+ },
452
+ async completeEnrollment(input, signal) {
453
+ return (await call(enrollmentUrl, "complete", input, parseMlsDevEnrollmentComplete, parseMlsDevEnrollmentResult, MLS_DEV_ENROLLMENT_MAX_BYTES, signal)).result;
454
+ },
455
+ async beginCredential(input, signal) {
456
+ const { request, result } = await call(credentialUrl, "begin", input, parseMlsDevCredentialBegin, parseMlsDevCredentialChallenge, MLS_DEV_CREDENTIAL_MAX_BYTES, signal);
457
+ if (result.credential.claims.device.deviceId !== request.deviceId || result.credential.claims.generation !== request.expectedCredentialGeneration + 1) throw invalidResponse();
458
+ return result;
459
+ },
460
+ async completeCredential(input, signal) {
461
+ return (await call(credentialUrl, "complete", input, parseMlsDevCredentialComplete, parseMlsDevCredentialReceipt, MLS_DEV_CREDENTIAL_MAX_BYTES, signal)).result;
462
+ },
463
+ async beginReconciliation(input, signal) {
464
+ const { request, result } = await call(reconciliationUrl, "begin", input, parseMlsDevCredentialReconciliationBegin, parseMlsDevCredentialReconciliationChallenge, MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES, signal);
465
+ if (result.device.deviceId !== request.deviceId || result.requestId !== request.requestId || result.expectedCredentialGeneration !== request.expectedCredentialGeneration) throw invalidResponse();
466
+ return result;
467
+ },
468
+ async completeReconciliation(input, signal) {
469
+ const { request, result } = await call(reconciliationUrl, "complete", input, parseMlsDevCredentialReconciliationComplete, parseMlsDevCredentialReconciliationReceipt, MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES, signal);
470
+ if (result.challenge.challengeId !== request.challengeId) throw invalidResponse();
471
+ const key = JSON.stringify(result);
472
+ observations.set(key, true);
473
+ if (observations.size > 32) observations.delete(observations.keys().next().value);
474
+ return result;
475
+ },
476
+ async revokeCredential(input, signal) {
477
+ const { request, result } = await call(credentialUrl, "revoke", input, parseMlsDevCredentialRevoke, parseMlsDevCredentialRevocationReceipt, MLS_DEV_CREDENTIAL_MAX_BYTES, signal);
478
+ if (result.deviceId !== request.deviceId) throw invalidResponse();
479
+ return result;
480
+ },
481
+ verifyGenerationObservation(receipt) {
482
+ try {
483
+ return observations.has(JSON.stringify(parseMlsDevCredentialReconciliationReceipt(receipt)));
484
+ } catch {
485
+ return false;
486
+ }
487
+ }
488
+ };
489
+ }
490
+ __name(createMlsDevDeviceHttpTransport, "createMlsDevDeviceHttpTransport");
491
+
492
+ // ../endpoint/src/transport.ts
493
+ var EndpointError = class extends Error {
494
+ static {
495
+ __name(this, "EndpointError");
496
+ }
497
+ status;
498
+ code;
499
+ retryable;
500
+ operationId;
501
+ retryAfterMs;
502
+ constructor(message, options = {}) {
503
+ super(message);
504
+ this.name = "EndpointError";
505
+ this.status = options.status ?? 0;
506
+ this.code = options.code ?? "ENDPOINT_UNAVAILABLE";
507
+ this.retryable = options.retryable ?? classifyTransferHttpFailure(this.status) === "retry";
508
+ this.operationId = options.operationId;
509
+ this.retryAfterMs = options.retryAfterMs;
510
+ }
511
+ };
512
+ var retryableStreamErrors = /* @__PURE__ */ new Set([
513
+ "rate_limited",
514
+ "connection_limit",
515
+ "service_overloaded",
516
+ "listener_failed",
517
+ "connection_reclaimed",
518
+ "connection_stale",
519
+ "server_shutting_down",
520
+ "authorization_changed",
521
+ "authorization_check_failed"
522
+ ]);
523
+ function decodeEndpointError(value, options = {}) {
524
+ const details = value && typeof value === "object" && !Array.isArray(value) ? value : {};
525
+ const status = options.status ?? (typeof details.status === "number" && Number.isInteger(details.status) && details.status >= 400 && details.status <= 599 ? details.status : 0);
526
+ const code = typeof details.code === "string" ? details.code : options.stream ? "STREAM_UNAVAILABLE" : `HTTP_${status}`;
527
+ const positive = /* @__PURE__ */ __name((value2) => typeof value2 === "number" && Number.isFinite(value2) && value2 > 0 ? value2 : void 0, "positive");
528
+ const retryHeader = options.retryAfter?.trim();
529
+ const retryAfterMs = positive(retryHeader ? /^\d+(?:\.\d+)?$/.test(retryHeader) ? Number(retryHeader) * 1e3 : Date.parse(retryHeader) - Date.now() : void 0) ?? positive(details.retryAfterMs) ?? positive(typeof details.retryAfter === "number" ? details.retryAfter * 1e3 : void 0);
530
+ const accessTokenExpired = status === 401 && code === "CONNECTOR_ACCESS_TOKEN_EXPIRED";
531
+ const retryable = details.retryable !== false && (status ? accessTokenExpired || status === 429 || status >= 500 : options.stream ? retryableStreamErrors.has(code) : true);
532
+ return new EndpointError(typeof details.error === "string" ? details.error : typeof details.message === "string" ? details.message : `Canon ${options.stream ? "stream" : "request"} failed${status ? ` (${status})` : ""}`, {
533
+ status,
534
+ code,
535
+ retryable,
536
+ ...retryAfterMs === void 0 ? {} : { retryAfterMs }
537
+ });
538
+ }
539
+ __name(decodeEndpointError, "decodeEndpointError");
540
+ async function readHttpError(response, signal) {
541
+ if (response.headers.get("Content-Type")?.toLowerCase().startsWith("text/event-stream")) {
542
+ try {
543
+ for await (const _event of readEndpointEvents(response, { signal })) {
544
+ }
545
+ } catch (error) {
546
+ if (error instanceof EndpointError) return error;
547
+ throw error;
548
+ }
549
+ } else {
550
+ return decodeEndpointError(await response.json().catch(() => null), { status: response.status, retryAfter: response.headers.get("Retry-After") });
551
+ }
552
+ return decodeEndpointError(null, { status: response.status, retryAfter: response.headers.get("Retry-After"), stream: true });
553
+ }
554
+ __name(readHttpError, "readHttpError");
555
+ async function readBoundedGroupJson(response, signal) {
556
+ const maximum = 4 * 1024 * 1024;
557
+ const invalid = /* @__PURE__ */ __name(() => new EndpointError("Invalid MLS group response", { status: 502, code: "INVALID_MLS_DEV_GROUP_RESPONSE", retryable: false }), "invalid");
558
+ const length = response.headers.get("Content-Length");
559
+ if (length !== null && (!/^\d+$/.test(length) || Number(length) > maximum)) throw invalid();
560
+ const reader = response.body?.getReader();
561
+ if (!reader) throw invalid();
562
+ let rejectAbort;
563
+ const stopped = new Promise((_resolve, reject) => {
564
+ rejectAbort = reject;
565
+ });
566
+ void stopped.catch(() => {
567
+ });
568
+ const abort = /* @__PURE__ */ __name(() => rejectAbort(new EndpointError("MLS group request aborted", { code: "MLS_DEV_REQUEST_ABORTED", retryable: true })), "abort");
569
+ if (signal.aborted) abort();
570
+ else signal.addEventListener("abort", abort, { once: true });
571
+ try {
572
+ const decoder = new TextDecoder("utf-8", { fatal: true }), parts = [];
573
+ let size = 0;
574
+ for (; ; ) {
575
+ if (signal.aborted) throw new EndpointError("MLS group request aborted", { code: "MLS_DEV_REQUEST_ABORTED", retryable: true });
576
+ const chunk = await Promise.race([reader.read(), stopped]);
577
+ if (chunk.done) break;
578
+ size += chunk.value.byteLength;
579
+ if (size > maximum) throw invalid();
580
+ parts.push(decoder.decode(chunk.value, { stream: true }));
581
+ }
582
+ parts.push(decoder.decode());
583
+ return JSON.parse(parts.join(""));
584
+ } catch (error) {
585
+ if (error instanceof EndpointError) throw error;
586
+ throw invalid();
587
+ } finally {
588
+ signal.removeEventListener("abort", abort);
589
+ try {
590
+ void reader.cancel().catch(() => {
591
+ });
592
+ } catch {
593
+ }
594
+ reader.releaseLock();
595
+ }
596
+ }
597
+ __name(readBoundedGroupJson, "readBoundedGroupJson");
598
+ function hasEnvironmentMismatch(response, expected) {
599
+ const environment = response.headers.get("X-Canon-Environment-Id");
600
+ return environment !== expected && !(environment === null && (response.status === 429 || response.status >= 500));
601
+ }
602
+ __name(hasEnvironmentMismatch, "hasEnvironmentMismatch");
603
+ function validateEndpointContentBoundary(lane, name, payload) {
604
+ try {
605
+ endpointContentBoundary(lane, name, payload);
606
+ } catch {
607
+ throw new EndpointError("Endpoint content lane requires a protocol update", { code: "ENDPOINT_CONTENT_UNCLASSIFIED", retryable: false });
608
+ }
609
+ }
610
+ __name(validateEndpointContentBoundary, "validateEndpointContentBoundary");
611
+ function createHttpEndpointTransport(options) {
612
+ const fetcher = options.fetch ?? globalThis.fetch;
613
+ const clientHeaders = options.client ? { [CANON_CLIENT_HEADER]: formatCanonClientHeader(options.client) } : {};
614
+ const updateRequired = /* @__PURE__ */ __name((error) => {
615
+ if (error.code === CLIENT_UPDATE_REQUIRED) try {
616
+ options.onClientUpdateRequired?.(error);
617
+ } catch {
618
+ }
619
+ return error;
620
+ }, "updateRequired");
621
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
622
+ let resolvedStreamId;
623
+ function streamIdentity() {
624
+ return resolvedStreamId ??= Promise.resolve().then(async () => {
625
+ const value = typeof options.streamId === "function" ? await options.streamId() : options.streamId;
626
+ if (value !== void 0 && (typeof value !== "string" || !/^[A-Za-z0-9_-]{1,128}$/.test(value))) {
627
+ throw new EndpointError("Invalid logical stream identity", { code: "INVALID_STREAM_ID", retryable: false });
628
+ }
629
+ return value;
630
+ });
631
+ }
632
+ __name(streamIdentity, "streamIdentity");
633
+ function ownershipHeaders() {
634
+ const version = typeof options.ownershipVersion === "function" ? options.ownershipVersion() : options.ownershipVersion;
635
+ if (version === void 0) return {};
636
+ if (!Number.isSafeInteger(version) || version < 0) throw new EndpointError("Invalid endpoint ownership generation", { code: "INVALID_OWNERSHIP_VERSION", retryable: false });
637
+ return { "X-Canon-Ownership-Version": String(version) };
638
+ }
639
+ __name(ownershipHeaders, "ownershipHeaders");
640
+ async function call(path, method, body, signal, origin = baseUrl, groupResponse = false) {
641
+ for (let authAttempt = 0; authAttempt < 2; authAttempt++) {
642
+ const token = await options.credentials.getToken(authAttempt > 0);
643
+ if (!token) throw new EndpointError("Authentication is required", { status: 401, code: "UNAUTHENTICATED", retryable: false });
644
+ const controller = new AbortController();
645
+ const abort = /* @__PURE__ */ __name(() => controller.abort(signal?.reason), "abort");
646
+ if (signal?.aborted) abort();
647
+ else signal?.addEventListener("abort", abort, { once: true });
648
+ const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 3e4);
649
+ try {
650
+ const response = await fetcher(`${origin.replace(/\/$/, "")}${path}`, {
651
+ method,
652
+ signal: controller.signal,
653
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", "X-Canon-Endpoint-Protocol": CANON_ENDPOINT_PROTOCOL, ...clientHeaders, ...ownershipHeaders() },
654
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
655
+ });
656
+ if (hasEnvironmentMismatch(response, options.environmentId)) {
657
+ throw new EndpointError("Canon environment does not match this endpoint", { status: 409, code: "ENVIRONMENT_MISMATCH", retryable: false });
658
+ }
659
+ if (response.status === 401 && authAttempt === 0) {
660
+ await response.body?.cancel();
661
+ continue;
662
+ }
663
+ if (!response.ok) throw updateRequired(await readHttpError(response, controller.signal));
664
+ return groupResponse ? await readBoundedGroupJson(response, controller.signal) : await response.json().catch(() => null);
665
+ } finally {
666
+ clearTimeout(timeout);
667
+ signal?.removeEventListener("abort", abort);
668
+ }
669
+ }
670
+ throw new EndpointError("Authentication is required", { status: 401, code: "UNAUTHENTICATED", retryable: false });
671
+ }
672
+ __name(call, "call");
673
+ function receipt(value, operationId, installationId, action) {
674
+ if (!isEndpointOperationReceipt(value) || classifyTransferReceipt(value, { protocol: CANON_ENDPOINT_PROTOCOL, operationId, installationId, action: action ?? value.action }) === "invalid") throw new EndpointError("Canon returned an invalid operation receipt", { status: 502, code: "INVALID_RECEIPT" });
675
+ if (value.operationId !== operationId || value.installationId !== installationId || action !== void 0 && value.action !== action) throw new EndpointError("Canon returned another operation receipt", { status: 502, code: "INVALID_RECEIPT" });
676
+ validateEndpointContentBoundary("operation.receipt", value.action, value);
677
+ return value;
678
+ }
679
+ __name(receipt, "receipt");
680
+ return {
681
+ ...options.mlsDevDevice ? { mlsDevDevice: createMlsDevDeviceHttpTransport(options.mlsDevDevice, {
682
+ environmentId: options.environmentId,
683
+ credentials: options.credentials,
684
+ fetch: fetcher,
685
+ timeoutMs: options.timeoutMs,
686
+ ownershipHeaders
687
+ }) } : {},
688
+ async capabilities() {
689
+ const result = await call("/endpoint/capabilities", "GET", void 0);
690
+ if (!result || typeof result !== "object" || result.protocol !== CANON_ENDPOINT_PROTOCOL || !Array.isArray(result.actions) || !Array.isArray(result.queries)) {
691
+ throw new EndpointError("Canon returned invalid endpoint capabilities", { status: 502, code: "INVALID_CAPABILITIES" });
692
+ }
693
+ return result;
694
+ },
695
+ ...options.streamUrl ? {
696
+ async *events(streamOptions) {
697
+ const streamId = await streamIdentity();
698
+ for (let attempt = 0; attempt < 2; attempt++) {
699
+ const token = await options.credentials.getToken(attempt > 0);
700
+ if (!token) throw new EndpointError("Authentication is required", { status: 401, code: "UNAUTHENTICATED", retryable: false });
701
+ const connection = new AbortController();
702
+ const abort = /* @__PURE__ */ __name(() => connection.abort(streamOptions.signal.reason), "abort");
703
+ if (streamOptions.signal.aborted) abort();
704
+ else streamOptions.signal.addEventListener("abort", abort, { once: true });
705
+ const handshake = setTimeout(() => connection.abort(), options.timeoutMs ?? 3e4);
706
+ try {
707
+ const scopeQuery = streamOptions.principalScopes?.length ? `&principalScopes=${encodeURIComponent([...new Set(streamOptions.principalScopes)].sort().join(","))}` : "";
708
+ const response = await fetcher(`${options.streamUrl.replace(/\/$/, "")}/endpoint/events?events=${encodeURIComponent((streamOptions.families ?? ["messages", "contacts"]).join(","))}${scopeQuery}`, {
709
+ signal: connection.signal,
710
+ headers: { Authorization: `Bearer ${token}`, Accept: "text/event-stream", "X-Canon-Endpoint-Protocol": CANON_ENDPOINT_PROTOCOL, ...clientHeaders, ...streamId ? { "X-Canon-Stream-Id": streamId } : {}, ...ownershipHeaders(), ...streamOptions.lastEventId ? { "Last-Event-ID": streamOptions.lastEventId } : {} }
711
+ });
712
+ if (hasEnvironmentMismatch(response, options.environmentId)) {
713
+ await response.body?.cancel();
714
+ throw new EndpointError("Canon stream environment does not match this endpoint", { status: 409, code: "ENVIRONMENT_MISMATCH", retryable: false });
715
+ }
716
+ if (response.status === 401 && attempt === 0) {
717
+ await response.body?.cancel();
718
+ continue;
719
+ }
720
+ if (!response.ok) throw updateRequired(await readHttpError(response, connection.signal));
721
+ if (!response.headers.get("Content-Type")?.toLowerCase().startsWith("text/event-stream")) {
722
+ await response.body?.cancel();
723
+ throw new EndpointError("Canon returned an invalid event stream", { status: 502, code: "INVALID_EVENT_STREAM" });
724
+ }
725
+ clearTimeout(handshake);
726
+ let connected = false;
727
+ for await (const event of readEndpointEvents(response, { ...streamOptions, signal: connection.signal })) {
728
+ if (!connected) {
729
+ connected = true;
730
+ streamOptions.onConnected?.();
731
+ }
732
+ yield event;
733
+ }
734
+ return;
735
+ } finally {
736
+ clearTimeout(handshake);
737
+ streamOptions.signal.removeEventListener("abort", abort);
738
+ connection.abort();
739
+ }
740
+ }
741
+ }
742
+ } : {},
743
+ async execute(operation, signal) {
744
+ validateEndpointContentBoundary("operation.request", operation.action, operation);
745
+ return receipt(await call("/endpoint/operations", "POST", operation, signal, baseUrl, operation.action === "mls_group_control"), operation.operationId, operation.installationId, operation.action);
746
+ },
747
+ async getOperation(operationId, installationId, signal) {
748
+ try {
749
+ return receipt(await call(`/endpoint/operations/${encodeURIComponent(operationId)}?installationId=${encodeURIComponent(installationId)}`, "GET", void 0, signal, baseUrl, true), operationId, installationId);
750
+ } catch (error) {
751
+ if (error instanceof EndpointError && error.status === 404) return null;
752
+ throw error;
753
+ }
754
+ },
755
+ async query(query, parameters, signal) {
756
+ validateEndpointContentBoundary("query.request", query, parameters);
757
+ if (query === "admit_messages" && !options.streamUrl) throw new EndpointError("Agent recovery requires the shared stream endpoint", { code: "STREAM_CONFIGURATION_REQUIRED", retryable: false });
758
+ const response = await call("/endpoint/query", "POST", { protocol: CANON_ENDPOINT_PROTOCOL, query, parameters }, signal, query === "admit_messages" ? options.streamUrl : baseUrl, ["mls_group_controls", "mls_group_feed"].includes(query));
759
+ if (!response || typeof response !== "object" || response.protocol !== CANON_ENDPOINT_PROTOCOL || !Object.hasOwn(response, "result")) {
760
+ throw new EndpointError("Canon returned an invalid query response", { status: 502, code: "INVALID_QUERY_RESPONSE" });
761
+ }
762
+ validateEndpointContentBoundary("query.result", query, response.result);
763
+ return response.result;
764
+ },
765
+ async publish(action, body, signal) {
766
+ validateEndpointContentBoundary("ephemeral.request", action, body);
767
+ const response = await call("/endpoint/ephemeral", "POST", { protocol: CANON_ENDPOINT_PROTOCOL, action, body }, signal);
768
+ if (!response || typeof response !== "object" || response.protocol !== CANON_ENDPOINT_PROTOCOL || !Object.hasOwn(response, "result")) {
769
+ throw new EndpointError("Canon returned an invalid ephemeral response", { status: 502, code: "INVALID_EPHEMERAL_RESPONSE" });
770
+ }
771
+ validateEndpointContentBoundary("ephemeral.result", action, response.result);
772
+ return response.result;
773
+ }
774
+ };
775
+ }
776
+ __name(createHttpEndpointTransport, "createHttpEndpointTransport");
777
+
778
+ // ../endpoint/src/node-mls-custody-protocol.ts
779
+ var NodeMlsDevCustodyError = class extends Error {
780
+ static {
781
+ __name(this, "NodeMlsDevCustodyError");
782
+ }
783
+ code = "MLS_NODE_CUSTODY_REJECTED";
784
+ constructor() {
785
+ super("MLS_NODE_CUSTODY_REJECTED");
786
+ this.name = "NodeMlsDevCustodyError";
787
+ }
788
+ };
789
+ var MAX_INPUT = 8 * 1024 * 1024;
790
+ var MAX_OUTPUT = 4 * 1024 * 1024;
791
+ var DOMAIN = Buffer.from("canon.mls.agent-host.v1\0");
792
+ function rejected() {
793
+ throw new NodeMlsDevCustodyError();
794
+ }
795
+ __name(rejected, "rejected");
796
+ function custodyValue(output, operation) {
797
+ const parsed = parseJson(output);
798
+ if (operation === 4 && isMlsDevReceiptClockSkew(parsed)) throw new EndpointError("Canon accepted this entry ahead of this device's clock", { code: MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW, retryable: true });
799
+ const result = record(parsed, ["version", "ok", "value"]);
800
+ requireValue(result.version === 1 && result.ok === true);
801
+ return result.value;
802
+ }
803
+ __name(custodyValue, "custodyValue");
804
+ function custodyFailure(error) {
805
+ if (error instanceof EndpointError && error.code === MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW) throw error;
806
+ return rejected();
807
+ }
808
+ __name(custodyFailure, "custodyFailure");
809
+ function requireValue(value) {
810
+ if (!value) rejected();
811
+ }
812
+ __name(requireValue, "requireValue");
813
+ function record(value, fields) {
814
+ requireValue(value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype);
815
+ const descriptors = Object.getOwnPropertyDescriptors(value);
816
+ requireValue(Object.keys(descriptors).length === fields.length && fields.every((key) => descriptors[key] && "value" in descriptors[key]));
817
+ return value;
818
+ }
819
+ __name(record, "record");
820
+ function sealed(value, maximum, nullable = false) {
821
+ if (nullable && value === null) return null;
822
+ requireValue(typeof value === "string" && value.length <= maximum && value.startsWith("v1:"));
823
+ const body = value.slice(3);
824
+ requireValue(body.length >= 40 && Buffer.from(body, "base64").toString("base64") === body);
825
+ return value;
826
+ }
827
+ __name(sealed, "sealed");
828
+ function frame(operation, values) {
829
+ const chunks = [DOMAIN, Buffer.from([operation])];
830
+ let length = DOMAIN.length + 1;
831
+ for (const value of values) {
832
+ const bytes = value === null ? null : Buffer.from(value, "utf8");
833
+ const prefix = Buffer.alloc(4);
834
+ prefix.writeUInt32BE(bytes?.length ?? 4294967295);
835
+ chunks.push(prefix);
836
+ if (bytes) chunks.push(bytes);
837
+ length += 4 + (bytes?.length ?? 0);
838
+ requireValue(length <= MAX_INPUT);
839
+ }
840
+ return Buffer.concat(chunks, length);
841
+ }
842
+ __name(frame, "frame");
843
+ function parseFrame(input) {
844
+ requireValue(input.length > DOMAIN.length && input.length <= MAX_INPUT && input.subarray(0, DOMAIN.length).equals(DOMAIN));
845
+ const values = [];
846
+ let offset = DOMAIN.length + 1;
847
+ while (offset < input.length) {
848
+ requireValue(input.length - offset >= 4);
849
+ const length = input.readUInt32BE(offset);
850
+ offset += 4;
851
+ if (length === 4294967295) {
852
+ values.push(null);
853
+ continue;
854
+ }
855
+ requireValue(length <= input.length - offset);
856
+ values.push(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(offset, offset + length)));
857
+ offset += length;
858
+ }
859
+ return { operation: input[DOMAIN.length], values };
860
+ }
861
+ __name(parseFrame, "parseFrame");
862
+ function parseJson(bytes) {
863
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
864
+ const value = JSON.parse(text);
865
+ let offset = 0;
866
+ const whitespace = /* @__PURE__ */ __name(() => {
867
+ while (/\s/.test(text[offset] ?? "") && offset < text.length) offset++;
868
+ }, "whitespace");
869
+ const string = /* @__PURE__ */ __name(() => {
870
+ const start = offset++;
871
+ while (offset < text.length) {
872
+ const c = text[offset++];
873
+ if (c === "\\") offset++;
874
+ else if (c === '"') return JSON.parse(text.slice(start, offset));
875
+ }
876
+ return rejected();
877
+ }, "string");
878
+ const scan = /* @__PURE__ */ __name((depth) => {
879
+ requireValue(depth <= 64);
880
+ whitespace();
881
+ const c = text[offset];
882
+ if (c === "{") {
883
+ offset++;
884
+ whitespace();
885
+ const keys = /* @__PURE__ */ new Set();
886
+ if (text[offset] === "}") {
887
+ offset++;
888
+ return;
889
+ }
890
+ while (true) {
891
+ whitespace();
892
+ requireValue(text[offset] === '"');
893
+ const key = string();
894
+ requireValue(!keys.has(key));
895
+ keys.add(key);
896
+ whitespace();
897
+ requireValue(text[offset++] === ":");
898
+ scan(depth + 1);
899
+ whitespace();
900
+ if (text[offset++] === "}") return;
901
+ }
902
+ }
903
+ if (c === "[") {
904
+ offset++;
905
+ whitespace();
906
+ if (text[offset] === "]") {
907
+ offset++;
908
+ return;
909
+ }
910
+ while (true) {
911
+ scan(depth + 1);
912
+ whitespace();
913
+ if (text[offset++] === "]") return;
914
+ }
915
+ }
916
+ if (c === '"') {
917
+ string();
918
+ return;
919
+ }
920
+ while (offset < text.length && !/[\s,\]}]/.test(text[offset])) offset++;
921
+ }, "scan");
922
+ scan(0);
923
+ whitespace();
924
+ requireValue(offset === text.length);
925
+ return value;
926
+ }
927
+ __name(parseJson, "parseJson");
928
+ function typedNodeMlsDevCustody(job, invoke, close) {
929
+ return Object.freeze({
930
+ prepareProtected: /* @__PURE__ */ __name(((context, commandValue, wrappedState) => job(async (signal) => {
931
+ const contextJson = JSON.stringify(parseMlsDevDeviceContext(context)), command = parseMlsDevDeviceCommand(commandValue);
932
+ const wrapped = sealed(wrappedState, 6144, true);
933
+ requireValue(command.op === "create" === (wrapped === null));
934
+ requireValue(await invoke(1, [contextJson], signal) === null);
935
+ return parseMlsDevDevicePreparation(await invoke(2, [contextJson, JSON.stringify(command), wrapped], signal), command.op);
936
+ })), "prepareProtected"),
937
+ prepareProtectedKeyPackage: /* @__PURE__ */ __name(((context, command, wrappedState) => job(async (signal) => {
938
+ const contextJson = JSON.stringify(parseMlsDevDeviceContext(context)), commandJson = JSON.stringify(parseMlsDevKeyPackageCommand(command));
939
+ const wrapped = sealed(wrappedState, 6144);
940
+ requireValue(await invoke(1, [contextJson], signal) === null);
941
+ return parseMlsDevKeyPackagePreparation(await invoke(3, [contextJson, commandJson, wrapped], signal));
942
+ })), "prepareProtectedKeyPackage"),
943
+ prepareProtectedGroup: /* @__PURE__ */ __name(((identity, group, command, identityState, groupState, packageState) => job(async (signal) => {
944
+ const contextJson = JSON.stringify(parseMlsDevDeviceContext(identity)), groupJson = JSON.stringify(parseMlsDevGroupContext(group)), commandJson = JSON.stringify(parseMlsDevGroupCommand(command));
945
+ const wrapped = sealed(identityState, 6144), protectedGroup = sealed(groupState, 3 * 1024 * 1024, true), protectedPackage = sealed(packageState, 192 * 1024, true);
946
+ requireValue(await invoke(1, [contextJson], signal) === null);
947
+ const prepared = parseMlsDevGroupPreparation(await invoke(4, [contextJson, groupJson, commandJson, wrapped, protectedGroup, protectedPackage], signal));
948
+ const expected = ["create", "add", "remove"].includes(command.op) ? ["control"] : ["join", "process", "finalize"].includes(command.op) ? ["applied"] : command.op === "encrypt_text" || command.op === "encrypt_item" ? ["application"] : command.op === "encrypt_interaction" ? ["interaction_application"] : command.op === "process_application" ? ["text", "item", "interaction", "invalid"] : command.op === "open_secret_answer" ? ["secret"] : ["discarded"];
949
+ requireValue(expected.includes(prepared.payload.kind));
950
+ if (prepared.payload.kind === "application") requireValue(command.op === "encrypt_item" === (prepared.payload.payload !== void 0));
951
+ return prepared;
952
+ })), "prepareProtectedGroup"),
953
+ close
954
+ });
955
+ }
956
+ __name(typedNodeMlsDevCustody, "typedNodeMlsDevCustody");
957
+
958
+ // ../endpoint/src/node-mls-custody.ts
959
+ import { spawn } from "node:child_process";
960
+ import { createHash } from "node:crypto";
961
+ import { lstat, open, realpath } from "node:fs/promises";
962
+ import { constants } from "node:fs";
963
+ import { isAbsolute, join, resolve } from "node:path";
964
+ import { platform } from "node:os";
965
+ var EXECUTABLE = "Contents/MacOS/CanonMlsAgent";
966
+ var HASH = /^[0-9a-f]{64}$/;
967
+ var matches = /* @__PURE__ */ __name((pattern, value) => pattern.exec(value)?.[0] === value, "matches");
968
+ var hash = /* @__PURE__ */ __name((bytes) => createHash("sha256").update(bytes).digest("hex"), "hash");
969
+ function createNodeMlsDevCustody(options) {
970
+ const captured = (() => {
971
+ try {
972
+ const keys = ["bundlePath", "manifestSha256", "executableSha256", "bundleIdentifier", "teamIdentifier"];
973
+ if (Object.hasOwn(options, "timeoutMs")) keys.push("timeoutMs");
974
+ if (Object.hasOwn(options, "signal")) keys.push("signal");
975
+ const data = record(options, keys);
976
+ requireValue(typeof data.bundlePath === "string" && isAbsolute(data.bundlePath) && resolve(data.bundlePath) === data.bundlePath && data.bundlePath.endsWith(".app"));
977
+ for (const key of ["manifestSha256", "executableSha256"]) requireValue(typeof data[key] === "string" && HASH.test(data[key]));
978
+ requireValue(typeof data.bundleIdentifier === "string" && matches(/^[A-Za-z0-9][A-Za-z0-9.-]{2,150}$/, data.bundleIdentifier));
979
+ requireValue(typeof data.teamIdentifier === "string" && matches(/^[A-Z0-9]{10}$/, data.teamIdentifier));
980
+ const timeoutMs = data.timeoutMs ?? 3e4;
981
+ requireValue(typeof timeoutMs === "number" && Number.isSafeInteger(timeoutMs) && timeoutMs >= 100 && timeoutMs <= 6e4);
982
+ requireValue(data.signal === void 0 || data.signal instanceof AbortSignal);
983
+ return Object.freeze({ ...data, timeoutMs });
984
+ } catch {
985
+ return rejected();
986
+ }
987
+ })();
988
+ let closed = false;
989
+ const jobs = /* @__PURE__ */ new Set(), children = /* @__PURE__ */ new Set();
990
+ const alive = /* @__PURE__ */ __name((signal) => requireValue(!closed && !signal.aborted && !captured.signal?.aborted), "alive");
991
+ async function file(path, maximum) {
992
+ const info = await lstat(path);
993
+ requireValue(info.isFile() && !info.isSymbolicLink() && (info.mode & 18) === 0 && info.size > 0 && info.size <= maximum && (info.uid === process.getuid?.() || info.uid === 0) && await realpath(path) === path);
994
+ const fd = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
995
+ try {
996
+ const current = await fd.stat();
997
+ requireValue(current.ino === info.ino && current.dev === info.dev && current.size === info.size);
998
+ const bytes = Buffer.alloc(current.size);
999
+ let count = 0;
1000
+ while (count < bytes.length) {
1001
+ const read = await fd.read(bytes, count, bytes.length - count, count);
1002
+ requireValue(read.bytesRead > 0);
1003
+ count += read.bytesRead;
1004
+ }
1005
+ const after = await fd.stat();
1006
+ requireValue(after.size === current.size && after.mtimeMs === current.mtimeMs);
1007
+ return bytes;
1008
+ } finally {
1009
+ await fd.close();
1010
+ }
1011
+ }
1012
+ __name(file, "file");
1013
+ function command(binary, args, input, signal, maximum) {
1014
+ alive(signal);
1015
+ const result = new Promise((resolveCommand, rejectCommand) => {
1016
+ const child = spawn(binary, args, { detached: true, stdio: ["pipe", "pipe", "pipe"], env: { PATH: "/usr/bin:/bin" } });
1017
+ let invalid = false, stdoutBytes = 0, stderrBytes = 0;
1018
+ const chunks = [];
1019
+ const kill = /* @__PURE__ */ __name(() => {
1020
+ try {
1021
+ if (child.pid) process.kill(-child.pid, "SIGKILL");
1022
+ } catch (error) {
1023
+ if (error.code !== "ESRCH") invalid = true;
1024
+ }
1025
+ }, "kill");
1026
+ const cancel = /* @__PURE__ */ __name(() => {
1027
+ invalid = true;
1028
+ kill();
1029
+ }, "cancel");
1030
+ signal.addEventListener("abort", cancel, { once: true });
1031
+ child.stdout.on("data", (chunk) => {
1032
+ stdoutBytes += chunk.length;
1033
+ if (stdoutBytes > maximum) cancel();
1034
+ else chunks.push(chunk);
1035
+ });
1036
+ child.stderr.on("data", (chunk) => {
1037
+ stderrBytes += chunk.length;
1038
+ if (stderrBytes > 65536) cancel();
1039
+ });
1040
+ child.on("error", cancel);
1041
+ child.stdin.on("error", cancel);
1042
+ child.on("close", (code) => {
1043
+ signal.removeEventListener("abort", cancel);
1044
+ kill();
1045
+ if (invalid || code !== 0 || signal.aborted || closed) rejectCommand(new NodeMlsDevCustodyError());
1046
+ else resolveCommand(Buffer.concat(chunks));
1047
+ });
1048
+ if (signal.aborted) cancel();
1049
+ else child.stdin.end(input);
1050
+ });
1051
+ children.add(result);
1052
+ void result.then(() => children.delete(result), () => children.delete(result));
1053
+ return result;
1054
+ }
1055
+ __name(command, "command");
1056
+ async function verify(signal) {
1057
+ alive(signal);
1058
+ requireValue(platform() === "darwin");
1059
+ const directory = await lstat(captured.bundlePath);
1060
+ requireValue(directory.isDirectory() && !directory.isSymbolicLink() && (directory.mode & 18) === 0 && await realpath(captured.bundlePath) === captured.bundlePath);
1061
+ const manifestBytes = await file(captured.bundlePath + ".manifest.json", 65536);
1062
+ alive(signal);
1063
+ requireValue(hash(manifestBytes) === captured.manifestSha256);
1064
+ const manifest = record(parseJson(manifestBytes), ["schemaVersion", "purpose", "buildStatus", "bundleIdentifier", "teamIdentifier", "executable", "infoPlist", "nativeLibrarySha256", "sourceSha", "sourceDirty", "sourceHashes"]);
1065
+ requireValue(manifest.schemaVersion === 1 && manifest.purpose === "canon.mls.dev-macos-custody" && manifest.buildStatus === "signed" && manifest.bundleIdentifier === captured.bundleIdentifier && manifest.teamIdentifier === captured.teamIdentifier);
1066
+ for (const [name, expectedPath, maximum] of [["executable", EXECUTABLE, 128 * 1024 * 1024], ["infoPlist", "Contents/Info.plist", 32768]]) {
1067
+ const artifact = record(manifest[name], ["path", "sha256", "bytes"]);
1068
+ requireValue(artifact.path === expectedPath && typeof artifact.sha256 === "string" && HASH.test(artifact.sha256));
1069
+ const bytes = await file(join(captured.bundlePath, expectedPath), maximum);
1070
+ alive(signal);
1071
+ requireValue(artifact.bytes === bytes.length && artifact.sha256 === hash(bytes));
1072
+ if (name === "executable") requireValue(artifact.sha256 === captured.executableSha256);
1073
+ }
1074
+ const requirement = `anchor apple generic and identifier "${captured.bundleIdentifier}" and certificate leaf[subject.OU] = "${captured.teamIdentifier}"`;
1075
+ await command("/usr/bin/codesign", ["--verify", "--strict", "--all-architectures", `-R=${requirement}`, captured.bundlePath], null, signal, 16384);
1076
+ alive(signal);
1077
+ return join(captured.bundlePath, EXECUTABLE);
1078
+ }
1079
+ __name(verify, "verify");
1080
+ async function invoke(operation, values, signal) {
1081
+ const executable = await verify(signal);
1082
+ alive(signal);
1083
+ const output = await command(executable, [], frame(operation, values), signal, MAX_OUTPUT);
1084
+ alive(signal);
1085
+ await verify(signal);
1086
+ alive(signal);
1087
+ return custodyValue(output, operation);
1088
+ }
1089
+ __name(invoke, "invoke");
1090
+ async function job(work) {
1091
+ const controller = new AbortController();
1092
+ let timer;
1093
+ const cancel = /* @__PURE__ */ __name(() => controller.abort(), "cancel");
1094
+ jobs.add(controller);
1095
+ try {
1096
+ alive(controller.signal);
1097
+ captured.signal?.addEventListener("abort", cancel, { once: true });
1098
+ const cancelled = new Promise((_, reject) => {
1099
+ controller.signal.addEventListener("abort", () => reject(new NodeMlsDevCustodyError()), { once: true });
1100
+ timer = setTimeout(cancel, captured.timeoutMs);
1101
+ });
1102
+ return await Promise.race([work(controller.signal), cancelled]);
1103
+ } catch (error) {
1104
+ return custodyFailure(error);
1105
+ } finally {
1106
+ if (timer) clearTimeout(timer);
1107
+ controller.abort();
1108
+ captured.signal?.removeEventListener("abort", cancel);
1109
+ jobs.delete(controller);
1110
+ }
1111
+ }
1112
+ __name(job, "job");
1113
+ return typedNodeMlsDevCustody(job, invoke, async () => {
1114
+ closed = true;
1115
+ for (const job2 of jobs) job2.abort();
1116
+ await Promise.allSettled([...children]);
1117
+ });
1118
+ }
1119
+ __name(createNodeMlsDevCustody, "createNodeMlsDevCustody");
1120
+
1121
+ // ../endpoint/src/node.ts
1122
+ import { mkdir, chmod } from "node:fs/promises";
1123
+ import { createHash as createHash3 } from "node:crypto";
1124
+ import { join as join2, resolve as resolve3 } from "node:path";
1125
+ import { DatabaseSync } from "node:sqlite";
1126
+
1127
+ // ../endpoint/src/sqlite.ts
1128
+ async function createSqliteEndpointStore(options) {
1129
+ const { database, namespace } = options;
1130
+ if (!namespace) throw new Error("Endpoint store namespace is required");
1131
+ await database.transaction(async (tx) => {
1132
+ await tx.run("CREATE TABLE IF NOT EXISTS canon_endpoint_records (namespace TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY(namespace, key))");
1133
+ });
1134
+ return {
1135
+ transaction: /* @__PURE__ */ __name((work) => database.transaction(async (sql) => {
1136
+ const transaction = {
1137
+ async get(key) {
1138
+ const rows = await sql.all("SELECT value FROM canon_endpoint_records WHERE namespace = ? AND key = ?", [namespace, key]);
1139
+ return rows[0] ? JSON.parse(rows[0].value) : void 0;
1140
+ },
1141
+ async put(key, value) {
1142
+ await sql.run("INSERT INTO canon_endpoint_records(namespace, key, value) VALUES (?, ?, ?) ON CONFLICT(namespace, key) DO UPDATE SET value = excluded.value", [namespace, key, JSON.stringify(value)]);
1143
+ },
1144
+ async delete(key) {
1145
+ await sql.run("DELETE FROM canon_endpoint_records WHERE namespace = ? AND key = ?", [namespace, key]);
1146
+ },
1147
+ async scan(prefix, scanOptions = {}) {
1148
+ const limit = Math.max(1, Math.min(scanOptions.limit ?? 100, 1e3));
1149
+ const rows = await sql.all(
1150
+ "SELECT key, value FROM canon_endpoint_records WHERE namespace = ? AND key >= ? AND key < ? AND key > ? ORDER BY key LIMIT ?",
1151
+ [namespace, prefix, `${prefix}\uFFFF`, scanOptions.after ?? "", limit]
1152
+ );
1153
+ return rows.map((row) => ({ key: row.key, value: JSON.parse(row.value) }));
1154
+ }
1155
+ };
1156
+ return work(transaction);
1157
+ }), "transaction"),
1158
+ async close() {
1159
+ await database.close?.();
1160
+ }
1161
+ };
1162
+ }
1163
+ __name(createSqliteEndpointStore, "createSqliteEndpointStore");
1164
+
1165
+ // ../endpoint/src/portable-node-mls-custody.ts
1166
+ import { spawn as spawn2 } from "node:child_process";
1167
+ import { createHash as createHash2 } from "node:crypto";
1168
+ import { constants as constants2 } from "node:fs";
1169
+ import { lstat as lstat2, open as open2, realpath as realpath2 } from "node:fs/promises";
1170
+ import { dirname, isAbsolute as isAbsolute2, relative, resolve as resolve2 } from "node:path";
1171
+ import { platform as platform2 } from "node:os";
1172
+ var HASH2 = /^[0-9a-f]{64}$/;
1173
+ var absolute = /* @__PURE__ */ __name((value) => typeof value === "string" && isAbsolute2(value) && resolve2(value) === value && !value.includes("\0"), "absolute");
1174
+ var hash2 = /* @__PURE__ */ __name((bytes) => createHash2("sha256").update(bytes).digest("hex"), "hash");
1175
+ var sameIdentity = /* @__PURE__ */ __name((a, b) => a.version === b.version && a.environmentId === b.environmentId && a.installationId === b.installationId && a.principal.type === b.principal.type && a.principal.id === b.principal.id && ("ownershipVersion" in a.principal ? a.principal.ownershipVersion : void 0) === ("ownershipVersion" in b.principal ? b.principal.ownershipVersion : void 0), "sameIdentity");
1176
+ function createPortableNodeMlsDevCustody(options) {
1177
+ const captured = (() => {
1178
+ try {
1179
+ const fields = ["executablePath", "executableSha256", "configurationPath", "configurationSha256", "secretSource"];
1180
+ if (Object.hasOwn(options, "timeoutMs")) fields.push("timeoutMs");
1181
+ if (Object.hasOwn(options, "signal")) fields.push("signal");
1182
+ const value = record(options, fields), source = record(value.secretSource, ["kind", "path"]);
1183
+ requireValue(absolute(value.executablePath) && absolute(value.configurationPath) && absolute(source.path) && source.kind === "managed-file");
1184
+ for (const field of ["executableSha256", "configurationSha256"]) requireValue(typeof value[field] === "string" && HASH2.test(value[field]));
1185
+ const timeoutMs = value.timeoutMs ?? 3e4;
1186
+ requireValue(typeof timeoutMs === "number" && Number.isSafeInteger(timeoutMs) && timeoutMs >= 100 && timeoutMs <= 6e4);
1187
+ requireValue(value.signal === void 0 || value.signal instanceof AbortSignal);
1188
+ return Object.freeze({ ...value, secretSource: Object.freeze({ ...source }), timeoutMs });
1189
+ } catch {
1190
+ return rejected();
1191
+ }
1192
+ })();
1193
+ let closed = false;
1194
+ const jobs = /* @__PURE__ */ new Set(), children = /* @__PURE__ */ new Set();
1195
+ const alive = /* @__PURE__ */ __name((signal) => requireValue(!closed && !signal.aborted && !captured.signal?.aborted), "alive");
1196
+ async function trustedExecutableAncestry() {
1197
+ for (let path = dirname(captured.executablePath); ; path = dirname(path)) {
1198
+ const info = await lstat2(path);
1199
+ requireValue(info.isDirectory() && !info.isSymbolicLink() && (info.uid === process.getuid?.() || info.uid === 0) && (info.mode & 18) === 0);
1200
+ if (dirname(path) === path) return;
1201
+ }
1202
+ }
1203
+ __name(trustedExecutableAncestry, "trustedExecutableAncestry");
1204
+ async function checkedFile(path, maximum, secret = false) {
1205
+ requireValue(await realpath2(path) === path);
1206
+ const info = await lstat2(path);
1207
+ requireValue(info.isFile() && !info.isSymbolicLink() && info.nlink === 1 && info.size > 0 && info.size <= maximum && (info.uid === process.getuid?.() || info.uid === 0) && (info.mode & (secret ? 63 : 18)) === 0);
1208
+ const file = await open2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
1209
+ try {
1210
+ const current = await file.stat();
1211
+ requireValue(current.ino === info.ino && current.dev === info.dev && current.size === info.size && current.mode === info.mode && current.uid === info.uid && current.nlink === 1 && await realpath2(path) === path);
1212
+ return file;
1213
+ } catch (error) {
1214
+ await file.close();
1215
+ throw error;
1216
+ }
1217
+ }
1218
+ __name(checkedFile, "checkedFile");
1219
+ async function publicBytes(path, maximum) {
1220
+ const file = await checkedFile(path, maximum);
1221
+ try {
1222
+ const before = await file.stat(), bytes = Buffer.alloc(before.size);
1223
+ let length = 0;
1224
+ while (length < bytes.length) {
1225
+ const read = await file.read(bytes, length, bytes.length - length, length);
1226
+ requireValue(read.bytesRead > 0);
1227
+ length += read.bytesRead;
1228
+ }
1229
+ const after = await file.stat();
1230
+ requireValue(after.size === before.size && after.mtimeMs === before.mtimeMs && after.ctimeMs === before.ctimeMs);
1231
+ return bytes;
1232
+ } finally {
1233
+ await file.close();
1234
+ }
1235
+ }
1236
+ __name(publicBytes, "publicBytes");
1237
+ async function verify(signal) {
1238
+ alive(signal);
1239
+ requireValue(platform2() === "darwin" || platform2() === "linux");
1240
+ await trustedExecutableAncestry();
1241
+ alive(signal);
1242
+ const binary = await publicBytes(captured.executablePath, 128 * 1024 * 1024);
1243
+ alive(signal);
1244
+ requireValue(hash2(binary) === captured.executableSha256);
1245
+ const bytes = await publicBytes(captured.configurationPath, 65536);
1246
+ alive(signal);
1247
+ requireValue(hash2(bytes) === captured.configurationSha256);
1248
+ const config = record(parseJson(bytes), ["version", "environmentId", "context", "vaultPath", "issuerKeys", "receiptKeys"]);
1249
+ requireValue(config.version === 1 && isMlsEnvironmentId(config.environmentId) && absolute(config.vaultPath));
1250
+ const configuredContext = record(config.context, ["version", "environmentId", "principal", "installationId"]);
1251
+ const context = parseMlsDevDeviceContext({ ...configuredContext, revision: 0 });
1252
+ requireValue(context.environmentId === config.environmentId);
1253
+ const vaultParent = dirname(config.vaultPath);
1254
+ requireValue(await realpath2(vaultParent) === vaultParent);
1255
+ const parent = await lstat2(vaultParent);
1256
+ requireValue(parent.isDirectory() && parent.uid === process.getuid?.() && (parent.mode & 63) === 0);
1257
+ const fromVault = relative(vaultParent, captured.secretSource.path);
1258
+ requireValue(fromVault.startsWith("../") || isAbsolute2(fromVault));
1259
+ requireValue(captured.secretSource.path !== captured.configurationPath && captured.secretSource.path !== captured.executablePath);
1260
+ return { context, vaultParent };
1261
+ }
1262
+ __name(verify, "verify");
1263
+ async function command(input, provision, signal) {
1264
+ const secret = await checkedFile(captured.secretSource.path, 32, true);
1265
+ try {
1266
+ requireValue((await secret.stat()).size === 32);
1267
+ alive(signal);
1268
+ const result = new Promise((resolveCommand, rejectCommand) => {
1269
+ const child = spawn2(
1270
+ captured.executablePath,
1271
+ [
1272
+ "--config",
1273
+ captured.configurationPath,
1274
+ "--config-sha256",
1275
+ captured.configurationSha256,
1276
+ "--secret-fd",
1277
+ "3",
1278
+ ...provision ? ["--provision"] : []
1279
+ ],
1280
+ { detached: true, stdio: ["pipe", "pipe", "pipe", secret.fd], env: { PATH: "/usr/bin:/bin" } }
1281
+ );
1282
+ let invalid = false, stdoutBytes = 0, stderrBytes = 0;
1283
+ const chunks = [];
1284
+ const kill = /* @__PURE__ */ __name(() => {
1285
+ try {
1286
+ if (child.pid) process.kill(-child.pid, "SIGKILL");
1287
+ } catch (error) {
1288
+ if (error.code !== "ESRCH") invalid = true;
1289
+ }
1290
+ }, "kill");
1291
+ const cancel = /* @__PURE__ */ __name(() => {
1292
+ invalid = true;
1293
+ kill();
1294
+ }, "cancel");
1295
+ signal.addEventListener("abort", cancel, { once: true });
1296
+ child.stdout.on("data", (chunk) => {
1297
+ stdoutBytes += chunk.length;
1298
+ if (stdoutBytes > MAX_OUTPUT) cancel();
1299
+ else chunks.push(chunk);
1300
+ });
1301
+ child.stderr.on("data", (chunk) => {
1302
+ stderrBytes += chunk.length;
1303
+ if (stderrBytes > 65536) cancel();
1304
+ });
1305
+ child.on("error", cancel);
1306
+ child.stdin.on("error", cancel);
1307
+ child.on("close", (code) => {
1308
+ signal.removeEventListener("abort", cancel);
1309
+ kill();
1310
+ if (invalid || code !== 0 || signal.aborted || closed) rejectCommand(new NodeMlsDevCustodyError());
1311
+ else resolveCommand(Buffer.concat(chunks));
1312
+ });
1313
+ if (signal.aborted) cancel();
1314
+ else child.stdin.end(input);
1315
+ });
1316
+ children.add(result);
1317
+ void result.then(() => children.delete(result), () => children.delete(result));
1318
+ return await result;
1319
+ } finally {
1320
+ await secret.close();
1321
+ }
1322
+ }
1323
+ __name(command, "command");
1324
+ async function invoke(operation, values, signal) {
1325
+ const config = await verify(signal);
1326
+ requireValue(values[0] !== null && sameIdentity(parseMlsDevDeviceContext(JSON.parse(values[0])), config.context));
1327
+ const output = await command(frame(operation, values), false, signal);
1328
+ alive(signal);
1329
+ await verify(signal);
1330
+ alive(signal);
1331
+ return custodyValue(output, operation);
1332
+ }
1333
+ __name(invoke, "invoke");
1334
+ async function job(work) {
1335
+ const controller = new AbortController();
1336
+ let timer;
1337
+ const cancel = /* @__PURE__ */ __name(() => controller.abort(), "cancel");
1338
+ jobs.add(controller);
1339
+ try {
1340
+ alive(controller.signal);
1341
+ captured.signal?.addEventListener("abort", cancel, { once: true });
1342
+ const cancellation = new Promise((_, reject) => {
1343
+ controller.signal.addEventListener("abort", () => reject(new NodeMlsDevCustodyError()), { once: true });
1344
+ timer = setTimeout(cancel, captured.timeoutMs);
1345
+ });
1346
+ return await Promise.race([work(controller.signal), cancellation]);
1347
+ } catch (error) {
1348
+ return custodyFailure(error);
1349
+ } finally {
1350
+ if (timer) clearTimeout(timer);
1351
+ controller.abort();
1352
+ captured.signal?.removeEventListener("abort", cancel);
1353
+ jobs.delete(controller);
1354
+ }
1355
+ }
1356
+ __name(job, "job");
1357
+ const custody = typedNodeMlsDevCustody(job, invoke, async () => {
1358
+ closed = true;
1359
+ for (const job2 of jobs) job2.abort();
1360
+ await Promise.allSettled([...children]);
1361
+ });
1362
+ return Object.freeze({ ...custody, provision: /* @__PURE__ */ __name((context) => job(async (signal) => {
1363
+ const config = await verify(signal);
1364
+ requireValue(sameIdentity(parseMlsDevDeviceContext(context), config.context));
1365
+ const output = await command(null, true, signal);
1366
+ alive(signal);
1367
+ await verify(signal);
1368
+ alive(signal);
1369
+ const result = record(parseJson(output), ["version", "ok", "value"]);
1370
+ requireValue(result.version === 1 && result.ok === true && result.value === null);
1371
+ }), "provision") });
1372
+ }
1373
+ __name(createPortableNodeMlsDevCustody, "createPortableNodeMlsDevCustody");
1374
+
1375
+ // ../endpoint/src/node.ts
1376
+ var databases = /* @__PURE__ */ new Map();
1377
+ async function createNodeEndpointStore(options) {
1378
+ const directory = resolve3(options.directory);
1379
+ await mkdir(directory, { recursive: true, mode: 448 });
1380
+ const filename = join2(directory, `${createHash3("sha256").update(options.namespace).digest("hex")}.sqlite`);
1381
+ let opening = databases.get(filename);
1382
+ if (!opening) {
1383
+ opening = (async () => {
1384
+ const sqlite = new DatabaseSync(filename);
1385
+ try {
1386
+ await chmod(filename, 384);
1387
+ sqlite.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL; PRAGMA busy_timeout = 5000;");
1388
+ return { sqlite, tail: Promise.resolve(), references: 0 };
1389
+ } catch (error) {
1390
+ sqlite.close();
1391
+ throw error;
1392
+ }
1393
+ })();
1394
+ databases.set(filename, opening);
1395
+ void opening.catch(() => {
1396
+ if (databases.get(filename) === opening) databases.delete(filename);
1397
+ });
1398
+ }
1399
+ const shared = await opening;
1400
+ if (shared.closed) return createNodeEndpointStore(options);
1401
+ shared.references++;
1402
+ let closing;
1403
+ const database = {
1404
+ transaction(work) {
1405
+ if (closing) return Promise.reject(new Error("Endpoint store is closed"));
1406
+ const result = shared.tail.then(async () => {
1407
+ shared.sqlite.exec("BEGIN IMMEDIATE");
1408
+ try {
1409
+ const transaction = {
1410
+ async all(sql, parameters = []) {
1411
+ return shared.sqlite.prepare(sql).all(...parameters);
1412
+ },
1413
+ async run(sql, parameters = []) {
1414
+ shared.sqlite.prepare(sql).run(...parameters);
1415
+ }
1416
+ };
1417
+ const value = await work(transaction);
1418
+ shared.sqlite.exec("COMMIT");
1419
+ return value;
1420
+ } catch (error) {
1421
+ shared.sqlite.exec("ROLLBACK");
1422
+ throw error;
1423
+ }
1424
+ });
1425
+ shared.tail = result.catch(() => void 0);
1426
+ return result;
1427
+ },
1428
+ close() {
1429
+ if (closing) return closing;
1430
+ closing = shared.tail.then(() => {
1431
+ shared.references--;
1432
+ if (!shared.references) {
1433
+ shared.closed = true;
1434
+ if (databases.get(filename) === opening) databases.delete(filename);
1435
+ shared.sqlite.close();
1436
+ }
1437
+ });
1438
+ return closing;
1439
+ }
1440
+ };
1441
+ try {
1442
+ return await createSqliteEndpointStore({ database, namespace: options.namespace });
1443
+ } catch (error) {
1444
+ await database.close?.();
1445
+ throw error;
1446
+ }
1447
+ }
1448
+ __name(createNodeEndpointStore, "createNodeEndpointStore");
1449
+
1450
+ // ../endpoint/src/store.ts
1451
+ function copyJson(value) {
1452
+ return JSON.parse(JSON.stringify(value));
1453
+ }
1454
+ __name(copyJson, "copyJson");
1455
+
1456
+ export {
1457
+ createEndpointEventHub,
1458
+ EndpointError,
1459
+ validateEndpointContentBoundary,
1460
+ createHttpEndpointTransport,
1461
+ NodeMlsDevCustodyError,
1462
+ MAX_INPUT,
1463
+ MAX_OUTPUT,
1464
+ rejected,
1465
+ custodyValue,
1466
+ custodyFailure,
1467
+ requireValue,
1468
+ frame,
1469
+ parseFrame,
1470
+ typedNodeMlsDevCustody,
1471
+ createNodeMlsDevCustody,
1472
+ createPortableNodeMlsDevCustody,
1473
+ createNodeEndpointStore,
1474
+ copyJson
1475
+ };