@cloudflare/sandbox 0.13.0-next.649.1 → 0.13.0-next.651.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.
- package/dist/bridge/index.d.ts.map +1 -1
- package/dist/bridge/index.js +3 -3
- package/dist/{contexts-DPFhc2nR.d.ts → contexts-BS0Bs6IU.d.ts} +1 -1
- package/dist/{contexts-DPFhc2nR.d.ts.map → contexts-BS0Bs6IU.d.ts.map} +1 -1
- package/dist/{dist-mAH_7Ui7.js → dist-DF8sudAg.js} +19 -2
- package/dist/dist-DF8sudAg.js.map +1 -0
- package/dist/{errors-CpoDEfUZ.js → errors-BG6NZiPD.js} +1 -1
- package/dist/{errors-CpoDEfUZ.js.map → errors-BG6NZiPD.js.map} +1 -1
- package/dist/extensions/index.d.ts +74 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +153 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/openai/index.d.ts +2 -2
- package/dist/openai/index.d.ts.map +1 -1
- package/dist/openai/index.js +1 -1
- package/dist/opencode/index.d.ts +3 -2
- package/dist/opencode/index.d.ts.map +1 -1
- package/dist/opencode/index.js +2 -2
- package/dist/{sandbox-44kEJjAc.d.ts → rpc-types-PBUY-xXM.d.ts} +97 -1070
- package/dist/rpc-types-PBUY-xXM.d.ts.map +1 -0
- package/dist/{sandbox-B-FLGbkP.js → sandbox-BgwMBQ7S.js} +7 -4
- package/dist/sandbox-BgwMBQ7S.js.map +1 -0
- package/dist/sandbox-D_MMqExx.d.ts +1077 -0
- package/dist/sandbox-D_MMqExx.d.ts.map +1 -0
- package/dist/sidecar/index.d.ts +77 -0
- package/dist/sidecar/index.d.ts.map +1 -0
- package/dist/sidecar/index.js +201 -0
- package/dist/sidecar/index.js.map +1 -0
- package/package.json +11 -1
- package/dist/dist-mAH_7Ui7.js.map +0 -1
- package/dist/sandbox-44kEJjAc.d.ts.map +0 -1
- package/dist/sandbox-B-FLGbkP.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox-D_MMqExx.d.ts","names":["LogLevel","LogComponent","LogContext","Logger","Partial","Error"],"sources":["../../shared/dist/logger/types.d.ts","../src/container-control/connection.ts","../src/container-control/client.ts","../src/tunnels/tunnels-handler.ts","../src/sandbox.ts"],"sourcesContent":["/**\n * Logger types for Cloudflare Sandbox SDK\n *\n * Provides structured, trace-aware logging across Worker, Durable Object, and Container.\n */\n/**\n * Log levels (from most to least verbose)\n */\nexport declare enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3\n}\nexport type LogComponent = 'container' | 'sandbox-do' | 'executor';\n/**\n * Context metadata included in every log entry\n */\nexport interface LogContext {\n /**\n * Unique trace ID for request correlation across distributed components\n * Format: \"tr_\" + 16 hex chars (e.g., \"tr_7f3a9b2c4e5d6f1a\")\n */\n traceId: string;\n /**\n * Component that generated the log\n */\n component: LogComponent;\n /**\n * Sandbox identifier (which sandbox instance)\n */\n sandboxId?: string;\n /**\n * Session identifier (which session within sandbox)\n */\n sessionId?: string;\n /**\n * Process identifier (which background process)\n */\n processId?: string;\n /**\n * Command identifier (which command execution)\n */\n commandId?: string;\n /**\n * Duration in milliseconds\n */\n durationMs?: number;\n /**\n * Service version (from SANDBOX_VERSION env var)\n */\n serviceVersion?: string;\n /**\n * Instance identifier (hostname or container ID)\n */\n instanceId?: string;\n /**\n * Extensible for additional metadata\n */\n [key: string]: unknown;\n}\n/**\n * Logger interface for structured logging\n *\n * All methods accept optional context that gets merged with the logger's base context.\n */\nexport interface Logger {\n /**\n * Log debug-level message (most verbose, typically disabled in production)\n *\n * @param message Human-readable message\n * @param context Optional additional context\n */\n debug(message: string, context?: Partial<LogContext>): void;\n /**\n * Log info-level message (normal operational events)\n *\n * @param message Human-readable message\n * @param context Optional additional context\n */\n info(message: string, context?: Partial<LogContext>): void;\n /**\n * Log warning-level message (recoverable issues, degraded state)\n *\n * @param message Human-readable message\n * @param context Optional additional context\n */\n warn(message: string, context?: Partial<LogContext>): void;\n /**\n * Log error-level message (failures, exceptions)\n *\n * @param message Human-readable message\n * @param error Optional Error object to include\n * @param context Optional additional context\n */\n error(message: string, error?: Error, context?: Partial<LogContext>): void;\n /**\n * Create a child logger with additional context\n *\n * The child logger inherits all context from the parent and adds new context.\n * This is useful for adding request-specific context without passing through parameters.\n *\n * @param context Additional context to merge\n * @returns New logger instance with merged context\n *\n * @example\n * const logger = createLogger({ component: 'sandbox-do', traceId: 'tr_abc123' });\n * const execLogger = logger.child({ commandId: 'cmd-456' });\n * execLogger.info('Command started'); // Includes all context: component, traceId, commandId\n */\n child(context: Partial<LogContext>): Logger;\n}\n//# sourceMappingURL=types.d.ts.map"],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { RpcTarget, RpcTransport } from "capnweb";
|
|
2
|
+
import net, { Socket } from "node:net";
|
|
3
|
+
|
|
4
|
+
//#region src/sidecar/socket-transport.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* capnweb `RpcTransport` over a unix-domain socket, mirroring the host-side
|
|
8
|
+
* transport in `@repo/sandbox-container`. The wire format is a 4-byte
|
|
9
|
+
* big-endian length prefix followed by UTF-8 JSON, identical on both sides.
|
|
10
|
+
*
|
|
11
|
+
* Exported from `@cloudflare/sandbox/sidecar` so a sidecar process can run
|
|
12
|
+
* its own capnweb session without copy-pasting the framing logic.
|
|
13
|
+
*/
|
|
14
|
+
declare class SocketTransport implements RpcTransport {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(socket: Socket);
|
|
17
|
+
send(message: string): Promise<void>;
|
|
18
|
+
receive(): Promise<string>;
|
|
19
|
+
abort(reason: unknown): void;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/sidecar/index.d.ts
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Base class for a sidecar's main capnweb RpcTarget. Subclasses add their
|
|
26
|
+
* own methods \u2014 these become the typed remote main visible to the SDK.
|
|
27
|
+
*
|
|
28
|
+
* `__ping__` is reserved for host health probes; do not override it.
|
|
29
|
+
*/
|
|
30
|
+
declare abstract class SandboxSidecar extends RpcTarget {
|
|
31
|
+
/**
|
|
32
|
+
* Health probe \u2014 the container's `ExtensionHost.health()` calls this and
|
|
33
|
+
* expects the literal string `'pong'`. The host bounds the call with its
|
|
34
|
+
* own timeout so a misbehaving sidecar cannot hang the probe.
|
|
35
|
+
*/
|
|
36
|
+
__ping__(): 'pong';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Options for {@link serveSandboxSidecar}. Mostly defaults; the socket path
|
|
40
|
+
* is always taken from `process.env.EXT_SOCKET` (set by the container's
|
|
41
|
+
* `ExtensionHost`) so authors do not have to plumb it themselves.
|
|
42
|
+
*/
|
|
43
|
+
interface ServeSidecarOptions {
|
|
44
|
+
/**
|
|
45
|
+
* Override the diagnostic log line printed to stdout after the sidecar opens
|
|
46
|
+
* its socket. Readiness is determined by the host's successful capnweb
|
|
47
|
+
* connection, not by this line. Defaults to `sandbox sidecar listening`.
|
|
48
|
+
*/
|
|
49
|
+
readyMessage?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Open the sidecar's unix socket and serve capnweb sessions backed by
|
|
53
|
+
* `target`. Each inbound connection gets its own `RpcSession`; the `target`
|
|
54
|
+
* instance is shared across all sessions, so authors can keep per-sidecar
|
|
55
|
+
* state on it without sync.
|
|
56
|
+
*
|
|
57
|
+
* Idiomatic usage:
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* import {
|
|
61
|
+
* SandboxSidecar,
|
|
62
|
+
* serveSandboxSidecar
|
|
63
|
+
* } from '@cloudflare/sandbox/sidecar';
|
|
64
|
+
*
|
|
65
|
+
* class MyApi extends SandboxSidecar {
|
|
66
|
+
* async isOdd(n: number): Promise<boolean> {
|
|
67
|
+
* return n % 2 === 1;
|
|
68
|
+
* }
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* serveSandboxSidecar(new MyApi());
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
declare function serveSandboxSidecar(target: SandboxSidecar, options?: ServeSidecarOptions): net.Server;
|
|
75
|
+
//#endregion
|
|
76
|
+
export { SandboxSidecar, ServeSidecarOptions, SocketTransport, serveSandboxSidecar };
|
|
77
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/sidecar/socket-transport.ts","../../src/sidecar/index.ts"],"sourcesContent":[],"mappings":";;;;;;;AAgBA;;;;;;cAAa,eAAA,YAA2B;;sBAWlB;ECEA,IAAA,CAAA,OAAA,EAAA,MAAe,CAAA,EDKN,OCLc,CAAA,IAAA,CAAA;EAgB5B,OAAA,CAAA,CAAA,EDgCJ,OChCI,CAAA,MAAmB,CAAA;EAgCpB,KAAA,CAAA,MAAA,EAAA,OAAA,CAAmB,EAAA,IAAA;;;;;;;;;;;uBAhDb,cAAA,SAAuB,SAAA;;;;;;;;;;;;;UAgB5B,mBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCD,mBAAA,SACN,0BACC,sBACR,GAAA,CAAI"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { RpcSession, RpcTarget } from "capnweb";
|
|
2
|
+
import net from "node:net";
|
|
3
|
+
|
|
4
|
+
//#region src/sidecar/socket-transport.ts
|
|
5
|
+
const HEADER_BYTES = 4;
|
|
6
|
+
const MAX_FRAME_BYTES = 16 * 1024 * 1024;
|
|
7
|
+
const MAX_QUEUED_MESSAGES = 128;
|
|
8
|
+
const RESUME_QUEUED_MESSAGES = 64;
|
|
9
|
+
/**
|
|
10
|
+
* capnweb `RpcTransport` over a unix-domain socket, mirroring the host-side
|
|
11
|
+
* transport in `@repo/sandbox-container`. The wire format is a 4-byte
|
|
12
|
+
* big-endian length prefix followed by UTF-8 JSON, identical on both sides.
|
|
13
|
+
*
|
|
14
|
+
* Exported from `@cloudflare/sandbox/sidecar` so a sidecar process can run
|
|
15
|
+
* its own capnweb session without copy-pasting the framing logic.
|
|
16
|
+
*/
|
|
17
|
+
var SocketTransport = class {
|
|
18
|
+
#socket;
|
|
19
|
+
#buffer = Buffer.alloc(0);
|
|
20
|
+
#queue = [];
|
|
21
|
+
#waiters = [];
|
|
22
|
+
#error = null;
|
|
23
|
+
#paused = false;
|
|
24
|
+
constructor(socket) {
|
|
25
|
+
this.#socket = socket;
|
|
26
|
+
socket.on("data", (chunk) => this.#onData(chunk));
|
|
27
|
+
socket.on("error", (err) => this.#fail(err));
|
|
28
|
+
socket.on("close", () => this.#fail(/* @__PURE__ */ new Error("Extension socket closed")));
|
|
29
|
+
}
|
|
30
|
+
async send(message) {
|
|
31
|
+
if (this.#error) throw this.#error;
|
|
32
|
+
const body = Buffer.from(message, "utf8");
|
|
33
|
+
if (body.length > MAX_FRAME_BYTES) throw new Error(`Extension capnweb message exceeds maximum size (${body.length} > ${MAX_FRAME_BYTES} bytes)`);
|
|
34
|
+
const header = Buffer.allocUnsafe(HEADER_BYTES);
|
|
35
|
+
header.writeUInt32BE(body.length, 0);
|
|
36
|
+
if (!this.#socket.write(Buffer.concat([header, body]))) await this.#awaitDrain();
|
|
37
|
+
}
|
|
38
|
+
#awaitDrain() {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const onDrain = () => {
|
|
41
|
+
cleanup();
|
|
42
|
+
resolve();
|
|
43
|
+
};
|
|
44
|
+
const onError = (err) => {
|
|
45
|
+
cleanup();
|
|
46
|
+
reject(err);
|
|
47
|
+
};
|
|
48
|
+
const onClose = () => {
|
|
49
|
+
cleanup();
|
|
50
|
+
reject(this.#error ?? /* @__PURE__ */ new Error("Extension socket closed"));
|
|
51
|
+
};
|
|
52
|
+
const cleanup = () => {
|
|
53
|
+
this.#socket.off("drain", onDrain);
|
|
54
|
+
this.#socket.off("error", onError);
|
|
55
|
+
this.#socket.off("close", onClose);
|
|
56
|
+
};
|
|
57
|
+
if (this.#error) {
|
|
58
|
+
reject(this.#error);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.#socket.once("drain", onDrain);
|
|
62
|
+
this.#socket.once("error", onError);
|
|
63
|
+
this.#socket.once("close", onClose);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
receive() {
|
|
67
|
+
if (this.#queue.length > 0) {
|
|
68
|
+
const message = this.#queue.shift();
|
|
69
|
+
this.#updateReadFlow();
|
|
70
|
+
return Promise.resolve(message);
|
|
71
|
+
}
|
|
72
|
+
if (this.#error) return Promise.reject(this.#error);
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
this.#waiters.push({
|
|
75
|
+
resolve,
|
|
76
|
+
reject
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
abort(reason) {
|
|
81
|
+
this.#fail(reason instanceof Error ? reason : new Error(String(reason)));
|
|
82
|
+
this.#socket.destroy();
|
|
83
|
+
}
|
|
84
|
+
#onData(chunk) {
|
|
85
|
+
this.#buffer = this.#buffer.length === 0 ? chunk : Buffer.concat([this.#buffer, chunk]);
|
|
86
|
+
while (this.#buffer.length >= HEADER_BYTES) {
|
|
87
|
+
const length = this.#buffer.readUInt32BE(0);
|
|
88
|
+
if (length > MAX_FRAME_BYTES) {
|
|
89
|
+
this.#fail(/* @__PURE__ */ new Error(`Extension capnweb message exceeds maximum size (${length} > ${MAX_FRAME_BYTES} bytes)`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (this.#buffer.length < HEADER_BYTES + length) break;
|
|
93
|
+
const message = this.#buffer.subarray(HEADER_BYTES, HEADER_BYTES + length).toString("utf8");
|
|
94
|
+
this.#buffer = this.#buffer.subarray(HEADER_BYTES + length);
|
|
95
|
+
const waiter = this.#waiters.shift();
|
|
96
|
+
if (waiter) waiter.resolve(message);
|
|
97
|
+
else {
|
|
98
|
+
this.#queue.push(message);
|
|
99
|
+
this.#updateReadFlow();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
#updateReadFlow() {
|
|
104
|
+
if (!this.#paused && this.#queue.length >= MAX_QUEUED_MESSAGES) {
|
|
105
|
+
this.#socket.pause();
|
|
106
|
+
this.#paused = true;
|
|
107
|
+
} else if (this.#paused && this.#queue.length <= RESUME_QUEUED_MESSAGES) {
|
|
108
|
+
this.#socket.resume();
|
|
109
|
+
this.#paused = false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
#fail(err) {
|
|
113
|
+
if (this.#error) return;
|
|
114
|
+
this.#error = err;
|
|
115
|
+
const waiters = this.#waiters.splice(0);
|
|
116
|
+
for (const waiter of waiters) waiter.reject(err);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/sidecar/index.ts
|
|
122
|
+
/**
|
|
123
|
+
* Helpers for authoring a Cloudflare Sandbox SDK extension's **sidecar**
|
|
124
|
+
* process \u2014 the program the container spawns inside the sandbox and that
|
|
125
|
+
* exchanges capnweb RPC with the host over a unix socket.
|
|
126
|
+
*
|
|
127
|
+
* This subpath is loaded only inside the sidecar (it depends on Node-style
|
|
128
|
+
* built-ins like `node:net`); the SDK-side `@cloudflare/sandbox/extensions`
|
|
129
|
+
* surface never imports it.
|
|
130
|
+
*
|
|
131
|
+
* Authoring contract:
|
|
132
|
+
* - Extend {@link SandboxSidecar}. The base implements `__ping__` so the
|
|
133
|
+
* host's health probe round-trips without per-sidecar boilerplate.
|
|
134
|
+
* - Pass an instance to {@link serveSandboxSidecar}. The helper opens the
|
|
135
|
+
* unix socket at `EXT_SOCKET` and serves a fresh capnweb session per
|
|
136
|
+
* connection.
|
|
137
|
+
*/
|
|
138
|
+
/**
|
|
139
|
+
* Base class for a sidecar's main capnweb RpcTarget. Subclasses add their
|
|
140
|
+
* own methods \u2014 these become the typed remote main visible to the SDK.
|
|
141
|
+
*
|
|
142
|
+
* `__ping__` is reserved for host health probes; do not override it.
|
|
143
|
+
*/
|
|
144
|
+
var SandboxSidecar = class extends RpcTarget {
|
|
145
|
+
/**
|
|
146
|
+
* Health probe \u2014 the container's `ExtensionHost.health()` calls this and
|
|
147
|
+
* expects the literal string `'pong'`. The host bounds the call with its
|
|
148
|
+
* own timeout so a misbehaving sidecar cannot hang the probe.
|
|
149
|
+
*/
|
|
150
|
+
__ping__() {
|
|
151
|
+
return "pong";
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Open the sidecar's unix socket and serve capnweb sessions backed by
|
|
156
|
+
* `target`. Each inbound connection gets its own `RpcSession`; the `target`
|
|
157
|
+
* instance is shared across all sessions, so authors can keep per-sidecar
|
|
158
|
+
* state on it without sync.
|
|
159
|
+
*
|
|
160
|
+
* Idiomatic usage:
|
|
161
|
+
*
|
|
162
|
+
* ```ts
|
|
163
|
+
* import {
|
|
164
|
+
* SandboxSidecar,
|
|
165
|
+
* serveSandboxSidecar
|
|
166
|
+
* } from '@cloudflare/sandbox/sidecar';
|
|
167
|
+
*
|
|
168
|
+
* class MyApi extends SandboxSidecar {
|
|
169
|
+
* async isOdd(n: number): Promise<boolean> {
|
|
170
|
+
* return n % 2 === 1;
|
|
171
|
+
* }
|
|
172
|
+
* }
|
|
173
|
+
*
|
|
174
|
+
* serveSandboxSidecar(new MyApi());
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
function serveSandboxSidecar(target, options = {}) {
|
|
178
|
+
const socketPath = process.env.EXT_SOCKET;
|
|
179
|
+
if (!socketPath) throw new Error("EXT_SOCKET is not set — sidecars are spawned by the container ExtensionHost, which injects this env var");
|
|
180
|
+
const server = net.createServer((socket) => {
|
|
181
|
+
new RpcSession(new SocketTransport(socket), target);
|
|
182
|
+
});
|
|
183
|
+
server.once("error", (error) => {
|
|
184
|
+
process.stderr.write(`sandbox sidecar failed to listen on ${socketPath}: ${error.message}\n`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
|
187
|
+
server.listen(socketPath, () => {
|
|
188
|
+
const message = options.readyMessage ?? "sandbox sidecar listening";
|
|
189
|
+
process.stdout.write(`${message}\n`);
|
|
190
|
+
});
|
|
191
|
+
const onSignal = () => {
|
|
192
|
+
server.close(() => process.exit(0));
|
|
193
|
+
};
|
|
194
|
+
process.once("SIGINT", onSignal);
|
|
195
|
+
process.once("SIGTERM", onSignal);
|
|
196
|
+
return server;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
//#endregion
|
|
200
|
+
export { SandboxSidecar, SocketTransport, serveSandboxSidecar };
|
|
201
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["#socket","#queue","#waiters","#onData","#fail","#error","#awaitDrain","#updateReadFlow","#buffer","#paused"],"sources":["../../src/sidecar/socket-transport.ts","../../src/sidecar/index.ts"],"sourcesContent":["import type { Socket } from 'node:net';\nimport type { RpcTransport } from 'capnweb';\n\nconst HEADER_BYTES = 4;\nconst MAX_FRAME_BYTES = 16 * 1024 * 1024;\nconst MAX_QUEUED_MESSAGES = 128;\nconst RESUME_QUEUED_MESSAGES = 64;\n\n/**\n * capnweb `RpcTransport` over a unix-domain socket, mirroring the host-side\n * transport in `@repo/sandbox-container`. The wire format is a 4-byte\n * big-endian length prefix followed by UTF-8 JSON, identical on both sides.\n *\n * Exported from `@cloudflare/sandbox/sidecar` so a sidecar process can run\n * its own capnweb session without copy-pasting the framing logic.\n */\nexport class SocketTransport implements RpcTransport {\n readonly #socket: Socket;\n #buffer: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n readonly #queue: string[] = [];\n readonly #waiters: Array<{\n resolve: (msg: string) => void;\n reject: (err: Error) => void;\n }> = [];\n #error: Error | null = null;\n #paused = false;\n\n constructor(socket: Socket) {\n this.#socket = socket;\n socket.on('data', (chunk: Buffer) => this.#onData(chunk));\n socket.on('error', (err) => this.#fail(err));\n socket.on('close', () => this.#fail(new Error('Extension socket closed')));\n }\n\n async send(message: string): Promise<void> {\n if (this.#error) throw this.#error;\n const body = Buffer.from(message, 'utf8');\n if (body.length > MAX_FRAME_BYTES) {\n throw new Error(\n `Extension capnweb message exceeds maximum size (${body.length} > ${MAX_FRAME_BYTES} bytes)`\n );\n }\n const header = Buffer.allocUnsafe(HEADER_BYTES);\n header.writeUInt32BE(body.length, 0);\n const flushed = this.#socket.write(Buffer.concat([header, body]));\n if (!flushed) await this.#awaitDrain();\n }\n\n #awaitDrain(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const onDrain = () => {\n cleanup();\n resolve();\n };\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n const onClose = () => {\n cleanup();\n reject(this.#error ?? new Error('Extension socket closed'));\n };\n const cleanup = () => {\n this.#socket.off('drain', onDrain);\n this.#socket.off('error', onError);\n this.#socket.off('close', onClose);\n };\n if (this.#error) {\n reject(this.#error);\n return;\n }\n this.#socket.once('drain', onDrain);\n this.#socket.once('error', onError);\n this.#socket.once('close', onClose);\n });\n }\n\n receive(): Promise<string> {\n if (this.#queue.length > 0) {\n const message = this.#queue.shift()!;\n this.#updateReadFlow();\n return Promise.resolve(message);\n }\n if (this.#error) return Promise.reject(this.#error);\n return new Promise<string>((resolve, reject) => {\n this.#waiters.push({ resolve, reject });\n });\n }\n\n abort(reason: unknown): void {\n this.#fail(reason instanceof Error ? reason : new Error(String(reason)));\n this.#socket.destroy();\n }\n\n #onData(chunk: Buffer): void {\n this.#buffer =\n this.#buffer.length === 0 ? chunk : Buffer.concat([this.#buffer, chunk]);\n while (this.#buffer.length >= HEADER_BYTES) {\n const length = this.#buffer.readUInt32BE(0);\n if (length > MAX_FRAME_BYTES) {\n this.#fail(\n new Error(\n `Extension capnweb message exceeds maximum size (${length} > ${MAX_FRAME_BYTES} bytes)`\n )\n );\n return;\n }\n if (this.#buffer.length < HEADER_BYTES + length) break;\n const message = this.#buffer\n .subarray(HEADER_BYTES, HEADER_BYTES + length)\n .toString('utf8');\n this.#buffer = this.#buffer.subarray(HEADER_BYTES + length);\n const waiter = this.#waiters.shift();\n if (waiter) {\n waiter.resolve(message);\n } else {\n this.#queue.push(message);\n this.#updateReadFlow();\n }\n }\n }\n\n #updateReadFlow(): void {\n if (!this.#paused && this.#queue.length >= MAX_QUEUED_MESSAGES) {\n this.#socket.pause();\n this.#paused = true;\n } else if (this.#paused && this.#queue.length <= RESUME_QUEUED_MESSAGES) {\n this.#socket.resume();\n this.#paused = false;\n }\n }\n\n #fail(err: Error): void {\n if (this.#error) return;\n this.#error = err;\n const waiters = this.#waiters.splice(0);\n for (const waiter of waiters) {\n waiter.reject(err);\n }\n }\n}\n","/**\n * Helpers for authoring a Cloudflare Sandbox SDK extension's **sidecar**\n * process \\u2014 the program the container spawns inside the sandbox and that\n * exchanges capnweb RPC with the host over a unix socket.\n *\n * This subpath is loaded only inside the sidecar (it depends on Node-style\n * built-ins like `node:net`); the SDK-side `@cloudflare/sandbox/extensions`\n * surface never imports it.\n *\n * Authoring contract:\n * - Extend {@link SandboxSidecar}. The base implements `__ping__` so the\n * host's health probe round-trips without per-sidecar boilerplate.\n * - Pass an instance to {@link serveSandboxSidecar}. The helper opens the\n * unix socket at `EXT_SOCKET` and serves a fresh capnweb session per\n * connection.\n */\n\nimport net from 'node:net';\nimport { RpcSession, RpcTarget } from 'capnweb';\nimport { SocketTransport } from './socket-transport.js';\n\nexport { SocketTransport } from './socket-transport.js';\n\n/**\n * Base class for a sidecar's main capnweb RpcTarget. Subclasses add their\n * own methods \\u2014 these become the typed remote main visible to the SDK.\n *\n * `__ping__` is reserved for host health probes; do not override it.\n */\nexport abstract class SandboxSidecar extends RpcTarget {\n /**\n * Health probe \\u2014 the container's `ExtensionHost.health()` calls this and\n * expects the literal string `'pong'`. The host bounds the call with its\n * own timeout so a misbehaving sidecar cannot hang the probe.\n */\n __ping__(): 'pong' {\n return 'pong';\n }\n}\n\n/**\n * Options for {@link serveSandboxSidecar}. Mostly defaults; the socket path\n * is always taken from `process.env.EXT_SOCKET` (set by the container's\n * `ExtensionHost`) so authors do not have to plumb it themselves.\n */\nexport interface ServeSidecarOptions {\n /**\n * Override the diagnostic log line printed to stdout after the sidecar opens\n * its socket. Readiness is determined by the host's successful capnweb\n * connection, not by this line. Defaults to `sandbox sidecar listening`.\n */\n readyMessage?: string;\n}\n\n/**\n * Open the sidecar's unix socket and serve capnweb sessions backed by\n * `target`. Each inbound connection gets its own `RpcSession`; the `target`\n * instance is shared across all sessions, so authors can keep per-sidecar\n * state on it without sync.\n *\n * Idiomatic usage:\n *\n * ```ts\n * import {\n * SandboxSidecar,\n * serveSandboxSidecar\n * } from '@cloudflare/sandbox/sidecar';\n *\n * class MyApi extends SandboxSidecar {\n * async isOdd(n: number): Promise<boolean> {\n * return n % 2 === 1;\n * }\n * }\n *\n * serveSandboxSidecar(new MyApi());\n * ```\n */\nexport function serveSandboxSidecar(\n target: SandboxSidecar,\n options: ServeSidecarOptions = {}\n): net.Server {\n const socketPath = process.env.EXT_SOCKET;\n if (!socketPath) {\n throw new Error(\n 'EXT_SOCKET is not set \\u2014 sidecars are spawned by the container ExtensionHost, which injects this env var'\n );\n }\n\n const server = net.createServer((socket) => {\n // One capnweb session per connection. The shared `target` is exposed as\n // each session's local main; capnweb hands a typed stub of it to the\n // host on connect via `session.getRemoteMain()`.\n new RpcSession(new SocketTransport(socket), target);\n });\n\n server.once('error', (error) => {\n process.stderr.write(\n `sandbox sidecar failed to listen on ${socketPath}: ${error.message}\\n`\n );\n process.exit(1);\n });\n\n server.listen(socketPath, () => {\n const message = options.readyMessage ?? 'sandbox sidecar listening';\n process.stdout.write(`${message}\\n`);\n });\n\n // Stay alive on terminal signals so the host's own SIGTERM \\u2192 SIGKILL\n // sequence is what tears us down (not a default Node handler).\n const onSignal = () => {\n server.close(() => process.exit(0));\n };\n process.once('SIGINT', onSignal);\n process.once('SIGTERM', onSignal);\n\n return server;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudflare/sandbox",
|
|
3
|
-
"version": "0.13.0-next.
|
|
3
|
+
"version": "0.13.0-next.651.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/cloudflare/sandbox-sdk"
|
|
@@ -61,6 +61,16 @@
|
|
|
61
61
|
"import": "./dist/index.js",
|
|
62
62
|
"require": "./dist/index.js"
|
|
63
63
|
},
|
|
64
|
+
"./extensions": {
|
|
65
|
+
"types": "./dist/extensions/index.d.ts",
|
|
66
|
+
"import": "./dist/extensions/index.js",
|
|
67
|
+
"require": "./dist/extensions/index.js"
|
|
68
|
+
},
|
|
69
|
+
"./sidecar": {
|
|
70
|
+
"types": "./dist/sidecar/index.d.ts",
|
|
71
|
+
"import": "./dist/sidecar/index.js",
|
|
72
|
+
"require": "./dist/sidecar/index.js"
|
|
73
|
+
},
|
|
64
74
|
"./openai": {
|
|
65
75
|
"types": "./dist/openai/index.d.ts",
|
|
66
76
|
"import": "./dist/openai/index.js",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dist-mAH_7Ui7.js","names":["parts","LogLevelEnum","LogLevelEnum"],"sources":["../../shared/dist/env.js","../../shared/dist/logger/sanitize.js","../../shared/dist/interpreter-types.js","../../shared/dist/logger/canonical.js","../../shared/dist/logger/types.js","../../shared/dist/logger/logger.js","../../shared/dist/logger/trace-context.js","../../shared/dist/logger/index.js","../../shared/dist/shell-escape.js","../../shared/dist/sse.js","../../shared/dist/types.js"],"sourcesContent":["/**\n * Safely extract a string value from an environment object\n *\n * @param env - Environment object with dynamic keys\n * @param key - The environment variable key to access\n * @returns The string value if present and is a string, undefined otherwise\n */\nexport function getEnvString(env, key) {\n const value = env?.[key];\n return typeof value === 'string' ? value : undefined;\n}\n/**\n * Filter environment variables object to only include string values.\n * Skips undefined, null, and non-string values.\n *\n * Use this when you only need the defined values (e.g., for per-command env\n * where undefined means \"don't override\").\n *\n * @param envVars - Object that may contain undefined values\n * @returns Clean object with only string values\n */\nexport function filterEnvVars(envVars) {\n const filtered = {};\n for (const [key, value] of Object.entries(envVars)) {\n if (value != null && typeof value === 'string') {\n filtered[key] = value;\n }\n }\n return filtered;\n}\n/**\n * Partition environment variables into values to set and keys to unset.\n *\n * - String values → toSet (will be exported)\n * - undefined/null → toUnset (will be unset)\n *\n * This enables idiomatic JS patterns where undefined means \"remove\":\n * ```typescript\n * await sandbox.setEnvVars({\n * API_KEY: 'new-key', // will be set\n * OLD_VAR: undefined, // will be unset\n * });\n * ```\n */\nexport function partitionEnvVars(envVars) {\n const toSet = {};\n const toUnset = [];\n for (const [key, value] of Object.entries(envVars)) {\n if (value != null && typeof value === 'string') {\n toSet[key] = value;\n }\n else {\n toUnset.push(key);\n }\n }\n return { toSet, toUnset };\n}\n","/**\n * Log-only sanitization helpers\n *\n * These functions redact sensitive values for logging output.\n * They MUST NOT be used to mutate command strings before execution.\n */\n/**\n * Sensitive query parameter names to redact from URLs.\n * Anchored to query string context ([?&]) to avoid matching path segments.\n * Value matching stops at & and common URL/command delimiters.\n */\nconst SENSITIVE_PARAMS = /([?&])(X-Amz-Credential|X-Amz-Signature|X-Amz-Security-Token|token|secret|password)=[^&\\s\"'`<>]*/gi;\n/**\n * Redact credentials from URLs for secure logging\n *\n * Replaces any credentials (username:password, tokens, etc.) embedded\n * in URLs with ****** to prevent sensitive data exposure in logs.\n * Works with URLs embedded in text.\n *\n * @param text - String that may contain URLs with credentials\n * @returns String with credentials redacted from any URLs\n */\nexport function redactCredentials(text) {\n // Scan for http(s):// URLs and redact any credentials found\n let result = text;\n let pos = 0;\n while (pos < result.length) {\n const httpPos = result.indexOf('http://', pos);\n const httpsPos = result.indexOf('https://', pos);\n let protocolPos = -1;\n let protocolLen = 0;\n if (httpPos === -1 && httpsPos === -1)\n break;\n if (httpPos !== -1 && (httpsPos === -1 || httpPos < httpsPos)) {\n protocolPos = httpPos;\n protocolLen = 7; // 'http://'.length\n }\n else {\n protocolPos = httpsPos;\n protocolLen = 8; // 'https://'.length\n }\n // Look for @ after the protocol\n const searchStart = protocolPos + protocolLen;\n const atPos = result.indexOf('@', searchStart);\n // Find where the URL ends (whitespace, quotes, or structural delimiters)\n let urlEnd = searchStart;\n while (urlEnd < result.length) {\n const char = result[urlEnd];\n if (/[\\s\"'`<>,;{}[\\]]/.test(char))\n break;\n urlEnd++;\n }\n if (atPos !== -1 && atPos < urlEnd) {\n result = `${result.substring(0, searchStart)}******${result.substring(atPos)}`;\n pos = searchStart + 6; // Move past '******'\n }\n else {\n pos = protocolPos + protocolLen;\n }\n }\n return result;\n}\n/**\n * Redact sensitive query parameters from URLs\n *\n * Strips X-Amz-Credential, X-Amz-Signature, X-Amz-Security-Token,\n * token, secret, and password query params from URLs. Returns\n * non-URL strings unchanged.\n *\n * @param input - String that may contain URLs with sensitive params\n * @returns String with sensitive params replaced by REDACTED\n */\nexport function redactSensitiveParams(input) {\n if (!input.includes('?') || !input.includes('='))\n return input;\n return input.replace(SENSITIVE_PARAMS, '$1$2=REDACTED');\n}\n/**\n * Redact sensitive data from a command string for logging\n *\n * Composes redactCredentials (URL credentials) and redactSensitiveParams\n * (presigned URL query params). For log values only — never mutate\n * command strings before execution.\n *\n * @param command - Command string to sanitize for logging\n * @returns Sanitized command string\n */\nexport function redactCommand(command) {\n return redactSensitiveParams(redactCredentials(command));\n}\n/**\n * Truncate a string for log output with a truncation indicator\n *\n * @param value - String to potentially truncate\n * @param maxLen - Maximum length before truncation (default 120)\n * @returns Object with truncated value and boolean flag\n */\nexport function truncateForLog(value, maxLen = 120) {\n if (value.length <= maxLen) {\n return { value, truncated: false };\n }\n const cutoff = Math.max(0, maxLen - 3);\n return { value: `${value.substring(0, cutoff)}...`, truncated: true };\n}\n","// Execution Result Container\nexport class Execution {\n code;\n context;\n /**\n * All results from the execution\n */\n results = [];\n /**\n * Accumulated stdout and stderr\n */\n logs = {\n stdout: [],\n stderr: []\n };\n /**\n * Execution error if any\n */\n error;\n /**\n * Execution count (for interpreter)\n */\n executionCount;\n constructor(code, context) {\n this.code = code;\n this.context = context;\n }\n /**\n * Convert to a plain object for serialization\n */\n toJSON() {\n return {\n code: this.code,\n logs: this.logs,\n error: this.error,\n executionCount: this.executionCount,\n results: this.results.map((result) => ({\n text: result.text,\n html: result.html,\n png: result.png,\n jpeg: result.jpeg,\n svg: result.svg,\n latex: result.latex,\n markdown: result.markdown,\n javascript: result.javascript,\n json: result.json,\n chart: result.chart,\n data: result.data\n }))\n };\n }\n}\n// Implementation of Result\nexport class ResultImpl {\n text;\n html;\n png;\n jpeg;\n svg;\n latex;\n markdown;\n javascript;\n json;\n chart;\n data;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- raw SSE data has dynamic shape\n constructor(raw) {\n this.text = raw.text || raw.data?.['text/plain'];\n this.html = raw.html || raw.data?.['text/html'];\n this.png = raw.png || raw.data?.['image/png'];\n this.jpeg = raw.jpeg || raw.data?.['image/jpeg'];\n this.svg = raw.svg || raw.data?.['image/svg+xml'];\n this.latex = raw.latex || raw.data?.['text/latex'];\n this.markdown = raw.markdown || raw.data?.['text/markdown'];\n this.javascript = raw.javascript || raw.data?.['application/javascript'];\n this.json = raw.json || raw.data?.['application/json'];\n this.chart = raw.chart;\n this.data = raw.data;\n }\n formats() {\n const fmts = [];\n if (this.text)\n fmts.push('text');\n if (this.html)\n fmts.push('html');\n if (this.png)\n fmts.push('png');\n if (this.jpeg)\n fmts.push('jpeg');\n if (this.svg)\n fmts.push('svg');\n if (this.latex)\n fmts.push('latex');\n if (this.markdown)\n fmts.push('markdown');\n if (this.javascript)\n fmts.push('javascript');\n if (this.json)\n fmts.push('json');\n if (this.chart)\n fmts.push('chart');\n return fmts;\n }\n}\n","import { redactCommand, truncateForLog } from './sanitize.js';\n/** Events that are low-value at info on success */\nconst DEBUG_ON_SUCCESS = new Set([\n 'session.create',\n 'session.destroy',\n 'file.read',\n 'file.write',\n 'file.delete',\n 'file.mkdir'\n]);\nexport function resolveLogLevel(payload, options) {\n if (payload.outcome === 'error')\n return 'error';\n if (options?.successLevel)\n return options.successLevel;\n if (payload.origin === 'internal')\n return 'debug';\n if (DEBUG_ON_SUCCESS.has(payload.event))\n return 'debug';\n return 'info';\n}\n/**\n * Sanitize an Error object by redacting sensitive data from message and stack.\n * Produces a copy so the caller's original Error is not mutated.\n */\nfunction sanitizeError(error) {\n if (!error)\n return undefined;\n const sanitized = new Error(redactCommand(error.message));\n sanitized.name = error.name;\n sanitized.stack = error.stack ? redactCommand(error.stack) : undefined;\n return sanitized;\n}\n/**\n * Sanitize and prepare payload fields for both message building and context emission.\n * Called once by logCanonicalEvent to avoid double-redaction.\n */\nfunction sanitizePayload(payload) {\n if (payload.command === undefined) {\n return { commandTruncated: false };\n }\n const redacted = redactCommand(payload.command);\n const { value, truncated } = truncateForLog(redacted);\n return { sanitizedCommand: value, commandTruncated: truncated };\n}\n/**\n * Build a human-readable canonical event message for dashboards and log viewers.\n *\n * Format: `{event} {outcome} {key_context} [— {reason}] ({durationMs}ms[, {sizeBytes}B])`\n *\n * The if/else chain for key context has implicit priority: command > path >\n * sessionId > port > repoUrl > pid. If a payload has multiple, only the\n * highest-priority one appears in the message. All fields are still present\n * as discrete queryable keys in the structured log context.\n */\nexport function buildMessage(payload, sanitizedCommand) {\n const { event } = payload;\n // version.check has its own format: no outcome, no duration\n if (event === 'version.check') {\n const parts = ['version.check'];\n if (payload.sdkVersion)\n parts.push(`sdk=${payload.sdkVersion}`);\n if (payload.containerVersion)\n parts.push(`container=${payload.containerVersion}`);\n if (payload.versionOutcome && payload.versionOutcome !== 'compatible') {\n parts.push(`(${payload.versionOutcome})`);\n }\n return parts.join(' ');\n }\n const parts = [event, payload.outcome];\n // Key context — highest priority field shown in message\n if (sanitizedCommand !== undefined) {\n parts.push(sanitizedCommand);\n }\n else if (payload.command !== undefined) {\n // Fallback for direct buildMessage calls without pre-sanitized command\n const redacted = redactCommand(payload.command);\n const { value } = truncateForLog(redacted);\n parts.push(value);\n }\n else if (payload.path !== undefined) {\n parts.push(payload.path);\n }\n else if (event.includes('session') && payload.sessionId !== undefined) {\n parts.push(payload.sessionId);\n }\n else if (payload.port !== undefined) {\n parts.push(String(payload.port));\n }\n else if (payload.repoUrl !== undefined) {\n let gitContext = payload.repoUrl;\n if (payload.branch !== undefined) {\n gitContext += ` ${payload.branch}`;\n }\n parts.push(gitContext);\n }\n else if (payload.pid !== undefined) {\n parts.push(String(payload.pid));\n }\n else if (payload.backupId !== undefined) {\n parts.push(payload.backupId);\n }\n else if (payload.repoPath !== undefined) {\n let gitContext = payload.repoPath;\n if (payload.branch !== undefined) {\n gitContext += ` branch=${payload.branch}`;\n }\n parts.push(gitContext);\n }\n else if (payload.mountsProcessed !== undefined) {\n let destroyContext = `${payload.mountsProcessed} mounts`;\n if (payload.mountFailures)\n destroyContext += `, ${payload.mountFailures} failed`;\n parts.push(destroyContext);\n }\n else if (payload.mountPath !== undefined) {\n parts.push(payload.mountPath);\n }\n // Error reason after em-dash\n if (payload.outcome === 'error') {\n if (payload.errorMessage !== undefined) {\n parts.push(`\\u2014 ${payload.errorMessage}`);\n }\n else if (payload.exitCode !== undefined) {\n parts.push(`\\u2014 exitCode=${payload.exitCode}`);\n }\n }\n // Duration suffix (and optional size)\n const durationSuffix = payload.sizeBytes !== undefined\n ? `(${payload.durationMs}ms, ${payload.sizeBytes}B)`\n : `(${payload.durationMs}ms)`;\n parts.push(durationSuffix);\n return parts.join(' ');\n}\n/**\n * Log a canonical event — the single entry point for all structured operational events.\n *\n * Sanitizes command fields once, builds the message, selects log level from\n * outcome, and emits a structured log entry with the full payload as context.\n */\nexport function logCanonicalEvent(logger, payload, options) {\n // Auto-derive errorMessage from error.message when not explicitly set,\n // then sanitize to prevent credential leaks (e.g., presigned URLs in error strings)\n const resolvedErrorMessage = payload.errorMessage ?? payload.error?.message;\n const sanitizedErrorMessage = resolvedErrorMessage\n ? redactCommand(resolvedErrorMessage)\n : undefined;\n const enrichedPayload = sanitizedErrorMessage !== undefined\n ? { ...payload, errorMessage: sanitizedErrorMessage }\n : payload;\n // Sanitize once, use for both message and context\n const { sanitizedCommand, commandTruncated } = sanitizePayload(enrichedPayload);\n const message = buildMessage(enrichedPayload, sanitizedCommand);\n // Build context from enriched payload, excluding the error object (passed separately)\n const context = {};\n for (const [key, value] of Object.entries(enrichedPayload)) {\n if (key === 'error')\n continue;\n context[key] = value;\n }\n // Apply sanitized command to context\n if (sanitizedCommand !== undefined) {\n context.command = sanitizedCommand;\n if (commandTruncated) {\n context.commandTruncated = true;\n }\n }\n const level = resolveLogLevel(enrichedPayload, options);\n if (level === 'error') {\n logger.error(message, sanitizeError(payload.error), context);\n }\n else if (level === 'warn') {\n logger.warn(message, context);\n }\n else if (level === 'debug') {\n logger.debug(message, context);\n }\n else {\n logger.info(message, context);\n }\n}\n","/**\n * Logger types for Cloudflare Sandbox SDK\n *\n * Provides structured, trace-aware logging across Worker, Durable Object, and Container.\n */\n/**\n * Log levels (from most to least verbose)\n */\nexport var LogLevel;\n(function (LogLevel) {\n LogLevel[LogLevel[\"DEBUG\"] = 0] = \"DEBUG\";\n LogLevel[LogLevel[\"INFO\"] = 1] = \"INFO\";\n LogLevel[LogLevel[\"WARN\"] = 2] = \"WARN\";\n LogLevel[LogLevel[\"ERROR\"] = 3] = \"ERROR\";\n})(LogLevel || (LogLevel = {}));\n","/**\n * Logger implementation\n */\nimport { LogLevel as LogLevelEnum } from './types.js';\n/**\n * ANSI color codes for terminal output\n */\nconst COLORS = {\n reset: '\\x1b[0m',\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n dim: '\\x1b[2m' // Dim\n};\n/**\n * CloudflareLogger implements structured logging with support for\n * both JSON output (production) and pretty printing (development).\n */\nexport class CloudflareLogger {\n baseContext;\n minLevel;\n outputMode;\n /**\n * Create a new CloudflareLogger\n *\n * @param baseContext Base context included in all log entries\n * @param minLevel Minimum log level to output (default: INFO)\n * @param outputMode How log entries are formatted and emitted (default: 'structured')\n */\n constructor(baseContext, minLevel = LogLevelEnum.INFO, outputMode = 'structured') {\n this.baseContext = baseContext;\n this.minLevel = minLevel;\n this.outputMode = outputMode;\n }\n /**\n * Log debug-level message\n */\n debug(message, context) {\n if (this.shouldLog(LogLevelEnum.DEBUG)) {\n const logData = this.buildLogData('debug', message, context);\n this.output(console.log, logData);\n }\n }\n /**\n * Log info-level message\n */\n info(message, context) {\n if (this.shouldLog(LogLevelEnum.INFO)) {\n const logData = this.buildLogData('info', message, context);\n this.output(console.log, logData);\n }\n }\n /**\n * Log warning-level message\n */\n warn(message, context) {\n if (this.shouldLog(LogLevelEnum.WARN)) {\n const logData = this.buildLogData('warn', message, context);\n this.output(console.warn, logData);\n }\n }\n /**\n * Log error-level message\n */\n error(message, error, context) {\n if (this.shouldLog(LogLevelEnum.ERROR)) {\n const logData = this.buildLogData('error', message, context, error);\n this.output(console.error, logData);\n }\n }\n /**\n * Create a child logger with additional context\n */\n child(context) {\n return new CloudflareLogger({ ...this.baseContext, ...context }, this.minLevel, this.outputMode);\n }\n /**\n * Check if a log level should be output\n */\n shouldLog(level) {\n return level >= this.minLevel;\n }\n /**\n * Build log data object\n */\n buildLogData(level, message, context, error) {\n const logData = {\n level,\n message,\n ...this.baseContext,\n ...context,\n timestamp: new Date().toISOString()\n };\n // Add error details if provided\n if (error) {\n logData.error = {\n message: error.message,\n stack: error.stack,\n name: error.name\n };\n }\n return logData;\n }\n /**\n * Output log data using the configured output mode\n */\n output(consoleFn, data) {\n switch (this.outputMode) {\n case 'pretty':\n this.outputPretty(consoleFn, data);\n break;\n case 'json-line':\n this.outputJsonLine(consoleFn, data);\n break;\n case 'structured':\n this.outputStructured(consoleFn, data);\n break;\n }\n }\n /**\n * Output as JSON string (container stdout — parsed by Containers pipeline)\n */\n outputJsonLine(consoleFn, data) {\n consoleFn(JSON.stringify(data));\n }\n /**\n * Output as raw object (Workers/DOs — Workers Logs auto-indexes fields)\n */\n outputStructured(consoleFn, data) {\n consoleFn(data);\n }\n /**\n * Output as pretty-printed, colored text (development)\n *\n * Each log event is a single consoleFn() call so it appears as one entry\n * in the Cloudflare dashboard. Context is rendered inline as compact key=value pairs.\n *\n * Format: LEVEL [component] message trace=tr_... key=value key=value\n */\n outputPretty(consoleFn, data) {\n const { level, message: msg, timestamp, traceId, component, sandboxId, sessionId, processId, commandId, durationMs, serviceVersion, instanceId, error, ...rest } = data;\n const levelStr = String(level || 'INFO').toUpperCase();\n const levelColor = this.getLevelColor(levelStr);\n const componentBadge = component ? `[${component}]` : '';\n const timeStr = timestamp\n ? `${COLORS.dim}${new Date(timestamp).toISOString().substring(11, 23)}${COLORS.reset} `\n : '';\n let logLine = `${timeStr}${levelColor}${levelStr.padEnd(5)}${COLORS.reset} ${componentBadge} ${msg}`;\n // Append all context as compact key=value pairs on the same line\n const pairs = [];\n if (traceId)\n pairs.push(`trace=${String(traceId).substring(0, 12)}`);\n if (commandId)\n pairs.push(`cmd=${String(commandId).substring(0, 12)}`);\n if (sandboxId)\n pairs.push(`sandbox=${sandboxId}`);\n if (sessionId)\n pairs.push(`session=${String(sessionId).substring(0, 12)}`);\n if (processId)\n pairs.push(`proc=${processId}`);\n if (durationMs !== undefined)\n pairs.push(`dur=${durationMs}ms`);\n // Append remaining context fields inline\n for (const [key, value] of Object.entries(rest)) {\n if (value === undefined || value === null)\n continue;\n const v = typeof value === 'object'\n ? JSON.stringify(value)\n : this.sanitizePrettyValue(String(value));\n pairs.push(`${key}=${v}`);\n }\n // Append error info inline\n if (error && typeof error === 'object') {\n const errorObj = error;\n if (errorObj.name)\n pairs.push(`err.name=${this.sanitizePrettyValue(errorObj.name)}`);\n if (errorObj.message)\n pairs.push(`err.msg=${this.sanitizePrettyValue(errorObj.message)}`);\n if (errorObj.stack)\n pairs.push(`err.stack=${this.sanitizePrettyValue(errorObj.stack)}`);\n }\n if (pairs.length > 0) {\n logLine += ` ${COLORS.dim}${pairs.join(' ')}${COLORS.reset}`;\n }\n // Single consoleFn call = single log entry in the dashboard\n consoleFn(logLine);\n }\n /**\n * Collapse newlines so a single consoleFn() call stays on one line.\n * Cloudflare's log pipeline splits on literal newlines, which fragments\n * stack traces and multi-line error messages into separate entries.\n */\n sanitizePrettyValue(value) {\n return value.replace(/\\r/g, '\\\\r').replace(/\\n/g, '\\\\n');\n }\n /**\n * Get ANSI color code for log level\n */\n getLevelColor(level) {\n const levelLower = level.toLowerCase();\n switch (levelLower) {\n case 'debug':\n return COLORS.debug;\n case 'info':\n return COLORS.info;\n case 'warn':\n return COLORS.warn;\n case 'error':\n return COLORS.error;\n default:\n return COLORS.reset;\n }\n }\n}\n","/**\n * Trace context utilities for request correlation\n *\n * Trace IDs enable correlating logs across distributed components:\n * Worker → Durable Object → Container → back\n *\n * The trace ID is propagated via the X-Trace-Id HTTP header.\n */\n/**\n * Utility for managing trace context across distributed components\n */\n// biome-ignore lint/complexity/noStaticOnlyClass: Keep as class for namespace grouping and discoverability\nexport class TraceContext {\n /**\n * HTTP header name for trace ID propagation\n */\n static TRACE_HEADER = 'X-Trace-Id';\n /**\n * Generate a new trace ID\n *\n * Format: \"tr_\" + 16 random hex characters\n * Example: \"tr_7f3a9b2c4e5d6f1a\"\n *\n * @returns Newly generated trace ID\n */\n static generate() {\n // Use crypto.randomUUID() for randomness, extract 16 hex chars\n const randomHex = crypto.randomUUID().replace(/-/g, '').substring(0, 16);\n return `tr_${randomHex}`;\n }\n /**\n * Extract trace ID from HTTP request headers\n *\n * @param headers Request headers\n * @returns Trace ID if present, null otherwise\n */\n static fromHeaders(headers) {\n return headers.get(TraceContext.TRACE_HEADER);\n }\n /**\n * Create headers object with trace ID for outgoing requests\n *\n * @param traceId Trace ID to include\n * @returns Headers object with X-Trace-Id set\n */\n static toHeaders(traceId) {\n return { [TraceContext.TRACE_HEADER]: traceId };\n }\n /**\n * Get the header name used for trace ID propagation\n *\n * @returns Header name (\"X-Trace-Id\")\n */\n static getHeaderName() {\n return TraceContext.TRACE_HEADER;\n }\n}\n","/**\n * Logger module\n *\n * Provides structured, trace-aware logging with:\n * - Explicit logger passing via constructor injection\n * - Three output modes: structured (Workers/DOs), json-line (container), pretty (local dev)\n * - Environment auto-detection\n * - Log level configuration\n *\n * Usage:\n *\n * ```typescript\n * // Create a logger at entry point\n * const logger = createLogger({ component: 'sandbox-do', traceId: 'tr_abc123' });\n *\n * // Pass to classes via constructor\n * const service = new MyService(logger);\n *\n * // Create child loggers for additional context\n * const execLogger = logger.child({ commandId: 'cmd-456' });\n * execLogger.info('Operation started');\n * ```\n */\nimport { CloudflareLogger } from './logger.js';\nimport { TraceContext } from './trace-context.js';\nimport { LogLevel as LogLevelEnum } from './types.js';\nexport { buildMessage, logCanonicalEvent, resolveLogLevel } from './canonical.js';\nexport { CloudflareLogger } from './logger.js';\nexport { TraceContext } from './trace-context.js';\nexport { LogLevel as LogLevelEnum } from './types.js';\n/**\n * Create a no-op logger for testing\n *\n * Returns a logger that implements the Logger interface but does nothing.\n * Useful for tests that don't need actual logging output.\n *\n * @returns No-op logger instance\n *\n * @example\n * ```typescript\n * // In tests\n * const client = new HttpClient({\n * baseUrl: 'http://test.com',\n * logger: createNoOpLogger() // Optional - tests can enable real logging if needed\n * });\n * ```\n */\nexport function createNoOpLogger() {\n return {\n debug: () => { },\n info: () => { },\n warn: () => { },\n error: () => { },\n child: () => createNoOpLogger()\n };\n}\n/**\n * Create a new logger instance\n *\n * @param context Base context for the logger. Must include 'component'.\n * TraceId will be auto-generated if not provided.\n * @returns New logger instance\n *\n * @example\n * ```typescript\n * // In Durable Object\n * const logger = createLogger({\n * component: 'sandbox-do',\n * traceId: TraceContext.fromHeaders(request.headers) || TraceContext.generate(),\n * sandboxId: this.id\n * });\n *\n * // In Container\n * const logger = createLogger({\n * component: 'container',\n * traceId: TraceContext.fromHeaders(request.headers)!,\n * sessionId: this.id\n * });\n * ```\n */\nexport function createLogger(context) {\n const minLevel = getLogLevelFromEnv();\n const outputMode = getOutputMode(context.component);\n const baseContext = {\n ...context,\n traceId: context.traceId || TraceContext.generate(),\n component: context.component,\n serviceVersion: context.serviceVersion || getEnvVar('SANDBOX_VERSION') || undefined,\n instanceId: context.instanceId ||\n getEnvVar('HOSTNAME') ||\n getEnvVar('SANDBOX_INSTANCE_ID') ||\n undefined\n };\n return new CloudflareLogger(baseContext, minLevel, outputMode);\n}\n/**\n * Get log level from environment variable\n *\n * Checks SANDBOX_LOG_LEVEL env var, falls back to default based on environment.\n * Default: 'debug' for development, 'info' for production\n */\nfunction getLogLevelFromEnv() {\n const envLevel = getEnvVar('SANDBOX_LOG_LEVEL') || 'info';\n switch (envLevel.toLowerCase()) {\n case 'debug':\n return LogLevelEnum.DEBUG;\n case 'info':\n return LogLevelEnum.INFO;\n case 'warn':\n return LogLevelEnum.WARN;\n case 'error':\n return LogLevelEnum.ERROR;\n default:\n // Invalid level, fall back to info\n return LogLevelEnum.INFO;\n }\n}\n/**\n * Determine output mode based on component and environment:\n * - SANDBOX_LOG_FORMAT=pretty → 'pretty' for all components (local wrangler dev)\n * - Container/Executor without pretty → 'json-line' (Bun stdout → Containers pipeline)\n * - Everything else without pretty → 'structured' (Workers/DOs → Workers Logs)\n *\n * In local dev, setting SANDBOX_LOG_FORMAT=pretty gives readable terminal\n * output on both the DO side and container side. In production (where the\n * var isn't set), DOs emit structured objects and containers emit single-line\n * JSON — both queryable by their respective observability pipelines.\n */\nfunction getOutputMode(component) {\n const format = getEnvVar('SANDBOX_LOG_FORMAT');\n if (format?.toLowerCase() === 'pretty') {\n return 'pretty';\n }\n if (component === 'container' || component === 'executor') {\n return 'json-line';\n }\n return 'structured';\n}\n/**\n * Get environment variable value\n *\n * Supports both Node.js (process.env) and Bun (Bun.env)\n */\nfunction getEnvVar(name) {\n // Try process.env first (Node.js / Bun)\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n // Try Bun.env (Bun runtime)\n if (typeof Bun !== 'undefined') {\n const bunEnv = Bun.env;\n if (bunEnv) {\n return bunEnv[name];\n }\n }\n return undefined;\n}\n","/**\n * Escapes a string for safe use in shell commands using POSIX single-quote escaping.\n * Prevents command injection by wrapping the string in single quotes and escaping\n * any single quotes within the string.\n */\nexport function shellEscape(str) {\n return `'${str.replace(/'/g, \"'\\\\''\")}'`;\n}\n","/**\n * Shared SSE parsing utilities.\n *\n * Parses SSE frames from arbitrary text chunks while preserving partial state\n * across chunk boundaries.\n */\n/**\n * Parse SSE frames from a buffer.\n *\n * Returns parsed events, remaining unparsed text, and the current partial event\n * so callers can continue parsing on the next chunk.\n */\nexport function parseSSEFrames(buffer, currentEvent = { data: [] }) {\n const events = [];\n let i = 0;\n while (i < buffer.length) {\n const newlineIndex = buffer.indexOf('\\n', i);\n if (newlineIndex === -1)\n break;\n const rawLine = buffer.substring(i, newlineIndex);\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine;\n i = newlineIndex + 1;\n if (line === '' && currentEvent.data.length > 0) {\n events.push({\n event: currentEvent.event,\n data: currentEvent.data.join('\\n')\n });\n currentEvent = { data: [] };\n continue;\n }\n if (line.startsWith('event:')) {\n const value = line.startsWith('event: ')\n ? line.substring(7)\n : line.substring(6);\n currentEvent.event = value;\n continue;\n }\n if (line.startsWith('data:')) {\n const value = line.startsWith('data: ')\n ? line.substring(6)\n : line.substring(5);\n currentEvent.data.push(value);\n }\n }\n return {\n events,\n remaining: buffer.substring(i),\n currentEvent\n };\n}\n","/**\n * Check if a process status indicates the process has terminated\n */\nexport function isTerminalStatus(status) {\n return (status === 'completed' ||\n status === 'failed' ||\n status === 'killed' ||\n status === 'error');\n}\n// Type guards for runtime validation\nexport function isExecResult(value) {\n return (value &&\n typeof value.success === 'boolean' &&\n typeof value.exitCode === 'number' &&\n typeof value.stdout === 'string' &&\n typeof value.stderr === 'string');\n}\nexport function isProcess(value) {\n return (value &&\n typeof value.id === 'string' &&\n typeof value.command === 'string' &&\n typeof value.status === 'string');\n}\nexport function isProcessStatus(value) {\n return [\n 'starting',\n 'running',\n 'completed',\n 'failed',\n 'killed',\n 'error'\n ].includes(value);\n}\n// Re-export interpreter types for convenience\nexport { Execution, ResultImpl } from './interpreter-types';\n"],"mappings":";;;;;;;;AAOA,SAAgB,aAAa,KAAK,KAAK;CACnC,MAAM,QAAQ,MAAM;AACpB,QAAO,OAAO,UAAU,WAAW,QAAQ;;;;;;;;;;;;AAY/C,SAAgB,cAAc,SAAS;CACnC,MAAM,WAAW,EAAE;AACnB,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,CAC9C,KAAI,SAAS,QAAQ,OAAO,UAAU,SAClC,UAAS,OAAO;AAGxB,QAAO;;;;;;;;;;;;;;;;AAgBX,SAAgB,iBAAiB,SAAS;CACtC,MAAM,QAAQ,EAAE;CAChB,MAAM,UAAU,EAAE;AAClB,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,CAC9C,KAAI,SAAS,QAAQ,OAAO,UAAU,SAClC,OAAM,OAAO;KAGb,SAAQ,KAAK,IAAI;AAGzB,QAAO;EAAE;EAAO;EAAS;;;;;;;;;;;;;;;;AC5C7B,MAAM,mBAAmB;;;;;;;;;;;AAWzB,SAAgB,kBAAkB,MAAM;CAEpC,IAAI,SAAS;CACb,IAAI,MAAM;AACV,QAAO,MAAM,OAAO,QAAQ;EACxB,MAAM,UAAU,OAAO,QAAQ,WAAW,IAAI;EAC9C,MAAM,WAAW,OAAO,QAAQ,YAAY,IAAI;EAChD,IAAI,cAAc;EAClB,IAAI,cAAc;AAClB,MAAI,YAAY,MAAM,aAAa,GAC/B;AACJ,MAAI,YAAY,OAAO,aAAa,MAAM,UAAU,WAAW;AAC3D,iBAAc;AACd,iBAAc;SAEb;AACD,iBAAc;AACd,iBAAc;;EAGlB,MAAM,cAAc,cAAc;EAClC,MAAM,QAAQ,OAAO,QAAQ,KAAK,YAAY;EAE9C,IAAI,SAAS;AACb,SAAO,SAAS,OAAO,QAAQ;GAC3B,MAAM,OAAO,OAAO;AACpB,OAAI,mBAAmB,KAAK,KAAK,CAC7B;AACJ;;AAEJ,MAAI,UAAU,MAAM,QAAQ,QAAQ;AAChC,YAAS,GAAG,OAAO,UAAU,GAAG,YAAY,CAAC,QAAQ,OAAO,UAAU,MAAM;AAC5E,SAAM,cAAc;QAGpB,OAAM,cAAc;;AAG5B,QAAO;;;;;;;;;;;;AAYX,SAAgB,sBAAsB,OAAO;AACzC,KAAI,CAAC,MAAM,SAAS,IAAI,IAAI,CAAC,MAAM,SAAS,IAAI,CAC5C,QAAO;AACX,QAAO,MAAM,QAAQ,kBAAkB,gBAAgB;;;;;;;;;;;;AAY3D,SAAgB,cAAc,SAAS;AACnC,QAAO,sBAAsB,kBAAkB,QAAQ,CAAC;;;;;;;;;AAS5D,SAAgB,eAAe,OAAO,SAAS,KAAK;AAChD,KAAI,MAAM,UAAU,OAChB,QAAO;EAAE;EAAO,WAAW;EAAO;CAEtC,MAAM,SAAS,KAAK,IAAI,GAAG,SAAS,EAAE;AACtC,QAAO;EAAE,OAAO,GAAG,MAAM,UAAU,GAAG,OAAO,CAAC;EAAM,WAAW;EAAM;;;;;ACrGzE,IAAa,YAAb,MAAuB;CACnB;CACA;;;;CAIA,UAAU,EAAE;;;;CAIZ,OAAO;EACH,QAAQ,EAAE;EACV,QAAQ,EAAE;EACb;;;;CAID;;;;CAIA;CACA,YAAY,MAAM,SAAS;AACvB,OAAK,OAAO;AACZ,OAAK,UAAU;;;;;CAKnB,SAAS;AACL,SAAO;GACH,MAAM,KAAK;GACX,MAAM,KAAK;GACX,OAAO,KAAK;GACZ,gBAAgB,KAAK;GACrB,SAAS,KAAK,QAAQ,KAAK,YAAY;IACnC,MAAM,OAAO;IACb,MAAM,OAAO;IACb,KAAK,OAAO;IACZ,MAAM,OAAO;IACb,KAAK,OAAO;IACZ,OAAO,OAAO;IACd,UAAU,OAAO;IACjB,YAAY,OAAO;IACnB,MAAM,OAAO;IACb,OAAO,OAAO;IACd,MAAM,OAAO;IAChB,EAAE;GACN;;;AAIT,IAAa,aAAb,MAAwB;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,KAAK;AACb,OAAK,OAAO,IAAI,QAAQ,IAAI,OAAO;AACnC,OAAK,OAAO,IAAI,QAAQ,IAAI,OAAO;AACnC,OAAK,MAAM,IAAI,OAAO,IAAI,OAAO;AACjC,OAAK,OAAO,IAAI,QAAQ,IAAI,OAAO;AACnC,OAAK,MAAM,IAAI,OAAO,IAAI,OAAO;AACjC,OAAK,QAAQ,IAAI,SAAS,IAAI,OAAO;AACrC,OAAK,WAAW,IAAI,YAAY,IAAI,OAAO;AAC3C,OAAK,aAAa,IAAI,cAAc,IAAI,OAAO;AAC/C,OAAK,OAAO,IAAI,QAAQ,IAAI,OAAO;AACnC,OAAK,QAAQ,IAAI;AACjB,OAAK,OAAO,IAAI;;CAEpB,UAAU;EACN,MAAM,OAAO,EAAE;AACf,MAAI,KAAK,KACL,MAAK,KAAK,OAAO;AACrB,MAAI,KAAK,KACL,MAAK,KAAK,OAAO;AACrB,MAAI,KAAK,IACL,MAAK,KAAK,MAAM;AACpB,MAAI,KAAK,KACL,MAAK,KAAK,OAAO;AACrB,MAAI,KAAK,IACL,MAAK,KAAK,MAAM;AACpB,MAAI,KAAK,MACL,MAAK,KAAK,QAAQ;AACtB,MAAI,KAAK,SACL,MAAK,KAAK,WAAW;AACzB,MAAI,KAAK,WACL,MAAK,KAAK,aAAa;AAC3B,MAAI,KAAK,KACL,MAAK,KAAK,OAAO;AACrB,MAAI,KAAK,MACL,MAAK,KAAK,QAAQ;AACtB,SAAO;;;;;;;ACnGf,MAAM,mBAAmB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACH,CAAC;AACF,SAAgB,gBAAgB,SAAS,SAAS;AAC9C,KAAI,QAAQ,YAAY,QACpB,QAAO;AACX,KAAI,SAAS,aACT,QAAO,QAAQ;AACnB,KAAI,QAAQ,WAAW,WACnB,QAAO;AACX,KAAI,iBAAiB,IAAI,QAAQ,MAAM,CACnC,QAAO;AACX,QAAO;;;;;;AAMX,SAAS,cAAc,OAAO;AAC1B,KAAI,CAAC,MACD,QAAO;CACX,MAAM,YAAY,IAAI,MAAM,cAAc,MAAM,QAAQ,CAAC;AACzD,WAAU,OAAO,MAAM;AACvB,WAAU,QAAQ,MAAM,QAAQ,cAAc,MAAM,MAAM,GAAG;AAC7D,QAAO;;;;;;AAMX,SAAS,gBAAgB,SAAS;AAC9B,KAAI,QAAQ,YAAY,OACpB,QAAO,EAAE,kBAAkB,OAAO;CAGtC,MAAM,EAAE,OAAO,cAAc,eADZ,cAAc,QAAQ,QAAQ,CACM;AACrD,QAAO;EAAE,kBAAkB;EAAO,kBAAkB;EAAW;;;;;;;;;;;;AAYnE,SAAgB,aAAa,SAAS,kBAAkB;CACpD,MAAM,EAAE,UAAU;AAElB,KAAI,UAAU,iBAAiB;EAC3B,MAAMA,UAAQ,CAAC,gBAAgB;AAC/B,MAAI,QAAQ,WACR,SAAM,KAAK,OAAO,QAAQ,aAAa;AAC3C,MAAI,QAAQ,iBACR,SAAM,KAAK,aAAa,QAAQ,mBAAmB;AACvD,MAAI,QAAQ,kBAAkB,QAAQ,mBAAmB,aACrD,SAAM,KAAK,IAAI,QAAQ,eAAe,GAAG;AAE7C,SAAOA,QAAM,KAAK,IAAI;;CAE1B,MAAM,QAAQ,CAAC,OAAO,QAAQ,QAAQ;AAEtC,KAAI,qBAAqB,OACrB,OAAM,KAAK,iBAAiB;UAEvB,QAAQ,YAAY,QAAW;EAGpC,MAAM,EAAE,UAAU,eADD,cAAc,QAAQ,QAAQ,CACL;AAC1C,QAAM,KAAK,MAAM;YAEZ,QAAQ,SAAS,OACtB,OAAM,KAAK,QAAQ,KAAK;UAEnB,MAAM,SAAS,UAAU,IAAI,QAAQ,cAAc,OACxD,OAAM,KAAK,QAAQ,UAAU;UAExB,QAAQ,SAAS,OACtB,OAAM,KAAK,OAAO,QAAQ,KAAK,CAAC;UAE3B,QAAQ,YAAY,QAAW;EACpC,IAAI,aAAa,QAAQ;AACzB,MAAI,QAAQ,WAAW,OACnB,eAAc,IAAI,QAAQ;AAE9B,QAAM,KAAK,WAAW;YAEjB,QAAQ,QAAQ,OACrB,OAAM,KAAK,OAAO,QAAQ,IAAI,CAAC;UAE1B,QAAQ,aAAa,OAC1B,OAAM,KAAK,QAAQ,SAAS;UAEvB,QAAQ,aAAa,QAAW;EACrC,IAAI,aAAa,QAAQ;AACzB,MAAI,QAAQ,WAAW,OACnB,eAAc,WAAW,QAAQ;AAErC,QAAM,KAAK,WAAW;YAEjB,QAAQ,oBAAoB,QAAW;EAC5C,IAAI,iBAAiB,GAAG,QAAQ,gBAAgB;AAChD,MAAI,QAAQ,cACR,mBAAkB,KAAK,QAAQ,cAAc;AACjD,QAAM,KAAK,eAAe;YAErB,QAAQ,cAAc,OAC3B,OAAM,KAAK,QAAQ,UAAU;AAGjC,KAAI,QAAQ,YAAY,SACpB;MAAI,QAAQ,iBAAiB,OACzB,OAAM,KAAK,UAAU,QAAQ,eAAe;WAEvC,QAAQ,aAAa,OAC1B,OAAM,KAAK,mBAAmB,QAAQ,WAAW;;CAIzD,MAAM,iBAAiB,QAAQ,cAAc,SACvC,IAAI,QAAQ,WAAW,MAAM,QAAQ,UAAU,MAC/C,IAAI,QAAQ,WAAW;AAC7B,OAAM,KAAK,eAAe;AAC1B,QAAO,MAAM,KAAK,IAAI;;;;;;;;AAQ1B,SAAgB,kBAAkB,QAAQ,SAAS,SAAS;CAGxD,MAAM,uBAAuB,QAAQ,gBAAgB,QAAQ,OAAO;CACpE,MAAM,wBAAwB,uBACxB,cAAc,qBAAqB,GACnC;CACN,MAAM,kBAAkB,0BAA0B,SAC5C;EAAE,GAAG;EAAS,cAAc;EAAuB,GACnD;CAEN,MAAM,EAAE,kBAAkB,qBAAqB,gBAAgB,gBAAgB;CAC/E,MAAM,UAAU,aAAa,iBAAiB,iBAAiB;CAE/D,MAAM,UAAU,EAAE;AAClB,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,gBAAgB,EAAE;AACxD,MAAI,QAAQ,QACR;AACJ,UAAQ,OAAO;;AAGnB,KAAI,qBAAqB,QAAW;AAChC,UAAQ,UAAU;AAClB,MAAI,iBACA,SAAQ,mBAAmB;;CAGnC,MAAM,QAAQ,gBAAgB,iBAAiB,QAAQ;AACvD,KAAI,UAAU,QACV,QAAO,MAAM,SAAS,cAAc,QAAQ,MAAM,EAAE,QAAQ;UAEvD,UAAU,OACf,QAAO,KAAK,SAAS,QAAQ;UAExB,UAAU,QACf,QAAO,MAAM,SAAS,QAAQ;KAG9B,QAAO,KAAK,SAAS,QAAQ;;;;;;;;;;;;;AC1KrC,IAAW;CACV,SAAU,YAAU;AACjB,YAAS,WAAS,WAAW,KAAK;AAClC,YAAS,WAAS,UAAU,KAAK;AACjC,YAAS,WAAS,UAAU,KAAK;AACjC,YAAS,WAAS,WAAW,KAAK;GACnC,aAAa,WAAW,EAAE,EAAE;;;;;;;;;;ACP/B,MAAM,SAAS;CACX,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,KAAK;CACR;;;;;AAKD,IAAa,mBAAb,MAAa,iBAAiB;CAC1B;CACA;CACA;;;;;;;;CAQA,YAAY,aAAa,WAAWC,SAAa,MAAM,aAAa,cAAc;AAC9E,OAAK,cAAc;AACnB,OAAK,WAAW;AAChB,OAAK,aAAa;;;;;CAKtB,MAAM,SAAS,SAAS;AACpB,MAAI,KAAK,UAAUA,SAAa,MAAM,EAAE;GACpC,MAAM,UAAU,KAAK,aAAa,SAAS,SAAS,QAAQ;AAC5D,QAAK,OAAO,QAAQ,KAAK,QAAQ;;;;;;CAMzC,KAAK,SAAS,SAAS;AACnB,MAAI,KAAK,UAAUA,SAAa,KAAK,EAAE;GACnC,MAAM,UAAU,KAAK,aAAa,QAAQ,SAAS,QAAQ;AAC3D,QAAK,OAAO,QAAQ,KAAK,QAAQ;;;;;;CAMzC,KAAK,SAAS,SAAS;AACnB,MAAI,KAAK,UAAUA,SAAa,KAAK,EAAE;GACnC,MAAM,UAAU,KAAK,aAAa,QAAQ,SAAS,QAAQ;AAC3D,QAAK,OAAO,QAAQ,MAAM,QAAQ;;;;;;CAM1C,MAAM,SAAS,OAAO,SAAS;AAC3B,MAAI,KAAK,UAAUA,SAAa,MAAM,EAAE;GACpC,MAAM,UAAU,KAAK,aAAa,SAAS,SAAS,SAAS,MAAM;AACnE,QAAK,OAAO,QAAQ,OAAO,QAAQ;;;;;;CAM3C,MAAM,SAAS;AACX,SAAO,IAAI,iBAAiB;GAAE,GAAG,KAAK;GAAa,GAAG;GAAS,EAAE,KAAK,UAAU,KAAK,WAAW;;;;;CAKpG,UAAU,OAAO;AACb,SAAO,SAAS,KAAK;;;;;CAKzB,aAAa,OAAO,SAAS,SAAS,OAAO;EACzC,MAAM,UAAU;GACZ;GACA;GACA,GAAG,KAAK;GACR,GAAG;GACH,4BAAW,IAAI,MAAM,EAAC,aAAa;GACtC;AAED,MAAI,MACA,SAAQ,QAAQ;GACZ,SAAS,MAAM;GACf,OAAO,MAAM;GACb,MAAM,MAAM;GACf;AAEL,SAAO;;;;;CAKX,OAAO,WAAW,MAAM;AACpB,UAAQ,KAAK,YAAb;GACI,KAAK;AACD,SAAK,aAAa,WAAW,KAAK;AAClC;GACJ,KAAK;AACD,SAAK,eAAe,WAAW,KAAK;AACpC;GACJ,KAAK;AACD,SAAK,iBAAiB,WAAW,KAAK;AACtC;;;;;;CAMZ,eAAe,WAAW,MAAM;AAC5B,YAAU,KAAK,UAAU,KAAK,CAAC;;;;;CAKnC,iBAAiB,WAAW,MAAM;AAC9B,YAAU,KAAK;;;;;;;;;;CAUnB,aAAa,WAAW,MAAM;EAC1B,MAAM,EAAE,OAAO,SAAS,KAAK,WAAW,SAAS,WAAW,WAAW,WAAW,WAAW,WAAW,YAAY,gBAAgB,YAAY,OAAO,GAAG,SAAS;EACnK,MAAM,WAAW,OAAO,SAAS,OAAO,CAAC,aAAa;EACtD,MAAM,aAAa,KAAK,cAAc,SAAS;EAC/C,MAAM,iBAAiB,YAAY,IAAI,UAAU,KAAK;EAItD,IAAI,UAAU,GAHE,YACV,GAAG,OAAO,MAAM,IAAI,KAAK,UAAU,CAAC,aAAa,CAAC,UAAU,IAAI,GAAG,GAAG,OAAO,MAAM,KACnF,KACqB,aAAa,SAAS,OAAO,EAAE,GAAG,OAAO,MAAM,GAAG,eAAe,GAAG;EAE/F,MAAM,QAAQ,EAAE;AAChB,MAAI,QACA,OAAM,KAAK,SAAS,OAAO,QAAQ,CAAC,UAAU,GAAG,GAAG,GAAG;AAC3D,MAAI,UACA,OAAM,KAAK,OAAO,OAAO,UAAU,CAAC,UAAU,GAAG,GAAG,GAAG;AAC3D,MAAI,UACA,OAAM,KAAK,WAAW,YAAY;AACtC,MAAI,UACA,OAAM,KAAK,WAAW,OAAO,UAAU,CAAC,UAAU,GAAG,GAAG,GAAG;AAC/D,MAAI,UACA,OAAM,KAAK,QAAQ,YAAY;AACnC,MAAI,eAAe,OACf,OAAM,KAAK,OAAO,WAAW,IAAI;AAErC,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,EAAE;AAC7C,OAAI,UAAU,UAAa,UAAU,KACjC;GACJ,MAAM,IAAI,OAAO,UAAU,WACrB,KAAK,UAAU,MAAM,GACrB,KAAK,oBAAoB,OAAO,MAAM,CAAC;AAC7C,SAAM,KAAK,GAAG,IAAI,GAAG,IAAI;;AAG7B,MAAI,SAAS,OAAO,UAAU,UAAU;GACpC,MAAM,WAAW;AACjB,OAAI,SAAS,KACT,OAAM,KAAK,YAAY,KAAK,oBAAoB,SAAS,KAAK,GAAG;AACrE,OAAI,SAAS,QACT,OAAM,KAAK,WAAW,KAAK,oBAAoB,SAAS,QAAQ,GAAG;AACvE,OAAI,SAAS,MACT,OAAM,KAAK,aAAa,KAAK,oBAAoB,SAAS,MAAM,GAAG;;AAE3E,MAAI,MAAM,SAAS,EACf,YAAW,IAAI,OAAO,MAAM,MAAM,KAAK,IAAI,GAAG,OAAO;AAGzD,YAAU,QAAQ;;;;;;;CAOtB,oBAAoB,OAAO;AACvB,SAAO,MAAM,QAAQ,OAAO,MAAM,CAAC,QAAQ,OAAO,MAAM;;;;;CAK5D,cAAc,OAAO;AAEjB,UADmB,MAAM,aAAa,EACtC;GACI,KAAK,QACD,QAAO,OAAO;GAClB,KAAK,OACD,QAAO,OAAO;GAClB,KAAK,OACD,QAAO,OAAO;GAClB,KAAK,QACD,QAAO,OAAO;GAClB,QACI,QAAO,OAAO;;;;;;;;;;;;;;;;;;ACvM9B,IAAa,eAAb,MAAa,aAAa;;;;CAItB,OAAO,eAAe;;;;;;;;;CAStB,OAAO,WAAW;AAGd,SAAO,MADW,OAAO,YAAY,CAAC,QAAQ,MAAM,GAAG,CAAC,UAAU,GAAG,GAAG;;;;;;;;CAS5E,OAAO,YAAY,SAAS;AACxB,SAAO,QAAQ,IAAI,aAAa,aAAa;;;;;;;;CAQjD,OAAO,UAAU,SAAS;AACtB,SAAO,GAAG,aAAa,eAAe,SAAS;;;;;;;CAOnD,OAAO,gBAAgB;AACnB,SAAO,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACP5B,SAAgB,mBAAmB;AAC/B,QAAO;EACH,aAAa;EACb,YAAY;EACZ,YAAY;EACZ,aAAa;EACb,aAAa,kBAAkB;EAClC;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BL,SAAgB,aAAa,SAAS;CAClC,MAAM,WAAW,oBAAoB;CACrC,MAAM,aAAa,cAAc,QAAQ,UAAU;AAWnD,QAAO,IAAI,iBAVS;EAChB,GAAG;EACH,SAAS,QAAQ,WAAW,aAAa,UAAU;EACnD,WAAW,QAAQ;EACnB,gBAAgB,QAAQ,kBAAkB,UAAU,kBAAkB,IAAI;EAC1E,YAAY,QAAQ,cAChB,UAAU,WAAW,IACrB,UAAU,sBAAsB,IAChC;EACP,EACwC,UAAU,WAAW;;;;;;;;AAQlE,SAAS,qBAAqB;AAE1B,UADiB,UAAU,oBAAoB,IAAI,QAClC,aAAa,EAA9B;EACI,KAAK,QACD,QAAOC,SAAa;EACxB,KAAK,OACD,QAAOA,SAAa;EACxB,KAAK,OACD,QAAOA,SAAa;EACxB,KAAK,QACD,QAAOA,SAAa;EACxB,QAEI,QAAOA,SAAa;;;;;;;;;;;;;;AAchC,SAAS,cAAc,WAAW;AAE9B,KADe,UAAU,qBAAqB,EAClC,aAAa,KAAK,SAC1B,QAAO;AAEX,KAAI,cAAc,eAAe,cAAc,WAC3C,QAAO;AAEX,QAAO;;;;;;;AAOX,SAAS,UAAU,MAAM;AAErB,KAAI,OAAO,YAAY,eAAe,QAAQ,IAC1C,QAAO,QAAQ,IAAI;AAGvB,KAAI,OAAO,QAAQ,aAAa;EAC5B,MAAM,SAAS,IAAI;AACnB,MAAI,OACA,QAAO,OAAO;;;;;;;;;;;ACnJ1B,SAAgB,YAAY,KAAK;AAC7B,QAAO,IAAI,IAAI,QAAQ,MAAM,QAAQ,CAAC;;;;;;;;;;;;;;;;;ACM1C,SAAgB,eAAe,QAAQ,eAAe,EAAE,MAAM,EAAE,EAAE,EAAE;CAChE,MAAM,SAAS,EAAE;CACjB,IAAI,IAAI;AACR,QAAO,IAAI,OAAO,QAAQ;EACtB,MAAM,eAAe,OAAO,QAAQ,MAAM,EAAE;AAC5C,MAAI,iBAAiB,GACjB;EACJ,MAAM,UAAU,OAAO,UAAU,GAAG,aAAa;EACjD,MAAM,OAAO,QAAQ,SAAS,KAAK,GAAG,QAAQ,MAAM,GAAG,GAAG,GAAG;AAC7D,MAAI,eAAe;AACnB,MAAI,SAAS,MAAM,aAAa,KAAK,SAAS,GAAG;AAC7C,UAAO,KAAK;IACR,OAAO,aAAa;IACpB,MAAM,aAAa,KAAK,KAAK,KAAK;IACrC,CAAC;AACF,kBAAe,EAAE,MAAM,EAAE,EAAE;AAC3B;;AAEJ,MAAI,KAAK,WAAW,SAAS,EAAE;AAI3B,gBAAa,QAHC,KAAK,WAAW,UAAU,GAClC,KAAK,UAAU,EAAE,GACjB,KAAK,UAAU,EAAE;AAEvB;;AAEJ,MAAI,KAAK,WAAW,QAAQ,EAAE;GAC1B,MAAM,QAAQ,KAAK,WAAW,SAAS,GACjC,KAAK,UAAU,EAAE,GACjB,KAAK,UAAU,EAAE;AACvB,gBAAa,KAAK,KAAK,MAAM;;;AAGrC,QAAO;EACH;EACA,WAAW,OAAO,UAAU,EAAE;EAC9B;EACH;;;;;ACtCL,SAAgB,aAAa,OAAO;AAChC,QAAQ,SACJ,OAAO,MAAM,YAAY,aACzB,OAAO,MAAM,aAAa,YAC1B,OAAO,MAAM,WAAW,YACxB,OAAO,MAAM,WAAW;;AAEhC,SAAgB,UAAU,OAAO;AAC7B,QAAQ,SACJ,OAAO,MAAM,OAAO,YACpB,OAAO,MAAM,YAAY,YACzB,OAAO,MAAM,WAAW;;AAEhC,SAAgB,gBAAgB,OAAO;AACnC,QAAO;EACH;EACA;EACA;EACA;EACA;EACA;EACH,CAAC,SAAS,MAAM"}
|