@cloudflare/sandbox 0.5.1 → 0.5.2
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/.turbo/turbo-build.log +17 -9
- package/CHANGELOG.md +12 -0
- package/dist/dist-gVyG2H2h.js +612 -0
- package/dist/dist-gVyG2H2h.js.map +1 -0
- package/dist/index.d.ts +2 -1726
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -686
- package/dist/index.js.map +1 -1
- package/dist/openai/index.d.ts +67 -0
- package/dist/openai/index.d.ts.map +1 -0
- package/dist/openai/index.js +362 -0
- package/dist/openai/index.js.map +1 -0
- package/dist/sandbox-B3vJ541e.d.ts +1729 -0
- package/dist/sandbox-B3vJ541e.d.ts.map +1 -0
- package/package.json +15 -1
- package/src/openai/index.ts +465 -0
- package/src/sandbox.ts +47 -38
- package/src/version.ts +1 -1
- package/tests/git-client.test.ts +7 -39
- package/tests/openai-shell-editor.test.ts +434 -0
- package/tests/port-client.test.ts +25 -35
- package/tests/process-client.test.ts +73 -107
- package/tests/sandbox.test.ts +17 -11
- package/tsconfig.json +2 -2
- package/tsdown.config.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -1,667 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as createLogger, c as Execution, d as getEnvString, i as shellEscape, l as ResultImpl, n as isProcess, o as createNoOpLogger, r as isProcessStatus, s as TraceContext, t as isExecResult, u as GitLogger } from "./dist-gVyG2H2h.js";
|
|
2
2
|
import { Container, getContainer, switchPort } from "@cloudflare/containers";
|
|
3
3
|
|
|
4
|
-
//#region ../shared/dist/env.js
|
|
5
|
-
/**
|
|
6
|
-
* Safely extract a string value from an environment object
|
|
7
|
-
*
|
|
8
|
-
* @param env - Environment object with dynamic keys
|
|
9
|
-
* @param key - The environment variable key to access
|
|
10
|
-
* @returns The string value if present and is a string, undefined otherwise
|
|
11
|
-
*/
|
|
12
|
-
function getEnvString(env, key) {
|
|
13
|
-
const value = env?.[key];
|
|
14
|
-
return typeof value === "string" ? value : void 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region ../shared/dist/git.js
|
|
19
|
-
/**
|
|
20
|
-
* Redact credentials from URLs for secure logging
|
|
21
|
-
*
|
|
22
|
-
* Replaces any credentials (username:password, tokens, etc.) embedded
|
|
23
|
-
* in URLs with ****** to prevent sensitive data exposure in logs.
|
|
24
|
-
* Works with URLs embedded in text (e.g., "Error: https://token@github.com/repo.git failed")
|
|
25
|
-
*
|
|
26
|
-
* @param text - String that may contain URLs with credentials
|
|
27
|
-
* @returns String with credentials redacted from any URLs
|
|
28
|
-
*/
|
|
29
|
-
function redactCredentials(text) {
|
|
30
|
-
let result = text;
|
|
31
|
-
let pos = 0;
|
|
32
|
-
while (pos < result.length) {
|
|
33
|
-
const httpPos = result.indexOf("http://", pos);
|
|
34
|
-
const httpsPos = result.indexOf("https://", pos);
|
|
35
|
-
let protocolPos = -1;
|
|
36
|
-
let protocolLen = 0;
|
|
37
|
-
if (httpPos === -1 && httpsPos === -1) break;
|
|
38
|
-
if (httpPos !== -1 && (httpsPos === -1 || httpPos < httpsPos)) {
|
|
39
|
-
protocolPos = httpPos;
|
|
40
|
-
protocolLen = 7;
|
|
41
|
-
} else {
|
|
42
|
-
protocolPos = httpsPos;
|
|
43
|
-
protocolLen = 8;
|
|
44
|
-
}
|
|
45
|
-
const searchStart = protocolPos + protocolLen;
|
|
46
|
-
const atPos = result.indexOf("@", searchStart);
|
|
47
|
-
let urlEnd = searchStart;
|
|
48
|
-
while (urlEnd < result.length) {
|
|
49
|
-
const char = result[urlEnd];
|
|
50
|
-
if (/[\s"'`<>,;{}[\]]/.test(char)) break;
|
|
51
|
-
urlEnd++;
|
|
52
|
-
}
|
|
53
|
-
if (atPos !== -1 && atPos < urlEnd) {
|
|
54
|
-
result = `${result.substring(0, searchStart)}******${result.substring(atPos)}`;
|
|
55
|
-
pos = searchStart + 6;
|
|
56
|
-
} else pos = protocolPos + protocolLen;
|
|
57
|
-
}
|
|
58
|
-
return result;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Sanitize data by redacting credentials from any strings
|
|
62
|
-
* Recursively processes objects and arrays to ensure credentials are never leaked
|
|
63
|
-
*/
|
|
64
|
-
function sanitizeGitData(data) {
|
|
65
|
-
if (typeof data === "string") return redactCredentials(data);
|
|
66
|
-
if (data === null || data === void 0) return data;
|
|
67
|
-
if (Array.isArray(data)) return data.map((item) => sanitizeGitData(item));
|
|
68
|
-
if (typeof data === "object") {
|
|
69
|
-
const result = {};
|
|
70
|
-
for (const [key, value] of Object.entries(data)) result[key] = sanitizeGitData(value);
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
return data;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Logger wrapper that automatically sanitizes git credentials
|
|
77
|
-
*/
|
|
78
|
-
var GitLogger = class GitLogger {
|
|
79
|
-
baseLogger;
|
|
80
|
-
constructor(baseLogger) {
|
|
81
|
-
this.baseLogger = baseLogger;
|
|
82
|
-
}
|
|
83
|
-
sanitizeContext(context) {
|
|
84
|
-
return context ? sanitizeGitData(context) : context;
|
|
85
|
-
}
|
|
86
|
-
sanitizeError(error) {
|
|
87
|
-
if (!error) return error;
|
|
88
|
-
const sanitized = new Error(redactCredentials(error.message));
|
|
89
|
-
sanitized.name = error.name;
|
|
90
|
-
if (error.stack) sanitized.stack = redactCredentials(error.stack);
|
|
91
|
-
const sanitizedRecord = sanitized;
|
|
92
|
-
const errorRecord = error;
|
|
93
|
-
for (const key of Object.keys(error)) if (key !== "message" && key !== "stack" && key !== "name") sanitizedRecord[key] = sanitizeGitData(errorRecord[key]);
|
|
94
|
-
return sanitized;
|
|
95
|
-
}
|
|
96
|
-
debug(message, context) {
|
|
97
|
-
this.baseLogger.debug(message, this.sanitizeContext(context));
|
|
98
|
-
}
|
|
99
|
-
info(message, context) {
|
|
100
|
-
this.baseLogger.info(message, this.sanitizeContext(context));
|
|
101
|
-
}
|
|
102
|
-
warn(message, context) {
|
|
103
|
-
this.baseLogger.warn(message, this.sanitizeContext(context));
|
|
104
|
-
}
|
|
105
|
-
error(message, error, context) {
|
|
106
|
-
this.baseLogger.error(message, this.sanitizeError(error), this.sanitizeContext(context));
|
|
107
|
-
}
|
|
108
|
-
child(context) {
|
|
109
|
-
const sanitized = sanitizeGitData(context);
|
|
110
|
-
return new GitLogger(this.baseLogger.child(sanitized));
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
//#endregion
|
|
115
|
-
//#region ../shared/dist/interpreter-types.js
|
|
116
|
-
var Execution = class {
|
|
117
|
-
code;
|
|
118
|
-
context;
|
|
119
|
-
/**
|
|
120
|
-
* All results from the execution
|
|
121
|
-
*/
|
|
122
|
-
results = [];
|
|
123
|
-
/**
|
|
124
|
-
* Accumulated stdout and stderr
|
|
125
|
-
*/
|
|
126
|
-
logs = {
|
|
127
|
-
stdout: [],
|
|
128
|
-
stderr: []
|
|
129
|
-
};
|
|
130
|
-
/**
|
|
131
|
-
* Execution error if any
|
|
132
|
-
*/
|
|
133
|
-
error;
|
|
134
|
-
/**
|
|
135
|
-
* Execution count (for interpreter)
|
|
136
|
-
*/
|
|
137
|
-
executionCount;
|
|
138
|
-
constructor(code, context) {
|
|
139
|
-
this.code = code;
|
|
140
|
-
this.context = context;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Convert to a plain object for serialization
|
|
144
|
-
*/
|
|
145
|
-
toJSON() {
|
|
146
|
-
return {
|
|
147
|
-
code: this.code,
|
|
148
|
-
logs: this.logs,
|
|
149
|
-
error: this.error,
|
|
150
|
-
executionCount: this.executionCount,
|
|
151
|
-
results: this.results.map((result) => ({
|
|
152
|
-
text: result.text,
|
|
153
|
-
html: result.html,
|
|
154
|
-
png: result.png,
|
|
155
|
-
jpeg: result.jpeg,
|
|
156
|
-
svg: result.svg,
|
|
157
|
-
latex: result.latex,
|
|
158
|
-
markdown: result.markdown,
|
|
159
|
-
javascript: result.javascript,
|
|
160
|
-
json: result.json,
|
|
161
|
-
chart: result.chart,
|
|
162
|
-
data: result.data
|
|
163
|
-
}))
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
var ResultImpl = class {
|
|
168
|
-
raw;
|
|
169
|
-
constructor(raw) {
|
|
170
|
-
this.raw = raw;
|
|
171
|
-
}
|
|
172
|
-
get text() {
|
|
173
|
-
return this.raw.text || this.raw.data?.["text/plain"];
|
|
174
|
-
}
|
|
175
|
-
get html() {
|
|
176
|
-
return this.raw.html || this.raw.data?.["text/html"];
|
|
177
|
-
}
|
|
178
|
-
get png() {
|
|
179
|
-
return this.raw.png || this.raw.data?.["image/png"];
|
|
180
|
-
}
|
|
181
|
-
get jpeg() {
|
|
182
|
-
return this.raw.jpeg || this.raw.data?.["image/jpeg"];
|
|
183
|
-
}
|
|
184
|
-
get svg() {
|
|
185
|
-
return this.raw.svg || this.raw.data?.["image/svg+xml"];
|
|
186
|
-
}
|
|
187
|
-
get latex() {
|
|
188
|
-
return this.raw.latex || this.raw.data?.["text/latex"];
|
|
189
|
-
}
|
|
190
|
-
get markdown() {
|
|
191
|
-
return this.raw.markdown || this.raw.data?.["text/markdown"];
|
|
192
|
-
}
|
|
193
|
-
get javascript() {
|
|
194
|
-
return this.raw.javascript || this.raw.data?.["application/javascript"];
|
|
195
|
-
}
|
|
196
|
-
get json() {
|
|
197
|
-
return this.raw.json || this.raw.data?.["application/json"];
|
|
198
|
-
}
|
|
199
|
-
get chart() {
|
|
200
|
-
return this.raw.chart;
|
|
201
|
-
}
|
|
202
|
-
get data() {
|
|
203
|
-
return this.raw.data;
|
|
204
|
-
}
|
|
205
|
-
formats() {
|
|
206
|
-
const formats = [];
|
|
207
|
-
if (this.text) formats.push("text");
|
|
208
|
-
if (this.html) formats.push("html");
|
|
209
|
-
if (this.png) formats.push("png");
|
|
210
|
-
if (this.jpeg) formats.push("jpeg");
|
|
211
|
-
if (this.svg) formats.push("svg");
|
|
212
|
-
if (this.latex) formats.push("latex");
|
|
213
|
-
if (this.markdown) formats.push("markdown");
|
|
214
|
-
if (this.javascript) formats.push("javascript");
|
|
215
|
-
if (this.json) formats.push("json");
|
|
216
|
-
if (this.chart) formats.push("chart");
|
|
217
|
-
return formats;
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
//#endregion
|
|
222
|
-
//#region ../shared/dist/logger/types.js
|
|
223
|
-
/**
|
|
224
|
-
* Logger types for Cloudflare Sandbox SDK
|
|
225
|
-
*
|
|
226
|
-
* Provides structured, trace-aware logging across Worker, Durable Object, and Container.
|
|
227
|
-
*/
|
|
228
|
-
/**
|
|
229
|
-
* Log levels (from most to least verbose)
|
|
230
|
-
*/
|
|
231
|
-
var LogLevel;
|
|
232
|
-
(function(LogLevel$1) {
|
|
233
|
-
LogLevel$1[LogLevel$1["DEBUG"] = 0] = "DEBUG";
|
|
234
|
-
LogLevel$1[LogLevel$1["INFO"] = 1] = "INFO";
|
|
235
|
-
LogLevel$1[LogLevel$1["WARN"] = 2] = "WARN";
|
|
236
|
-
LogLevel$1[LogLevel$1["ERROR"] = 3] = "ERROR";
|
|
237
|
-
})(LogLevel || (LogLevel = {}));
|
|
238
|
-
|
|
239
|
-
//#endregion
|
|
240
|
-
//#region ../shared/dist/logger/logger.js
|
|
241
|
-
/**
|
|
242
|
-
* Logger implementation
|
|
243
|
-
*/
|
|
244
|
-
/**
|
|
245
|
-
* ANSI color codes for terminal output
|
|
246
|
-
*/
|
|
247
|
-
const COLORS = {
|
|
248
|
-
reset: "\x1B[0m",
|
|
249
|
-
debug: "\x1B[36m",
|
|
250
|
-
info: "\x1B[32m",
|
|
251
|
-
warn: "\x1B[33m",
|
|
252
|
-
error: "\x1B[31m",
|
|
253
|
-
dim: "\x1B[2m"
|
|
254
|
-
};
|
|
255
|
-
/**
|
|
256
|
-
* CloudflareLogger implements structured logging with support for
|
|
257
|
-
* both JSON output (production) and pretty printing (development).
|
|
258
|
-
*/
|
|
259
|
-
var CloudflareLogger = class CloudflareLogger {
|
|
260
|
-
baseContext;
|
|
261
|
-
minLevel;
|
|
262
|
-
pretty;
|
|
263
|
-
/**
|
|
264
|
-
* Create a new CloudflareLogger
|
|
265
|
-
*
|
|
266
|
-
* @param baseContext Base context included in all log entries
|
|
267
|
-
* @param minLevel Minimum log level to output (default: INFO)
|
|
268
|
-
* @param pretty Enable pretty printing for human-readable output (default: false)
|
|
269
|
-
*/
|
|
270
|
-
constructor(baseContext, minLevel = LogLevel.INFO, pretty = false) {
|
|
271
|
-
this.baseContext = baseContext;
|
|
272
|
-
this.minLevel = minLevel;
|
|
273
|
-
this.pretty = pretty;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Log debug-level message
|
|
277
|
-
*/
|
|
278
|
-
debug(message, context) {
|
|
279
|
-
if (this.shouldLog(LogLevel.DEBUG)) {
|
|
280
|
-
const logData = this.buildLogData("debug", message, context);
|
|
281
|
-
this.output(console.log, logData);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Log info-level message
|
|
286
|
-
*/
|
|
287
|
-
info(message, context) {
|
|
288
|
-
if (this.shouldLog(LogLevel.INFO)) {
|
|
289
|
-
const logData = this.buildLogData("info", message, context);
|
|
290
|
-
this.output(console.log, logData);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Log warning-level message
|
|
295
|
-
*/
|
|
296
|
-
warn(message, context) {
|
|
297
|
-
if (this.shouldLog(LogLevel.WARN)) {
|
|
298
|
-
const logData = this.buildLogData("warn", message, context);
|
|
299
|
-
this.output(console.warn, logData);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Log error-level message
|
|
304
|
-
*/
|
|
305
|
-
error(message, error, context) {
|
|
306
|
-
if (this.shouldLog(LogLevel.ERROR)) {
|
|
307
|
-
const logData = this.buildLogData("error", message, context, error);
|
|
308
|
-
this.output(console.error, logData);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Create a child logger with additional context
|
|
313
|
-
*/
|
|
314
|
-
child(context) {
|
|
315
|
-
return new CloudflareLogger({
|
|
316
|
-
...this.baseContext,
|
|
317
|
-
...context
|
|
318
|
-
}, this.minLevel, this.pretty);
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Check if a log level should be output
|
|
322
|
-
*/
|
|
323
|
-
shouldLog(level) {
|
|
324
|
-
return level >= this.minLevel;
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Build log data object
|
|
328
|
-
*/
|
|
329
|
-
buildLogData(level, message, context, error) {
|
|
330
|
-
const logData = {
|
|
331
|
-
level,
|
|
332
|
-
msg: message,
|
|
333
|
-
...this.baseContext,
|
|
334
|
-
...context,
|
|
335
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
336
|
-
};
|
|
337
|
-
if (error) logData.error = {
|
|
338
|
-
message: error.message,
|
|
339
|
-
stack: error.stack,
|
|
340
|
-
name: error.name
|
|
341
|
-
};
|
|
342
|
-
return logData;
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Output log data to console (pretty or JSON)
|
|
346
|
-
*/
|
|
347
|
-
output(consoleFn, data) {
|
|
348
|
-
if (this.pretty) this.outputPretty(consoleFn, data);
|
|
349
|
-
else this.outputJson(consoleFn, data);
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Output as JSON (production)
|
|
353
|
-
*/
|
|
354
|
-
outputJson(consoleFn, data) {
|
|
355
|
-
consoleFn(JSON.stringify(data));
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Output as pretty-printed, colored text (development)
|
|
359
|
-
*
|
|
360
|
-
* Format: LEVEL [component] message (trace: tr_...) {context}
|
|
361
|
-
* Example: INFO [sandbox-do] Command started (trace: tr_7f3a9b2c) {commandId: "cmd-123"}
|
|
362
|
-
*/
|
|
363
|
-
outputPretty(consoleFn, data) {
|
|
364
|
-
const { level, msg, timestamp, traceId, component, sandboxId, sessionId, processId, commandId, operation, duration, error, ...rest } = data;
|
|
365
|
-
const levelStr = String(level || "INFO").toUpperCase();
|
|
366
|
-
const levelColor = this.getLevelColor(levelStr);
|
|
367
|
-
const componentBadge = component ? `[${component}]` : "";
|
|
368
|
-
const traceIdShort = traceId ? String(traceId).substring(0, 12) : "";
|
|
369
|
-
let logLine = `${levelColor}${levelStr.padEnd(5)}${COLORS.reset} ${componentBadge} ${msg}`;
|
|
370
|
-
if (traceIdShort) logLine += ` ${COLORS.dim}(trace: ${traceIdShort})${COLORS.reset}`;
|
|
371
|
-
const contextFields = [];
|
|
372
|
-
if (operation) contextFields.push(`operation: ${operation}`);
|
|
373
|
-
if (commandId) contextFields.push(`commandId: ${String(commandId).substring(0, 12)}`);
|
|
374
|
-
if (sandboxId) contextFields.push(`sandboxId: ${sandboxId}`);
|
|
375
|
-
if (sessionId) contextFields.push(`sessionId: ${String(sessionId).substring(0, 12)}`);
|
|
376
|
-
if (processId) contextFields.push(`processId: ${processId}`);
|
|
377
|
-
if (duration !== void 0) contextFields.push(`duration: ${duration}ms`);
|
|
378
|
-
if (contextFields.length > 0) logLine += ` ${COLORS.dim}{${contextFields.join(", ")}}${COLORS.reset}`;
|
|
379
|
-
consoleFn(logLine);
|
|
380
|
-
if (error && typeof error === "object") {
|
|
381
|
-
const errorObj = error;
|
|
382
|
-
if (errorObj.message) consoleFn(` ${COLORS.error}Error: ${errorObj.message}${COLORS.reset}`);
|
|
383
|
-
if (errorObj.stack) consoleFn(` ${COLORS.dim}${errorObj.stack}${COLORS.reset}`);
|
|
384
|
-
}
|
|
385
|
-
if (Object.keys(rest).length > 0) consoleFn(` ${COLORS.dim}${JSON.stringify(rest, null, 2)}${COLORS.reset}`);
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* Get ANSI color code for log level
|
|
389
|
-
*/
|
|
390
|
-
getLevelColor(level) {
|
|
391
|
-
switch (level.toLowerCase()) {
|
|
392
|
-
case "debug": return COLORS.debug;
|
|
393
|
-
case "info": return COLORS.info;
|
|
394
|
-
case "warn": return COLORS.warn;
|
|
395
|
-
case "error": return COLORS.error;
|
|
396
|
-
default: return COLORS.reset;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
//#endregion
|
|
402
|
-
//#region ../shared/dist/logger/trace-context.js
|
|
403
|
-
/**
|
|
404
|
-
* Trace context utilities for request correlation
|
|
405
|
-
*
|
|
406
|
-
* Trace IDs enable correlating logs across distributed components:
|
|
407
|
-
* Worker → Durable Object → Container → back
|
|
408
|
-
*
|
|
409
|
-
* The trace ID is propagated via the X-Trace-Id HTTP header.
|
|
410
|
-
*/
|
|
411
|
-
/**
|
|
412
|
-
* Utility for managing trace context across distributed components
|
|
413
|
-
*/
|
|
414
|
-
var TraceContext = class TraceContext {
|
|
415
|
-
/**
|
|
416
|
-
* HTTP header name for trace ID propagation
|
|
417
|
-
*/
|
|
418
|
-
static TRACE_HEADER = "X-Trace-Id";
|
|
419
|
-
/**
|
|
420
|
-
* Generate a new trace ID
|
|
421
|
-
*
|
|
422
|
-
* Format: "tr_" + 16 random hex characters
|
|
423
|
-
* Example: "tr_7f3a9b2c4e5d6f1a"
|
|
424
|
-
*
|
|
425
|
-
* @returns Newly generated trace ID
|
|
426
|
-
*/
|
|
427
|
-
static generate() {
|
|
428
|
-
return `tr_${crypto.randomUUID().replace(/-/g, "").substring(0, 16)}`;
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Extract trace ID from HTTP request headers
|
|
432
|
-
*
|
|
433
|
-
* @param headers Request headers
|
|
434
|
-
* @returns Trace ID if present, null otherwise
|
|
435
|
-
*/
|
|
436
|
-
static fromHeaders(headers) {
|
|
437
|
-
return headers.get(TraceContext.TRACE_HEADER);
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Create headers object with trace ID for outgoing requests
|
|
441
|
-
*
|
|
442
|
-
* @param traceId Trace ID to include
|
|
443
|
-
* @returns Headers object with X-Trace-Id set
|
|
444
|
-
*/
|
|
445
|
-
static toHeaders(traceId) {
|
|
446
|
-
return { [TraceContext.TRACE_HEADER]: traceId };
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Get the header name used for trace ID propagation
|
|
450
|
-
*
|
|
451
|
-
* @returns Header name ("X-Trace-Id")
|
|
452
|
-
*/
|
|
453
|
-
static getHeaderName() {
|
|
454
|
-
return TraceContext.TRACE_HEADER;
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
//#endregion
|
|
459
|
-
//#region ../shared/dist/logger/index.js
|
|
460
|
-
/**
|
|
461
|
-
* Logger module
|
|
462
|
-
*
|
|
463
|
-
* Provides structured, trace-aware logging with:
|
|
464
|
-
* - AsyncLocalStorage for implicit context propagation
|
|
465
|
-
* - Pretty printing for local development
|
|
466
|
-
* - JSON output for production
|
|
467
|
-
* - Environment auto-detection
|
|
468
|
-
* - Log level configuration
|
|
469
|
-
*
|
|
470
|
-
* Usage:
|
|
471
|
-
*
|
|
472
|
-
* ```typescript
|
|
473
|
-
* // Create a logger at entry point
|
|
474
|
-
* const logger = createLogger({ component: 'sandbox-do', traceId: 'tr_abc123' });
|
|
475
|
-
*
|
|
476
|
-
* // Store in AsyncLocalStorage for entire request
|
|
477
|
-
* await runWithLogger(logger, async () => {
|
|
478
|
-
* await handleRequest();
|
|
479
|
-
* });
|
|
480
|
-
*
|
|
481
|
-
* // Retrieve logger anywhere in call stack
|
|
482
|
-
* const logger = getLogger();
|
|
483
|
-
* logger.info('Operation started');
|
|
484
|
-
*
|
|
485
|
-
* // Add operation-specific context
|
|
486
|
-
* const execLogger = logger.child({ operation: 'exec', commandId: 'cmd-456' });
|
|
487
|
-
* await runWithLogger(execLogger, async () => {
|
|
488
|
-
* // All nested calls automatically get execLogger
|
|
489
|
-
* await executeCommand();
|
|
490
|
-
* });
|
|
491
|
-
* ```
|
|
492
|
-
*/
|
|
493
|
-
/**
|
|
494
|
-
* Create a no-op logger for testing
|
|
495
|
-
*
|
|
496
|
-
* Returns a logger that implements the Logger interface but does nothing.
|
|
497
|
-
* Useful for tests that don't need actual logging output.
|
|
498
|
-
*
|
|
499
|
-
* @returns No-op logger instance
|
|
500
|
-
*
|
|
501
|
-
* @example
|
|
502
|
-
* ```typescript
|
|
503
|
-
* // In tests
|
|
504
|
-
* const client = new HttpClient({
|
|
505
|
-
* baseUrl: 'http://test.com',
|
|
506
|
-
* logger: createNoOpLogger() // Optional - tests can enable real logging if needed
|
|
507
|
-
* });
|
|
508
|
-
* ```
|
|
509
|
-
*/
|
|
510
|
-
function createNoOpLogger() {
|
|
511
|
-
return {
|
|
512
|
-
debug: () => {},
|
|
513
|
-
info: () => {},
|
|
514
|
-
warn: () => {},
|
|
515
|
-
error: () => {},
|
|
516
|
-
child: () => createNoOpLogger()
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* AsyncLocalStorage for logger context
|
|
521
|
-
*
|
|
522
|
-
* Enables implicit logger propagation throughout the call stack without
|
|
523
|
-
* explicit parameter passing. The logger is stored per async context.
|
|
524
|
-
*/
|
|
525
|
-
const loggerStorage = new AsyncLocalStorage();
|
|
526
|
-
/**
|
|
527
|
-
* Run a function with a logger stored in AsyncLocalStorage
|
|
528
|
-
*
|
|
529
|
-
* The logger is available to all code within the function via getLogger().
|
|
530
|
-
* This is typically called at request entry points (fetch handler) and when
|
|
531
|
-
* creating child loggers with additional context.
|
|
532
|
-
*
|
|
533
|
-
* @param logger Logger instance to store in context
|
|
534
|
-
* @param fn Function to execute with logger context
|
|
535
|
-
* @returns Result of the function
|
|
536
|
-
*
|
|
537
|
-
* @example
|
|
538
|
-
* ```typescript
|
|
539
|
-
* // At request entry point
|
|
540
|
-
* async fetch(request: Request): Promise<Response> {
|
|
541
|
-
* const logger = createLogger({ component: 'sandbox-do', traceId: 'tr_abc' });
|
|
542
|
-
* return runWithLogger(logger, async () => {
|
|
543
|
-
* return await this.handleRequest(request);
|
|
544
|
-
* });
|
|
545
|
-
* }
|
|
546
|
-
*
|
|
547
|
-
* // When adding operation context
|
|
548
|
-
* async exec(command: string) {
|
|
549
|
-
* const logger = getLogger().child({ operation: 'exec', commandId: 'cmd-123' });
|
|
550
|
-
* return runWithLogger(logger, async () => {
|
|
551
|
-
* logger.info('Command started');
|
|
552
|
-
* await this.executeCommand(command); // Nested calls get the child logger
|
|
553
|
-
* logger.info('Command completed');
|
|
554
|
-
* });
|
|
555
|
-
* }
|
|
556
|
-
* ```
|
|
557
|
-
*/
|
|
558
|
-
function runWithLogger(logger, fn) {
|
|
559
|
-
return loggerStorage.run(logger, fn);
|
|
560
|
-
}
|
|
561
|
-
/**
|
|
562
|
-
* Create a new logger instance
|
|
563
|
-
*
|
|
564
|
-
* @param context Base context for the logger. Must include 'component'.
|
|
565
|
-
* TraceId will be auto-generated if not provided.
|
|
566
|
-
* @returns New logger instance
|
|
567
|
-
*
|
|
568
|
-
* @example
|
|
569
|
-
* ```typescript
|
|
570
|
-
* // In Durable Object
|
|
571
|
-
* const logger = createLogger({
|
|
572
|
-
* component: 'sandbox-do',
|
|
573
|
-
* traceId: TraceContext.fromHeaders(request.headers) || TraceContext.generate(),
|
|
574
|
-
* sandboxId: this.id
|
|
575
|
-
* });
|
|
576
|
-
*
|
|
577
|
-
* // In Container
|
|
578
|
-
* const logger = createLogger({
|
|
579
|
-
* component: 'container',
|
|
580
|
-
* traceId: TraceContext.fromHeaders(request.headers)!,
|
|
581
|
-
* sessionId: this.id
|
|
582
|
-
* });
|
|
583
|
-
* ```
|
|
584
|
-
*/
|
|
585
|
-
function createLogger(context) {
|
|
586
|
-
const minLevel = getLogLevelFromEnv();
|
|
587
|
-
const pretty = isPrettyPrintEnabled();
|
|
588
|
-
return new CloudflareLogger({
|
|
589
|
-
...context,
|
|
590
|
-
traceId: context.traceId || TraceContext.generate(),
|
|
591
|
-
component: context.component
|
|
592
|
-
}, minLevel, pretty);
|
|
593
|
-
}
|
|
594
|
-
/**
|
|
595
|
-
* Get log level from environment variable
|
|
596
|
-
*
|
|
597
|
-
* Checks SANDBOX_LOG_LEVEL env var, falls back to default based on environment.
|
|
598
|
-
* Default: 'debug' for development, 'info' for production
|
|
599
|
-
*/
|
|
600
|
-
function getLogLevelFromEnv() {
|
|
601
|
-
switch ((getEnvVar("SANDBOX_LOG_LEVEL") || "info").toLowerCase()) {
|
|
602
|
-
case "debug": return LogLevel.DEBUG;
|
|
603
|
-
case "info": return LogLevel.INFO;
|
|
604
|
-
case "warn": return LogLevel.WARN;
|
|
605
|
-
case "error": return LogLevel.ERROR;
|
|
606
|
-
default: return LogLevel.INFO;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* Check if pretty printing should be enabled
|
|
611
|
-
*
|
|
612
|
-
* Checks SANDBOX_LOG_FORMAT env var, falls back to auto-detection:
|
|
613
|
-
* - Local development: pretty (colored, human-readable)
|
|
614
|
-
* - Production: json (structured)
|
|
615
|
-
*/
|
|
616
|
-
function isPrettyPrintEnabled() {
|
|
617
|
-
const format = getEnvVar("SANDBOX_LOG_FORMAT");
|
|
618
|
-
if (format) return format.toLowerCase() === "pretty";
|
|
619
|
-
return false;
|
|
620
|
-
}
|
|
621
|
-
/**
|
|
622
|
-
* Get environment variable value
|
|
623
|
-
*
|
|
624
|
-
* Supports both Node.js (process.env) and Bun (Bun.env)
|
|
625
|
-
*/
|
|
626
|
-
function getEnvVar(name) {
|
|
627
|
-
if (typeof process !== "undefined" && process.env) return process.env[name];
|
|
628
|
-
if (typeof Bun !== "undefined") {
|
|
629
|
-
const bunEnv = Bun.env;
|
|
630
|
-
if (bunEnv) return bunEnv[name];
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
//#endregion
|
|
635
|
-
//#region ../shared/dist/shell-escape.js
|
|
636
|
-
/**
|
|
637
|
-
* Escapes a string for safe use in shell commands using POSIX single-quote escaping.
|
|
638
|
-
* Prevents command injection by wrapping the string in single quotes and escaping
|
|
639
|
-
* any single quotes within the string.
|
|
640
|
-
*/
|
|
641
|
-
function shellEscape(str) {
|
|
642
|
-
return `'${str.replace(/'/g, "'\\''")}'`;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
//#endregion
|
|
646
|
-
//#region ../shared/dist/types.js
|
|
647
|
-
function isExecResult(value) {
|
|
648
|
-
return value && typeof value.success === "boolean" && typeof value.exitCode === "number" && typeof value.stdout === "string" && typeof value.stderr === "string";
|
|
649
|
-
}
|
|
650
|
-
function isProcess(value) {
|
|
651
|
-
return value && typeof value.id === "string" && typeof value.command === "string" && typeof value.status === "string";
|
|
652
|
-
}
|
|
653
|
-
function isProcessStatus(value) {
|
|
654
|
-
return [
|
|
655
|
-
"starting",
|
|
656
|
-
"running",
|
|
657
|
-
"completed",
|
|
658
|
-
"failed",
|
|
659
|
-
"killed",
|
|
660
|
-
"error"
|
|
661
|
-
].includes(value);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
//#endregion
|
|
665
4
|
//#region ../shared/dist/errors/codes.js
|
|
666
5
|
/**
|
|
667
6
|
* Centralized error code registry
|
|
@@ -2703,7 +2042,7 @@ function resolveS3fsOptions(provider, userOptions) {
|
|
|
2703
2042
|
* This file is auto-updated by .github/changeset-version.ts during releases
|
|
2704
2043
|
* DO NOT EDIT MANUALLY - Changes will be overwritten on the next version bump
|
|
2705
2044
|
*/
|
|
2706
|
-
const SDK_VERSION = "0.5.
|
|
2045
|
+
const SDK_VERSION = "0.5.2";
|
|
2707
2046
|
|
|
2708
2047
|
//#endregion
|
|
2709
2048
|
//#region src/sandbox.ts
|
|
@@ -3028,8 +2367,12 @@ var Sandbox = class extends Container {
|
|
|
3028
2367
|
this.logger.debug("Version compatibility check encountered an error", { error: error instanceof Error ? error.message : String(error) });
|
|
3029
2368
|
}
|
|
3030
2369
|
}
|
|
3031
|
-
onStop() {
|
|
2370
|
+
async onStop() {
|
|
3032
2371
|
this.logger.debug("Sandbox stopped");
|
|
2372
|
+
this.portTokens.clear();
|
|
2373
|
+
this.defaultSession = null;
|
|
2374
|
+
this.activeMounts.clear();
|
|
2375
|
+
await Promise.all([this.ctx.storage.delete("portTokens"), this.ctx.storage.delete("defaultSession")]);
|
|
3033
2376
|
}
|
|
3034
2377
|
onError(error) {
|
|
3035
2378
|
this.logger.error("Sandbox error", error instanceof Error ? error : new Error(String(error)));
|
|
@@ -3109,28 +2452,26 @@ var Sandbox = class extends Container {
|
|
|
3109
2452
|
traceId,
|
|
3110
2453
|
operation: "fetch"
|
|
3111
2454
|
});
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
return await this.containerFetch(request, port);
|
|
3133
|
-
});
|
|
2455
|
+
const url = new URL(request.url);
|
|
2456
|
+
if (!this.sandboxName && request.headers.has("X-Sandbox-Name")) {
|
|
2457
|
+
const name = request.headers.get("X-Sandbox-Name");
|
|
2458
|
+
this.sandboxName = name;
|
|
2459
|
+
await this.ctx.storage.put("sandboxName", name);
|
|
2460
|
+
}
|
|
2461
|
+
const upgradeHeader = request.headers.get("Upgrade");
|
|
2462
|
+
const connectionHeader = request.headers.get("Connection");
|
|
2463
|
+
if (upgradeHeader?.toLowerCase() === "websocket" && connectionHeader?.toLowerCase().includes("upgrade")) try {
|
|
2464
|
+
requestLogger.debug("WebSocket upgrade requested", {
|
|
2465
|
+
path: url.pathname,
|
|
2466
|
+
port: this.determinePort(url)
|
|
2467
|
+
});
|
|
2468
|
+
return await super.fetch(request);
|
|
2469
|
+
} catch (error) {
|
|
2470
|
+
requestLogger.error("WebSocket connection failed", error instanceof Error ? error : new Error(String(error)), { path: url.pathname });
|
|
2471
|
+
throw error;
|
|
2472
|
+
}
|
|
2473
|
+
const port = this.determinePort(url);
|
|
2474
|
+
return await this.containerFetch(request, port);
|
|
3134
2475
|
}
|
|
3135
2476
|
wsConnect(request, port) {
|
|
3136
2477
|
throw new Error("Not implemented here to avoid RPC serialization issues");
|