hyperbrane 0.2.0 → 0.2.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 (50) hide show
  1. package/README.md +43 -3
  2. package/dist/agent/agentService.js +3 -2
  3. package/dist/agent/api.d.ts +26 -2
  4. package/dist/agent/control.js +1 -1
  5. package/dist/agent/dimensionAgentClient.d.ts +8 -0
  6. package/dist/agent/dimensionAgentClient.js +99 -0
  7. package/dist/agent/session/backend.d.ts +2 -1
  8. package/dist/agent/session/claude/transcript.d.ts +1 -0
  9. package/dist/agent/session/claude/transcript.js +1 -1
  10. package/dist/agent/session/worker/protocol.d.ts +6 -3
  11. package/dist/agent/session/worker/protocol.js +1 -1
  12. package/dist/agent/session/worker/workerParams.js +10 -3
  13. package/dist/agent/vendor-types/manifest.json +5 -60
  14. package/dist/agent/workerBundle.generated.js +1 -1
  15. package/dist/dimension/api.d.ts +3 -0
  16. package/dist/dimension/control.d.ts +2 -1
  17. package/dist/dimension/control.js +18 -0
  18. package/dist/dimension/service.js +27 -1
  19. package/dist/dimension/transportBundle.generated.js +1 -1
  20. package/dist/index.d.ts +3 -1
  21. package/dist/index.js +3 -1
  22. package/dist/lib/control.d.ts +4 -1
  23. package/dist/lib/control.js +2 -2
  24. package/dist/lib/jsonDirectory.js +24 -3
  25. package/dist/lib/unixSocket.d.ts +2 -0
  26. package/dist/lib/unixSocket.js +28 -0
  27. package/dist/portal-gateway/api.d.ts +1 -0
  28. package/dist/portal-gateway/gatewayService.js +14 -0
  29. package/dist/portal-gateway/transportBundle.generated.js +1 -1
  30. package/dist/restore.d.ts +9 -0
  31. package/dist/restore.js +10 -0
  32. package/dist/sandbox/api.d.ts +5 -0
  33. package/dist/sandbox/client.d.ts +2 -1
  34. package/dist/sandbox/client.js +4 -1
  35. package/dist/sandbox/lib/sshKey.d.ts +6 -0
  36. package/dist/sandbox/lib/sshKey.js +75 -0
  37. package/dist/sandbox/providers/boat/boatService.d.ts +2 -1
  38. package/dist/sandbox/providers/boat/boatService.js +72 -20
  39. package/dist/sandbox/providers/exedev/exeDevService.js +4 -2
  40. package/dist/sandbox/providers/exedev/exeDevToken.js +2 -6
  41. package/dist/sandbox/providers/local/localService.js +4 -26
  42. package/dist/sandbox/providers/ssh/sshService.js +2 -1
  43. package/dist/sandbox/service.d.ts +2 -1
  44. package/dist/sandbox/service.js +9 -1
  45. package/dist/vendor-types/claude/agent-sdk/bridge.d.ts +20 -4
  46. package/dist/vendor-types/claude/agent-sdk/browser-sdk.d.ts +24 -0
  47. package/dist/vendor-types/claude/agent-sdk/core.d.ts +2 -0
  48. package/dist/vendor-types/claude/agent-sdk/sdk-tools.d.ts +75 -6
  49. package/dist/vendor-types/claude/agent-sdk/sdk.d.ts +569 -100
  50. package/package.json +2 -1
package/dist/index.d.ts CHANGED
@@ -6,7 +6,9 @@ export { createDimensionClient } from "./dimension/client.ts";
6
6
  export { createGatewayClient } from "./portal-gateway/gatewayClient.ts";
7
7
  export { createPortalClient } from "./portal-gateway/portalClient.ts";
8
8
  export { createAgentClient } from "./agent/agentClient.ts";
9
- export { createSandboxClient } from "./sandbox/client.ts";
9
+ export { createDimensionAgentClient, currentSession } from "./agent/dimensionAgentClient.ts";
10
+ export { createSandboxClient, listSandboxes } from "./sandbox/client.ts";
11
+ export { restore, type Restored } from "./restore.ts";
10
12
  export * from "./agent/client.ts";
11
13
  export { createDockerClient, DockerError, type DockerClient } from "./lib/docker.ts";
12
14
  export { BUILTIN_UPSTREAMS } from "./upstreams/index.ts";
package/dist/index.js CHANGED
@@ -6,7 +6,9 @@ export { createDimensionClient } from "./dimension/client.js";
6
6
  export { createGatewayClient } from "./portal-gateway/gatewayClient.js";
7
7
  export { createPortalClient } from "./portal-gateway/portalClient.js";
8
8
  export { createAgentClient } from "./agent/agentClient.js";
9
- export { createSandboxClient } from "./sandbox/client.js";
9
+ export { createDimensionAgentClient, currentSession } from "./agent/dimensionAgentClient.js";
10
+ export { createSandboxClient, listSandboxes } from "./sandbox/client.js";
11
+ export { restore } from "./restore.js";
10
12
  export * from "./agent/client.js";
11
13
  export { createDockerClient, DockerError } from "./lib/docker.js";
12
14
  export { BUILTIN_UPSTREAMS } from "./upstreams/index.js";
@@ -3,6 +3,9 @@ export type ControlOptions = {
3
3
  dataDirectory?: string;
4
4
  };
5
5
  export declare const RECORD_END = "\n\u001E\n";
6
- export declare function runControl(host: Sandbox, prelude: string, script: string, env: Record<string, string>, options: ControlOptions): Promise<RunCommandResult>;
6
+ export declare function runControl(host: Sandbox, prelude: string, script: string, env: Record<string, string>, options: ControlOptions, { args, stdin, }?: {
7
+ args?: string[];
8
+ stdin?: string | Uint8Array;
9
+ }): Promise<RunCommandResult>;
7
10
  export declare function exitEnv(codes: Record<string, number>): Record<string, string>;
8
11
  export declare function records<T>(stdout: string): [string, T][];
@@ -1,8 +1,8 @@
1
1
  import { sh } from "./sh.js";
2
2
  export const RECORD_END = "\n\x1e\n";
3
- export function runControl(host, prelude, script, env, options) {
3
+ export function runControl(host, prelude, script, env, options, { args = [], stdin, } = {}) {
4
4
  const data = options.dataDirectory === undefined ? {} : { DATA: options.dataDirectory };
5
- return host.runCommand(["sh", "-c", `${CONTROL_FUNCTIONS}\n${prelude}\n${script}`], { env: { ...data, ...env } });
5
+ return host.runCommand(["sh", "-c", `${CONTROL_FUNCTIONS}\n${prelude}\n${script}`, "sh", ...args], { env: { ...data, ...env }, ...(stdin === undefined ? {} : { stdin }) });
6
6
  }
7
7
  export function exitEnv(codes) {
8
8
  return Object.fromEntries(Object.entries(codes).map(([name, code]) => [`E_${name.replace(/[A-Z]/g, (c) => `_${c}`).toUpperCase()}`, String(code)]));
@@ -5,6 +5,21 @@ const NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$/;
5
5
  export class InvalidNameError extends Error {
6
6
  name = "InvalidNameError";
7
7
  }
8
+ class NotARecordError extends Error {
9
+ name = "NotARecordError";
10
+ }
11
+ function parseRecord(path, text) {
12
+ let record;
13
+ try {
14
+ record = JSON.parse(text);
15
+ }
16
+ catch (error) {
17
+ throw new NotARecordError(`${path} isn't JSON: ${error.message}`);
18
+ }
19
+ if (record === null || typeof record !== "object" || Array.isArray(record))
20
+ throw new NotARecordError(`${path} isn't a JSON record (an object)`);
21
+ return record;
22
+ }
8
23
  export function checkName(name) {
9
24
  if (!NAME_PATTERN.test(name)) {
10
25
  throw new InvalidNameError(`Invalid name "${name}": use letters, digits, "_", "." and "-", up to 63 characters`);
@@ -20,7 +35,7 @@ export function createJsonDirectory(sandbox, dir) {
20
35
  }
21
36
  async function readPath(path) {
22
37
  try {
23
- return JSON.parse(new TextDecoder().decode(await sandbox.readFile(path)));
38
+ return parseRecord(path, new TextDecoder().decode(await sandbox.readFile(path)));
24
39
  }
25
40
  catch (error) {
26
41
  if (error instanceof SandboxNotFoundError)
@@ -54,8 +69,9 @@ export function createJsonDirectory(sandbox, dir) {
54
69
  .filter((text) => text.trim())
55
70
  .flatMap((text) => {
56
71
  const newline = text.indexOf("\n");
72
+ const file = text.slice(0, newline);
57
73
  try {
58
- return [[text.slice(0, newline).slice(0, -EXTENSION.length), JSON.parse(text.slice(newline + 1))]];
74
+ return [[file.slice(0, -EXTENSION.length), parseRecord(`${path}/${file}`, text.slice(newline + 1))]];
59
75
  }
60
76
  catch {
61
77
  return [];
@@ -73,7 +89,7 @@ export function createJsonDirectory(sandbox, dir) {
73
89
  }
74
90
  const records = await Promise.all(files
75
91
  .filter((file) => file.endsWith(EXTENSION))
76
- .map(async (file) => [file.slice(0, -EXTENSION.length), await readPath(`${path}/${file}`)]));
92
+ .map(async (file) => [file.slice(0, -EXTENSION.length), await readPath(`${path}/${file}`).catch(skipNotARecord)]));
77
93
  return records.filter((entry) => entry[1] !== null);
78
94
  }
79
95
  async function list() {
@@ -92,6 +108,11 @@ for file in *"$EXTENSION"; do
92
108
  printf '\n\036\n'
93
109
  done
94
110
  `;
111
+ function skipNotARecord(error) {
112
+ if (error instanceof NotARecordError)
113
+ return null;
114
+ throw error;
115
+ }
95
116
  export async function defaultDataDir(sandbox) {
96
117
  return `${await sandbox.homeDirectory()}/.local/share/hyperbrane`;
97
118
  }
@@ -0,0 +1,2 @@
1
+ import { type SandboxConnection } from "../sandbox/api.ts";
2
+ export declare function connectUnixSocket(path: string, label?: string): Promise<SandboxConnection>;
@@ -0,0 +1,28 @@
1
+ import { Socket } from "node:net";
2
+ import { SandboxConnectionRefusedError } from "../sandbox/api.js";
3
+ export function connectUnixSocket(path, label = path) {
4
+ return new Promise((resolve, reject) => {
5
+ const socket = new Socket();
6
+ let markClosed;
7
+ const closed = new Promise((done) => (markClosed = done));
8
+ const listeners = [];
9
+ socket.on("data", (chunk) => {
10
+ for (const listener of listeners)
11
+ listener(new Uint8Array(chunk));
12
+ });
13
+ socket.once("close", () => markClosed());
14
+ socket.once("end", () => markClosed());
15
+ socket.on("error", (error) => {
16
+ markClosed();
17
+ const message = `connecting to ${label} failed: ${error.message}`;
18
+ reject(error.code === "ENOENT" || error.code === "ECONNREFUSED" ? new SandboxConnectionRefusedError(message) : new Error(message));
19
+ });
20
+ socket.once("connect", () => resolve({
21
+ write: (data) => void socket.write(data),
22
+ onData: (listener) => void listeners.push(listener),
23
+ closed,
24
+ close: () => socket.end(),
25
+ }));
26
+ socket.connect({ path });
27
+ });
28
+ }
@@ -29,6 +29,7 @@ export interface GatewayClient {
29
29
  destroyGateway(gatewayName: string): Promise<void>;
30
30
  listGateways(): Promise<Gateway[]>;
31
31
  restartGateway(gatewayName: string): Promise<void>;
32
+ restoreGateways(): Promise<string[]>;
32
33
  endpoint(gatewayName: string, options?: {
33
34
  ttlSeconds?: number;
34
35
  }): Promise<SandboxAccess>;
@@ -39,6 +39,19 @@ export function createGatewayService(host, { dataDirectory } = {}) {
39
39
  await control.restartGateway(gatewayName);
40
40
  await control.startTransport(gatewayName, await host.openEndpoint(endpointName(gatewayName)), { restart: true });
41
41
  }
42
+ async function restoreGateways() {
43
+ const restored = await Promise.all((await control.listGateways()).map(async ({ name }) => {
44
+ try {
45
+ await control.startTransport(name, await host.openEndpoint(endpointName(name)), { restart: false });
46
+ return name;
47
+ }
48
+ catch (error) {
49
+ console.warn(`starting gateway ${name}'s transport worker failed:`, error.message);
50
+ return null;
51
+ }
52
+ }));
53
+ return restored.filter((name) => name !== null);
54
+ }
42
55
  async function endpoint(gatewayName, options) {
43
56
  assertValidName("gateway", gatewayName);
44
57
  const name = endpointName(gatewayName);
@@ -133,6 +146,7 @@ export function createGatewayService(host, { dataDirectory } = {}) {
133
146
  destroyGateway: (gatewayName) => destroyGateway(gatewayName),
134
147
  listGateways: () => control.listGateways(),
135
148
  restartGateway: (gatewayName) => restartGateway(gatewayName),
149
+ restoreGateways,
136
150
  endpoint,
137
151
  listCredentials,
138
152
  deleteCredential: (gatewayName, credentialName) => deleteCredential(gatewayName, credentialName),
@@ -1,2 +1,2 @@
1
1
  // Generated by scripts/bundle-worker.ts — do not edit; run `bun run bundle:worker`.
2
- export const TRANSPORT_SOURCE = "// @bun\n// portal-gateway/transport-worker.ts\nimport { randomBytes as randomBytes2, randomUUID as randomUUID2 } from \"crypto\";\nimport { readdir as readdir2, readFile as readFile3, writeFile as writeFile3 } from \"fs/promises\";\nimport { join as join2 } from \"path\";\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/buffer_utils.js\nvar encoder = new TextEncoder;\nvar decoder = new TextDecoder;\nvar strictDecoder = new TextDecoder(\"utf-8\", { fatal: true });\nvar UINT32_RANGE = 2 ** 32;\nfunction concat(...buffers) {\n const size = buffers.reduce((acc, { length }) => acc + length, 0), buf = new Uint8Array(size);\n let i = 0;\n for (const buffer of buffers)\n buf.set(buffer, i), i += buffer.length;\n return buf;\n}\nvar NON_ASCII = /[^\\x00-\\x7f]/;\nfunction encode(string) {\n if (typeof string == \"string\" && string.length >= 128) {\n if (NON_ASCII.test(string))\n throw new TypeError(\"non-ASCII string encountered in encode()\");\n return encoder.encode(string);\n }\n const bytes = new Uint8Array(string.length);\n for (let i = 0;i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code > 127)\n throw new TypeError(\"non-ASCII string encountered in encode()\");\n bytes[i] = code;\n }\n return bytes;\n}\nfunction encodeBase64(input, url = false) {\n if (Uint8Array.prototype.toBase64)\n return input.toBase64({ alphabet: url ? \"base64url\" : \"base64\", omitPadding: url });\n const CHUNK_SIZE = 32768, arr = [];\n for (let i = 0;i < input.length; i += CHUNK_SIZE)\n arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));\n const encoded = btoa(arr.join(\"\"));\n return url ? encoded.replace(/=/g, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\") : encoded;\n}\nfunction decodeBase64(encoded, url = false) {\n if (Uint8Array.fromBase64)\n return Uint8Array.fromBase64(encoded, { alphabet: url ? \"base64url\" : \"base64\" });\n if (url) {\n if (encoded.includes(\"+\") || encoded.includes(\"/\"))\n throw new TypeError(\"Invalid base64url\");\n encoded = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n }\n const binary = atob(encoded), bytes = new Uint8Array(binary.length);\n for (let i = 0;i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/util/errors.js\nclass JOSEError extends Error {\n static code = \"ERR_JOSE_GENERIC\";\n code = \"ERR_JOSE_GENERIC\";\n constructor(message, options) {\n super(message, options), this.name = this.constructor.name, Error.captureStackTrace?.(this, this.constructor);\n }\n}\n\nclass JWTClaimValidationFailed extends JOSEError {\n static code = \"ERR_JWT_CLAIM_VALIDATION_FAILED\";\n code = \"ERR_JWT_CLAIM_VALIDATION_FAILED\";\n claim;\n reason;\n payload;\n constructor(message, payload, claim = \"unspecified\", reason = \"unspecified\") {\n super(message, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;\n }\n}\n\nclass JWTExpired extends JOSEError {\n static code = \"ERR_JWT_EXPIRED\";\n code = \"ERR_JWT_EXPIRED\";\n claim;\n reason;\n payload;\n constructor(message, payload, claim = \"unspecified\", reason = \"unspecified\") {\n super(message, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;\n }\n}\n\nclass JOSEAlgNotAllowed extends JOSEError {\n static code = \"ERR_JOSE_ALG_NOT_ALLOWED\";\n code = \"ERR_JOSE_ALG_NOT_ALLOWED\";\n}\n\nclass JOSENotSupported extends JOSEError {\n static code = \"ERR_JOSE_NOT_SUPPORTED\";\n code = \"ERR_JOSE_NOT_SUPPORTED\";\n}\nclass JWSInvalid extends JOSEError {\n static code = \"ERR_JWS_INVALID\";\n code = \"ERR_JWS_INVALID\";\n}\n\nclass JWTInvalid extends JOSEError {\n static code = \"ERR_JWT_INVALID\";\n code = \"ERR_JWT_INVALID\";\n}\nclass JWSSignatureVerificationFailed extends JOSEError {\n static code = \"ERR_JWS_SIGNATURE_VERIFICATION_FAILED\";\n code = \"ERR_JWS_SIGNATURE_VERIFICATION_FAILED\";\n constructor(message = \"signature verification failed\", options) {\n super(message, options);\n }\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/util/base64url.js\nvar invalid = \"The input to be decoded is not correctly encoded.\";\nfunction decode(input) {\n try {\n return decodeBase64(typeof input == \"string\" ? input : decoder.decode(input), true);\n } catch (cause) {\n throw new TypeError(invalid, { cause });\n }\n}\nfunction encode2(input) {\n return encodeBase64(typeof input == \"string\" ? encoder.encode(input) : input, true);\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/validate.js\nfunction isObject(input) {\n if (typeof input != \"object\" || input === null || Object.prototype.toString.call(input) !== \"[object Object]\")\n return false;\n const prototype = Object.getPrototypeOf(input);\n return prototype === null || Object.getPrototypeOf(prototype) === null;\n}\nfunction isDisjoint(...headers) {\n const parameters = /* @__PURE__ */ new Set;\n for (const header of headers)\n if (header)\n for (const parameter of Object.keys(header)) {\n if (parameters.has(parameter))\n return false;\n parameters.add(parameter);\n }\n return true;\n}\nfunction assertNotSet(value, name) {\n if (value !== undefined)\n throw new TypeError(`${name} can only be called once`);\n}\nfunction decodeBase64url(value, label, ErrorClass) {\n try {\n return decode(value);\n } catch (cause) {\n throw new ErrorClass(`Failed to base64url decode the ${label}`, { cause });\n }\n}\nfunction encodeAsciiMember(value, label, ErrorClass) {\n try {\n return encode(value);\n } catch (cause) {\n throw new ErrorClass(`The ${label} is not a valid base64url string`, { cause });\n }\n}\nfunction parseJoseHeader(encodedProtectedHeader, ErrorClass, message) {\n let parsed;\n try {\n parsed = JSON.parse(strictDecoder.decode(decode(encodedProtectedHeader)));\n } catch (cause) {\n throw new ErrorClass(message, { cause });\n }\n if (!isObject(parsed))\n throw new ErrorClass(message);\n return parsed;\n}\nvar JWS_RECOGNIZED = { __proto__: null, b64: true };\nfunction validateAlgorithms(option, algorithms) {\n if (algorithms !== undefined && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s != \"string\")))\n throw new TypeError(`\"${option}\" option must be an array of strings`);\n return algorithms === undefined ? undefined : new Set(algorithms);\n}\nfunction validateCritDuplicates(Err, protectedHeader) {\n const { crit } = protectedHeader ?? {};\n if (Array.isArray(crit) && new Set(crit).size !== crit.length)\n throw new Err('\"crit\" (Critical) Header Parameter MUST NOT contain duplicate values');\n}\nfunction validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {\n if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined)\n throw new Err('\"crit\" (Critical) Header Parameter MUST be integrity protected');\n if (!protectedHeader || protectedHeader.crit === undefined)\n return [];\n if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input != \"string\" || input.length === 0))\n throw new Err('\"crit\" (Critical) Header Parameter MUST be an array of non-empty strings when present');\n const recognized = recognizedOption === undefined ? recognizedDefault : { __proto__: null, ...recognizedOption, ...recognizedDefault };\n for (const parameter of protectedHeader.crit) {\n if (!(parameter in recognized))\n throw new JOSENotSupported(`Extension Header Parameter \"${parameter}\" is not recognized`);\n if (!Object.hasOwn(joseHeader, parameter) || joseHeader[parameter] === undefined)\n throw new Err(`Extension Header Parameter \"${parameter}\" is missing`);\n if (recognized[parameter] && (!Object.hasOwn(protectedHeader, parameter) || protectedHeader[parameter] === undefined))\n throw new Err(`Extension Header Parameter \"${parameter}\" MUST be integrity protected`);\n }\n return protectedHeader.crit;\n}\nfunction validateB64(protectedHeader, extensions) {\n if (extensions.includes(\"b64\")) {\n const b64 = protectedHeader.b64;\n if (typeof b64 != \"boolean\")\n throw new JWSInvalid('The \"b64\" (base64url-encode payload) Header Parameter must be a boolean');\n return b64;\n }\n return true;\n}\nfunction serializeJoseHeader(Err, header) {\n let serialized, parsed;\n try {\n serialized = JSON.stringify(header), parsed = JSON.parse(serialized);\n } catch (cause) {\n throw new Err(\"JOSE Header is not valid JSON\", { cause });\n }\n if (!isObject(parsed))\n throw new Err(\"JOSE Header is not a JSON object\");\n return [parsed, serialized];\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/key.js\nvar tag = (key) => key[Symbol.toStringTag];\nvar jwkMatchesOp = (entry, key, usage) => {\n const { alg } = entry;\n if (key.use !== undefined) {\n const expected = usage === \"sign\" || usage === \"verify\" ? \"sig\" : \"enc\";\n if (key.use !== expected)\n throw new TypeError(`Invalid key for this operation, its \"use\" must be \"${expected}\" when present`);\n }\n if (key.alg !== undefined && key.alg !== alg)\n throw new TypeError(`Invalid key for this operation, its \"alg\" must be \"${alg}\" when present`);\n if (Array.isArray(key.key_ops)) {\n const expectedKeyOp = usage === \"encrypt\" || usage === \"decrypt\" ? entry.ops?.[usage === \"encrypt\" ? 0 : 1] : usage;\n if (expectedKeyOp && !key.key_ops.includes(expectedKeyOp))\n throw new TypeError(`Invalid key for this operation, its \"key_ops\" must include \"${expectedKeyOp}\" when present`);\n }\n};\nasync function prepareKey(entry, key, usage) {\n const { alg, secret } = entry, requiresPrivateKey = usage === \"decrypt\" || usage === \"sign\";\n if (secret && key instanceof Uint8Array)\n return key;\n let normalized, keyObject;\n if (isObject(key)) {\n if (normalized = normalizeJwk(key), typeof normalized.kty != \"string\")\n throw invalidKeyType(alg, key, secret);\n if (!(secret ? normalized.kty === \"oct\" && typeof normalized.k == \"string\" : normalized.kty !== \"oct\" && (requiresPrivateKey ? normalized.kty === \"AKP\" && typeof normalized.priv == \"string\" || typeof normalized.d == \"string\" : normalized.d === undefined && normalized.priv === undefined)))\n throw new TypeError(secret ? 'JSON Web Key for symmetric algorithms must have JWK \"kty\" (Key Type) equal to \"oct\" and the JWK \"k\" (Key Value) present' : `JSON Web Key for this operation must be a ${requiresPrivateKey ? \"private\" : \"public\"} JWK`);\n if (jwkMatchesOp(entry, normalized, usage), normalized.kty === \"oct\")\n return decode(normalized.k);\n if (!Object.isFrozen(key)) {\n const { key_ops } = key;\n Array.isArray(key_ops) && Object.freeze(key_ops), Object.freeze(key);\n }\n } else {\n if (!isKeyLike(key))\n throw invalidKeyType(alg, key, secret);\n const expectedType = secret ? \"secret\" : requiresPrivateKey ? \"private\" : \"public\";\n if (key.type !== expectedType && (secret || [\"secret\", \"public\", \"private\"].includes(key.type)))\n throw new TypeError(`${tag(key)} instances must be of type \"${expectedType}\" for the ${alg} algorithm`);\n if (isCryptoKey(key))\n return key;\n if (keyObject = key, keyObject.type === \"secret\")\n return keyObject.export();\n }\n cache ||= /* @__PURE__ */ new WeakMap;\n const cacheKey = key;\n let cached = cache.get(cacheKey);\n if (cached?.[alg])\n return cached[alg];\n if (cached || cache.set(cacheKey, cached = {}), keyObject && typeof keyObject.toCryptoKey == \"function\") {\n const isPublic = keyObject.type === \"public\", crv = nist[keyObject.asymmetricKeyDetails?.namedCurve], params = entry.resolve?.({ crv, asymmetricKeyType: keyObject.asymmetricKeyType }) ?? entry.subtle;\n return cached[alg] = keyObject.toCryptoKey(params, isPublic, entry.usages[isPublic ? 0 : 1]);\n }\n return normalized ??= keyObject.export({ format: \"jwk\" }), normalized.alg = alg, cached[alg] = await jwkToKey(entry, normalized);\n}\nvar cache;\nvar nist = {\n __proto__: null,\n prime256v1: \"P-256\",\n secp384r1: \"P-384\",\n secp521r1: \"P-521\"\n};\nvar isCryptoKey = (key) => {\n if (key?.[Symbol.toStringTag] === \"CryptoKey\")\n return true;\n try {\n return key instanceof CryptoKey;\n } catch {\n return false;\n }\n};\nvar isKeyObject = (key) => key?.[Symbol.toStringTag] === \"KeyObject\";\nvar isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);\nfunction message(msg, actual, ...types) {\n if (types.length > 2) {\n const last = types.pop();\n msg += `one of type ${types.join(\", \")}, or ${last}.`;\n } else\n types.length === 2 ? msg += `one of type ${types[0]} or ${types[1]}.` : msg += `of type ${types[0]}.`;\n return actual == null ? msg += ` Received ${actual}` : typeof actual == \"function\" && actual.name ? msg += ` Received function ${actual.name}` : typeof actual == \"object\" && actual != null && actual.constructor?.name && (msg += ` Received an instance of ${actual.constructor.name}`), msg;\n}\nfunction invalidKeyType(alg, actual, secret) {\n const types = [\"CryptoKey\", \"KeyObject\", \"JSON Web Key\"];\n return secret && types.push(\"Uint8Array\"), new TypeError(message(`Key for the ${alg} algorithm must be `, actual, ...types));\n}\nvar unusable = (name, prop = \"algorithm.name\") => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);\nfunction checkUsage(key, usage) {\n if (usage && !key.usages.includes(usage))\n throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);\n}\nfunction checkModulusLength(alg, key) {\n const { modulusLength } = key.algorithm;\n if (typeof modulusLength != \"number\" || modulusLength < 2048)\n throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);\n}\nfunction checkCryptoKey(key, expected, usage) {\n const algorithm = key.algorithm;\n if (algorithm.name !== expected.name)\n throw unusable(expected.name);\n if (expected.hash && algorithm.hash?.name !== expected.hash)\n throw unusable(expected.hash, \"algorithm.hash\");\n if (expected.namedCurve && algorithm.namedCurve !== expected.namedCurve)\n throw unusable(expected.namedCurve, \"algorithm.namedCurve\");\n if (expected.length !== undefined && algorithm.length !== expected.length)\n throw unusable(expected.length, \"algorithm.length\");\n checkUsage(key, usage);\n}\nfunction snapshotJwk(jwk) {\n return { __proto__: null, ...jwk };\n}\nfunction normalizeJwk(jwk) {\n const normalized = snapshotJwk(jwk);\n if (normalized.ext !== undefined && typeof normalized.ext != \"boolean\")\n throw new TypeError('\"ext\" (Extractable) Parameter must be a boolean');\n if (normalized.key_ops !== undefined) {\n const value = normalized.key_ops, keyOps = Array.isArray(value) ? [...value] : undefined;\n if (!keyOps || keyOps.some((operation) => typeof operation != \"string\") || new Set(keyOps).size !== keyOps.length)\n throw new TypeError('\"key_ops\" (Key Operations) Parameter must be an array of unique strings');\n normalized.key_ops = keyOps;\n }\n return normalized;\n}\nasync function jwkToKey(entry, jwk, extractable) {\n if (!entry.kty.includes(jwk.kty))\n throw new JOSENotSupported('Invalid or unsupported JWK \"alg\" (Algorithm) Parameter value');\n const algorithm = entry.resolve?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle, isPrivate = !!(jwk.d || jwk.priv), keyData = { ...jwk, ext: extractable ?? jwk.ext };\n return keyData.kty !== \"AKP\" && delete keyData.alg, delete keyData.use, crypto.subtle.importKey(\"jwk\", keyData, algorithm, keyData.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);\n}\nasync function rawKey(key, expected, usage, extractable = false) {\n return key instanceof Uint8Array && (key = await crypto.subtle.importKey(\"raw\", key, expected, extractable, [usage])), checkCryptoKey(key, expected, usage), key;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/key_descriptor.js\nfunction table(entries) {\n const out = { __proto__: null };\n for (const alg in entries)\n out[alg] = { ...entries[alg], alg };\n return out;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_algorithms.js\nvar sig = [[\"verify\"], [\"sign\"]];\nfunction hmac(bits) {\n const subtle = { name: \"HMAC\", hash: `SHA-${bits}` };\n return { kty: [\"oct\"], secret: true, subtle, signing: subtle, usages: sig };\n}\nfunction rsa(bits, saltLength) {\n const subtle = { name: saltLength ? \"RSA-PSS\" : \"RSASSA-PKCS1-v1_5\", hash: `SHA-${bits}` };\n return {\n kty: [\"RSA\"],\n subtle,\n signing: saltLength ? { ...subtle, saltLength } : subtle,\n usages: sig,\n minRsaBits: 2048\n };\n}\nfunction ecdsa(crv, bits) {\n return {\n kty: [\"EC\"],\n crv,\n subtle: { name: \"ECDSA\", namedCurve: crv },\n signing: { name: \"ECDSA\", hash: `SHA-${bits}` },\n usages: sig\n };\n}\nfunction eddsa() {\n const subtle = { name: \"Ed25519\" };\n return {\n kty: [\"OKP\"],\n crv: \"Ed25519\",\n subtle,\n signing: subtle,\n usages: sig\n };\n}\nfunction mldsa(parameterSet) {\n const subtle = { name: `ML-DSA-${parameterSet}` };\n return {\n kty: [\"AKP\"],\n subtle,\n signing: subtle,\n usages: sig\n };\n}\nvar JWS = table({\n HS256: hmac(256),\n HS384: hmac(384),\n HS512: hmac(512),\n RS256: rsa(256),\n RS384: rsa(384),\n RS512: rsa(512),\n PS256: rsa(256, 32),\n PS384: rsa(384, 48),\n PS512: rsa(512, 64),\n ES256: ecdsa(\"P-256\", 256),\n ES384: ecdsa(\"P-384\", 384),\n ES512: ecdsa(\"P-521\", 512),\n EdDSA: eddsa(),\n Ed25519: eddsa(),\n \"ML-DSA-44\": mldsa(44),\n \"ML-DSA-65\": mldsa(65),\n \"ML-DSA-87\": mldsa(87)\n});\nfunction jwsAlgorithm(alg) {\n const entry = typeof alg == \"string\" ? JWS[alg] : undefined;\n if (!entry)\n throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);\n return entry;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_verify.js\nfunction prepareVerify(options) {\n return [options && validateAlgorithms(\"algorithms\", options.algorithms), options?.crit];\n}\nfunction parseProtectedHeader(encodedProtectedHeader) {\n return encodedProtectedHeader === undefined ? {} : parseJoseHeader(encodedProtectedHeader, JWSInvalid, \"JWS Protected Header is invalid\");\n}\nfunction encodeCompactUnencodedPayload(payload) {\n try {\n return encode(payload);\n } catch (cause) {\n throw new JWSInvalid(\"JWS Compact Serialization payload must use only ASCII characters\", {\n cause\n });\n }\n}\nasync function verifySignature(jws, shared, key, encodeUnencodedPayload, parsedProtected) {\n const { protected: encodedProtectedHeader, header, payload: inputPayload } = jws, protectedHeader = parsedProtected ?? parseProtectedHeader(encodedProtectedHeader);\n if (!isDisjoint(protectedHeader, header))\n throw new JWSInvalid(\"JWS Protected and JWS Unprotected Header Parameter names must be disjoint\");\n const joseHeader = { ...protectedHeader, ...header }, b64 = validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], protectedHeader, joseHeader)), { alg } = joseHeader;\n if (typeof alg != \"string\" || !alg)\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n if (shared[0] && !shared[0].has(alg))\n throw new JOSEAlgNotAllowed('\"alg\" (Algorithm) Header Parameter value not allowed');\n if (b64) {\n if (typeof inputPayload != \"string\")\n throw new JWSInvalid(\"JWS Payload must be a string\");\n } else if (typeof inputPayload != \"string\" && !(inputPayload instanceof Uint8Array))\n throw new JWSInvalid(\"JWS Payload must be a string or an Uint8Array instance\");\n const signingPayload = b64 || typeof inputPayload != \"string\" ? inputPayload : encodeUnencodedPayload(inputPayload);\n let resolvedKey = false;\n typeof key == \"function\" && (key = await key(protectedHeader, jws), resolvedKey = true);\n const entry = jwsAlgorithm(alg), signingInput = concat(encodedProtectedHeader !== undefined ? encode(encodedProtectedHeader) : new Uint8Array, encode(\".\"), typeof signingPayload == \"string\" ? shared[2] ??= encodeAsciiMember(signingPayload, \"payload\", JWSInvalid) : signingPayload), signature = decodeBase64url(jws.signature, \"signature\", JWSInvalid), verificationKey = await prepareKey(entry, key, \"verify\"), cryptoKey = await rawKey(verificationKey, entry.subtle, \"verify\");\n entry.minRsaBits && checkModulusLength(entry.alg, cryptoKey);\n let verified = false;\n try {\n verified = await crypto.subtle.verify(entry.signing, cryptoKey, signature, signingInput);\n } catch (cause) {\n throw new JWSSignatureVerificationFailed(undefined, { cause });\n }\n if (!verified)\n throw new JWSSignatureVerificationFailed;\n const result = { payload: typeof signingPayload == \"string\" ? decodeBase64url(signingPayload, \"payload\", JWSInvalid) : signingPayload };\n return encodedProtectedHeader !== undefined && (result.protectedHeader = protectedHeader), header !== undefined && (result.unprotectedHeader = header), resolvedKey ? [{ ...result, key: verificationKey }, b64] : [result, b64];\n}\nasync function verifyCompact(jws, shared, key) {\n if (jws instanceof Uint8Array && (jws = decoder.decode(jws)), typeof jws != \"string\")\n throw new JWSInvalid(\"Compact JWS must be a string or Uint8Array\");\n const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split(\".\");\n if (length !== 3)\n throw new JWSInvalid(\"Invalid Compact JWS\");\n return verifySignature({ payload, protected: protectedHeader, signature }, shared, key, encodeCompactUnencodedPayload);\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jwt_claims_set.js\nvar epoch = (date) => Math.floor(date.getTime() / 1000);\nvar multipliers = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n y: 31557600\n};\nvar REGEX = /^(\\+|\\-)? ?(\\d+|\\d+\\.\\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;\nvar checkFailed = \"check_failed\";\nfunction invalidDuration() {\n throw new TypeError(\"Invalid time period format\");\n}\nfunction secs(str) {\n typeof str != \"string\" && invalidDuration();\n const matched = REGEX.exec(str);\n (!matched || matched[4] && matched[1]) && invalidDuration();\n const value = parseFloat(matched[2]), durationSeconds = Math.round(value * multipliers[matched[3][0].toLowerCase()]);\n return Number.isFinite(durationSeconds) || invalidDuration(), matched[1] === \"-\" || matched[4] === \"ago\" ? -durationSeconds : durationSeconds;\n}\nfunction validateInput(label, input) {\n if (!Number.isFinite(input))\n throw new TypeError(`Invalid ${label} input`);\n return input;\n}\nfunction validateStringClaim(claim, value) {\n if (typeof value != \"string\")\n throw new TypeError(`\"${claim}\" claim must be a string`);\n}\nfunction validateAudienceClaim(value) {\n if (typeof value != \"string\" && (!Array.isArray(value) || Array.from(value).some((member) => typeof member != \"string\")))\n throw new TypeError('\"aud\" claim must be a string or an array of strings');\n}\nfunction numericDate(value, label) {\n return typeof value == \"number\" ? validateInput(label, value) : value instanceof Date ? validateInput(label, epoch(value)) : epoch(/* @__PURE__ */ new Date) + secs(value);\n}\nvar normalizeTyp = (value) => {\n const normalized = value.toLowerCase();\n return value.includes(\"/\") ? normalized : `application/${normalized}`;\n};\nvar checkAudiencePresence = (audPayload, audOption) => typeof audPayload == \"string\" ? audOption.includes(audPayload) : Array.isArray(audPayload) ? audOption.some((aud) => audPayload.includes(aud)) : false;\nfunction validateNumericDate(claimsSet, claim, required = false) {\n const value = claimsSet[claim];\n if (!(value === undefined && !required)) {\n if (typeof value != \"number\")\n throw new JWTClaimValidationFailed(`\"${claim}\" claim must be a number`, claimsSet, claim, \"invalid\");\n return value;\n }\n}\nfunction unexpectedClaim(claimsSet, claim) {\n throw new JWTClaimValidationFailed(`unexpected \"${claim}\" claim value`, claimsSet, claim, checkFailed);\n}\nfunction validateClaimsSet(protectedHeader, claimsSetBytes, options = {}) {\n let claimsSet;\n try {\n claimsSet = JSON.parse(strictDecoder.decode(claimsSetBytes));\n } catch (cause) {\n throw new JWTInvalid(\"JWT Claims Set must be a top-level JSON object\", { cause });\n }\n if (!isObject(claimsSet))\n throw new JWTInvalid(\"JWT Claims Set must be a top-level JSON object\");\n const { typ } = options;\n if (typ !== undefined && (typeof protectedHeader.typ != \"string\" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ)))\n throw new JWTClaimValidationFailed('unexpected \"typ\" JWT header value', claimsSet, \"typ\", checkFailed);\n const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options, presenceCheck = [...requiredClaims];\n maxTokenAge !== undefined && presenceCheck.push(\"iat\"), audience !== undefined && presenceCheck.push(\"aud\"), subject !== undefined && presenceCheck.push(\"sub\"), issuer !== undefined && presenceCheck.push(\"iss\");\n for (const claim of new Set(presenceCheck.reverse()))\n if (!Object.hasOwn(claimsSet, claim))\n throw new JWTClaimValidationFailed(`missing required \"${claim}\" claim`, claimsSet, claim, \"missing\");\n issuer !== undefined && !(Array.isArray(issuer) ? issuer : [issuer]).includes(claimsSet.iss) && unexpectedClaim(claimsSet, \"iss\"), subject !== undefined && claimsSet.sub !== subject && unexpectedClaim(claimsSet, \"sub\"), audience !== undefined && !checkAudiencePresence(claimsSet.aud, typeof audience == \"string\" ? [audience] : audience) && unexpectedClaim(claimsSet, \"aud\");\n const { clockTolerance } = options;\n let tolerance = 0;\n if (typeof clockTolerance == \"string\")\n tolerance = secs(clockTolerance);\n else if (clockTolerance !== undefined) {\n if (typeof clockTolerance != \"number\")\n throw new TypeError(\"Invalid clockTolerance option type\");\n tolerance = clockTolerance;\n }\n validateInput(\"clockTolerance option\", tolerance);\n const { currentDate } = options, now = validateInput(\"currentDate option\", epoch(currentDate === undefined ? /* @__PURE__ */ new Date : currentDate)), iat = validateNumericDate(claimsSet, \"iat\", maxTokenAge !== undefined), nbf = validateNumericDate(claimsSet, \"nbf\");\n if (nbf !== undefined && nbf > now + tolerance)\n throw new JWTClaimValidationFailed('\"nbf\" claim timestamp check failed', claimsSet, \"nbf\", checkFailed);\n const exp = validateNumericDate(claimsSet, \"exp\");\n if (exp !== undefined && exp <= now - tolerance)\n throw new JWTExpired('\"exp\" claim timestamp check failed', claimsSet, \"exp\", checkFailed);\n if (maxTokenAge !== undefined) {\n const age = now - iat, max = validateInput(\"maxTokenAge option\", typeof maxTokenAge == \"number\" ? maxTokenAge : secs(maxTokenAge));\n if (age - tolerance > max)\n throw new JWTExpired('\"iat\" claim timestamp check failed (too far in the past)', claimsSet, \"iat\", checkFailed);\n if (age < -tolerance)\n throw new JWTClaimValidationFailed('\"iat\" claim timestamp check failed (it should be in the past)', claimsSet, \"iat\", checkFailed);\n }\n return claimsSet;\n}\nvar producerClaimsSets;\nfunction producerClaimsSet(producer) {\n return producerClaimsSets.get(producer);\n}\nfunction jwtClaimsSetBytes(producer) {\n const claimsSet = producerClaimsSet(producer);\n for (const claim of [\"iat\", \"nbf\", \"exp\"]) {\n const value = claimsSet[claim];\n if (typeof value == \"number\" && !Number.isFinite(value))\n throw new TypeError(`\"${claim}\" claim must be a finite number`);\n }\n return encoder.encode(JSON.stringify(claimsSet));\n}\nclass JWTClaimsBuilder {\n constructor(claimsSet = {}) {\n if (!isObject(claimsSet))\n throw new TypeError(\"JWT Claims Set MUST be an object\");\n (producerClaimsSets ||= /* @__PURE__ */ new WeakMap).set(this, structuredClone(claimsSet));\n }\n setIssuer(value) {\n return validateStringClaim(\"iss\", value), producerClaimsSet(this).iss = value, this;\n }\n setSubject(value) {\n return validateStringClaim(\"sub\", value), producerClaimsSet(this).sub = value, this;\n }\n setAudience(value) {\n return validateAudienceClaim(value), producerClaimsSet(this).aud = value, this;\n }\n setJti(value) {\n return validateStringClaim(\"jti\", value), producerClaimsSet(this).jti = value, this;\n }\n setNotBefore(value) {\n return producerClaimsSet(this).nbf = numericDate(value, \"setNotBefore\"), this;\n }\n setExpirationTime(value) {\n return producerClaimsSet(this).exp = numericDate(value, \"setExpirationTime\"), this;\n }\n setIssuedAt(value) {\n const claimsSet = producerClaimsSet(this);\n return value === undefined ? claimsSet.iat = epoch(/* @__PURE__ */ new Date) : typeof value == \"string\" ? claimsSet.iat = validateInput(\"setIssuedAt\", epoch(/* @__PURE__ */ new Date) + secs(value)) : claimsSet.iat = numericDate(value, \"setIssuedAt\"), this;\n }\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/jwt/verify.js\nasync function jwtVerify(jwt, key, options) {\n const [verified, b64] = await verifyCompact(jwt, prepareVerify(options), key);\n if (!b64)\n throw new JWTInvalid(\"JWTs MUST NOT use unencoded payload\");\n const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);\n return { ...verified, payload };\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_sign.js\nasync function createSignature(input, key, rejectUnencoded) {\n let [payload, protectedHeader, unprotectedHeader, crit] = input, encodedProtectedHeader = \"\";\n if (protectedHeader !== undefined) {\n const normalized = serializeJoseHeader(JWSInvalid, protectedHeader);\n protectedHeader = normalized[0], encodedProtectedHeader = encode2(normalized[1]);\n }\n if (unprotectedHeader !== undefined && (unprotectedHeader = serializeJoseHeader(JWSInvalid, unprotectedHeader)[0]), !protectedHeader && !unprotectedHeader)\n throw new JWSInvalid(\"either setProtectedHeader or setUnprotectedHeader must be called before #sign()\");\n if (!isDisjoint(protectedHeader, unprotectedHeader))\n throw new JWSInvalid(\"JWS Protected and JWS Unprotected Header Parameter names must be disjoint\");\n const joseHeader = { ...protectedHeader, ...unprotectedHeader };\n validateCritDuplicates(JWSInvalid, protectedHeader);\n const b64 = validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, crit, protectedHeader, joseHeader));\n b64 || rejectUnencoded?.();\n const { alg } = joseHeader;\n if (typeof alg != \"string\" || !alg)\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n const entry = jwsAlgorithm(alg);\n let encodedPayload = \"\", signingPayload = payload, signingInput;\n if (b64) {\n const encodedPayloadCache = input[4];\n encodedPayloadCache ? (encodedPayload = encodedPayloadCache[0] ??= encode2(payload), signingPayload = encodedPayloadCache[1] ??= encode(encodedPayload)) : (encodedPayload = encode2(payload), signingInput = encoder.encode(`${encodedProtectedHeader}.${encodedPayload}`));\n }\n signingInput ??= concat(encode(encodedProtectedHeader), encode(\".\"), signingPayload);\n const signingKey = await rawKey(await prepareKey(entry, key, \"sign\"), entry.subtle, \"sign\");\n entry.minRsaBits && checkModulusLength(entry.alg, signingKey);\n const jws = {\n signature: encode2(new Uint8Array(await crypto.subtle.sign(entry.signing, signingKey, signingInput))),\n payload: encodedPayload\n };\n return protectedHeader && (jws.protected = encodedProtectedHeader), unprotectedHeader && (jws.header = unprotectedHeader), [jws, b64];\n}\nasync function createCompactSignature(payload, protectedHeader, crit, key, rejectUnencoded) {\n const [jws] = await createSignature([payload, protectedHeader, undefined, crit], key, rejectUnencoded);\n return `${jws.protected}.${jws.payload}.${jws.signature}`;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/jwt/sign.js\nvar SignJWT_base = JWTClaimsBuilder;\n\nclass SignJWT extends SignJWT_base {\n #protectedHeader;\n setProtectedHeader(protectedHeader) {\n return assertNotSet(this.#protectedHeader, \"setProtectedHeader\"), this.#protectedHeader = protectedHeader, this;\n }\n async sign(key, options) {\n return createCompactSignature(jwtClaimsSetBytes(this), this.#protectedHeader, options?.crit, key, () => {\n throw new JWTInvalid(\"JWTs MUST NOT use unencoded payload\");\n });\n }\n}\n\n// dimension/paths.ts\nfunction containerName(dimensionName) {\n return `hyb-${dimensionName}`;\n}\nvar WORKSPACE_PATH = \"/workspace\";\nvar RUN_PATH = \"/run/hyperbrane\";\nvar STATE_PATH = \"/var/lib/hyperbrane\";\nvar CA_BUNDLE_PATH = \"/etc/ssl/certs/ca-certificates.crt\";\nvar TRANSPORT_SOCKET = \".transport.sock\";\nfunction dimensionRunDirectory(dataDirectory, dimensionName) {\n return `${dataDirectory}/dimension-run/${dimensionName}`;\n}\nfunction dimensionStateDirectory(dataDirectory, dimensionName) {\n return `${dataDirectory}/dimension-state/${dimensionName}`;\n}\nvar dimensionLayout = {\n containerName,\n workspacePath: () => WORKSPACE_PATH,\n runPath: () => RUN_PATH,\n statePath: () => STATE_PATH,\n caBundlePath: () => CA_BUNDLE_PATH,\n runDirectory: dimensionRunDirectory,\n stateDirectory: dimensionStateDirectory,\n transportSocketPath: (dataDirectory, dimensionName) => `${dimensionRunDirectory(dataDirectory, dimensionName)}/${TRANSPORT_SOCKET}`\n};\n\n// dimension/api.ts\nvar MEMORY_UNITS = {\n \"\": 1,\n K: 1024,\n M: 1024 ** 2,\n G: 1024 ** 3,\n T: 1024 ** 4\n};\nvar DIMENSION_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$/;\nfunction isDimensionName(name) {\n return DIMENSION_NAME.test(name);\n}\n\n// sandbox/providers/local/localService.ts\nimport { spawn } from \"child_process\";\nimport { randomUUID } from \"crypto\";\nimport * as fs from \"fs/promises\";\nimport { connect as netConnect, Socket } from \"net\";\nimport { homedir, userInfo } from \"os\";\nimport { basename, dirname, join, resolve } from \"path\";\n\n// sandbox/api.ts\nclass SandboxNotFoundError extends Error {\n name = \"SandboxNotFoundError\";\n}\nclass SandboxConnectionRefusedError extends Error {\n name = \"SandboxConnectionRefusedError\";\n}\n\n// sandbox/storage/descriptors.ts\nfunction makeDescriptor(ctx, id, spec) {\n return {\n id,\n kind: ctx.kind,\n name: spec.name,\n ...ctx.sshHost === undefined ? {} : { sshHost: ctx.sshHost },\n ...ctx.sshUser === undefined ? {} : { sshUser: ctx.sshUser },\n ...ctx.sshPort === undefined ? {} : { sshPort: ctx.sshPort },\n ...spec.dataEndpointOptions === undefined ? {} : { dataEndpointOptions: spec.dataEndpointOptions }\n };\n}\nasync function createDescriptor(ctx, storage, id, spec) {\n const descriptor = makeDescriptor(ctx, id, spec);\n await storage.saveDescriptor(descriptor);\n return [{ ...ctx, id }, descriptor];\n}\nasync function openDescriptor(ctx, storage, id) {\n return await storage.readDescriptor() ?? storage.insertDescriptor(makeDescriptor(ctx, id, { name: id }));\n}\nasync function getDescriptor(storage, where) {\n const descriptor = await storage.readDescriptor();\n if (!descriptor)\n throw new Error(`${where} has no descriptor: opening it saves one`);\n return descriptor;\n}\n\n// sandbox/data-endpoints/dataEndpoints.ts\nimport { randomBytes } from \"crypto\";\n\n// lib/sh.ts\nfunction sh(strings, ...values) {\n if (values.length > 0)\n throw new Error(\"sh`\\u2026` takes no interpolations: pass inputs as environment variables or arguments\");\n return scriptText(strings.raw[0]);\n}\nfunction scriptText(raw) {\n return raw.replace(/\\\\(\\$\\{|`)/g, \"$1\").replace(/^\\n/, \"\");\n}\n\n// sandbox/data-endpoints/dataEndpoints.ts\nvar DEFAULT_TTL_S = 12 * 3600;\nfunction signToken(secret, ttlSeconds) {\n return new SignJWT().setProtectedHeader({ alg: \"HS256\" }).setExpirationTime(`${ttlSeconds}s`).sign(new TextEncoder().encode(secret));\n}\nasync function listeningPorts(sandbox) {\n const listed = await sandbox.runCommand([\"sh\", \"-c\", LISTENING_PORTS]);\n if (listed.exitCode !== 0)\n throw new Error(`listing the ports in use failed: ${listed.stderr.trim()}`);\n return listed.stdout.split(`\n`).filter(Boolean).map(Number);\n}\nasync function withLock(sandbox, body) {\n const lock = `${await sandbox.homeDirectory()}/.local/state/hyperbrane/endpoints.lock`;\n const taken = await sandbox.runCommand([\"sh\", \"-c\", TAKE_LOCK, \"sh\", lock]);\n if (taken.exitCode !== 0)\n throw new Error(`${lock} stayed locked: ${taken.stderr.trim()}`);\n try {\n return await body();\n } finally {\n await sandbox.runCommand([\"rm\", \"-rf\", lock]);\n }\n}\nfunction under(base, endpoint) {\n if (base === undefined || new URL(base).pathname === \"/\")\n return endpoint;\n return { ...endpoint, url: `${base.replace(/\\/+$/, \"\")}/${endpoint.name}` };\n}\nfunction createEndpoints(sandbox, storage, ports) {\n const located = async (endpoints) => {\n const base = (await storage.readDescriptor())?.dataEndpointOptions?.url;\n return endpoints.map((endpoint) => under(base, endpoint));\n };\n async function openEndpoint(name) {\n const [endpoint] = await located([await openStored(name)]);\n return endpoint;\n }\n async function openStored(name) {\n return withLock(sandbox, async () => {\n const existing = await storage.readEndpoint(name);\n if (existing)\n return existing;\n const { from, to } = await ports.ports();\n const used = new Set([...(await storage.listEndpoints()).map((endpoint) => endpoint.port), ...await listeningPorts(sandbox)]);\n let port = from;\n while (port <= to && used.has(port))\n port++;\n if (port > to)\n throw new Error(`no data endpoint ports are left in ${from}-${to}`);\n const endpoint = { name, url: await ports.expose(port), port, secret: randomBytes(32).toString(\"base64url\") };\n await storage.saveEndpoint(endpoint);\n return endpoint;\n });\n }\n async function closeEndpoint(name) {\n await withLock(sandbox, async () => {\n const endpoint = await storage.readEndpoint(name);\n if (!endpoint)\n return;\n await ports.conceal(endpoint.port);\n await storage.removeEndpoint(name);\n });\n }\n async function credentials(name, {\n ttlSeconds = DEFAULT_TTL_S\n } = {}) {\n const endpoint = await storage.readEndpoint(name);\n if (!endpoint)\n throw new SandboxNotFoundError(`there is no data endpoint ${name}`);\n return { token: await signToken(endpoint.secret, ttlSeconds), ...await ports.credentials?.(endpoint, ttlSeconds) };\n }\n return {\n openEndpoint,\n closeEndpoint,\n listEndpoints: async () => located(await storage.listEndpoints()),\n credentials\n };\n}\nvar LISTENING_PORTS = sh`\nawk 'FNR > 1 && $4 == \"0A\" { n = split($2, local, \":\"); print local[n] }' /proc/net/tcp /proc/net/tcp6 2> /dev/null | while read -r hex; do\n printf '%d\\n' \"0x$hex\"\ndone\n`;\nvar TAKE_LOCK = sh`\nmkdir -p \"$(dirname \"$1\")\"\nwaited=0\nuntil mkdir \"$1\" 2> /dev/null; do\n if [ -n \"$(find \"$1\" -maxdepth 0 -mmin +2 2> /dev/null)\" ]; then\n rm -rf \"$1\"\n continue\n fi\n [ \"$waited\" -lt 1200 ] || exit 1\n sleep 0.1\n waited=$((waited + 1))\ndone\n`;\n\n// sandbox/data-endpoints/firewalledPorts.ts\nfunction firewalledPorts(sandbox, storage) {\n const options = async () => (await storage.readDescriptor())?.dataEndpointOptions;\n async function firewall(rule) {\n const result = await sandbox.runCommand([\"sudo\", \"-n\", \"ufw\", ...rule]);\n if (result.exitCode !== 0)\n throw new Error(`ufw ${rule.join(\" \")} failed: ${result.stderr.trim()}`);\n }\n return {\n async ports() {\n const ports = (await options())?.ports;\n if (!ports)\n throw new Error(\"this machine has no data endpoints: it's created with dataEndpointOptions.ports\");\n return ports;\n },\n async expose(port) {\n const found = await options();\n if (found?.needsFirewall)\n await firewall([\"allow\", `${port}/tcp`]);\n const url = new URL(found?.url ?? \"http://127.0.0.1\");\n url.port = String(port);\n return url.toString().replace(/\\/$/, \"\");\n },\n async conceal(port) {\n if ((await options())?.needsFirewall)\n await firewall([\"delete\", \"allow\", `${port}/tcp`]);\n }\n };\n}\n\n// sandbox/lib/httpOverStream.ts\nimport { request as httpRequest } from \"http\";\nimport { Readable } from \"stream\";\nfunction httpOver(url, connection, { method = \"GET\", headers = {}, body, signal, timeout }) {\n return new Promise((resolve, reject) => {\n const target = new URL(url);\n const request = httpRequest({ createConnection: connection, path: `${target.pathname}${target.search}`, method, headers: { host: target.host, ...headers }, signal }, (response) => {\n const responseHeaders = new Headers;\n for (let i = 0;i + 1 < response.rawHeaders.length; i += 2)\n responseHeaders.append(response.rawHeaders[i], response.rawHeaders[i + 1]);\n const status = response.statusCode ?? 502;\n const nullBody = status === 204 || status === 304 || method === \"HEAD\";\n if (nullBody)\n response.resume();\n resolve(new Response(nullBody ? null : Readable.toWeb(response), { status, headers: responseHeaders }));\n });\n request.once(\"error\", reject);\n if (typeof timeout === \"number\")\n request.setTimeout(timeout, () => request.destroy(new Error(`fetch ${url}: no data for ${timeout}ms`)));\n request.end(body === undefined ? undefined : typeof body === \"string\" ? body : Buffer.from(body));\n });\n}\n\n// lib/jsonDirectory.ts\nvar EXTENSION = \".json\";\nvar NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$/;\n\nclass InvalidNameError extends Error {\n name = \"InvalidNameError\";\n}\nfunction checkName(name) {\n if (!NAME_PATTERN.test(name)) {\n throw new InvalidNameError(`Invalid name \"${name}\": use letters, digits, \"_\", \".\" and \"-\", up to 63 characters`);\n }\n return name;\n}\nfunction createJsonDirectory(sandbox, dir) {\n async function pathFor(name) {\n return `${await dir()}/${checkName(name)}${EXTENSION}`;\n }\n function serialize(record) {\n return JSON.stringify(record, null, 2) + `\n`;\n }\n async function readPath(path) {\n try {\n return JSON.parse(new TextDecoder().decode(await sandbox.readFile(path)));\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return null;\n throw error;\n }\n }\n async function read(name) {\n return readPath(await pathFor(name));\n }\n async function insert(name, record) {\n const path = await pathFor(name);\n if (await sandbox.writeFileExclusive(path, serialize(record)))\n return record;\n return await readPath(path) ?? insert(name, record);\n }\n async function save(name, record) {\n await sandbox.writeFile(await pathFor(name), serialize(record));\n }\n async function remove(name) {\n await sandbox.removeFile(await pathFor(name));\n }\n async function entries() {\n const path = await dir();\n const all = await sandbox.runCommand([\"sh\", \"-c\", LIST_SCRIPT, \"sh\", path], { env: { MISSING: String(MISSING), EXTENSION } }).catch(() => null);\n if (all?.exitCode === MISSING)\n return [];\n if (all?.exitCode === 0) {\n return all.stdout.split(RECORD_END).filter((text) => text.trim()).flatMap((text) => {\n const newline = text.indexOf(`\n`);\n try {\n return [[text.slice(0, newline).slice(0, -EXTENSION.length), JSON.parse(text.slice(newline + 1))]];\n } catch {\n return [];\n }\n });\n }\n let files;\n try {\n files = await sandbox.listDirectory(path);\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return [];\n throw error;\n }\n const records = await Promise.all(files.filter((file) => file.endsWith(EXTENSION)).map(async (file) => [file.slice(0, -EXTENSION.length), await readPath(`${path}/${file}`)]));\n return records.filter((entry) => entry[1] !== null);\n }\n async function list() {\n return (await entries()).map(([, record]) => record);\n }\n return { read, insert, save, remove, list, entries };\n}\nvar MISSING = 44;\nvar RECORD_END = `\n\\x1E\n`;\nvar LIST_SCRIPT = sh`\ncd \"$1\" 2>/dev/null || exit \"$MISSING\"\nfor file in *\"$EXTENSION\"; do\n [ -f \"$file\" ] || continue\n printf '%s\\n' \"$file\"\n cat \"$file\"\n printf '\\n\\036\\n'\ndone\n`;\n\n// sandbox/storage/sandboxStorage.ts\nvar DESCRIPTOR = \"sandbox\";\nfunction createSandboxStorage(sandbox) {\n const stateDirectory = async () => `${await sandbox.homeDirectory()}/.local/state/hyperbrane`;\n const endpointsDirectory = async () => `${await stateDirectory()}/endpoints`;\n const descriptors = createJsonDirectory(sandbox, stateDirectory);\n const endpoints = createJsonDirectory(sandbox, endpointsDirectory);\n async function readDescriptor() {\n return descriptors.read(DESCRIPTOR);\n }\n async function insertDescriptor(descriptor) {\n return descriptors.insert(DESCRIPTOR, descriptor);\n }\n async function saveDescriptor(descriptor) {\n await descriptors.save(DESCRIPTOR, descriptor);\n }\n async function readEndpoint(name) {\n return endpoints.read(name);\n }\n async function saveEndpoint(endpoint) {\n const directory = await endpointsDirectory();\n const made = await sandbox.runCommand([\"sh\", \"-c\", 'mkdir -p \"$1\" && chmod 700 \"$1\"', \"sh\", directory]);\n if (made.exitCode !== 0)\n throw new Error(`making ${directory} private failed: ${made.stderr.trim()}`);\n await endpoints.save(endpoint.name, endpoint);\n }\n async function removeEndpoint(name) {\n await endpoints.remove(name);\n }\n async function listEndpoints() {\n return endpoints.list();\n }\n return {\n readDescriptor,\n insertDescriptor,\n saveDescriptor,\n readEndpoint,\n saveEndpoint,\n removeEndpoint,\n listEndpoints\n };\n}\n\n// sandbox/providers/local/localService.ts\nfunction hostPath(path) {\n return resolve(\"/\", path);\n}\nasync function notFoundAs(path, operation) {\n try {\n return await operation;\n } catch (error) {\n if (error.code === \"ENOENT\")\n throw new SandboxNotFoundError(`Not found: \"${path}\"`);\n throw error;\n }\n}\nasync function writeTemp(full, data, mode = 438) {\n await fs.mkdir(dirname(full), { recursive: true });\n const tmp = join(dirname(full), `.${basename(full)}.${randomUUID()}.tmp`);\n await fs.writeFile(tmp, data, { mode });\n return tmp;\n}\nasync function readFile2(path) {\n return new Uint8Array(await notFoundAs(path, fs.readFile(hostPath(path))));\n}\nasync function readFileRange(path, start, end) {\n const file = await notFoundAs(path, fs.open(hostPath(path), \"r\"));\n try {\n const buffer = new Uint8Array(Math.max(0, end - start));\n let filled = 0;\n while (filled < buffer.length) {\n const { bytesRead } = await file.read(buffer, filled, buffer.length - filled, start + filled);\n if (bytesRead === 0)\n break;\n filled += bytesRead;\n }\n return buffer.subarray(0, filled);\n } finally {\n await file.close();\n }\n}\nasync function writeFile2(path, data) {\n const full = hostPath(path);\n await fs.rename(await writeTemp(full, data), full);\n}\nasync function writeFileExclusive(path, data) {\n const full = hostPath(path);\n const tmp = await writeTemp(full, data);\n try {\n await fs.link(tmp, full);\n return true;\n } catch (error) {\n if (error.code === \"EEXIST\")\n return false;\n throw error;\n } finally {\n await fs.unlink(tmp);\n }\n}\nasync function removeFile(path) {\n await fs.rm(hostPath(path), { force: true });\n}\nasync function createDirectory(path) {\n await fs.mkdir(hostPath(path), { recursive: true });\n}\nasync function removeDirectory(path) {\n await fs.rm(hostPath(path), { recursive: true, force: true });\n}\nasync function listDirectory(path) {\n return notFoundAs(path, fs.readdir(hostPath(path)));\n}\nasync function stat2(path) {\n const stats = await notFoundAs(path, fs.stat(hostPath(path)));\n const type = stats.isFile() ? \"file\" : stats.isDirectory() ? \"directory\" : \"other\";\n return { type, uid: stats.uid, gid: stats.gid, size: stats.size };\n}\nasync function homeDirectory() {\n return process.env.HOME || homedir();\n}\nfunction runCommand(cmd, { cwd, env = {}, stdin } = {}) {\n return new Promise((resolve, reject) => {\n const [command, ...args] = cmd;\n if (!command)\n return reject(new Error(\"no command to run\"));\n const child = spawn(command, args, {\n cwd: hostPath(cwd ?? \".\"),\n env: { ...process.env, ...env },\n stdio: [stdin === undefined ? \"ignore\" : \"pipe\", \"pipe\", \"pipe\"]\n });\n const stdout = [];\n const stderr = [];\n child.stdout.on(\"data\", (chunk) => stdout.push(chunk));\n child.stderr.on(\"data\", (chunk) => stderr.push(chunk));\n child.once(\"error\", reject);\n child.once(\"close\", (code) => resolve({ exitCode: code ?? -1, stdout: Buffer.concat(stdout).toString(), stderr: Buffer.concat(stderr).toString() }));\n if (stdin !== undefined)\n child.stdin.end(stdin);\n });\n}\nasync function state() {\n return \"running\";\n}\nasync function start() {}\nasync function destroy() {}\nfunction hasExpiration() {\n return false;\n}\nasync function getExpiration() {\n return null;\n}\nasync function setExpiration() {\n return null;\n}\nfunction hasShell(ctx) {\n return ctx.sshKey !== undefined;\n}\nasync function sshEndpoint(ctx) {\n if (ctx.sshKey === undefined)\n throw new Error(\"this machine has no shell: its context has no SSH key (sshKey)\");\n return {\n host: ctx.sshHost ?? \"127.0.0.1\",\n port: ctx.sshPort ?? 22,\n user: ctx.sshUser ?? userInfo().username,\n privateKey: ctx.sshKey\n };\n}\nasync function fetch(url, { unix, ...init } = {}) {\n if (unix === undefined || \"Bun\" in globalThis)\n return globalThis.fetch(url, { ...init, unix: unix === undefined ? undefined : hostPath(unix) });\n return httpOver(url, () => netConnect(hostPath(unix)), init);\n}\nfunction connect(path) {\n return new Promise((resolve, reject) => {\n const socket = new Socket;\n let markClosed;\n const closed = new Promise((done) => markClosed = done);\n const listeners = [];\n socket.on(\"data\", (chunk) => {\n for (const listener of listeners)\n listener(new Uint8Array(chunk));\n });\n socket.once(\"close\", () => markClosed());\n socket.once(\"end\", () => markClosed());\n socket.on(\"error\", (error) => {\n markClosed();\n const message = `connecting to ${path} failed: ${error.message}`;\n reject(error.code === \"ENOENT\" || error.code === \"ECONNREFUSED\" ? new SandboxConnectionRefusedError(message) : new Error(message));\n });\n socket.once(\"connect\", () => resolve({\n write: (data) => void socket.write(data),\n onData: (listener) => void listeners.push(listener),\n closed,\n close: () => socket.end()\n }));\n socket.connect({ path: hostPath(path) });\n });\n}\nfunction createLocalService(ctx) {\n const fileOps = {\n readFile: readFile2,\n readFileRange,\n writeFile: writeFile2,\n writeFileExclusive,\n removeFile,\n createDirectory,\n removeDirectory,\n listDirectory,\n stat: stat2,\n homeDirectory\n };\n const cmdOps = {\n runCommand\n };\n const netOps = {\n fetch,\n connect\n };\n const machine = { ...fileOps, ...cmdOps, ...netOps };\n const storage = createSandboxStorage(machine);\n const lifecycleOps = {\n create: (spec) => createDescriptor(ctx, storage, ctx.id ?? spec.name, spec),\n open: () => openDescriptor(ctx, storage, ctx.id ?? \"this machine\"),\n get: () => getDescriptor(storage, \"this machine\"),\n state,\n start,\n destroy\n };\n const dataOps = createEndpoints(machine, storage, firewalledPorts(machine, storage));\n const expirationOps = {\n hasExpiration,\n getExpiration,\n setExpiration\n };\n const shellOps = {\n hasShell: () => hasShell(ctx),\n sshEndpoint: () => sshEndpoint(ctx)\n };\n return {\n ...fileOps,\n ...lifecycleOps,\n ...cmdOps,\n ...dataOps,\n ...expirationOps,\n ...shellOps,\n ...netOps\n };\n}\n\n// portal-gateway/transport-worker.ts\nvar TRANSPORT_PROTOCOL = 1;\nvar TOKEN_PARAM = \"hyb_token\";\nvar HEARTBEAT_MS = 15000;\nvar connections = new Map;\nasync function wired(dimensionName) {\n const files = await readdir2(join2(data, \"portals\")).catch(() => []);\n for (const file of files.filter((name) => name.endsWith(\".json\"))) {\n const portal = await readFile3(join2(data, \"portals\", file), \"utf8\").then((text) => JSON.parse(text), () => null);\n if (portal?.gatewayName === gateway && portal.dimensionName === dimensionName && portal.state === \"ready\")\n return true;\n }\n return false;\n}\nasync function authorized(url) {\n const token = url.searchParams.get(TOKEN_PARAM);\n if (!token)\n return false;\n try {\n await jwtVerify(token, key, { algorithms: [\"HS256\"], requiredClaims: [\"exp\"] });\n return true;\n } catch {\n return false;\n }\n}\nfunction cors(request, response) {\n const origin = request.headers.get(\"origin\");\n if (!origin)\n return response;\n response.headers.set(\"access-control-allow-origin\", origin);\n response.headers.append(\"vary\", \"Origin\");\n return response;\n}\nasync function preflight(request) {\n const headers = new Headers({\n \"access-control-allow-methods\": \"GET, POST, OPTIONS\",\n \"access-control-max-age\": \"600\"\n });\n const asked = request.headers.get(\"access-control-request-headers\");\n if (asked)\n headers.set(\"access-control-allow-headers\", asked);\n return cors(request, new Response(null, { status: 204, headers }));\n}\nasync function answer(handler, request, server) {\n server.timeout(request, 0);\n if (!await authorized(new URL(request.url)))\n return Response.json({ error: \"a valid token is required\" }, { status: 401 });\n try {\n return await handler(request, server);\n } catch (error) {\n const { name, message } = error instanceof Error ? error : new Error(String(error));\n return Response.json({ name, message }, { status: 500 });\n }\n}\nfunction guarded(handler) {\n return async (request, server) => {\n const response = await answer(handler, request, server);\n return response && cors(request, response);\n };\n}\nfunction relay(connection, send, end) {\n connection.onData(send);\n connection.closed.then(end);\n}\nvar encoder2 = new TextEncoder;\nfunction multipartFrames() {\n const boundary = randomBytes2(24).toString(\"hex\");\n const part = (type, data) => Buffer.concat([encoder2.encode(`\\r\ncontent-type: ${type}\\r\n\\r\n`), data, encoder2.encode(`\\r\n--${boundary}`)]);\n return {\n contentType: `multipart/mixed; boundary=\"${boundary}\"`,\n open: (id) => Buffer.concat([encoder2.encode(`--${boundary}`), part(\"application/json\", encoder2.encode(JSON.stringify({ connection: id })))]),\n data: (chunk) => part(\"application/octet-stream\", chunk),\n heartbeat: part(\"application/octet-stream\", new Uint8Array),\n end: encoder2.encode(`--\\r\n`)\n };\n}\nfunction lineFrames() {\n const decoder = new TextDecoder;\n let pending = \"\";\n return {\n contentType: \"text/event-stream\",\n open: (id) => encoder2.encode(`event: open\ndata: ${JSON.stringify({ connection: id })}\n\n`),\n data(chunk) {\n const lines = (pending + decoder.decode(chunk, { stream: true })).split(`\n`);\n pending = lines.pop();\n return encoder2.encode(lines.map((line) => `data: ${line.replace(/\\r$/, \"\")}\n\n`).join(\"\"));\n },\n heartbeat: encoder2.encode(`: ping\n\n`),\n end: encoder2.encode(`event: close\ndata: {}\n\n`)\n };\n}\nfunction stream(connection, frames) {\n const id = randomUUID2();\n let heartbeat;\n let open = true;\n const finish = () => {\n open = false;\n clearInterval(heartbeat);\n connections.delete(id);\n connection.close();\n };\n connections.set(id, connection);\n const body = new ReadableStream({\n start(controller) {\n const send = (bytes) => {\n if (open && bytes.length > 0)\n controller.enqueue(bytes);\n };\n send(frames.open(id));\n heartbeat = setInterval(() => send(frames.heartbeat), HEARTBEAT_MS);\n relay(connection, (data) => send(frames.data(data)), () => {\n if (!open)\n return;\n send(frames.end);\n finish();\n controller.close();\n });\n },\n cancel: finish\n });\n return new Response(body, { headers: { \"content-type\": frames.contentType, \"cache-control\": \"no-cache, no-transform\", \"x-accel-buffering\": \"no\" } });\n}\nasync function write(request) {\n const connection = connections.get(request.params.id);\n if (!connection)\n return Response.json({ error: \"no such connection\" }, { status: 404 });\n connection.write(new Uint8Array(await request.arrayBuffer()));\n return new Response(null, { status: 204 });\n}\nasync function connect2(request, server) {\n const params = new URL(request.url).searchParams;\n const dimensionName = params.get(\"dimension\") ?? \"\";\n const agentName = params.get(\"agent\");\n if (!isDimensionName(dimensionName) || agentName !== null && !isDimensionName(agentName)) {\n return Response.json({ error: \"dimension and agent are names\" }, { status: 400 });\n }\n if (!await wired(dimensionName))\n return Response.json({ error: `dimension ${dimensionName} has no portal to gateway ${gateway}` }, { status: 403 });\n const socket = agentName === null ? dimensionLayout.transportSocketPath(data, dimensionName) : join2(dimensionLayout.runDirectory(data, dimensionName), `${agentName}.sock`);\n const connection = await machine.connect(socket).catch((error) => error);\n if (connection instanceof Error)\n return Response.json({ error: connection.message }, { status: 502 });\n if (request.headers.get(\"upgrade\")?.toLowerCase() !== \"websocket\")\n return stream(connection, agentName === null ? multipartFrames() : lineFrames());\n if (server.upgrade(request, { data: connection }))\n return;\n connection.close();\n return Response.json({ error: \"the websocket upgrade failed\" }, { status: 400 });\n}\nfunction serve(port) {\n return Bun.serve({\n port,\n hostname: \"0.0.0.0\",\n routes: {\n \"/connect\": {\n GET: guarded(connect2),\n OPTIONS: preflight\n },\n \"/connect/:id\": {\n POST: guarded(write),\n OPTIONS: preflight\n }\n },\n websocket: {\n open(socket) {\n relay(socket.data, (data) => socket.send(data), () => socket.close());\n },\n message(socket, message) {\n socket.data.write(typeof message === \"string\" ? Buffer.from(message) : message);\n },\n close(socket) {\n socket.data.close();\n }\n }\n });\n}\nfunction required(name) {\n const value = process.env[name];\n if (!value)\n throw new Error(`${name} is not set`);\n return value;\n}\nprocess.umask(63);\nvar dir = required(\"HYB_TRANSPORT_DIR\");\nvar build = required(\"HYB_TRANSPORT_BUILD\");\nvar port = Number(required(\"HYB_TRANSPORT_PORT\"));\nvar gateway = required(\"HYB_GATEWAY\");\nvar data = required(\"HYB_DATA\");\nvar key = new TextEncoder().encode(await readFile3(join2(dir, \"secret\"), \"utf8\"));\nvar machine = createLocalService({ kind: \"local\" });\nvar server = serve(port);\nfor (const signal of [\"SIGTERM\", \"SIGINT\"]) {\n process.once(signal, () => {\n server.stop(true);\n process.exit(0);\n });\n}\nawait writeFile3(join2(dir, \"ready\"), `${process.pid} ${build} ${TRANSPORT_PROTOCOL}\n`);\n";
2
+ export const TRANSPORT_SOURCE = "// @bun\n// portal-gateway/transport-worker.ts\nimport { randomBytes as randomBytes2, randomUUID as randomUUID2 } from \"crypto\";\nimport { readdir as readdir2, readFile as readFile3, writeFile as writeFile3 } from \"fs/promises\";\nimport { join as join2 } from \"path\";\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/buffer_utils.js\nvar encoder = new TextEncoder;\nvar decoder = new TextDecoder;\nvar strictDecoder = new TextDecoder(\"utf-8\", { fatal: true });\nvar UINT32_RANGE = 2 ** 32;\nfunction concat(...buffers) {\n const size = buffers.reduce((acc, { length }) => acc + length, 0), buf = new Uint8Array(size);\n let i = 0;\n for (const buffer of buffers)\n buf.set(buffer, i), i += buffer.length;\n return buf;\n}\nvar NON_ASCII = /[^\\x00-\\x7f]/;\nfunction encode(string) {\n if (typeof string == \"string\" && string.length >= 128) {\n if (NON_ASCII.test(string))\n throw new TypeError(\"non-ASCII string encountered in encode()\");\n return encoder.encode(string);\n }\n const bytes = new Uint8Array(string.length);\n for (let i = 0;i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code > 127)\n throw new TypeError(\"non-ASCII string encountered in encode()\");\n bytes[i] = code;\n }\n return bytes;\n}\nfunction encodeBase64(input, url = false) {\n if (Uint8Array.prototype.toBase64)\n return input.toBase64({ alphabet: url ? \"base64url\" : \"base64\", omitPadding: url });\n const CHUNK_SIZE = 32768, arr = [];\n for (let i = 0;i < input.length; i += CHUNK_SIZE)\n arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));\n const encoded = btoa(arr.join(\"\"));\n return url ? encoded.replace(/=/g, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\") : encoded;\n}\nfunction decodeBase64(encoded, url = false) {\n if (Uint8Array.fromBase64)\n return Uint8Array.fromBase64(encoded, { alphabet: url ? \"base64url\" : \"base64\" });\n if (url) {\n if (encoded.includes(\"+\") || encoded.includes(\"/\"))\n throw new TypeError(\"Invalid base64url\");\n encoded = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n }\n const binary = atob(encoded), bytes = new Uint8Array(binary.length);\n for (let i = 0;i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/util/errors.js\nclass JOSEError extends Error {\n static code = \"ERR_JOSE_GENERIC\";\n code = \"ERR_JOSE_GENERIC\";\n constructor(message, options) {\n super(message, options), this.name = this.constructor.name, Error.captureStackTrace?.(this, this.constructor);\n }\n}\n\nclass JWTClaimValidationFailed extends JOSEError {\n static code = \"ERR_JWT_CLAIM_VALIDATION_FAILED\";\n code = \"ERR_JWT_CLAIM_VALIDATION_FAILED\";\n claim;\n reason;\n payload;\n constructor(message, payload, claim = \"unspecified\", reason = \"unspecified\") {\n super(message, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;\n }\n}\n\nclass JWTExpired extends JOSEError {\n static code = \"ERR_JWT_EXPIRED\";\n code = \"ERR_JWT_EXPIRED\";\n claim;\n reason;\n payload;\n constructor(message, payload, claim = \"unspecified\", reason = \"unspecified\") {\n super(message, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;\n }\n}\n\nclass JOSEAlgNotAllowed extends JOSEError {\n static code = \"ERR_JOSE_ALG_NOT_ALLOWED\";\n code = \"ERR_JOSE_ALG_NOT_ALLOWED\";\n}\n\nclass JOSENotSupported extends JOSEError {\n static code = \"ERR_JOSE_NOT_SUPPORTED\";\n code = \"ERR_JOSE_NOT_SUPPORTED\";\n}\nclass JWSInvalid extends JOSEError {\n static code = \"ERR_JWS_INVALID\";\n code = \"ERR_JWS_INVALID\";\n}\n\nclass JWTInvalid extends JOSEError {\n static code = \"ERR_JWT_INVALID\";\n code = \"ERR_JWT_INVALID\";\n}\nclass JWSSignatureVerificationFailed extends JOSEError {\n static code = \"ERR_JWS_SIGNATURE_VERIFICATION_FAILED\";\n code = \"ERR_JWS_SIGNATURE_VERIFICATION_FAILED\";\n constructor(message = \"signature verification failed\", options) {\n super(message, options);\n }\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/util/base64url.js\nvar invalid = \"The input to be decoded is not correctly encoded.\";\nfunction decode(input) {\n try {\n return decodeBase64(typeof input == \"string\" ? input : decoder.decode(input), true);\n } catch (cause) {\n throw new TypeError(invalid, { cause });\n }\n}\nfunction encode2(input) {\n return encodeBase64(typeof input == \"string\" ? encoder.encode(input) : input, true);\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/validate.js\nfunction isObject(input) {\n if (typeof input != \"object\" || input === null || Object.prototype.toString.call(input) !== \"[object Object]\")\n return false;\n const prototype = Object.getPrototypeOf(input);\n return prototype === null || Object.getPrototypeOf(prototype) === null;\n}\nfunction isDisjoint(...headers) {\n const parameters = /* @__PURE__ */ new Set;\n for (const header of headers)\n if (header)\n for (const parameter of Object.keys(header)) {\n if (parameters.has(parameter))\n return false;\n parameters.add(parameter);\n }\n return true;\n}\nfunction assertNotSet(value, name) {\n if (value !== undefined)\n throw new TypeError(`${name} can only be called once`);\n}\nfunction decodeBase64url(value, label, ErrorClass) {\n try {\n return decode(value);\n } catch (cause) {\n throw new ErrorClass(`Failed to base64url decode the ${label}`, { cause });\n }\n}\nfunction encodeAsciiMember(value, label, ErrorClass) {\n try {\n return encode(value);\n } catch (cause) {\n throw new ErrorClass(`The ${label} is not a valid base64url string`, { cause });\n }\n}\nfunction parseJoseHeader(encodedProtectedHeader, ErrorClass, message) {\n let parsed;\n try {\n parsed = JSON.parse(strictDecoder.decode(decode(encodedProtectedHeader)));\n } catch (cause) {\n throw new ErrorClass(message, { cause });\n }\n if (!isObject(parsed))\n throw new ErrorClass(message);\n return parsed;\n}\nvar JWS_RECOGNIZED = { __proto__: null, b64: true };\nfunction validateAlgorithms(option, algorithms) {\n if (algorithms !== undefined && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s != \"string\")))\n throw new TypeError(`\"${option}\" option must be an array of strings`);\n return algorithms === undefined ? undefined : new Set(algorithms);\n}\nfunction validateCritDuplicates(Err, protectedHeader) {\n const { crit } = protectedHeader ?? {};\n if (Array.isArray(crit) && new Set(crit).size !== crit.length)\n throw new Err('\"crit\" (Critical) Header Parameter MUST NOT contain duplicate values');\n}\nfunction validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {\n if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined)\n throw new Err('\"crit\" (Critical) Header Parameter MUST be integrity protected');\n if (!protectedHeader || protectedHeader.crit === undefined)\n return [];\n if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input != \"string\" || input.length === 0))\n throw new Err('\"crit\" (Critical) Header Parameter MUST be an array of non-empty strings when present');\n const recognized = recognizedOption === undefined ? recognizedDefault : { __proto__: null, ...recognizedOption, ...recognizedDefault };\n for (const parameter of protectedHeader.crit) {\n if (!(parameter in recognized))\n throw new JOSENotSupported(`Extension Header Parameter \"${parameter}\" is not recognized`);\n if (!Object.hasOwn(joseHeader, parameter) || joseHeader[parameter] === undefined)\n throw new Err(`Extension Header Parameter \"${parameter}\" is missing`);\n if (recognized[parameter] && (!Object.hasOwn(protectedHeader, parameter) || protectedHeader[parameter] === undefined))\n throw new Err(`Extension Header Parameter \"${parameter}\" MUST be integrity protected`);\n }\n return protectedHeader.crit;\n}\nfunction validateB64(protectedHeader, extensions) {\n if (extensions.includes(\"b64\")) {\n const b64 = protectedHeader.b64;\n if (typeof b64 != \"boolean\")\n throw new JWSInvalid('The \"b64\" (base64url-encode payload) Header Parameter must be a boolean');\n return b64;\n }\n return true;\n}\nfunction serializeJoseHeader(Err, header) {\n let serialized, parsed;\n try {\n serialized = JSON.stringify(header), parsed = JSON.parse(serialized);\n } catch (cause) {\n throw new Err(\"JOSE Header is not valid JSON\", { cause });\n }\n if (!isObject(parsed))\n throw new Err(\"JOSE Header is not a JSON object\");\n return [parsed, serialized];\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/key.js\nvar tag = (key) => key[Symbol.toStringTag];\nvar jwkMatchesOp = (entry, key, usage) => {\n const { alg } = entry;\n if (key.use !== undefined) {\n const expected = usage === \"sign\" || usage === \"verify\" ? \"sig\" : \"enc\";\n if (key.use !== expected)\n throw new TypeError(`Invalid key for this operation, its \"use\" must be \"${expected}\" when present`);\n }\n if (key.alg !== undefined && key.alg !== alg)\n throw new TypeError(`Invalid key for this operation, its \"alg\" must be \"${alg}\" when present`);\n if (Array.isArray(key.key_ops)) {\n const expectedKeyOp = usage === \"encrypt\" || usage === \"decrypt\" ? entry.ops?.[usage === \"encrypt\" ? 0 : 1] : usage;\n if (expectedKeyOp && !key.key_ops.includes(expectedKeyOp))\n throw new TypeError(`Invalid key for this operation, its \"key_ops\" must include \"${expectedKeyOp}\" when present`);\n }\n};\nasync function prepareKey(entry, key, usage) {\n const { alg, secret } = entry, requiresPrivateKey = usage === \"decrypt\" || usage === \"sign\";\n if (secret && key instanceof Uint8Array)\n return key;\n let normalized, keyObject;\n if (isObject(key)) {\n if (normalized = normalizeJwk(key), typeof normalized.kty != \"string\")\n throw invalidKeyType(alg, key, secret);\n if (!(secret ? normalized.kty === \"oct\" && typeof normalized.k == \"string\" : normalized.kty !== \"oct\" && (requiresPrivateKey ? normalized.kty === \"AKP\" && typeof normalized.priv == \"string\" || typeof normalized.d == \"string\" : normalized.d === undefined && normalized.priv === undefined)))\n throw new TypeError(secret ? 'JSON Web Key for symmetric algorithms must have JWK \"kty\" (Key Type) equal to \"oct\" and the JWK \"k\" (Key Value) present' : `JSON Web Key for this operation must be a ${requiresPrivateKey ? \"private\" : \"public\"} JWK`);\n if (jwkMatchesOp(entry, normalized, usage), normalized.kty === \"oct\")\n return decode(normalized.k);\n if (!Object.isFrozen(key)) {\n const { key_ops } = key;\n Array.isArray(key_ops) && Object.freeze(key_ops), Object.freeze(key);\n }\n } else {\n if (!isKeyLike(key))\n throw invalidKeyType(alg, key, secret);\n const expectedType = secret ? \"secret\" : requiresPrivateKey ? \"private\" : \"public\";\n if (key.type !== expectedType && (secret || [\"secret\", \"public\", \"private\"].includes(key.type)))\n throw new TypeError(`${tag(key)} instances must be of type \"${expectedType}\" for the ${alg} algorithm`);\n if (isCryptoKey(key))\n return key;\n if (keyObject = key, keyObject.type === \"secret\")\n return keyObject.export();\n }\n cache ||= /* @__PURE__ */ new WeakMap;\n const cacheKey = key;\n let cached = cache.get(cacheKey);\n if (cached?.[alg])\n return cached[alg];\n if (cached || cache.set(cacheKey, cached = {}), keyObject && typeof keyObject.toCryptoKey == \"function\") {\n const isPublic = keyObject.type === \"public\", crv = nist[keyObject.asymmetricKeyDetails?.namedCurve], params = entry.resolve?.({ crv, asymmetricKeyType: keyObject.asymmetricKeyType }) ?? entry.subtle;\n return cached[alg] = keyObject.toCryptoKey(params, isPublic, entry.usages[isPublic ? 0 : 1]);\n }\n return normalized ??= keyObject.export({ format: \"jwk\" }), normalized.alg = alg, cached[alg] = await jwkToKey(entry, normalized);\n}\nvar cache;\nvar nist = {\n __proto__: null,\n prime256v1: \"P-256\",\n secp384r1: \"P-384\",\n secp521r1: \"P-521\"\n};\nvar isCryptoKey = (key) => {\n if (key?.[Symbol.toStringTag] === \"CryptoKey\")\n return true;\n try {\n return key instanceof CryptoKey;\n } catch {\n return false;\n }\n};\nvar isKeyObject = (key) => key?.[Symbol.toStringTag] === \"KeyObject\";\nvar isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);\nfunction message(msg, actual, ...types) {\n if (types.length > 2) {\n const last = types.pop();\n msg += `one of type ${types.join(\", \")}, or ${last}.`;\n } else\n types.length === 2 ? msg += `one of type ${types[0]} or ${types[1]}.` : msg += `of type ${types[0]}.`;\n return actual == null ? msg += ` Received ${actual}` : typeof actual == \"function\" && actual.name ? msg += ` Received function ${actual.name}` : typeof actual == \"object\" && actual != null && actual.constructor?.name && (msg += ` Received an instance of ${actual.constructor.name}`), msg;\n}\nfunction invalidKeyType(alg, actual, secret) {\n const types = [\"CryptoKey\", \"KeyObject\", \"JSON Web Key\"];\n return secret && types.push(\"Uint8Array\"), new TypeError(message(`Key for the ${alg} algorithm must be `, actual, ...types));\n}\nvar unusable = (name, prop = \"algorithm.name\") => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);\nfunction checkUsage(key, usage) {\n if (usage && !key.usages.includes(usage))\n throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);\n}\nfunction checkModulusLength(alg, key) {\n const { modulusLength } = key.algorithm;\n if (typeof modulusLength != \"number\" || modulusLength < 2048)\n throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);\n}\nfunction checkCryptoKey(key, expected, usage) {\n const algorithm = key.algorithm;\n if (algorithm.name !== expected.name)\n throw unusable(expected.name);\n if (expected.hash && algorithm.hash?.name !== expected.hash)\n throw unusable(expected.hash, \"algorithm.hash\");\n if (expected.namedCurve && algorithm.namedCurve !== expected.namedCurve)\n throw unusable(expected.namedCurve, \"algorithm.namedCurve\");\n if (expected.length !== undefined && algorithm.length !== expected.length)\n throw unusable(expected.length, \"algorithm.length\");\n checkUsage(key, usage);\n}\nfunction snapshotJwk(jwk) {\n return { __proto__: null, ...jwk };\n}\nfunction normalizeJwk(jwk) {\n const normalized = snapshotJwk(jwk);\n if (normalized.ext !== undefined && typeof normalized.ext != \"boolean\")\n throw new TypeError('\"ext\" (Extractable) Parameter must be a boolean');\n if (normalized.key_ops !== undefined) {\n const value = normalized.key_ops, keyOps = Array.isArray(value) ? [...value] : undefined;\n if (!keyOps || keyOps.some((operation) => typeof operation != \"string\") || new Set(keyOps).size !== keyOps.length)\n throw new TypeError('\"key_ops\" (Key Operations) Parameter must be an array of unique strings');\n normalized.key_ops = keyOps;\n }\n return normalized;\n}\nasync function jwkToKey(entry, jwk, extractable) {\n if (!entry.kty.includes(jwk.kty))\n throw new JOSENotSupported('Invalid or unsupported JWK \"alg\" (Algorithm) Parameter value');\n const algorithm = entry.resolve?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle, isPrivate = !!(jwk.d || jwk.priv), keyData = { ...jwk, ext: extractable ?? jwk.ext };\n return keyData.kty !== \"AKP\" && delete keyData.alg, delete keyData.use, crypto.subtle.importKey(\"jwk\", keyData, algorithm, keyData.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);\n}\nasync function rawKey(key, expected, usage, extractable = false) {\n return key instanceof Uint8Array && (key = await crypto.subtle.importKey(\"raw\", key, expected, extractable, [usage])), checkCryptoKey(key, expected, usage), key;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/key_descriptor.js\nfunction table(entries) {\n const out = { __proto__: null };\n for (const alg in entries)\n out[alg] = { ...entries[alg], alg };\n return out;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_algorithms.js\nvar sig = [[\"verify\"], [\"sign\"]];\nfunction hmac(bits) {\n const subtle = { name: \"HMAC\", hash: `SHA-${bits}` };\n return { kty: [\"oct\"], secret: true, subtle, signing: subtle, usages: sig };\n}\nfunction rsa(bits, saltLength) {\n const subtle = { name: saltLength ? \"RSA-PSS\" : \"RSASSA-PKCS1-v1_5\", hash: `SHA-${bits}` };\n return {\n kty: [\"RSA\"],\n subtle,\n signing: saltLength ? { ...subtle, saltLength } : subtle,\n usages: sig,\n minRsaBits: 2048\n };\n}\nfunction ecdsa(crv, bits) {\n return {\n kty: [\"EC\"],\n crv,\n subtle: { name: \"ECDSA\", namedCurve: crv },\n signing: { name: \"ECDSA\", hash: `SHA-${bits}` },\n usages: sig\n };\n}\nfunction eddsa() {\n const subtle = { name: \"Ed25519\" };\n return {\n kty: [\"OKP\"],\n crv: \"Ed25519\",\n subtle,\n signing: subtle,\n usages: sig\n };\n}\nfunction mldsa(parameterSet) {\n const subtle = { name: `ML-DSA-${parameterSet}` };\n return {\n kty: [\"AKP\"],\n subtle,\n signing: subtle,\n usages: sig\n };\n}\nvar JWS = table({\n HS256: hmac(256),\n HS384: hmac(384),\n HS512: hmac(512),\n RS256: rsa(256),\n RS384: rsa(384),\n RS512: rsa(512),\n PS256: rsa(256, 32),\n PS384: rsa(384, 48),\n PS512: rsa(512, 64),\n ES256: ecdsa(\"P-256\", 256),\n ES384: ecdsa(\"P-384\", 384),\n ES512: ecdsa(\"P-521\", 512),\n EdDSA: eddsa(),\n Ed25519: eddsa(),\n \"ML-DSA-44\": mldsa(44),\n \"ML-DSA-65\": mldsa(65),\n \"ML-DSA-87\": mldsa(87)\n});\nfunction jwsAlgorithm(alg) {\n const entry = typeof alg == \"string\" ? JWS[alg] : undefined;\n if (!entry)\n throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);\n return entry;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_verify.js\nfunction prepareVerify(options) {\n return [options && validateAlgorithms(\"algorithms\", options.algorithms), options?.crit];\n}\nfunction parseProtectedHeader(encodedProtectedHeader) {\n return encodedProtectedHeader === undefined ? {} : parseJoseHeader(encodedProtectedHeader, JWSInvalid, \"JWS Protected Header is invalid\");\n}\nfunction encodeCompactUnencodedPayload(payload) {\n try {\n return encode(payload);\n } catch (cause) {\n throw new JWSInvalid(\"JWS Compact Serialization payload must use only ASCII characters\", {\n cause\n });\n }\n}\nasync function verifySignature(jws, shared, key, encodeUnencodedPayload, parsedProtected) {\n const { protected: encodedProtectedHeader, header, payload: inputPayload } = jws, protectedHeader = parsedProtected ?? parseProtectedHeader(encodedProtectedHeader);\n if (!isDisjoint(protectedHeader, header))\n throw new JWSInvalid(\"JWS Protected and JWS Unprotected Header Parameter names must be disjoint\");\n const joseHeader = { ...protectedHeader, ...header }, b64 = validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], protectedHeader, joseHeader)), { alg } = joseHeader;\n if (typeof alg != \"string\" || !alg)\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n if (shared[0] && !shared[0].has(alg))\n throw new JOSEAlgNotAllowed('\"alg\" (Algorithm) Header Parameter value not allowed');\n if (b64) {\n if (typeof inputPayload != \"string\")\n throw new JWSInvalid(\"JWS Payload must be a string\");\n } else if (typeof inputPayload != \"string\" && !(inputPayload instanceof Uint8Array))\n throw new JWSInvalid(\"JWS Payload must be a string or an Uint8Array instance\");\n const signingPayload = b64 || typeof inputPayload != \"string\" ? inputPayload : encodeUnencodedPayload(inputPayload);\n let resolvedKey = false;\n typeof key == \"function\" && (key = await key(protectedHeader, jws), resolvedKey = true);\n const entry = jwsAlgorithm(alg), signingInput = concat(encodedProtectedHeader !== undefined ? encode(encodedProtectedHeader) : new Uint8Array, encode(\".\"), typeof signingPayload == \"string\" ? shared[2] ??= encodeAsciiMember(signingPayload, \"payload\", JWSInvalid) : signingPayload), signature = decodeBase64url(jws.signature, \"signature\", JWSInvalid), verificationKey = await prepareKey(entry, key, \"verify\"), cryptoKey = await rawKey(verificationKey, entry.subtle, \"verify\");\n entry.minRsaBits && checkModulusLength(entry.alg, cryptoKey);\n let verified = false;\n try {\n verified = await crypto.subtle.verify(entry.signing, cryptoKey, signature, signingInput);\n } catch (cause) {\n throw new JWSSignatureVerificationFailed(undefined, { cause });\n }\n if (!verified)\n throw new JWSSignatureVerificationFailed;\n const result = { payload: typeof signingPayload == \"string\" ? decodeBase64url(signingPayload, \"payload\", JWSInvalid) : signingPayload };\n return encodedProtectedHeader !== undefined && (result.protectedHeader = protectedHeader), header !== undefined && (result.unprotectedHeader = header), resolvedKey ? [{ ...result, key: verificationKey }, b64] : [result, b64];\n}\nasync function verifyCompact(jws, shared, key) {\n if (jws instanceof Uint8Array && (jws = decoder.decode(jws)), typeof jws != \"string\")\n throw new JWSInvalid(\"Compact JWS must be a string or Uint8Array\");\n const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split(\".\");\n if (length !== 3)\n throw new JWSInvalid(\"Invalid Compact JWS\");\n return verifySignature({ payload, protected: protectedHeader, signature }, shared, key, encodeCompactUnencodedPayload);\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jwt_claims_set.js\nvar epoch = (date) => Math.floor(date.getTime() / 1000);\nvar multipliers = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n y: 31557600\n};\nvar REGEX = /^(\\+|\\-)? ?(\\d+|\\d+\\.\\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;\nvar checkFailed = \"check_failed\";\nfunction invalidDuration() {\n throw new TypeError(\"Invalid time period format\");\n}\nfunction secs(str) {\n typeof str != \"string\" && invalidDuration();\n const matched = REGEX.exec(str);\n (!matched || matched[4] && matched[1]) && invalidDuration();\n const value = parseFloat(matched[2]), durationSeconds = Math.round(value * multipliers[matched[3][0].toLowerCase()]);\n return Number.isFinite(durationSeconds) || invalidDuration(), matched[1] === \"-\" || matched[4] === \"ago\" ? -durationSeconds : durationSeconds;\n}\nfunction validateInput(label, input) {\n if (!Number.isFinite(input))\n throw new TypeError(`Invalid ${label} input`);\n return input;\n}\nfunction validateStringClaim(claim, value) {\n if (typeof value != \"string\")\n throw new TypeError(`\"${claim}\" claim must be a string`);\n}\nfunction validateAudienceClaim(value) {\n if (typeof value != \"string\" && (!Array.isArray(value) || Array.from(value).some((member) => typeof member != \"string\")))\n throw new TypeError('\"aud\" claim must be a string or an array of strings');\n}\nfunction numericDate(value, label) {\n return typeof value == \"number\" ? validateInput(label, value) : value instanceof Date ? validateInput(label, epoch(value)) : epoch(/* @__PURE__ */ new Date) + secs(value);\n}\nvar normalizeTyp = (value) => {\n const normalized = value.toLowerCase();\n return value.includes(\"/\") ? normalized : `application/${normalized}`;\n};\nvar checkAudiencePresence = (audPayload, audOption) => typeof audPayload == \"string\" ? audOption.includes(audPayload) : Array.isArray(audPayload) ? audOption.some((aud) => audPayload.includes(aud)) : false;\nfunction validateNumericDate(claimsSet, claim, required = false) {\n const value = claimsSet[claim];\n if (!(value === undefined && !required)) {\n if (typeof value != \"number\")\n throw new JWTClaimValidationFailed(`\"${claim}\" claim must be a number`, claimsSet, claim, \"invalid\");\n return value;\n }\n}\nfunction unexpectedClaim(claimsSet, claim) {\n throw new JWTClaimValidationFailed(`unexpected \"${claim}\" claim value`, claimsSet, claim, checkFailed);\n}\nfunction validateClaimsSet(protectedHeader, claimsSetBytes, options = {}) {\n let claimsSet;\n try {\n claimsSet = JSON.parse(strictDecoder.decode(claimsSetBytes));\n } catch (cause) {\n throw new JWTInvalid(\"JWT Claims Set must be a top-level JSON object\", { cause });\n }\n if (!isObject(claimsSet))\n throw new JWTInvalid(\"JWT Claims Set must be a top-level JSON object\");\n const { typ } = options;\n if (typ !== undefined && (typeof protectedHeader.typ != \"string\" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ)))\n throw new JWTClaimValidationFailed('unexpected \"typ\" JWT header value', claimsSet, \"typ\", checkFailed);\n const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options, presenceCheck = [...requiredClaims];\n maxTokenAge !== undefined && presenceCheck.push(\"iat\"), audience !== undefined && presenceCheck.push(\"aud\"), subject !== undefined && presenceCheck.push(\"sub\"), issuer !== undefined && presenceCheck.push(\"iss\");\n for (const claim of new Set(presenceCheck.reverse()))\n if (!Object.hasOwn(claimsSet, claim))\n throw new JWTClaimValidationFailed(`missing required \"${claim}\" claim`, claimsSet, claim, \"missing\");\n issuer !== undefined && !(Array.isArray(issuer) ? issuer : [issuer]).includes(claimsSet.iss) && unexpectedClaim(claimsSet, \"iss\"), subject !== undefined && claimsSet.sub !== subject && unexpectedClaim(claimsSet, \"sub\"), audience !== undefined && !checkAudiencePresence(claimsSet.aud, typeof audience == \"string\" ? [audience] : audience) && unexpectedClaim(claimsSet, \"aud\");\n const { clockTolerance } = options;\n let tolerance = 0;\n if (typeof clockTolerance == \"string\")\n tolerance = secs(clockTolerance);\n else if (clockTolerance !== undefined) {\n if (typeof clockTolerance != \"number\")\n throw new TypeError(\"Invalid clockTolerance option type\");\n tolerance = clockTolerance;\n }\n validateInput(\"clockTolerance option\", tolerance);\n const { currentDate } = options, now = validateInput(\"currentDate option\", epoch(currentDate === undefined ? /* @__PURE__ */ new Date : currentDate)), iat = validateNumericDate(claimsSet, \"iat\", maxTokenAge !== undefined), nbf = validateNumericDate(claimsSet, \"nbf\");\n if (nbf !== undefined && nbf > now + tolerance)\n throw new JWTClaimValidationFailed('\"nbf\" claim timestamp check failed', claimsSet, \"nbf\", checkFailed);\n const exp = validateNumericDate(claimsSet, \"exp\");\n if (exp !== undefined && exp <= now - tolerance)\n throw new JWTExpired('\"exp\" claim timestamp check failed', claimsSet, \"exp\", checkFailed);\n if (maxTokenAge !== undefined) {\n const age = now - iat, max = validateInput(\"maxTokenAge option\", typeof maxTokenAge == \"number\" ? maxTokenAge : secs(maxTokenAge));\n if (age - tolerance > max)\n throw new JWTExpired('\"iat\" claim timestamp check failed (too far in the past)', claimsSet, \"iat\", checkFailed);\n if (age < -tolerance)\n throw new JWTClaimValidationFailed('\"iat\" claim timestamp check failed (it should be in the past)', claimsSet, \"iat\", checkFailed);\n }\n return claimsSet;\n}\nvar producerClaimsSets;\nfunction producerClaimsSet(producer) {\n return producerClaimsSets.get(producer);\n}\nfunction jwtClaimsSetBytes(producer) {\n const claimsSet = producerClaimsSet(producer);\n for (const claim of [\"iat\", \"nbf\", \"exp\"]) {\n const value = claimsSet[claim];\n if (typeof value == \"number\" && !Number.isFinite(value))\n throw new TypeError(`\"${claim}\" claim must be a finite number`);\n }\n return encoder.encode(JSON.stringify(claimsSet));\n}\nclass JWTClaimsBuilder {\n constructor(claimsSet = {}) {\n if (!isObject(claimsSet))\n throw new TypeError(\"JWT Claims Set MUST be an object\");\n (producerClaimsSets ||= /* @__PURE__ */ new WeakMap).set(this, structuredClone(claimsSet));\n }\n setIssuer(value) {\n return validateStringClaim(\"iss\", value), producerClaimsSet(this).iss = value, this;\n }\n setSubject(value) {\n return validateStringClaim(\"sub\", value), producerClaimsSet(this).sub = value, this;\n }\n setAudience(value) {\n return validateAudienceClaim(value), producerClaimsSet(this).aud = value, this;\n }\n setJti(value) {\n return validateStringClaim(\"jti\", value), producerClaimsSet(this).jti = value, this;\n }\n setNotBefore(value) {\n return producerClaimsSet(this).nbf = numericDate(value, \"setNotBefore\"), this;\n }\n setExpirationTime(value) {\n return producerClaimsSet(this).exp = numericDate(value, \"setExpirationTime\"), this;\n }\n setIssuedAt(value) {\n const claimsSet = producerClaimsSet(this);\n return value === undefined ? claimsSet.iat = epoch(/* @__PURE__ */ new Date) : typeof value == \"string\" ? claimsSet.iat = validateInput(\"setIssuedAt\", epoch(/* @__PURE__ */ new Date) + secs(value)) : claimsSet.iat = numericDate(value, \"setIssuedAt\"), this;\n }\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/jwt/verify.js\nasync function jwtVerify(jwt, key, options) {\n const [verified, b64] = await verifyCompact(jwt, prepareVerify(options), key);\n if (!b64)\n throw new JWTInvalid(\"JWTs MUST NOT use unencoded payload\");\n const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);\n return { ...verified, payload };\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_sign.js\nasync function createSignature(input, key, rejectUnencoded) {\n let [payload, protectedHeader, unprotectedHeader, crit] = input, encodedProtectedHeader = \"\";\n if (protectedHeader !== undefined) {\n const normalized = serializeJoseHeader(JWSInvalid, protectedHeader);\n protectedHeader = normalized[0], encodedProtectedHeader = encode2(normalized[1]);\n }\n if (unprotectedHeader !== undefined && (unprotectedHeader = serializeJoseHeader(JWSInvalid, unprotectedHeader)[0]), !protectedHeader && !unprotectedHeader)\n throw new JWSInvalid(\"either setProtectedHeader or setUnprotectedHeader must be called before #sign()\");\n if (!isDisjoint(protectedHeader, unprotectedHeader))\n throw new JWSInvalid(\"JWS Protected and JWS Unprotected Header Parameter names must be disjoint\");\n const joseHeader = { ...protectedHeader, ...unprotectedHeader };\n validateCritDuplicates(JWSInvalid, protectedHeader);\n const b64 = validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, crit, protectedHeader, joseHeader));\n b64 || rejectUnencoded?.();\n const { alg } = joseHeader;\n if (typeof alg != \"string\" || !alg)\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n const entry = jwsAlgorithm(alg);\n let encodedPayload = \"\", signingPayload = payload, signingInput;\n if (b64) {\n const encodedPayloadCache = input[4];\n encodedPayloadCache ? (encodedPayload = encodedPayloadCache[0] ??= encode2(payload), signingPayload = encodedPayloadCache[1] ??= encode(encodedPayload)) : (encodedPayload = encode2(payload), signingInput = encoder.encode(`${encodedProtectedHeader}.${encodedPayload}`));\n }\n signingInput ??= concat(encode(encodedProtectedHeader), encode(\".\"), signingPayload);\n const signingKey = await rawKey(await prepareKey(entry, key, \"sign\"), entry.subtle, \"sign\");\n entry.minRsaBits && checkModulusLength(entry.alg, signingKey);\n const jws = {\n signature: encode2(new Uint8Array(await crypto.subtle.sign(entry.signing, signingKey, signingInput))),\n payload: encodedPayload\n };\n return protectedHeader && (jws.protected = encodedProtectedHeader), unprotectedHeader && (jws.header = unprotectedHeader), [jws, b64];\n}\nasync function createCompactSignature(payload, protectedHeader, crit, key, rejectUnencoded) {\n const [jws] = await createSignature([payload, protectedHeader, undefined, crit], key, rejectUnencoded);\n return `${jws.protected}.${jws.payload}.${jws.signature}`;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/jwt/sign.js\nvar SignJWT_base = JWTClaimsBuilder;\n\nclass SignJWT extends SignJWT_base {\n #protectedHeader;\n setProtectedHeader(protectedHeader) {\n return assertNotSet(this.#protectedHeader, \"setProtectedHeader\"), this.#protectedHeader = protectedHeader, this;\n }\n async sign(key, options) {\n return createCompactSignature(jwtClaimsSetBytes(this), this.#protectedHeader, options?.crit, key, () => {\n throw new JWTInvalid(\"JWTs MUST NOT use unencoded payload\");\n });\n }\n}\n\n// dimension/paths.ts\nfunction containerName(dimensionName) {\n return `hyb-${dimensionName}`;\n}\nvar WORKSPACE_PATH = \"/workspace\";\nvar RUN_PATH = \"/run/hyperbrane\";\nvar STATE_PATH = \"/var/lib/hyperbrane\";\nvar CA_BUNDLE_PATH = \"/etc/ssl/certs/ca-certificates.crt\";\nvar TRANSPORT_SOCKET = \".transport.sock\";\nfunction dimensionRunDirectory(dataDirectory, dimensionName) {\n return `${dataDirectory}/dimension-run/${dimensionName}`;\n}\nfunction dimensionStateDirectory(dataDirectory, dimensionName) {\n return `${dataDirectory}/dimension-state/${dimensionName}`;\n}\nvar dimensionLayout = {\n containerName,\n workspacePath: () => WORKSPACE_PATH,\n runPath: () => RUN_PATH,\n statePath: () => STATE_PATH,\n caBundlePath: () => CA_BUNDLE_PATH,\n runDirectory: dimensionRunDirectory,\n stateDirectory: dimensionStateDirectory,\n transportSocketPath: (dataDirectory, dimensionName) => `${dimensionRunDirectory(dataDirectory, dimensionName)}/${TRANSPORT_SOCKET}`\n};\n\n// dimension/api.ts\nvar MEMORY_UNITS = {\n \"\": 1,\n K: 1024,\n M: 1024 ** 2,\n G: 1024 ** 3,\n T: 1024 ** 4\n};\nvar DIMENSION_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$/;\nfunction isDimensionName(name) {\n return DIMENSION_NAME.test(name);\n}\n\n// sandbox/providers/local/localService.ts\nimport { spawn } from \"child_process\";\nimport { randomUUID } from \"crypto\";\nimport * as fs from \"fs/promises\";\nimport { connect as netConnect } from \"net\";\nimport { homedir, userInfo } from \"os\";\nimport { basename, dirname, join, resolve } from \"path\";\n\n// sandbox/api.ts\nclass SandboxNotFoundError extends Error {\n name = \"SandboxNotFoundError\";\n}\nclass SandboxConnectionRefusedError extends Error {\n name = \"SandboxConnectionRefusedError\";\n}\n\n// sandbox/storage/descriptors.ts\nfunction makeDescriptor(ctx, id, spec) {\n return {\n id,\n kind: ctx.kind,\n name: spec.name,\n ...ctx.sshHost === undefined ? {} : { sshHost: ctx.sshHost },\n ...ctx.sshUser === undefined ? {} : { sshUser: ctx.sshUser },\n ...ctx.sshPort === undefined ? {} : { sshPort: ctx.sshPort },\n ...spec.dataEndpointOptions === undefined ? {} : { dataEndpointOptions: spec.dataEndpointOptions }\n };\n}\nasync function createDescriptor(ctx, storage, id, spec) {\n const descriptor = makeDescriptor(ctx, id, spec);\n await storage.saveDescriptor(descriptor);\n return [{ ...ctx, id }, descriptor];\n}\nasync function openDescriptor(ctx, storage, id) {\n return await storage.readDescriptor() ?? storage.insertDescriptor(makeDescriptor(ctx, id, { name: id }));\n}\nasync function getDescriptor(storage, where) {\n const descriptor = await storage.readDescriptor();\n if (!descriptor)\n throw new Error(`${where} has no descriptor: opening it saves one`);\n return descriptor;\n}\n\n// sandbox/data-endpoints/dataEndpoints.ts\nimport { randomBytes } from \"crypto\";\n\n// lib/sh.ts\nfunction sh(strings, ...values) {\n if (values.length > 0)\n throw new Error(\"sh`\\u2026` takes no interpolations: pass inputs as environment variables or arguments\");\n return scriptText(strings.raw[0]);\n}\nfunction scriptText(raw) {\n return raw.replace(/\\\\(\\$\\{|`)/g, \"$1\").replace(/^\\n/, \"\");\n}\n\n// sandbox/data-endpoints/dataEndpoints.ts\nvar DEFAULT_TTL_S = 12 * 3600;\nfunction signToken(secret, ttlSeconds) {\n return new SignJWT().setProtectedHeader({ alg: \"HS256\" }).setExpirationTime(`${ttlSeconds}s`).sign(new TextEncoder().encode(secret));\n}\nasync function listeningPorts(sandbox) {\n const listed = await sandbox.runCommand([\"sh\", \"-c\", LISTENING_PORTS]);\n if (listed.exitCode !== 0)\n throw new Error(`listing the ports in use failed: ${listed.stderr.trim()}`);\n return listed.stdout.split(`\n`).filter(Boolean).map(Number);\n}\nasync function withLock(sandbox, body) {\n const lock = `${await sandbox.homeDirectory()}/.local/state/hyperbrane/endpoints.lock`;\n const taken = await sandbox.runCommand([\"sh\", \"-c\", TAKE_LOCK, \"sh\", lock]);\n if (taken.exitCode !== 0)\n throw new Error(`${lock} stayed locked: ${taken.stderr.trim()}`);\n try {\n return await body();\n } finally {\n await sandbox.runCommand([\"rm\", \"-rf\", lock]);\n }\n}\nfunction under(base, endpoint) {\n if (base === undefined || new URL(base).pathname === \"/\")\n return endpoint;\n return { ...endpoint, url: `${base.replace(/\\/+$/, \"\")}/${endpoint.name}` };\n}\nfunction createEndpoints(sandbox, storage, ports) {\n const located = async (endpoints) => {\n const base = (await storage.readDescriptor())?.dataEndpointOptions?.url;\n return endpoints.map((endpoint) => under(base, endpoint));\n };\n async function openEndpoint(name) {\n const [endpoint] = await located([await openStored(name)]);\n return endpoint;\n }\n async function openStored(name) {\n return withLock(sandbox, async () => {\n const existing = await storage.readEndpoint(name);\n if (existing)\n return existing;\n const { from, to } = await ports.ports();\n const used = new Set([...(await storage.listEndpoints()).map((endpoint) => endpoint.port), ...await listeningPorts(sandbox)]);\n let port = from;\n while (port <= to && used.has(port))\n port++;\n if (port > to)\n throw new Error(`no data endpoint ports are left in ${from}-${to}`);\n const endpoint = { name, url: await ports.expose(port), port, secret: randomBytes(32).toString(\"base64url\") };\n await storage.saveEndpoint(endpoint);\n return endpoint;\n });\n }\n async function closeEndpoint(name) {\n await withLock(sandbox, async () => {\n const endpoint = await storage.readEndpoint(name);\n if (!endpoint)\n return;\n await ports.conceal(endpoint.port);\n await storage.removeEndpoint(name);\n });\n }\n async function credentials(name, {\n ttlSeconds = DEFAULT_TTL_S\n } = {}) {\n const endpoint = await storage.readEndpoint(name);\n if (!endpoint)\n throw new SandboxNotFoundError(`there is no data endpoint ${name}`);\n return { token: await signToken(endpoint.secret, ttlSeconds), ...await ports.credentials?.(endpoint, ttlSeconds) };\n }\n return {\n openEndpoint,\n closeEndpoint,\n listEndpoints: async () => located(await storage.listEndpoints()),\n credentials\n };\n}\nvar LISTENING_PORTS = sh`\nawk 'FNR > 1 && $4 == \"0A\" { n = split($2, local, \":\"); print local[n] }' /proc/net/tcp /proc/net/tcp6 2> /dev/null | while read -r hex; do\n printf '%d\\n' \"0x$hex\"\ndone\n`;\nvar TAKE_LOCK = sh`\nmkdir -p \"$(dirname \"$1\")\"\nwaited=0\nuntil mkdir \"$1\" 2> /dev/null; do\n if [ -n \"$(find \"$1\" -maxdepth 0 -mmin +2 2> /dev/null)\" ]; then\n rm -rf \"$1\"\n continue\n fi\n [ \"$waited\" -lt 1200 ] || exit 1\n sleep 0.1\n waited=$((waited + 1))\ndone\n`;\n\n// sandbox/data-endpoints/firewalledPorts.ts\nfunction firewalledPorts(sandbox, storage) {\n const options = async () => (await storage.readDescriptor())?.dataEndpointOptions;\n async function firewall(rule) {\n const result = await sandbox.runCommand([\"sudo\", \"-n\", \"ufw\", ...rule]);\n if (result.exitCode !== 0)\n throw new Error(`ufw ${rule.join(\" \")} failed: ${result.stderr.trim()}`);\n }\n return {\n async ports() {\n const ports = (await options())?.ports;\n if (!ports)\n throw new Error(\"this machine has no data endpoints: it's created with dataEndpointOptions.ports\");\n return ports;\n },\n async expose(port) {\n const found = await options();\n if (found?.needsFirewall)\n await firewall([\"allow\", `${port}/tcp`]);\n const url = new URL(found?.url ?? \"http://127.0.0.1\");\n url.port = String(port);\n return url.toString().replace(/\\/$/, \"\");\n },\n async conceal(port) {\n if ((await options())?.needsFirewall)\n await firewall([\"delete\", \"allow\", `${port}/tcp`]);\n }\n };\n}\n\n// sandbox/lib/httpOverStream.ts\nimport { request as httpRequest } from \"http\";\nimport { Readable } from \"stream\";\nfunction httpOver(url, connection, { method = \"GET\", headers = {}, body, signal, timeout }) {\n return new Promise((resolve, reject) => {\n const target = new URL(url);\n const request = httpRequest({ createConnection: connection, path: `${target.pathname}${target.search}`, method, headers: { host: target.host, ...headers }, signal }, (response) => {\n const responseHeaders = new Headers;\n for (let i = 0;i + 1 < response.rawHeaders.length; i += 2)\n responseHeaders.append(response.rawHeaders[i], response.rawHeaders[i + 1]);\n const status = response.statusCode ?? 502;\n const nullBody = status === 204 || status === 304 || method === \"HEAD\";\n if (nullBody)\n response.resume();\n resolve(new Response(nullBody ? null : Readable.toWeb(response), { status, headers: responseHeaders }));\n });\n request.once(\"error\", reject);\n if (typeof timeout === \"number\")\n request.setTimeout(timeout, () => request.destroy(new Error(`fetch ${url}: no data for ${timeout}ms`)));\n request.end(body === undefined ? undefined : typeof body === \"string\" ? body : Buffer.from(body));\n });\n}\n\n// lib/unixSocket.ts\nimport { Socket } from \"net\";\nfunction connectUnixSocket(path, label = path) {\n return new Promise((resolve, reject) => {\n const socket = new Socket;\n let markClosed;\n const closed = new Promise((done) => markClosed = done);\n const listeners = [];\n socket.on(\"data\", (chunk) => {\n for (const listener of listeners)\n listener(new Uint8Array(chunk));\n });\n socket.once(\"close\", () => markClosed());\n socket.once(\"end\", () => markClosed());\n socket.on(\"error\", (error) => {\n markClosed();\n const message = `connecting to ${label} failed: ${error.message}`;\n reject(error.code === \"ENOENT\" || error.code === \"ECONNREFUSED\" ? new SandboxConnectionRefusedError(message) : new Error(message));\n });\n socket.once(\"connect\", () => resolve({\n write: (data) => void socket.write(data),\n onData: (listener) => void listeners.push(listener),\n closed,\n close: () => socket.end()\n }));\n socket.connect({ path });\n });\n}\n\n// lib/jsonDirectory.ts\nvar EXTENSION = \".json\";\nvar NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$/;\n\nclass InvalidNameError extends Error {\n name = \"InvalidNameError\";\n}\n\nclass NotARecordError extends Error {\n name = \"NotARecordError\";\n}\nfunction parseRecord(path, text) {\n let record;\n try {\n record = JSON.parse(text);\n } catch (error) {\n throw new NotARecordError(`${path} isn't JSON: ${error.message}`);\n }\n if (record === null || typeof record !== \"object\" || Array.isArray(record))\n throw new NotARecordError(`${path} isn't a JSON record (an object)`);\n return record;\n}\nfunction checkName(name) {\n if (!NAME_PATTERN.test(name)) {\n throw new InvalidNameError(`Invalid name \"${name}\": use letters, digits, \"_\", \".\" and \"-\", up to 63 characters`);\n }\n return name;\n}\nfunction createJsonDirectory(sandbox, dir) {\n async function pathFor(name) {\n return `${await dir()}/${checkName(name)}${EXTENSION}`;\n }\n function serialize(record) {\n return JSON.stringify(record, null, 2) + `\n`;\n }\n async function readPath(path) {\n try {\n return parseRecord(path, new TextDecoder().decode(await sandbox.readFile(path)));\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return null;\n throw error;\n }\n }\n async function read(name) {\n return readPath(await pathFor(name));\n }\n async function insert(name, record) {\n const path = await pathFor(name);\n if (await sandbox.writeFileExclusive(path, serialize(record)))\n return record;\n return await readPath(path) ?? insert(name, record);\n }\n async function save(name, record) {\n await sandbox.writeFile(await pathFor(name), serialize(record));\n }\n async function remove(name) {\n await sandbox.removeFile(await pathFor(name));\n }\n async function entries() {\n const path = await dir();\n const all = await sandbox.runCommand([\"sh\", \"-c\", LIST_SCRIPT, \"sh\", path], { env: { MISSING: String(MISSING), EXTENSION } }).catch(() => null);\n if (all?.exitCode === MISSING)\n return [];\n if (all?.exitCode === 0) {\n return all.stdout.split(RECORD_END).filter((text) => text.trim()).flatMap((text) => {\n const newline = text.indexOf(`\n`);\n const file = text.slice(0, newline);\n try {\n return [[file.slice(0, -EXTENSION.length), parseRecord(`${path}/${file}`, text.slice(newline + 1))]];\n } catch {\n return [];\n }\n });\n }\n let files;\n try {\n files = await sandbox.listDirectory(path);\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return [];\n throw error;\n }\n const records = await Promise.all(files.filter((file) => file.endsWith(EXTENSION)).map(async (file) => [file.slice(0, -EXTENSION.length), await readPath(`${path}/${file}`).catch(skipNotARecord)]));\n return records.filter((entry) => entry[1] !== null);\n }\n async function list() {\n return (await entries()).map(([, record]) => record);\n }\n return { read, insert, save, remove, list, entries };\n}\nvar MISSING = 44;\nvar RECORD_END = `\n\\x1E\n`;\nvar LIST_SCRIPT = sh`\ncd \"$1\" 2>/dev/null || exit \"$MISSING\"\nfor file in *\"$EXTENSION\"; do\n [ -f \"$file\" ] || continue\n printf '%s\\n' \"$file\"\n cat \"$file\"\n printf '\\n\\036\\n'\ndone\n`;\nfunction skipNotARecord(error) {\n if (error instanceof NotARecordError)\n return null;\n throw error;\n}\n\n// sandbox/storage/sandboxStorage.ts\nvar DESCRIPTOR = \"sandbox\";\nfunction createSandboxStorage(sandbox) {\n const stateDirectory = async () => `${await sandbox.homeDirectory()}/.local/state/hyperbrane`;\n const endpointsDirectory = async () => `${await stateDirectory()}/endpoints`;\n const descriptors = createJsonDirectory(sandbox, stateDirectory);\n const endpoints = createJsonDirectory(sandbox, endpointsDirectory);\n async function readDescriptor() {\n return descriptors.read(DESCRIPTOR);\n }\n async function insertDescriptor(descriptor) {\n return descriptors.insert(DESCRIPTOR, descriptor);\n }\n async function saveDescriptor(descriptor) {\n await descriptors.save(DESCRIPTOR, descriptor);\n }\n async function readEndpoint(name) {\n return endpoints.read(name);\n }\n async function saveEndpoint(endpoint) {\n const directory = await endpointsDirectory();\n const made = await sandbox.runCommand([\"sh\", \"-c\", 'mkdir -p \"$1\" && chmod 700 \"$1\"', \"sh\", directory]);\n if (made.exitCode !== 0)\n throw new Error(`making ${directory} private failed: ${made.stderr.trim()}`);\n await endpoints.save(endpoint.name, endpoint);\n }\n async function removeEndpoint(name) {\n await endpoints.remove(name);\n }\n async function listEndpoints() {\n return endpoints.list();\n }\n return {\n readDescriptor,\n insertDescriptor,\n saveDescriptor,\n readEndpoint,\n saveEndpoint,\n removeEndpoint,\n listEndpoints\n };\n}\n\n// sandbox/providers/local/localService.ts\nfunction hostPath(path) {\n return resolve(\"/\", path);\n}\nasync function notFoundAs(path, operation) {\n try {\n return await operation;\n } catch (error) {\n if (error.code === \"ENOENT\")\n throw new SandboxNotFoundError(`Not found: \"${path}\"`);\n throw error;\n }\n}\nasync function writeTemp(full, data, mode = 438) {\n await fs.mkdir(dirname(full), { recursive: true });\n const tmp = join(dirname(full), `.${basename(full)}.${randomUUID()}.tmp`);\n await fs.writeFile(tmp, data, { mode });\n return tmp;\n}\nasync function readFile2(path) {\n return new Uint8Array(await notFoundAs(path, fs.readFile(hostPath(path))));\n}\nasync function readFileRange(path, start, end) {\n const file = await notFoundAs(path, fs.open(hostPath(path), \"r\"));\n try {\n const buffer = new Uint8Array(Math.max(0, end - start));\n let filled = 0;\n while (filled < buffer.length) {\n const { bytesRead } = await file.read(buffer, filled, buffer.length - filled, start + filled);\n if (bytesRead === 0)\n break;\n filled += bytesRead;\n }\n return buffer.subarray(0, filled);\n } finally {\n await file.close();\n }\n}\nasync function writeFile2(path, data) {\n const full = hostPath(path);\n await fs.rename(await writeTemp(full, data), full);\n}\nasync function writeFileExclusive(path, data) {\n const full = hostPath(path);\n const tmp = await writeTemp(full, data);\n try {\n await fs.link(tmp, full);\n return true;\n } catch (error) {\n if (error.code === \"EEXIST\")\n return false;\n throw error;\n } finally {\n await fs.unlink(tmp);\n }\n}\nasync function removeFile(path) {\n await fs.rm(hostPath(path), { force: true });\n}\nasync function createDirectory(path) {\n await fs.mkdir(hostPath(path), { recursive: true });\n}\nasync function removeDirectory(path) {\n await fs.rm(hostPath(path), { recursive: true, force: true });\n}\nasync function listDirectory(path) {\n return notFoundAs(path, fs.readdir(hostPath(path)));\n}\nasync function stat2(path) {\n const stats = await notFoundAs(path, fs.stat(hostPath(path)));\n const type = stats.isFile() ? \"file\" : stats.isDirectory() ? \"directory\" : \"other\";\n return { type, uid: stats.uid, gid: stats.gid, size: stats.size };\n}\nasync function homeDirectory() {\n return process.env.HOME || homedir();\n}\nfunction runCommand(cmd, { cwd, env = {}, stdin } = {}) {\n return new Promise((resolve, reject) => {\n const [command, ...args] = cmd;\n if (!command)\n return reject(new Error(\"no command to run\"));\n const child = spawn(command, args, {\n cwd: hostPath(cwd ?? \".\"),\n env: { ...process.env, ...env },\n stdio: [stdin === undefined ? \"ignore\" : \"pipe\", \"pipe\", \"pipe\"]\n });\n const stdout = [];\n const stderr = [];\n child.stdout.on(\"data\", (chunk) => stdout.push(chunk));\n child.stderr.on(\"data\", (chunk) => stderr.push(chunk));\n child.once(\"error\", reject);\n child.once(\"close\", (code) => resolve({ exitCode: code ?? -1, stdout: Buffer.concat(stdout).toString(), stderr: Buffer.concat(stderr).toString() }));\n if (stdin !== undefined)\n child.stdin.end(stdin);\n });\n}\nasync function state() {\n return \"running\";\n}\nasync function start() {}\nasync function destroy() {}\nfunction hasExpiration() {\n return false;\n}\nasync function getExpiration() {\n return null;\n}\nasync function setExpiration() {\n return null;\n}\nfunction hasShell(ctx) {\n return ctx.sshKey !== undefined;\n}\nasync function sshEndpoint(ctx) {\n if (ctx.sshKey === undefined)\n throw new Error(\"this machine has no shell: its context has no SSH key (sshKey)\");\n return {\n host: ctx.sshHost ?? \"127.0.0.1\",\n port: ctx.sshPort ?? 22,\n user: ctx.sshUser ?? userInfo().username,\n privateKey: ctx.sshKey\n };\n}\nasync function fetch(url, { unix, ...init } = {}) {\n if (unix === undefined || \"Bun\" in globalThis)\n return globalThis.fetch(url, { ...init, unix: unix === undefined ? undefined : hostPath(unix) });\n return httpOver(url, () => netConnect(hostPath(unix)), init);\n}\nfunction connect(path) {\n return connectUnixSocket(hostPath(path), path);\n}\nfunction createLocalService(ctx) {\n const fileOps = {\n readFile: readFile2,\n readFileRange,\n writeFile: writeFile2,\n writeFileExclusive,\n removeFile,\n createDirectory,\n removeDirectory,\n listDirectory,\n stat: stat2,\n homeDirectory\n };\n const cmdOps = {\n runCommand\n };\n const netOps = {\n fetch,\n connect\n };\n const machine = { ...fileOps, ...cmdOps, ...netOps };\n const storage = createSandboxStorage(machine);\n const lifecycleOps = {\n create: (spec) => createDescriptor(ctx, storage, ctx.id ?? spec.name, spec),\n open: () => openDescriptor(ctx, storage, ctx.id ?? \"this machine\"),\n get: () => getDescriptor(storage, \"this machine\"),\n state,\n start,\n destroy\n };\n const dataOps = createEndpoints(machine, storage, firewalledPorts(machine, storage));\n const expirationOps = {\n hasExpiration,\n getExpiration,\n setExpiration\n };\n const shellOps = {\n hasShell: () => hasShell(ctx),\n sshEndpoint: () => sshEndpoint(ctx)\n };\n return {\n ...fileOps,\n ...lifecycleOps,\n ...cmdOps,\n ...dataOps,\n ...expirationOps,\n ...shellOps,\n ...netOps\n };\n}\n\n// portal-gateway/transport-worker.ts\nvar TRANSPORT_PROTOCOL = 1;\nvar TOKEN_PARAM = \"hyb_token\";\nvar HEARTBEAT_MS = 15000;\nvar connections = new Map;\nasync function wired(dimensionName) {\n const files = await readdir2(join2(data, \"portals\")).catch(() => []);\n for (const file of files.filter((name) => name.endsWith(\".json\"))) {\n const portal = await readFile3(join2(data, \"portals\", file), \"utf8\").then((text) => JSON.parse(text), () => null);\n if (portal?.gatewayName === gateway && portal.dimensionName === dimensionName && portal.state === \"ready\")\n return true;\n }\n return false;\n}\nasync function authorized(url) {\n const token = url.searchParams.get(TOKEN_PARAM);\n if (!token)\n return false;\n try {\n await jwtVerify(token, key, { algorithms: [\"HS256\"], requiredClaims: [\"exp\"] });\n return true;\n } catch {\n return false;\n }\n}\nfunction cors(request, response) {\n const origin = request.headers.get(\"origin\");\n if (!origin)\n return response;\n response.headers.set(\"access-control-allow-origin\", origin);\n response.headers.append(\"vary\", \"Origin\");\n return response;\n}\nasync function preflight(request) {\n const headers = new Headers({\n \"access-control-allow-methods\": \"GET, POST, OPTIONS\",\n \"access-control-max-age\": \"600\"\n });\n const asked = request.headers.get(\"access-control-request-headers\");\n if (asked)\n headers.set(\"access-control-allow-headers\", asked);\n return cors(request, new Response(null, { status: 204, headers }));\n}\nasync function answer(handler, request, server) {\n server.timeout(request, 0);\n if (!await authorized(new URL(request.url)))\n return Response.json({ error: \"a valid token is required\" }, { status: 401 });\n try {\n return await handler(request, server);\n } catch (error) {\n const { name, message } = error instanceof Error ? error : new Error(String(error));\n return Response.json({ name, message }, { status: 500 });\n }\n}\nfunction guarded(handler) {\n return async (request, server) => {\n const response = await answer(handler, request, server);\n return response && cors(request, response);\n };\n}\nfunction relay(connection, send, end) {\n connection.onData(send);\n connection.closed.then(end);\n}\nvar encoder2 = new TextEncoder;\nfunction multipartFrames() {\n const boundary = randomBytes2(24).toString(\"hex\");\n const part = (type, data) => Buffer.concat([encoder2.encode(`\\r\ncontent-type: ${type}\\r\n\\r\n`), data, encoder2.encode(`\\r\n--${boundary}`)]);\n return {\n contentType: `multipart/mixed; boundary=\"${boundary}\"`,\n open: (id) => Buffer.concat([encoder2.encode(`--${boundary}`), part(\"application/json\", encoder2.encode(JSON.stringify({ connection: id })))]),\n data: (chunk) => part(\"application/octet-stream\", chunk),\n heartbeat: part(\"application/octet-stream\", new Uint8Array),\n end: encoder2.encode(`--\\r\n`)\n };\n}\nfunction lineFrames() {\n const decoder = new TextDecoder;\n let pending = \"\";\n return {\n contentType: \"text/event-stream\",\n open: (id) => encoder2.encode(`event: open\ndata: ${JSON.stringify({ connection: id })}\n\n`),\n data(chunk) {\n const lines = (pending + decoder.decode(chunk, { stream: true })).split(`\n`);\n pending = lines.pop();\n return encoder2.encode(lines.map((line) => `data: ${line.replace(/\\r$/, \"\")}\n\n`).join(\"\"));\n },\n heartbeat: encoder2.encode(`: ping\n\n`),\n end: encoder2.encode(`event: close\ndata: {}\n\n`)\n };\n}\nfunction stream(connection, frames) {\n const id = randomUUID2();\n let heartbeat;\n let open = true;\n const finish = () => {\n open = false;\n clearInterval(heartbeat);\n connections.delete(id);\n connection.close();\n };\n connections.set(id, connection);\n const body = new ReadableStream({\n start(controller) {\n const send = (bytes) => {\n if (open && bytes.length > 0)\n controller.enqueue(bytes);\n };\n send(frames.open(id));\n heartbeat = setInterval(() => send(frames.heartbeat), HEARTBEAT_MS);\n relay(connection, (data) => send(frames.data(data)), () => {\n if (!open)\n return;\n send(frames.end);\n finish();\n controller.close();\n });\n },\n cancel: finish\n });\n return new Response(body, { headers: { \"content-type\": frames.contentType, \"cache-control\": \"no-cache, no-transform\", \"x-accel-buffering\": \"no\" } });\n}\nasync function write(request) {\n const connection = connections.get(request.params.id);\n if (!connection)\n return Response.json({ error: \"no such connection\" }, { status: 404 });\n connection.write(new Uint8Array(await request.arrayBuffer()));\n return new Response(null, { status: 204 });\n}\nasync function connect2(request, server) {\n const params = new URL(request.url).searchParams;\n const dimensionName = params.get(\"dimension\") ?? \"\";\n const agentName = params.get(\"agent\");\n if (!isDimensionName(dimensionName) || agentName !== null && !isDimensionName(agentName)) {\n return Response.json({ error: \"dimension and agent are names\" }, { status: 400 });\n }\n if (!await wired(dimensionName))\n return Response.json({ error: `dimension ${dimensionName} has no portal to gateway ${gateway}` }, { status: 403 });\n const socket = agentName === null ? dimensionLayout.transportSocketPath(data, dimensionName) : join2(dimensionLayout.runDirectory(data, dimensionName), `${agentName}.sock`);\n const connection = await machine.connect(socket).catch((error) => error);\n if (connection instanceof Error)\n return Response.json({ error: connection.message }, { status: 502 });\n if (request.headers.get(\"upgrade\")?.toLowerCase() !== \"websocket\")\n return stream(connection, agentName === null ? multipartFrames() : lineFrames());\n if (server.upgrade(request, { data: connection }))\n return;\n connection.close();\n return Response.json({ error: \"the websocket upgrade failed\" }, { status: 400 });\n}\nfunction serve(port) {\n return Bun.serve({\n port,\n hostname: \"0.0.0.0\",\n routes: {\n \"/connect\": {\n GET: guarded(connect2),\n OPTIONS: preflight\n },\n \"/connect/:id\": {\n POST: guarded(write),\n OPTIONS: preflight\n }\n },\n websocket: {\n open(socket) {\n relay(socket.data, (data) => socket.send(data), () => socket.close());\n },\n message(socket, message) {\n socket.data.write(typeof message === \"string\" ? Buffer.from(message) : message);\n },\n close(socket) {\n socket.data.close();\n }\n }\n });\n}\nfunction required(name) {\n const value = process.env[name];\n if (!value)\n throw new Error(`${name} is not set`);\n return value;\n}\nprocess.umask(63);\nvar dir = required(\"HYB_TRANSPORT_DIR\");\nvar build = required(\"HYB_TRANSPORT_BUILD\");\nvar port = Number(required(\"HYB_TRANSPORT_PORT\"));\nvar gateway = required(\"HYB_GATEWAY\");\nvar data = required(\"HYB_DATA\");\nvar key = new TextEncoder().encode(await readFile3(join2(dir, \"secret\"), \"utf8\"));\nvar machine = createLocalService({ kind: \"local\" });\nvar server = serve(port);\nfor (const signal of [\"SIGTERM\", \"SIGINT\"]) {\n process.once(signal, () => {\n server.stop(true);\n process.exit(0);\n });\n}\nawait writeFile3(join2(dir, \"ready\"), `${process.pid} ${build} ${TRANSPORT_PROTOCOL}\n`);\n";
@@ -0,0 +1,9 @@
1
+ import type { Sandbox, SandboxDataEndpoint } from "./sandbox/api.ts";
2
+ export type Restored = {
3
+ readonly gateways: string[];
4
+ readonly dimensions: string[];
5
+ readonly agents: string[];
6
+ };
7
+ export declare function restore(host: Sandbox & SandboxDataEndpoint, options?: {
8
+ dataDirectory?: string;
9
+ }): Promise<Restored>;
@@ -0,0 +1,10 @@
1
+ import { createAgentClient } from "./agent/agentClient.js";
2
+ import { createDimensionClient } from "./dimension/client.js";
3
+ import { createGatewayClient } from "./portal-gateway/gatewayClient.js";
4
+ import { createPortalClient } from "./portal-gateway/portalClient.js";
5
+ export async function restore(host, options = {}) {
6
+ const [gateways, dimensions] = await Promise.all([createGatewayClient(host, options).restoreGateways(), createDimensionClient(host, options).restoreDimensions()]);
7
+ await createPortalClient(host, options).restorePortals();
8
+ const agents = await createAgentClient(host, options).restoreAgents();
9
+ return { gateways, dimensions, agents };
10
+ }
@@ -130,6 +130,11 @@ export type SandboxSpec = {
130
130
  name: string;
131
131
  dataEndpointOptions?: SandboxDataEndpointOptions;
132
132
  };
133
+ export type SandboxListing = {
134
+ readonly id: string;
135
+ readonly name: string;
136
+ readonly state: "running" | "stopped";
137
+ };
133
138
  export type SandboxDescriptor = {
134
139
  id: string;
135
140
  kind: SandboxAuthContext["kind"];
@@ -1,2 +1,3 @@
1
- import type { SandboxAuthContext, SandboxClient } from "./api.ts";
1
+ import type { SandboxAuthContext, SandboxClient, SandboxListing } from "./api.ts";
2
2
  export declare function createSandboxClient(ctx: SandboxAuthContext): SandboxClient;
3
+ export declare function listSandboxes(ctx: SandboxAuthContext): Promise<SandboxListing[]>;
@@ -1,4 +1,7 @@
1
- import { createSandboxService } from "./service.js";
1
+ import { createSandboxService, listSandboxes as listServiceSandboxes } from "./service.js";
2
2
  export function createSandboxClient(ctx) {
3
3
  return createSandboxService(ctx);
4
4
  }
5
+ export function listSandboxes(ctx) {
6
+ return listServiceSandboxes(ctx);
7
+ }
@@ -0,0 +1,6 @@
1
+ import { type ParsedKey } from "ssh2";
2
+ export declare function parseSshKey(privateKey: string, whose: string): {
3
+ privateKey: string;
4
+ key: ParsedKey;
5
+ };
6
+ export declare function sshPrivateKey(privateKey: string, whose: string): string;