@cloudflare/sandbox 0.13.0-next.681.1 → 0.13.0-next.709.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/Dockerfile +6 -6
  2. package/README.md +49 -3
  3. package/dist/bridge/index.d.ts.map +1 -1
  4. package/dist/bridge/index.js +1364 -1072
  5. package/dist/bridge/index.js.map +1 -1
  6. package/dist/{contexts-DY1LHU1v.d.ts → contexts-1EsLHByO.d.ts} +156 -20
  7. package/dist/contexts-1EsLHByO.d.ts.map +1 -0
  8. package/dist/{dist-BStBkGIC.js → dist-Duor5GbS.js} +7 -56
  9. package/dist/dist-Duor5GbS.js.map +1 -0
  10. package/dist/errors/index.d.ts +4 -4
  11. package/dist/errors/index.js +3 -3
  12. package/dist/{errors-k3B8orjH.js → errors-CXR0xBpw.js} +42 -10
  13. package/dist/errors-CXR0xBpw.js.map +1 -0
  14. package/dist/{errors-ewgSNicb.js → errors-QYlSkVGz.js} +402 -250
  15. package/dist/errors-QYlSkVGz.js.map +1 -0
  16. package/dist/extensions/index.d.ts +4 -2
  17. package/dist/extensions/index.js +5 -3
  18. package/dist/extensions-CFB2xHqY.js +1023 -0
  19. package/dist/extensions-CFB2xHqY.js.map +1 -0
  20. package/dist/filesystem-BWAZCZER.d.ts +732 -0
  21. package/dist/filesystem-BWAZCZER.d.ts.map +1 -0
  22. package/dist/git/index.d.ts +8 -19
  23. package/dist/git/index.d.ts.map +1 -1
  24. package/dist/git/index.js +16 -23
  25. package/dist/git/index.js.map +1 -1
  26. package/dist/index-Bs4bqXDR.d.ts +438 -0
  27. package/dist/index-Bs4bqXDR.d.ts.map +1 -0
  28. package/dist/{index-Dy6u9r60.d.ts → index-HNYBk-az.d.ts} +233 -179
  29. package/dist/index-HNYBk-az.d.ts.map +1 -0
  30. package/dist/index.d.ts +480 -11
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +6 -6
  33. package/dist/interpreter/index.d.ts +3 -1
  34. package/dist/interpreter/index.d.ts.map +1 -1
  35. package/dist/interpreter/index.js +46 -18
  36. package/dist/interpreter/index.js.map +1 -1
  37. package/dist/openai/index.d.ts +5 -5
  38. package/dist/openai/index.d.ts.map +1 -1
  39. package/dist/openai/index.js +11 -6
  40. package/dist/openai/index.js.map +1 -1
  41. package/dist/opencode/index.d.ts +7 -11
  42. package/dist/opencode/index.d.ts.map +1 -1
  43. package/dist/opencode/index.js +56 -85
  44. package/dist/opencode/index.js.map +1 -1
  45. package/dist/process-types-GStiZ8f8.d.ts +73 -0
  46. package/dist/process-types-GStiZ8f8.d.ts.map +1 -0
  47. package/dist/{sandbox-boKWPIcd.js → sandbox-Auuwfnur.js} +4152 -3359
  48. package/dist/sandbox-Auuwfnur.js.map +1 -0
  49. package/dist/sandbox-BbAabq93.d.ts +42 -0
  50. package/dist/sandbox-BbAabq93.d.ts.map +1 -0
  51. package/dist/xterm/index.d.ts +5 -1
  52. package/dist/xterm/index.d.ts.map +1 -1
  53. package/dist/xterm/index.js +55 -12
  54. package/dist/xterm/index.js.map +1 -1
  55. package/package.json +3 -2
  56. package/dist/codes-y-U76vnQ.d.ts +0 -79
  57. package/dist/codes-y-U76vnQ.d.ts.map +0 -1
  58. package/dist/contexts-DY1LHU1v.d.ts.map +0 -1
  59. package/dist/dist-BStBkGIC.js.map +0 -1
  60. package/dist/errors-ewgSNicb.js.map +0 -1
  61. package/dist/errors-k3B8orjH.js.map +0 -1
  62. package/dist/extensions-CepYdzro.js +0 -191
  63. package/dist/extensions-CepYdzro.js.map +0 -1
  64. package/dist/index-B7QgIs0N.d.ts +0 -1841
  65. package/dist/index-B7QgIs0N.d.ts.map +0 -1
  66. package/dist/index-Dy6u9r60.d.ts.map +0 -1
  67. package/dist/sandbox-DHNO89IF.d.ts +0 -828
  68. package/dist/sandbox-DHNO89IF.d.ts.map +0 -1
  69. package/dist/sandbox-boKWPIcd.js.map +0 -1
@@ -0,0 +1,42 @@
1
+ import { o as ProcessStatus, s as SandboxCommand } from "./process-types-GStiZ8f8.js";
2
+ import { A as CreateTerminalOptions, D as MountBucketOptions, M as Terminal, R as ExecOptions, S as BackupOptions, T as DirectoryBackup, _ as RenameFileResult, b as WriteFileResult, d as ListFilesOptions, f as ListFilesResult, g as ReadFileStreamResult, h as ReadFileResult, i as DeleteFileResult, k as RestoreBackupResult, m as MoveFileResult, o as FileEncoding, p as MkdirResult, q as SandboxProcess, r as CheckChangesResult, s as FileExistsResult, t as CheckChangesOptions, v as WatchOptions } from "./filesystem-BWAZCZER.js";
3
+
4
+ //#region ../shared/dist/types/sandbox.d.ts
5
+
6
+ interface ISandbox {
7
+ exec(command: SandboxCommand, options?: ExecOptions): Promise<SandboxProcess>;
8
+ getProcess(id: string): Promise<SandboxProcess | null>;
9
+ listProcesses(): Promise<ProcessStatus[]>;
10
+ writeFile(path: string, content: string | ReadableStream<Uint8Array>, options?: {
11
+ encoding?: string;
12
+ }): Promise<WriteFileResult>;
13
+ readFile(path: string, options: {
14
+ encoding: 'none';
15
+ }): Promise<ReadFileStreamResult>;
16
+ readFile(path: string, options?: {
17
+ encoding?: Exclude<FileEncoding, 'none'>;
18
+ }): Promise<ReadFileResult>;
19
+ readFileStream(path: string): Promise<ReadableStream<Uint8Array>>;
20
+ watch(path: string, options?: WatchOptions): Promise<ReadableStream<Uint8Array>>;
21
+ checkChanges(path: string, options?: CheckChangesOptions): Promise<CheckChangesResult>;
22
+ mkdir(path: string, options?: {
23
+ recursive?: boolean;
24
+ }): Promise<MkdirResult>;
25
+ deleteFile(path: string): Promise<DeleteFileResult>;
26
+ renameFile(oldPath: string, newPath: string, options?: Record<string, never>): Promise<RenameFileResult>;
27
+ moveFile(sourcePath: string, destinationPath: string, options?: Record<string, never>): Promise<MoveFileResult>;
28
+ listFiles(path: string, options?: ListFilesOptions): Promise<ListFilesResult>;
29
+ exists(path: string): Promise<FileExistsResult>;
30
+ setEnvVars(envVars: Record<string, string | undefined>): Promise<void>;
31
+ mountBucket(bucket: string, mountPath: string, options: MountBucketOptions): Promise<void>;
32
+ unmountBucket(mountPath: string): Promise<void>;
33
+ createBackup(options: BackupOptions): Promise<DirectoryBackup>;
34
+ restoreBackup(backup: DirectoryBackup): Promise<RestoreBackupResult>;
35
+ wsConnect(request: Request, port: number): Promise<Response>;
36
+ createTerminal(options: CreateTerminalOptions): Promise<Terminal>;
37
+ getTerminal(id: string): Promise<Terminal | null>;
38
+ listTerminals(): Promise<Terminal[]>;
39
+ }
40
+ //#endregion
41
+ export { ISandbox as t };
42
+ //# sourceMappingURL=sandbox-BbAabq93.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-BbAabq93.d.ts","names":["ProcessStatus","SandboxCommand","CreateTerminalOptions","Terminal","BackupOptions","DirectoryBackup","MountBucketOptions","RestoreBackupResult","ExecOptions","SandboxProcess","CheckChangesOptions","CheckChangesResult","DeleteFileResult","FileEncoding","FileExistsResult","ListFilesOptions","ListFilesResult","MkdirResult","MoveFileResult","ReadFileResult","ReadFileStreamResult","RenameFileResult","WatchOptions","WriteFileResult","ISandbox","Promise","Uint8Array","ReadableStream","Exclude","Record","Request","Response"],"sources":["../../shared/dist/types/sandbox.d.ts"],"sourcesContent":["import type { ProcessStatus, SandboxCommand } from '../process-types.js';\nimport type { CreateTerminalOptions, Terminal } from '../pty-types.js';\nimport type { BackupOptions, DirectoryBackup, MountBucketOptions, RestoreBackupResult } from './backup-mounts.js';\nimport type { ExecOptions, SandboxProcess } from './core.js';\nimport type { CheckChangesOptions, CheckChangesResult, DeleteFileResult, FileEncoding, FileExistsResult, ListFilesOptions, ListFilesResult, MkdirResult, MoveFileResult, ReadFileResult, ReadFileStreamResult, RenameFileResult, WatchOptions, WriteFileResult } from './filesystem.js';\nexport interface ISandbox {\n exec(command: SandboxCommand, options?: ExecOptions): Promise<SandboxProcess>;\n getProcess(id: string): Promise<SandboxProcess | null>;\n listProcesses(): Promise<ProcessStatus[]>;\n writeFile(path: string, content: string | ReadableStream<Uint8Array>, options?: {\n encoding?: string;\n }): Promise<WriteFileResult>;\n readFile(path: string, options: {\n encoding: 'none';\n }): Promise<ReadFileStreamResult>;\n readFile(path: string, options?: {\n encoding?: Exclude<FileEncoding, 'none'>;\n }): Promise<ReadFileResult>;\n readFileStream(path: string): Promise<ReadableStream<Uint8Array>>;\n watch(path: string, options?: WatchOptions): Promise<ReadableStream<Uint8Array>>;\n checkChanges(path: string, options?: CheckChangesOptions): Promise<CheckChangesResult>;\n mkdir(path: string, options?: {\n recursive?: boolean;\n }): Promise<MkdirResult>;\n deleteFile(path: string): Promise<DeleteFileResult>;\n renameFile(oldPath: string, newPath: string, options?: Record<string, never>): Promise<RenameFileResult>;\n moveFile(sourcePath: string, destinationPath: string, options?: Record<string, never>): Promise<MoveFileResult>;\n listFiles(path: string, options?: ListFilesOptions): Promise<ListFilesResult>;\n exists(path: string): Promise<FileExistsResult>;\n setEnvVars(envVars: Record<string, string | undefined>): Promise<void>;\n mountBucket(bucket: string, mountPath: string, options: MountBucketOptions): Promise<void>;\n unmountBucket(mountPath: string): Promise<void>;\n createBackup(options: BackupOptions): Promise<DirectoryBackup>;\n restoreBackup(backup: DirectoryBackup): Promise<RestoreBackupResult>;\n wsConnect(request: Request, port: number): Promise<Response>;\n createTerminal(options: CreateTerminalOptions): Promise<Terminal>;\n getTerminal(id: string): Promise<Terminal | null>;\n listTerminals(): Promise<Terminal[]>;\n}\n//# sourceMappingURL=sandbox.d.ts.map"],"mappings":"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"}
@@ -29,6 +29,7 @@ interface SandboxAddonOptions {
29
29
  getWebSocketUrl: (params: {
30
30
  sandboxId: string;
31
31
  terminalId?: string;
32
+ cursor?: string;
32
33
  /** WebSocket origin derived from `window.location` (e.g. `wss://example.com`). */
33
34
  origin: string;
34
35
  }) => string;
@@ -56,9 +57,11 @@ declare class SandboxAddon implements ITerminalAddon {
56
57
  private disposables;
57
58
  private reconnectAttempts;
58
59
  private reconnectTimer;
59
- private hasReceivedBuffer;
60
+ private restoredOutput;
60
61
  private intentionalDisconnect;
61
62
  private textEncoder;
63
+ private pendingChunk;
64
+ private cursor;
62
65
  private _state;
63
66
  private _sandboxId;
64
67
  private _terminalId;
@@ -75,6 +78,7 @@ declare class SandboxAddon implements ITerminalAddon {
75
78
  private registerSocketListener;
76
79
  private onSocketOpen;
77
80
  private onSocketMessage;
81
+ private consumeBinary;
78
82
  private handleControlMessage;
79
83
  private onSocketClose;
80
84
  private onSocketError;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/xterm/types.ts","../../src/xterm/addon.ts"],"sourcesContent":[],"mappings":";;;;;;AAGY,KAAA,eAAA,GAAe,cAAA,GAAA,YAAA,GAAA,WAAA;AAK3B;AAQA;;UARiB,gBAAA;;ECKJ,UAAA,CAAA,EAAA,MAAa;;;;;AAAW,UDGpB,mBAAA,CCHoB;EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BDmCzB,yBAAyB;;;;cCnCtC,YAAA,YAAwB;EDVzB,QAAA,OAAA;EAKK,QAAA,QAAA;EAQA,QAAA,MAAA;;;;ECHJ,QAAA,iBAAa;EAcX,QAAA,qBAAA;EAUgB,QAAA,WAAA;EAEV,QAAA,MAAA;EAWH,QAAA,UAAA;EArCmB,QAAA,WAAA;EAAc,IAAA,KAAA,CAAA,CAAA,EAcpC,eAdoC;;;uBAwBpB;qBAEV;;kBAWH"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/xterm/types.ts","../../src/xterm/addon.ts"],"sourcesContent":[],"mappings":";;;;;;AAGY,KAAA,eAAA,GAAe,cAAA,GAAA,YAAA,GAAA,WAAA;AAK3B;AAQA;;UARiB,gBAAA;;ECKJ,UAAA,CAAA,EAAA,MAAa;;;;;AAAW,UDGpB,mBAAA,CCHoB;EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BDoCzB,yBAAyB;;;;cCpCtC,YAAA,YAAwB;EDVzB,QAAA,OAAA;EAKK,QAAA,QAAA;EAQA,QAAA,MAAA;;;;ECHJ,QAAA,cAAa;EAgBX,QAAA,qBAAA;EAUgB,QAAA,WAAA;EAEV,QAAA,YAAA;EAWH,QAAA,MAAA;EAvCmB,QAAA,MAAA;EAAc,QAAA,UAAA;;eAgBpC;;;uBAUgB;qBAEV;;kBAWH"}
@@ -9,9 +9,11 @@ var SandboxAddon = class {
9
9
  disposables = [];
10
10
  reconnectAttempts = 0;
11
11
  reconnectTimer = null;
12
- hasReceivedBuffer = false;
12
+ restoredOutput = false;
13
13
  intentionalDisconnect = false;
14
14
  textEncoder = new TextEncoder();
15
+ pendingChunk = null;
16
+ cursor;
15
17
  _state = "disconnected";
16
18
  _sandboxId;
17
19
  _terminalId;
@@ -44,6 +46,9 @@ var SandboxAddon = class {
44
46
  this.cancelReconnect();
45
47
  this.closeSocket();
46
48
  this.reconnectAttempts = 0;
49
+ this.cursor = void 0;
50
+ this.restoredOutput = false;
51
+ this.pendingChunk = null;
47
52
  this.intentionalDisconnect = false;
48
53
  if (this._state !== "disconnected") this.terminal.clear();
49
54
  this.doConnect();
@@ -55,18 +60,18 @@ var SandboxAddon = class {
55
60
  this.setState("disconnected");
56
61
  }
57
62
  setState(state, error) {
58
- if (this._state === state) return;
63
+ if (this._state === state && !error) return;
59
64
  this._state = state;
60
65
  this.options.onStateChange?.(state, error);
61
66
  }
62
67
  doConnect() {
63
68
  if (!this.terminal || !this._sandboxId) return;
64
69
  this.setState("connecting");
65
- this.hasReceivedBuffer = false;
66
70
  const origin = `${location.protocol === "https:" ? "wss:" : "ws:"}//${location.host}`;
67
71
  const url = this.options.getWebSocketUrl({
68
72
  sandboxId: this._sandboxId,
69
73
  terminalId: this._terminalId,
74
+ cursor: this.cursor,
70
75
  origin
71
76
  });
72
77
  const socket = new WebSocket(url);
@@ -90,29 +95,67 @@ var SandboxAddon = class {
90
95
  if (!this.terminal) return;
91
96
  const { data } = event;
92
97
  if (data instanceof ArrayBuffer) {
93
- this.hasReceivedBuffer = true;
94
- this.terminal.write(new Uint8Array(data));
98
+ this.consumeBinary(new Uint8Array(data));
95
99
  return;
96
100
  }
97
101
  if (typeof data === "string") try {
98
- const msg = JSON.parse(data);
99
- this.handleControlMessage(msg);
100
- } catch {}
102
+ this.handleControlMessage(JSON.parse(data));
103
+ } catch {
104
+ this.setState(this._state, /* @__PURE__ */ new Error("Invalid terminal control frame"));
105
+ this.closeSocket();
106
+ }
107
+ }
108
+ consumeBinary(data) {
109
+ if (!this.pendingChunk) {
110
+ this.setState(this._state, /* @__PURE__ */ new Error("Unexpected terminal data frame"));
111
+ this.closeSocket();
112
+ return;
113
+ }
114
+ if (data.byteLength !== this.pendingChunk.byteLength) {
115
+ this.setState(this._state, /* @__PURE__ */ new Error("Terminal data frame length mismatch"));
116
+ this.closeSocket();
117
+ return;
118
+ }
119
+ this.restoredOutput = true;
120
+ this.terminal?.write(data);
121
+ this.cursor = this.pendingChunk.cursor;
122
+ this.pendingChunk = null;
101
123
  }
102
124
  handleControlMessage(msg) {
103
125
  switch (msg.type) {
104
126
  case "ready":
105
- if (!this.hasReceivedBuffer) this.terminal?.clear();
127
+ if (msg.cursor) this.cursor = msg.cursor;
128
+ if (!this.restoredOutput) this.terminal?.clear();
106
129
  this.reconnectAttempts = 0;
107
130
  this.setState("connected");
108
131
  this.terminal?.focus();
109
132
  if (this.terminal) this.sendResize(this.terminal.cols, this.terminal.rows);
110
133
  break;
134
+ case "chunk":
135
+ if (this.pendingChunk) {
136
+ this.setState(this._state, /* @__PURE__ */ new Error("Terminal data frame missing"));
137
+ this.closeSocket();
138
+ return;
139
+ }
140
+ this.pendingChunk = {
141
+ cursor: msg.cursor,
142
+ byteLength: msg.byteLength
143
+ };
144
+ break;
145
+ case "truncated":
146
+ this.cursor = msg.cursor;
147
+ this.terminal?.clear();
148
+ this.restoredOutput = false;
149
+ break;
111
150
  case "error":
112
- this.options.onStateChange?.(this._state, new Error(msg.message ?? "Unknown error"));
151
+ this.options.onStateChange?.(this._state, new Error(msg.message));
113
152
  break;
114
153
  case "exit":
115
- this.options.onStateChange?.(this._state, /* @__PURE__ */ new Error(`Session exited with code ${msg.code}${msg.signal ? ` (${msg.signal})` : ""}`));
154
+ this.cursor = msg.cursor;
155
+ this.intentionalDisconnect = true;
156
+ this.options.onStateChange?.(this._state, /* @__PURE__ */ new Error(`Session exited with code ${msg.exit.code}${msg.exit.signal ? ` (${msg.exit.signal})` : ""}`));
157
+ this.closeSocket();
158
+ this.setState("disconnected");
116
159
  break;
117
160
  }
118
161
  }
@@ -142,7 +185,6 @@ var SandboxAddon = class {
142
185
  this.reconnectTimer = setTimeout(() => {
143
186
  this.reconnectTimer = null;
144
187
  this.reconnectAttempts++;
145
- this.terminal?.clear();
146
188
  this.doConnect();
147
189
  }, delay + jitter);
148
190
  }
@@ -157,6 +199,7 @@ var SandboxAddon = class {
157
199
  this.socket.close();
158
200
  this.socket = null;
159
201
  }
202
+ this.pendingChunk = null;
160
203
  for (const d of this.disposables) d.dispose();
161
204
  this.disposables = [];
162
205
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["options: SandboxAddonOptions"],"sources":["../../src/xterm/addon.ts"],"sourcesContent":["import type { PtyStatusMessage } from '@repo/shared';\nimport type { IDisposable, ITerminalAddon, Terminal } from '@xterm/xterm';\nimport type {\n ConnectionState,\n ConnectionTarget,\n SandboxAddonOptions\n} from './types';\n\nconst DEFAULT_RECONNECT_DELAY = 1000;\nconst MAX_RECONNECT_ATTEMPTS = 10;\nconst MAX_BACKOFF_EXPONENT = 5;\nconst JITTER_FACTOR = 0.1;\n\nexport class SandboxAddon implements ITerminalAddon {\n private terminal: Terminal | null = null;\n private socket: WebSocket | null = null;\n private disposables: IDisposable[] = [];\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private hasReceivedBuffer = false;\n private intentionalDisconnect = false;\n private textEncoder = new TextEncoder();\n\n private _state: ConnectionState = 'disconnected';\n private _sandboxId: string | undefined;\n private _terminalId: string | undefined;\n\n get state(): ConnectionState {\n return this._state;\n }\n get sandboxId(): string | undefined {\n return this._sandboxId;\n }\n get terminalId(): string | undefined {\n return this._terminalId;\n }\n\n constructor(private options: SandboxAddonOptions) {}\n\n activate(terminal: Terminal): void {\n this.terminal = terminal;\n }\n\n dispose(): void {\n this.intentionalDisconnect = true;\n this.cancelReconnect();\n this.closeSocket();\n this.terminal = null;\n }\n\n connect(target: ConnectionTarget): void {\n if (!this.terminal) return;\n\n const isSameTarget =\n target.sandboxId === this._sandboxId &&\n target.terminalId === this._terminalId;\n\n if (isSameTarget && this._state !== 'disconnected') {\n return;\n }\n\n this._sandboxId = target.sandboxId;\n this._terminalId = target.terminalId;\n\n this.cancelReconnect();\n this.closeSocket();\n this.reconnectAttempts = 0;\n this.intentionalDisconnect = false;\n\n if (this._state !== 'disconnected') {\n this.terminal.clear();\n }\n\n this.doConnect();\n }\n\n disconnect(): void {\n this.intentionalDisconnect = true;\n this.cancelReconnect();\n this.closeSocket();\n this.setState('disconnected');\n }\n\n private setState(state: ConnectionState, error?: Error): void {\n if (this._state === state) return;\n this._state = state;\n this.options.onStateChange?.(state, error);\n }\n\n private doConnect(): void {\n if (!this.terminal || !this._sandboxId) return;\n\n this.setState('connecting');\n this.hasReceivedBuffer = false;\n\n const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';\n const origin = `${wsProtocol}//${location.host}`;\n\n const url = this.options.getWebSocketUrl({\n sandboxId: this._sandboxId,\n terminalId: this._terminalId,\n origin\n });\n\n const socket = new WebSocket(url);\n socket.binaryType = 'arraybuffer';\n this.socket = socket;\n\n this.registerSocketListener(socket, 'open', this.onSocketOpen.bind(this));\n this.registerSocketListener(socket, 'message', (e) =>\n this.onSocketMessage(e)\n );\n this.registerSocketListener(socket, 'close', this.onSocketClose.bind(this));\n this.registerSocketListener(socket, 'error', this.onSocketError.bind(this));\n }\n\n private registerSocketListener(\n socket: WebSocket,\n type: string,\n listener: (ev: Event) => void\n ): void {\n socket.addEventListener(type, listener);\n this.disposables.push({\n dispose: () => socket.removeEventListener(type, listener)\n });\n }\n\n private onSocketOpen(): void {\n if (!this.terminal) return;\n this.disposables.push(this.terminal.onData((data) => this.sendData(data)));\n this.disposables.push(\n this.terminal.onResize(({ cols, rows }) => this.sendResize(cols, rows))\n );\n }\n\n private onSocketMessage(event: Event): void {\n if (!this.terminal) return;\n const { data } = event as MessageEvent;\n\n if (data instanceof ArrayBuffer) {\n this.hasReceivedBuffer = true;\n this.terminal.write(new Uint8Array(data));\n return;\n }\n\n if (typeof data === 'string') {\n try {\n const msg = JSON.parse(data) as PtyStatusMessage;\n this.handleControlMessage(msg);\n } catch {\n // Non-JSON string messages are silently ignored - protocol expects\n // binary for terminal data and JSON for control messages only\n }\n }\n }\n\n private handleControlMessage(msg: PtyStatusMessage): void {\n switch (msg.type) {\n case 'ready':\n if (!this.hasReceivedBuffer) {\n this.terminal?.clear();\n }\n this.reconnectAttempts = 0;\n this.setState('connected');\n this.terminal?.focus();\n if (this.terminal) {\n this.sendResize(this.terminal.cols, this.terminal.rows);\n }\n break;\n\n case 'error':\n this.options.onStateChange?.(\n this._state,\n new Error(msg.message ?? 'Unknown error')\n );\n break;\n\n case 'exit':\n this.options.onStateChange?.(\n this._state,\n new Error(\n `Session exited with code ${msg.code}${msg.signal ? ` (${msg.signal})` : ''}`\n )\n );\n break;\n }\n }\n\n private onSocketClose(): void {\n this.closeSocket();\n\n if (this.intentionalDisconnect) {\n this.setState('disconnected');\n return;\n }\n\n const shouldReconnect = this.options.reconnect !== false;\n\n if (!shouldReconnect) {\n this.setState('disconnected');\n return;\n }\n\n if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n this.setState(\n 'disconnected',\n new Error('Max reconnection attempts exceeded')\n );\n return;\n }\n\n this.scheduleReconnect();\n }\n\n private onSocketError(): void {\n this.options.onStateChange?.(this._state, new Error('WebSocket error'));\n }\n\n private scheduleReconnect(): void {\n const exponent = Math.min(this.reconnectAttempts, MAX_BACKOFF_EXPONENT);\n const delay = DEFAULT_RECONNECT_DELAY * 2 ** exponent;\n const jitter = delay * JITTER_FACTOR * Math.random();\n\n this.setState('disconnected');\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.reconnectAttempts++;\n this.terminal?.clear();\n this.doConnect();\n }, delay + jitter);\n }\n\n private cancelReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n private closeSocket(): void {\n if (this.socket) {\n this.socket.close();\n this.socket = null;\n }\n for (const d of this.disposables) {\n d.dispose();\n }\n this.disposables = [];\n }\n\n private sendData(data: string): void {\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(this.textEncoder.encode(data));\n }\n }\n\n private sendResize(cols: number, rows: number): void {\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify({ type: 'resize', cols, rows }));\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["options: SandboxAddonOptions"],"sources":["../../src/xterm/addon.ts"],"sourcesContent":["import type { PtyServerControlMessage } from '@repo/shared';\nimport type { IDisposable, ITerminalAddon, Terminal } from '@xterm/xterm';\nimport type {\n ConnectionState,\n ConnectionTarget,\n SandboxAddonOptions\n} from './types';\n\nconst DEFAULT_RECONNECT_DELAY = 1000;\nconst MAX_RECONNECT_ATTEMPTS = 10;\nconst MAX_BACKOFF_EXPONENT = 5;\nconst JITTER_FACTOR = 0.1;\n\nexport class SandboxAddon implements ITerminalAddon {\n private terminal: Terminal | null = null;\n private socket: WebSocket | null = null;\n private disposables: IDisposable[] = [];\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private restoredOutput = false;\n private intentionalDisconnect = false;\n private textEncoder = new TextEncoder();\n private pendingChunk: { cursor: string; byteLength: number } | null = null;\n private cursor: string | undefined;\n\n private _state: ConnectionState = 'disconnected';\n private _sandboxId: string | undefined;\n private _terminalId: string | undefined;\n\n get state(): ConnectionState {\n return this._state;\n }\n get sandboxId(): string | undefined {\n return this._sandboxId;\n }\n get terminalId(): string | undefined {\n return this._terminalId;\n }\n\n constructor(private options: SandboxAddonOptions) {}\n\n activate(terminal: Terminal): void {\n this.terminal = terminal;\n }\n\n dispose(): void {\n this.intentionalDisconnect = true;\n this.cancelReconnect();\n this.closeSocket();\n this.terminal = null;\n }\n\n connect(target: ConnectionTarget): void {\n if (!this.terminal) return;\n\n const isSameTarget =\n target.sandboxId === this._sandboxId &&\n target.terminalId === this._terminalId;\n\n if (isSameTarget && this._state !== 'disconnected') {\n return;\n }\n\n this._sandboxId = target.sandboxId;\n this._terminalId = target.terminalId;\n\n this.cancelReconnect();\n this.closeSocket();\n this.reconnectAttempts = 0;\n this.cursor = undefined;\n this.restoredOutput = false;\n this.pendingChunk = null;\n this.intentionalDisconnect = false;\n\n if (this._state !== 'disconnected') {\n this.terminal.clear();\n }\n\n this.doConnect();\n }\n\n disconnect(): void {\n this.intentionalDisconnect = true;\n this.cancelReconnect();\n this.closeSocket();\n this.setState('disconnected');\n }\n\n private setState(state: ConnectionState, error?: Error): void {\n if (this._state === state && !error) return;\n this._state = state;\n this.options.onStateChange?.(state, error);\n }\n\n private doConnect(): void {\n if (!this.terminal || !this._sandboxId) return;\n\n this.setState('connecting');\n\n const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';\n const origin = `${wsProtocol}//${location.host}`;\n\n const url = this.options.getWebSocketUrl({\n sandboxId: this._sandboxId,\n terminalId: this._terminalId,\n cursor: this.cursor,\n origin\n });\n\n const socket = new WebSocket(url);\n socket.binaryType = 'arraybuffer';\n this.socket = socket;\n\n this.registerSocketListener(socket, 'open', this.onSocketOpen.bind(this));\n this.registerSocketListener(socket, 'message', (e) =>\n this.onSocketMessage(e)\n );\n this.registerSocketListener(socket, 'close', this.onSocketClose.bind(this));\n this.registerSocketListener(socket, 'error', this.onSocketError.bind(this));\n }\n\n private registerSocketListener(\n socket: WebSocket,\n type: string,\n listener: (ev: Event) => void\n ): void {\n socket.addEventListener(type, listener);\n this.disposables.push({\n dispose: () => socket.removeEventListener(type, listener)\n });\n }\n\n private onSocketOpen(): void {\n if (!this.terminal) return;\n this.disposables.push(this.terminal.onData((data) => this.sendData(data)));\n this.disposables.push(\n this.terminal.onResize(({ cols, rows }) => this.sendResize(cols, rows))\n );\n }\n\n private onSocketMessage(event: Event): void {\n if (!this.terminal) return;\n const { data } = event as MessageEvent;\n\n if (data instanceof ArrayBuffer) {\n this.consumeBinary(new Uint8Array(data));\n return;\n }\n\n if (typeof data === 'string') {\n try {\n this.handleControlMessage(JSON.parse(data) as PtyServerControlMessage);\n } catch {\n this.setState(this._state, new Error('Invalid terminal control frame'));\n this.closeSocket();\n }\n }\n }\n\n private consumeBinary(data: Uint8Array): void {\n if (!this.pendingChunk) {\n this.setState(this._state, new Error('Unexpected terminal data frame'));\n this.closeSocket();\n return;\n }\n if (data.byteLength !== this.pendingChunk.byteLength) {\n this.setState(\n this._state,\n new Error('Terminal data frame length mismatch')\n );\n this.closeSocket();\n return;\n }\n this.restoredOutput = true;\n this.terminal?.write(data);\n this.cursor = this.pendingChunk.cursor;\n this.pendingChunk = null;\n }\n\n private handleControlMessage(msg: PtyServerControlMessage): void {\n switch (msg.type) {\n case 'ready':\n if (msg.cursor) this.cursor = msg.cursor;\n if (!this.restoredOutput) {\n this.terminal?.clear();\n }\n this.reconnectAttempts = 0;\n this.setState('connected');\n this.terminal?.focus();\n if (this.terminal) {\n this.sendResize(this.terminal.cols, this.terminal.rows);\n }\n break;\n\n case 'chunk':\n if (this.pendingChunk) {\n this.setState(this._state, new Error('Terminal data frame missing'));\n this.closeSocket();\n return;\n }\n this.pendingChunk = { cursor: msg.cursor, byteLength: msg.byteLength };\n break;\n\n case 'truncated':\n this.cursor = msg.cursor;\n this.terminal?.clear();\n this.restoredOutput = false;\n break;\n\n case 'error':\n this.options.onStateChange?.(this._state, new Error(msg.message));\n break;\n\n case 'exit':\n this.cursor = msg.cursor;\n this.intentionalDisconnect = true;\n this.options.onStateChange?.(\n this._state,\n new Error(\n `Session exited with code ${msg.exit.code}${msg.exit.signal ? ` (${msg.exit.signal})` : ''}`\n )\n );\n this.closeSocket();\n this.setState('disconnected');\n break;\n }\n }\n\n private onSocketClose(): void {\n this.closeSocket();\n\n if (this.intentionalDisconnect) {\n this.setState('disconnected');\n return;\n }\n\n const shouldReconnect = this.options.reconnect !== false;\n\n if (!shouldReconnect) {\n this.setState('disconnected');\n return;\n }\n\n if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n this.setState(\n 'disconnected',\n new Error('Max reconnection attempts exceeded')\n );\n return;\n }\n\n this.scheduleReconnect();\n }\n\n private onSocketError(): void {\n this.options.onStateChange?.(this._state, new Error('WebSocket error'));\n }\n\n private scheduleReconnect(): void {\n const exponent = Math.min(this.reconnectAttempts, MAX_BACKOFF_EXPONENT);\n const delay = DEFAULT_RECONNECT_DELAY * 2 ** exponent;\n const jitter = delay * JITTER_FACTOR * Math.random();\n\n this.setState('disconnected');\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.reconnectAttempts++;\n this.doConnect();\n }, delay + jitter);\n }\n\n private cancelReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n private closeSocket(): void {\n if (this.socket) {\n this.socket.close();\n this.socket = null;\n }\n this.pendingChunk = null;\n for (const d of this.disposables) {\n d.dispose();\n }\n this.disposables = [];\n }\n\n private sendData(data: string): void {\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(this.textEncoder.encode(data));\n }\n }\n\n private sendResize(cols: number, rows: number): void {\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify({ type: 'resize', cols, rows }));\n }\n }\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.681.1",
3
+ "version": "0.13.0-next.709.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cloudflare/sandbox-sdk"
@@ -8,7 +8,7 @@
8
8
  "description": "A sandboxed environment for running commands",
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "@cloudflare/containers": "^0.3.5",
11
+ "@cloudflare/containers": "^0.3.7",
12
12
  "aws4fetch": "^1.0.20",
13
13
  "capnweb": "^0.8.0",
14
14
  "hono": "^4.12.26"
@@ -57,6 +57,7 @@
57
57
  "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.sidecar.json",
58
58
  "docker:local": "scripts/docker-local.sh",
59
59
  "test": "vitest run --config vitest.config.ts \"$@\"",
60
+ "test:harness": "vitest run --config vitest.harness.config.ts \"$@\"",
60
61
  "test:sidecar": "bun test ../../extensions/interpreter/src/sidecar",
61
62
  "test:e2e": "npm run test:e2e:vitest && npm run test:e2e:browser",
62
63
  "test:e2e:vitest": "cd ../../tests/e2e/test-worker && ./generate-config.sh && cd ../../.. && vitest run --config vitest.e2e.config.ts \"$@\"",
@@ -1,79 +0,0 @@
1
- //#region ../shared/dist/errors/codes.d.ts
2
- /**
3
- * Centralized error code registry
4
- * Each code maps to a specific error type with consistent semantics
5
- */
6
- declare const ErrorCode: {
7
- readonly FILE_NOT_FOUND: "FILE_NOT_FOUND";
8
- readonly PERMISSION_DENIED: "PERMISSION_DENIED";
9
- readonly FILE_EXISTS: "FILE_EXISTS";
10
- readonly IS_DIRECTORY: "IS_DIRECTORY";
11
- readonly NOT_DIRECTORY: "NOT_DIRECTORY";
12
- readonly FILE_TOO_LARGE: "FILE_TOO_LARGE";
13
- readonly NO_SPACE: "NO_SPACE";
14
- readonly TOO_MANY_FILES: "TOO_MANY_FILES";
15
- readonly RESOURCE_BUSY: "RESOURCE_BUSY";
16
- readonly READ_ONLY: "READ_ONLY";
17
- readonly NAME_TOO_LONG: "NAME_TOO_LONG";
18
- readonly TOO_MANY_LINKS: "TOO_MANY_LINKS";
19
- readonly FILESYSTEM_ERROR: "FILESYSTEM_ERROR";
20
- readonly COMMAND_NOT_FOUND: "COMMAND_NOT_FOUND";
21
- readonly COMMAND_PERMISSION_DENIED: "COMMAND_PERMISSION_DENIED";
22
- readonly INVALID_COMMAND: "INVALID_COMMAND";
23
- readonly COMMAND_EXECUTION_ERROR: "COMMAND_EXECUTION_ERROR";
24
- readonly STREAM_START_ERROR: "STREAM_START_ERROR";
25
- readonly PROCESS_NOT_FOUND: "PROCESS_NOT_FOUND";
26
- readonly PROCESS_PERMISSION_DENIED: "PROCESS_PERMISSION_DENIED";
27
- readonly PROCESS_ERROR: "PROCESS_ERROR";
28
- readonly SESSION_ALREADY_EXISTS: "SESSION_ALREADY_EXISTS";
29
- readonly SESSION_DESTROYED: "SESSION_DESTROYED";
30
- readonly SESSION_TERMINATED: "SESSION_TERMINATED";
31
- readonly PORT_ALREADY_EXPOSED: "PORT_ALREADY_EXPOSED";
32
- readonly PORT_IN_USE: "PORT_IN_USE";
33
- readonly PORT_NOT_EXPOSED: "PORT_NOT_EXPOSED";
34
- readonly INVALID_PORT_NUMBER: "INVALID_PORT_NUMBER";
35
- readonly INVALID_PORT: "INVALID_PORT";
36
- readonly SERVICE_NOT_RESPONDING: "SERVICE_NOT_RESPONDING";
37
- readonly PORT_OPERATION_ERROR: "PORT_OPERATION_ERROR";
38
- readonly CUSTOM_DOMAIN_REQUIRED: "CUSTOM_DOMAIN_REQUIRED";
39
- readonly GIT_REPOSITORY_NOT_FOUND: "GIT_REPOSITORY_NOT_FOUND";
40
- readonly GIT_BRANCH_NOT_FOUND: "GIT_BRANCH_NOT_FOUND";
41
- readonly GIT_AUTH_FAILED: "GIT_AUTH_FAILED";
42
- readonly GIT_NETWORK_ERROR: "GIT_NETWORK_ERROR";
43
- readonly INVALID_GIT_URL: "INVALID_GIT_URL";
44
- readonly GIT_CLONE_FAILED: "GIT_CLONE_FAILED";
45
- readonly GIT_CHECKOUT_FAILED: "GIT_CHECKOUT_FAILED";
46
- readonly GIT_OPERATION_FAILED: "GIT_OPERATION_FAILED";
47
- readonly BUCKET_MOUNT_ERROR: "BUCKET_MOUNT_ERROR";
48
- readonly BUCKET_UNMOUNT_ERROR: "BUCKET_UNMOUNT_ERROR";
49
- readonly S3FS_MOUNT_ERROR: "S3FS_MOUNT_ERROR";
50
- readonly MISSING_CREDENTIALS: "MISSING_CREDENTIALS";
51
- readonly INVALID_MOUNT_CONFIG: "INVALID_MOUNT_CONFIG";
52
- readonly BACKUP_CREATE_FAILED: "BACKUP_CREATE_FAILED";
53
- readonly BACKUP_RESTORE_FAILED: "BACKUP_RESTORE_FAILED";
54
- readonly BACKUP_NOT_FOUND: "BACKUP_NOT_FOUND";
55
- readonly BACKUP_EXPIRED: "BACKUP_EXPIRED";
56
- readonly INVALID_BACKUP_CONFIG: "INVALID_BACKUP_CONFIG";
57
- readonly INTERPRETER_NOT_READY: "INTERPRETER_NOT_READY";
58
- readonly CONTEXT_NOT_FOUND: "CONTEXT_NOT_FOUND";
59
- readonly CODE_EXECUTION_ERROR: "CODE_EXECUTION_ERROR";
60
- readonly PYTHON_NOT_AVAILABLE: "PYTHON_NOT_AVAILABLE";
61
- readonly JAVASCRIPT_NOT_AVAILABLE: "JAVASCRIPT_NOT_AVAILABLE";
62
- readonly OPENCODE_STARTUP_FAILED: "OPENCODE_STARTUP_FAILED";
63
- readonly PROCESS_READY_TIMEOUT: "PROCESS_READY_TIMEOUT";
64
- readonly PROCESS_EXITED_BEFORE_READY: "PROCESS_EXITED_BEFORE_READY";
65
- readonly WATCH_NOT_FOUND: "WATCH_NOT_FOUND";
66
- readonly WATCH_START_ERROR: "WATCH_START_ERROR";
67
- readonly WATCH_STOP_ERROR: "WATCH_STOP_ERROR";
68
- readonly VALIDATION_FAILED: "VALIDATION_FAILED";
69
- readonly INVALID_JSON_RESPONSE: "INVALID_JSON_RESPONSE";
70
- readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
71
- readonly INTERNAL_ERROR: "INTERNAL_ERROR";
72
- readonly CONTAINER_UNAVAILABLE: "CONTAINER_UNAVAILABLE";
73
- readonly OPERATION_INTERRUPTED: "OPERATION_INTERRUPTED";
74
- readonly RPC_TRANSPORT_ERROR: "RPC_TRANSPORT_ERROR";
75
- };
76
- type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
77
- //#endregion
78
- export { ErrorCode as t };
79
- //# sourceMappingURL=codes-y-U76vnQ.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"codes-y-U76vnQ.d.ts","names":["ErrorCode"],"sources":["../../shared/dist/errors/codes.d.ts"],"sourcesContent":["/**\n * Centralized error code registry\n * Each code maps to a specific error type with consistent semantics\n */\nexport declare const ErrorCode: {\n readonly FILE_NOT_FOUND: \"FILE_NOT_FOUND\";\n readonly PERMISSION_DENIED: \"PERMISSION_DENIED\";\n readonly FILE_EXISTS: \"FILE_EXISTS\";\n readonly IS_DIRECTORY: \"IS_DIRECTORY\";\n readonly NOT_DIRECTORY: \"NOT_DIRECTORY\";\n readonly FILE_TOO_LARGE: \"FILE_TOO_LARGE\";\n readonly NO_SPACE: \"NO_SPACE\";\n readonly TOO_MANY_FILES: \"TOO_MANY_FILES\";\n readonly RESOURCE_BUSY: \"RESOURCE_BUSY\";\n readonly READ_ONLY: \"READ_ONLY\";\n readonly NAME_TOO_LONG: \"NAME_TOO_LONG\";\n readonly TOO_MANY_LINKS: \"TOO_MANY_LINKS\";\n readonly FILESYSTEM_ERROR: \"FILESYSTEM_ERROR\";\n readonly COMMAND_NOT_FOUND: \"COMMAND_NOT_FOUND\";\n readonly COMMAND_PERMISSION_DENIED: \"COMMAND_PERMISSION_DENIED\";\n readonly INVALID_COMMAND: \"INVALID_COMMAND\";\n readonly COMMAND_EXECUTION_ERROR: \"COMMAND_EXECUTION_ERROR\";\n readonly STREAM_START_ERROR: \"STREAM_START_ERROR\";\n readonly PROCESS_NOT_FOUND: \"PROCESS_NOT_FOUND\";\n readonly PROCESS_PERMISSION_DENIED: \"PROCESS_PERMISSION_DENIED\";\n readonly PROCESS_ERROR: \"PROCESS_ERROR\";\n readonly SESSION_ALREADY_EXISTS: \"SESSION_ALREADY_EXISTS\";\n readonly SESSION_DESTROYED: \"SESSION_DESTROYED\";\n readonly SESSION_TERMINATED: \"SESSION_TERMINATED\";\n readonly PORT_ALREADY_EXPOSED: \"PORT_ALREADY_EXPOSED\";\n readonly PORT_IN_USE: \"PORT_IN_USE\";\n readonly PORT_NOT_EXPOSED: \"PORT_NOT_EXPOSED\";\n readonly INVALID_PORT_NUMBER: \"INVALID_PORT_NUMBER\";\n readonly INVALID_PORT: \"INVALID_PORT\";\n readonly SERVICE_NOT_RESPONDING: \"SERVICE_NOT_RESPONDING\";\n readonly PORT_OPERATION_ERROR: \"PORT_OPERATION_ERROR\";\n readonly CUSTOM_DOMAIN_REQUIRED: \"CUSTOM_DOMAIN_REQUIRED\";\n readonly GIT_REPOSITORY_NOT_FOUND: \"GIT_REPOSITORY_NOT_FOUND\";\n readonly GIT_BRANCH_NOT_FOUND: \"GIT_BRANCH_NOT_FOUND\";\n readonly GIT_AUTH_FAILED: \"GIT_AUTH_FAILED\";\n readonly GIT_NETWORK_ERROR: \"GIT_NETWORK_ERROR\";\n readonly INVALID_GIT_URL: \"INVALID_GIT_URL\";\n readonly GIT_CLONE_FAILED: \"GIT_CLONE_FAILED\";\n readonly GIT_CHECKOUT_FAILED: \"GIT_CHECKOUT_FAILED\";\n readonly GIT_OPERATION_FAILED: \"GIT_OPERATION_FAILED\";\n readonly BUCKET_MOUNT_ERROR: \"BUCKET_MOUNT_ERROR\";\n readonly BUCKET_UNMOUNT_ERROR: \"BUCKET_UNMOUNT_ERROR\";\n readonly S3FS_MOUNT_ERROR: \"S3FS_MOUNT_ERROR\";\n readonly MISSING_CREDENTIALS: \"MISSING_CREDENTIALS\";\n readonly INVALID_MOUNT_CONFIG: \"INVALID_MOUNT_CONFIG\";\n readonly BACKUP_CREATE_FAILED: \"BACKUP_CREATE_FAILED\";\n readonly BACKUP_RESTORE_FAILED: \"BACKUP_RESTORE_FAILED\";\n readonly BACKUP_NOT_FOUND: \"BACKUP_NOT_FOUND\";\n readonly BACKUP_EXPIRED: \"BACKUP_EXPIRED\";\n readonly INVALID_BACKUP_CONFIG: \"INVALID_BACKUP_CONFIG\";\n readonly INTERPRETER_NOT_READY: \"INTERPRETER_NOT_READY\";\n readonly CONTEXT_NOT_FOUND: \"CONTEXT_NOT_FOUND\";\n readonly CODE_EXECUTION_ERROR: \"CODE_EXECUTION_ERROR\";\n readonly PYTHON_NOT_AVAILABLE: \"PYTHON_NOT_AVAILABLE\";\n readonly JAVASCRIPT_NOT_AVAILABLE: \"JAVASCRIPT_NOT_AVAILABLE\";\n readonly OPENCODE_STARTUP_FAILED: \"OPENCODE_STARTUP_FAILED\";\n readonly PROCESS_READY_TIMEOUT: \"PROCESS_READY_TIMEOUT\";\n readonly PROCESS_EXITED_BEFORE_READY: \"PROCESS_EXITED_BEFORE_READY\";\n readonly WATCH_NOT_FOUND: \"WATCH_NOT_FOUND\";\n readonly WATCH_START_ERROR: \"WATCH_START_ERROR\";\n readonly WATCH_STOP_ERROR: \"WATCH_STOP_ERROR\";\n readonly VALIDATION_FAILED: \"VALIDATION_FAILED\";\n readonly INVALID_JSON_RESPONSE: \"INVALID_JSON_RESPONSE\";\n readonly UNKNOWN_ERROR: \"UNKNOWN_ERROR\";\n readonly INTERNAL_ERROR: \"INTERNAL_ERROR\";\n readonly CONTAINER_UNAVAILABLE: \"CONTAINER_UNAVAILABLE\";\n readonly OPERATION_INTERRUPTED: \"OPERATION_INTERRUPTED\";\n readonly RPC_TRANSPORT_ERROR: \"RPC_TRANSPORT_ERROR\";\n};\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n//# sourceMappingURL=codes.d.ts.map"],"mappings":";;AAIA;AAsEA;;cAtEqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAsETA,SAAAA,WAAoBA,wBAAwBA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"contexts-DY1LHU1v.d.ts","names":["ErrorCode","Operation","OperationType","ErrorResponse","TContext","Record","ServiceError","ServiceResult","T","OperationType","FileNotFoundContext","FileExistsContext","FileTooLargeContext","FileSystemContext","CommandNotFoundContext","CommandErrorContext","ProcessNotFoundContext","ProcessErrorContext","SessionAlreadyExistsContext","SessionDestroyedContext","SessionTerminatedContext","ProcessReadyTimeoutContext","ProcessExitedBeforeReadyContext","PortAlreadyExposedContext","PortNotExposedContext","InvalidPortContext","PortErrorContext","GitRepositoryNotFoundContext","GitAuthFailedContext","GitBranchNotFoundContext","GitErrorContext","InterpreterNotReadyContext","ContextNotFoundContext","CodeExecutionContext","ValidationFailedContext","Array","BucketMountContext","MissingCredentialsContext","InvalidMountConfigContext","BackupCreateContext","BackupRestoreContext","BackupNotFoundContext","BackupExpiredContext","InvalidBackupConfigContext","OpenCodeStartupContext","InternalErrorContext","ContainerUnavailableContext","OperationInterruptedReason","OperationInterruptedContext","RPCTransportErrorKind","RPCTransportContext"],"sources":["../../shared/dist/errors/types.d.ts","../../shared/dist/errors/contexts.d.ts"],"sourcesContent":["import type { ErrorCode } from './codes';\n/**\n * Standard operation types\n */\nexport declare const Operation: {\n readonly FILE_READ: \"file.read\";\n readonly FILE_WRITE: \"file.write\";\n readonly FILE_DELETE: \"file.delete\";\n readonly FILE_MOVE: \"file.move\";\n readonly FILE_RENAME: \"file.rename\";\n readonly FILE_STAT: \"file.stat\";\n readonly DIRECTORY_CREATE: \"directory.create\";\n readonly DIRECTORY_LIST: \"directory.list\";\n readonly COMMAND_EXECUTE: \"command.execute\";\n readonly COMMAND_STREAM: \"command.stream\";\n readonly PROCESS_START: \"process.start\";\n readonly PROCESS_KILL: \"process.kill\";\n readonly PROCESS_LIST: \"process.list\";\n readonly PROCESS_GET: \"process.get\";\n readonly PROCESS_LOGS: \"process.logs\";\n readonly GIT_CLONE: \"git.clone\";\n readonly GIT_CHECKOUT: \"git.checkout\";\n readonly GIT_OPERATION: \"git.operation\";\n readonly BACKUP_CREATE: \"backup.create\";\n readonly BACKUP_RESTORE: \"backup.restore\";\n readonly BACKUP_UNMOUNT: \"backup.unmount\";\n readonly CODE_EXECUTE: \"code.execute\";\n readonly CODE_CONTEXT_CREATE: \"code.context.create\";\n readonly CODE_CONTEXT_DELETE: \"code.context.delete\";\n};\nexport type OperationType = (typeof Operation)[keyof typeof Operation];\n/**\n * Standard error response format with generic context type\n * TContext allows type-safe access to error-specific context\n */\nexport interface ErrorResponse<TContext = Record<string, unknown>> {\n /**\n * Error type code (machine-readable)\n */\n code: ErrorCode;\n /**\n * Human-readable error message\n */\n message: string;\n /**\n * Operation that was attempted (useful for debugging and logging)\n */\n operation?: OperationType;\n /**\n * Structured error context with relevant details\n * Type varies based on error code\n */\n context: TContext;\n /**\n * HTTP status code (for client SDK)\n */\n httpStatus: number;\n /**\n * Timestamp when error occurred\n */\n timestamp: string;\n /**\n * Actionable suggestion for fixing the error\n */\n suggestion?: string;\n /**\n * Link to documentation\n */\n documentation?: string;\n}\n/**\n * Container ServiceError (lightweight, enriched by handlers)\n */\nexport interface ServiceError {\n message: string;\n code: ErrorCode;\n details?: Record<string, unknown>;\n}\n/**\n * ServiceResult type for container services\n */\nexport type ServiceResult<T> = {\n success: true;\n data: T;\n} | {\n success: false;\n error: ServiceError;\n};\n//# sourceMappingURL=types.d.ts.map","import type { OperationType } from './types';\n/**\n * File system error contexts\n */\nexport interface FileNotFoundContext {\n path: string;\n operation: OperationType;\n}\nexport interface FileExistsContext {\n path: string;\n operation: OperationType;\n}\nexport interface FileTooLargeContext {\n path: string;\n operation: OperationType;\n maxSize: number;\n actualSize: number;\n}\nexport interface FileSystemContext {\n path: string;\n operation: OperationType;\n stderr?: string;\n exitCode?: number;\n}\n/**\n * Command error contexts\n */\nexport interface CommandNotFoundContext {\n command: string;\n}\nexport interface CommandErrorContext {\n command: string;\n exitCode?: number;\n stdout?: string;\n stderr?: string;\n}\n/**\n * Process error contexts\n */\nexport interface ProcessNotFoundContext {\n processId: string;\n}\nexport interface ProcessErrorContext {\n processId: string;\n pid?: number;\n exitCode?: number;\n stderr?: string;\n}\nexport interface SessionAlreadyExistsContext {\n sessionId: string;\n /**\n * `CLOUDFLARE_PLACEMENT_ID` captured from the container at the moment the\n * duplicate create was detected. Included so a restarted DO can learn the\n * container's placement ID from an idempotent session-create. `null` when\n * the env var is not set (for example, in local development).\n */\n containerPlacementId?: string | null;\n}\nexport interface SessionDestroyedContext {\n sessionId: string;\n}\nexport interface SessionTerminatedContext {\n sessionId: string;\n exitCode: number | null;\n}\n/**\n * Process readiness error contexts\n */\nexport interface ProcessReadyTimeoutContext {\n processId: string;\n command: string;\n condition: string;\n timeout: number;\n}\nexport interface ProcessExitedBeforeReadyContext {\n processId: string;\n command: string;\n condition: string;\n exitCode: number;\n}\n/**\n * Port error contexts\n */\nexport interface PortAlreadyExposedContext {\n port: number;\n portName?: string;\n}\nexport interface PortNotExposedContext {\n port: number;\n}\nexport interface InvalidPortContext {\n port: number;\n reason: string;\n}\nexport interface PortErrorContext {\n port: number;\n portName?: string;\n stderr?: string;\n}\n/**\n * Git error contexts\n */\nexport interface GitRepositoryNotFoundContext {\n repository: string;\n}\nexport interface GitAuthFailedContext {\n repository: string;\n}\nexport interface GitBranchNotFoundContext {\n branch: string;\n repository?: string;\n}\nexport interface GitErrorContext {\n repository?: string;\n branch?: string;\n targetDir?: string;\n stderr?: string;\n exitCode?: number;\n}\n/**\n * Code interpreter error contexts\n */\nexport interface InterpreterNotReadyContext {\n retryAfter?: number;\n progress?: number;\n}\nexport interface ContextNotFoundContext {\n contextId: string;\n}\nexport interface CodeExecutionContext {\n contextId?: string;\n ename?: string;\n evalue?: string;\n traceback?: string[];\n}\n/**\n * Validation error contexts\n */\nexport interface ValidationFailedContext {\n validationErrors: Array<{\n field: string;\n message: string;\n code?: string;\n }>;\n}\n/**\n * Bucket mounting error contexts\n */\nexport interface BucketMountContext {\n bucket: string;\n mountPath: string;\n endpoint: string;\n stderr?: string;\n exitCode?: number;\n}\nexport interface MissingCredentialsContext {\n bucket: string;\n endpoint: string;\n}\nexport interface InvalidMountConfigContext {\n bucket?: string;\n mountPath?: string;\n endpoint?: string;\n reason?: string;\n}\n/**\n * Backup error contexts\n */\nexport interface BackupCreateContext {\n dir: string;\n backupId?: string;\n stderr?: string;\n exitCode?: number;\n}\nexport interface BackupRestoreContext {\n dir: string;\n backupId: string;\n stderr?: string;\n exitCode?: number;\n}\nexport interface BackupNotFoundContext {\n backupId: string;\n}\nexport interface BackupExpiredContext {\n backupId: string;\n expiredAt?: string;\n}\nexport interface InvalidBackupConfigContext {\n reason: string;\n}\n/**\n * OpenCode error contexts\n */\nexport interface OpenCodeStartupContext {\n port: number;\n stderr?: string;\n command?: string;\n}\n/**\n * Generic error contexts\n */\nexport interface InternalErrorContext {\n originalError?: string;\n stack?: string;\n [key: string]: unknown;\n}\n/**\n * Container availability error context. Surfaced when the sandbox container\n * cannot accept the incoming RPC connection. The container may be starting\n * up, undergoing a runtime replacement, or temporarily unhealthy. The\n * caller should retry the same operation.\n */\nexport interface ContainerUnavailableContext {\n /**\n * Categorical reason distinguishing startup unavailability from runtime\n * replacement and exhausted upgrade retries.\n */\n reason: 'container_starting' | 'container_unhealthy' | 'container_replaced' | 'rpc_upgrade_failed';\n /**\n * Always true — this error represents a transient unavailability, not a\n * permanent failure. Callers should retry the same operation.\n */\n retryable: true;\n /** Suggested delay in milliseconds before the next retry attempt. */\n retryAfterMs?: number;\n}\nexport type OperationInterruptedReason = 'runtime_replaced' | 'container_stopped' | 'transport_disposed' | 'sandbox_destroyed' | 'sandbox_lifetime_changed' | 'recovery_exhausted' | 'unknown';\nexport interface OperationInterruptedContext {\n reason: OperationInterruptedReason;\n operation: string;\n phase?: string;\n admitted: true | 'unknown';\n retryable: boolean;\n operationId?: string;\n operationKey?: string;\n idempotencyKey?: string;\n recoveryAttempts?: number;\n maxRecoveryAttempts?: number;\n backupId?: string;\n dir?: string;\n}\n/**\n * RPC transport error contexts. Surfaced when the capnweb WebSocket session\n * fails on the SDK side rather than the container reporting a structured\n * error. Always retryable — the next call will open a fresh connection.\n */\nexport type RPCTransportErrorKind = \n/** Server closed the WebSocket (container crash, DO eviction, network blip). */\n'peer_closed'\n/** Underlying socket fired the `error` event. */\n | 'connection_failed'\n/** WebSocket upgrade failed before the session was established. */\n | 'upgrade_failed'\n/** Peer sent a non-string frame; capnweb's wire format is JSON text only. */\n | 'invalid_frame'\n/** Peer sent a frame the wire-format parser rejected (capnweb readLoop SyntaxError). */\n | 'protocol_error'\n/** Session was disposed (locally or remotely) while a call was pending. */\n | 'session_disposed'\n/** Anything else that bubbled up from the transport with no recognisable shape. */\n | 'unknown';\nexport interface RPCTransportContext {\n /** Categorical bucket so callers can branch on `peer_closed` vs `upgrade_failed` etc. */\n kind: RPCTransportErrorKind;\n /** Original error message, verbatim from capnweb / our DeferredTransport. */\n originalMessage: string;\n /**\n * The underlying Error's `name` property. capnweb preserves this across\n * the wire for the standard built-ins (TypeError, SyntaxError, etc.), so\n * it's a more reliable hint than the message string for those cases.\n */\n errorName: string;\n /** WebSocket close code, when available (kind === 'peer_closed'). */\n closeCode?: number;\n /** WebSocket close reason, when available (kind === 'peer_closed'). */\n closeReason?: string;\n}\n//# sourceMappingURL=contexts.d.ts.map"],"mappings":";;;;;;AAIA;AA0BYE,cA1BSD,SA0BI,EAAA;EAKRE,SAAAA,SAAa,EAAA,WAAAC;EAAYC,SAAAA,UAAAA,EAAAA,YAAAA;EAIhCL,SAAAA,WAAAA,EAAAA,aAAAA;EAQME,SAAAA,SAAAA,EAAAA,WAAAA;EAKHE,SAAAA,WAAAA,EAAAA,aAAAA;EAAQ,SAAA,SAAA,EAAA,WAAA;;;;EChDJM,SAAAA,cAAmB,EAAA,gBAErBD;EAEEE,SAAAA,aAAiB,EAAA,eAEnBF;EAEEG,SAAAA,YAAAA,EAAmB,cAErBH;EAIEI,SAAAA,YAAiB,EAAA,cAEnBJ;EAOEK,SAAAA,WAAAA,EAAAA,aAAsB;EAGtBC,SAAAA,YAAAA,EAAmB,cAAA;EASnBC,SAAAA,SAAAA,EAAAA,WAAsB;EAGtBC,SAAAA,YAAAA,EAAmB,cAAA;EAMnBC,SAAAA,aAAAA,EAAAA,eAA2B;EAU3BC,SAAAA,aAAAA,EAAAA,eAAuB;EAGvBC,SAAAA,cAAAA,EAAAA,gBAAwB;EAOxBC,SAAAA,cAAAA,EAAAA,gBAA0B;EAM1BC,SAAAA,YAAAA,EAAAA,cAA+B;EAS/BC,SAAAA,mBAAyB,EAAA,qBAAA;EAIzBC,SAAAA,mBAAqB,EAAA,qBAAA;AAGtC,CAAA;AAIiBE,KDhELxB,aAAAA,GCgEqB,CAAA,ODhEGD,SCgEH,CAAA,CAAA,MAAA,ODhE2BA,SCgE3B,CAAA;AAQjC;AAGA;AAGA;AAIA;AAUiB8B,UDvFA5B,aCuFA4B,CAAAA,WDvFyB1B,MCuFC,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA;EAI1B2B;AAGjB;AASA;EA8BiBO,IAAAA,EDjIPvC,SCiIOuC;EAMAC;AAMjB;AAGA;EAIiBG,OAAAA,EAAAA,MAAAA;EAMAC;AAQjB;AAWA;EAcYG,SAAAA,CAAAA,EDnLI7C,aCmLJ6C;EACKC;AAmBjB;AAeA;;WDjNa5C;;;;;;;;;;;;;;;;;;;;;;AAhDb;AA0BYF,UC1BKQ,mBAAAA,CD0BmBT;EAKnBE,IAAAA,EAAAA,MAAAA;EAAyBE,SAAAA,EC7B3BI,aD6B2BJ;;AAY1BH,UCvCCS,iBAAAA,CDuCDT;EAKHE,IAAAA,EAAAA,MAAAA;EAAQ,SAAA,EC1CNK,aD0CM;;UCxCJG,mBAAAA;;EARAF,SAAAA,EAUFD,aAVqB;EAInBE,OAAAA,EAAAA,MAAAA;EAIAC,UAAAA,EAAAA,MAAAA;AAMjB;AASiBE,UATAD,iBAAAA,CASsB;EAGtBE,IAAAA,EAAAA,MAAAA;EASAC,SAAAA,EAnBFP,aAmBEO;EAGAC,MAAAA,CAAAA,EAAAA,MAAAA;EAMAC,QAAAA,CAAAA,EAAAA,MAAAA;AAUjB;AAGA;AAOA;AAMA;AASiBK,UAxDAT,sBAAAA,CAwDyB;EAIzBU,OAAAA,EAAAA,MAAAA;AAGjB;AAIiBE,UAhEAX,mBAAAA,CAgEgB;EAQhBY,OAAAA,EAAAA,MAAAA;EAGAC,QAAAA,CAAAA,EAAAA,MAAAA;EAGAC,MAAAA,CAAAA,EAAAA,MAAAA;EAIAC,MAAAA,CAAAA,EAAAA,MAAAA;AAUjB;AAIA;AAGA;AASA;AA8BiBS,UAjIAvB,sBAAAA,CAiImB;EAMnBwB,SAAAA,EAAAA,MAAAA;AAMjB;AAGiBE,UA7IAzB,mBAAAA,CA6IoB;EAIpB0B,SAAAA,EAAAA,MAAAA;EAMAC,GAAAA,CAAAA,EAAAA,MAAAA;EAQAC,QAAAA,CAAAA,EAAAA,MAAAA;EAWAC,MAAAA,CAAAA,EAAAA,MAAAA;AAcjB;AACiBE,UAnLA9B,2BAAAA,CAmL2B;EAmBhC+B,SAAAA,EAAAA,MAAAA;EAeKC;;;;;;;;UA3MA/B,uBAAAA;;;UAGAC,wBAAAA;;;;;;;UAOAC,0BAAAA;;;;;;UAMAC,+BAAAA;;;;;;;;;UASAC,yBAAAA;;;;UAIAC,qBAAAA;;;UAGAC,kBAAAA;;;;UAIAC,gBAAAA;;;;;;;;UAQAC,4BAAAA;;;UAGAC,oBAAAA;;;UAGAC,wBAAAA;;;;UAIAC,eAAAA;;;;;;;;;;UAUAC,0BAAAA;;;;UAIAC,sBAAAA;;;UAGAC,oBAAAA;;;;;;;;;UASAC,uBAAAA;oBACKC;;;;;;;;;UA6BLI,mBAAAA;;;;;;UAMAC,oBAAAA;;;;;;UAMAC,qBAAAA;;;UAGAC,oBAAAA;;;;UAIAC,0BAAAA;;;;;;UAMAC,sBAAAA;;;;;;;;UAQAC,oBAAAA;;;;;;;;;;;UAWAC,2BAAAA;;;;;;;;;;;;;;KAcLC,0BAAAA;UACKC,2BAAAA;UACLD;;;;;;;;;;;;;;;;;;KAkBAE,qBAAAA;;;;;;;;UAeKC,mBAAAA;;QAEPD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"dist-BStBkGIC.js","names":["parts","LogLevelEnum","LogLevelEnum"],"sources":["../../shared/dist/env.js","../../shared/dist/logger/sanitize.js","../../shared/dist/git.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/rpc-types.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","import { redactCommand } from './logger/sanitize.js';\n/**\n * Fallback repository name used when URL parsing fails\n */\nexport const FALLBACK_REPO_NAME = 'repository';\n/** Default wall-clock timeout in milliseconds for git clone operations. */\nexport const DEFAULT_GIT_CLONE_TIMEOUT_MS = 600_000;\n/**\n * Extract repository name from a Git URL\n *\n * Supports multiple URL formats:\n * - HTTPS: https://github.com/user/repo.git → repo\n * - HTTPS without .git: https://github.com/user/repo → repo\n * - SSH: git@github.com:user/repo.git → repo\n * - GitLab/others: https://gitlab.com/org/project.git → project\n *\n * @param repoUrl - Git repository URL (HTTPS or SSH format)\n * @returns Repository name extracted from URL, or 'repository' as fallback\n */\nexport function extractRepoName(repoUrl) {\n // Try parsing as standard URL (https://, http://)\n try {\n const url = new URL(repoUrl);\n const pathParts = url.pathname.split('/');\n const lastPart = pathParts[pathParts.length - 1];\n if (lastPart) {\n return lastPart.replace(/\\.git$/, '');\n }\n }\n catch {\n // Not a standard URL, try SSH format\n }\n // For SSH URLs (git@github.com:user/repo.git), split by : and / to get last segment\n // Only process if the URL contains path delimiters\n if (repoUrl.includes(':') || repoUrl.includes('/')) {\n const segments = repoUrl.split(/[:/]/).filter(Boolean);\n const lastSegment = segments[segments.length - 1];\n if (lastSegment) {\n return lastSegment.replace(/\\.git$/, '');\n }\n }\n return FALLBACK_REPO_NAME;\n}\n/**\n * Sanitize data by redacting credentials from any strings\n * Recursively processes objects and arrays to ensure credentials are never leaked\n */\nexport function sanitizeGitData(data) {\n // Handle primitives\n if (typeof data === 'string') {\n return redactCommand(data);\n }\n if (data === null || data === undefined) {\n return data;\n }\n // Handle arrays\n if (Array.isArray(data)) {\n return data.map((item) => sanitizeGitData(item));\n }\n // Handle objects - recursively sanitize all fields\n if (typeof data === 'object') {\n const result = {};\n for (const [key, value] of Object.entries(data)) {\n result[key] = sanitizeGitData(value);\n }\n return result;\n }\n return data;\n}\n/**\n * Logger wrapper that automatically sanitizes git credentials\n */\nexport class GitLogger {\n baseLogger;\n constructor(baseLogger) {\n this.baseLogger = baseLogger;\n }\n sanitizeContext(context) {\n return context\n ? sanitizeGitData(context)\n : context;\n }\n sanitizeError(error) {\n if (!error)\n return error;\n // Create a new error with sanitized message and stack\n const sanitized = new Error(redactCommand(error.message));\n sanitized.name = error.name;\n if (error.stack) {\n sanitized.stack = redactCommand(error.stack);\n }\n // Preserve other enumerable properties\n const sanitizedRecord = sanitized;\n const errorRecord = error;\n for (const key of Object.keys(error)) {\n if (key !== 'message' && key !== 'stack' && key !== 'name') {\n sanitizedRecord[key] = sanitizeGitData(errorRecord[key]);\n }\n }\n return sanitized;\n }\n debug(message, context) {\n this.baseLogger.debug(message, this.sanitizeContext(context));\n }\n info(message, context) {\n this.baseLogger.info(message, this.sanitizeContext(context));\n }\n warn(message, context) {\n this.baseLogger.warn(message, this.sanitizeContext(context));\n }\n error(message, error, context) {\n this.baseLogger.error(message, this.sanitizeError(error), this.sanitizeContext(context));\n }\n child(context) {\n const sanitized = sanitizeGitData(context);\n const childLogger = this.baseLogger.child(sanitized);\n return new GitLogger(childLogger);\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 * Shared interface types for the container-control path.\n *\n * Defines the contract between the SDK control client and the container\n * control-plane API. The current wire implementation uses capnweb RPC.\n */\n/**\n * Error name thrown by the host's `connect` when it has not yet provisioned\n * the requested hash and the request did not carry tarball bytes. The SDK\n * recognises this name via `Error.name` and retries the connect with the\n * bytes attached. Kept as a string constant so it survives capnweb\n * cross-realm error reconstruction.\n */\nexport const EXTENSION_TARBALL_REQUIRED = 'ExtensionTarballRequired';\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"],"mappings":"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"}