@agentuity/sandbox 3.0.12 → 3.1.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 (122) hide show
  1. package/AGENTS.md +3 -3
  2. package/dist/api-reference.d.ts +1221 -0
  3. package/dist/api-reference.d.ts.map +1 -0
  4. package/dist/api-reference.js +1046 -0
  5. package/dist/api-reference.js.map +1 -0
  6. package/dist/base64.d.ts +2 -0
  7. package/dist/base64.d.ts.map +1 -0
  8. package/dist/base64.js +14 -0
  9. package/dist/base64.js.map +1 -0
  10. package/dist/client.d.ts +431 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +632 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/create.d.ts +203 -0
  15. package/dist/create.d.ts.map +1 -0
  16. package/dist/create.js +235 -0
  17. package/dist/create.js.map +1 -0
  18. package/dist/destroy.d.ts +23 -0
  19. package/dist/destroy.d.ts.map +1 -0
  20. package/dist/destroy.js +30 -0
  21. package/dist/destroy.js.map +1 -0
  22. package/dist/disk-checkpoint.d.ts +108 -0
  23. package/dist/disk-checkpoint.d.ts.map +1 -0
  24. package/dist/disk-checkpoint.js +124 -0
  25. package/dist/disk-checkpoint.js.map +1 -0
  26. package/dist/events.d.ts +56 -0
  27. package/dist/events.d.ts.map +1 -0
  28. package/dist/events.js +54 -0
  29. package/dist/events.js.map +1 -0
  30. package/dist/execute.d.ts +99 -0
  31. package/dist/execute.d.ts.map +1 -0
  32. package/dist/execute.js +138 -0
  33. package/dist/execute.js.map +1 -0
  34. package/dist/execution.d.ts +150 -0
  35. package/dist/execution.d.ts.map +1 -0
  36. package/dist/execution.js +120 -0
  37. package/dist/execution.js.map +1 -0
  38. package/dist/files.d.ts +283 -0
  39. package/dist/files.d.ts.map +1 -0
  40. package/dist/files.js +471 -0
  41. package/dist/files.js.map +1 -0
  42. package/dist/get.d.ts +288 -0
  43. package/dist/get.d.ts.map +1 -0
  44. package/dist/get.js +256 -0
  45. package/dist/get.js.map +1 -0
  46. package/dist/getStatus.d.ts +23 -0
  47. package/dist/getStatus.d.ts.map +1 -0
  48. package/dist/getStatus.js +53 -0
  49. package/dist/getStatus.js.map +1 -0
  50. package/dist/index.d.ts +42 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +22 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/job.d.ts +227 -0
  55. package/dist/job.d.ts.map +1 -0
  56. package/dist/job.js +109 -0
  57. package/dist/job.js.map +1 -0
  58. package/dist/list.d.ts +330 -0
  59. package/dist/list.d.ts.map +1 -0
  60. package/dist/list.js +209 -0
  61. package/dist/list.js.map +1 -0
  62. package/dist/pause.d.ts +39 -0
  63. package/dist/pause.d.ts.map +1 -0
  64. package/dist/pause.js +48 -0
  65. package/dist/pause.js.map +1 -0
  66. package/dist/resolve.d.ts +75 -0
  67. package/dist/resolve.d.ts.map +1 -0
  68. package/dist/resolve.js +76 -0
  69. package/dist/resolve.js.map +1 -0
  70. package/dist/resume.d.ts +23 -0
  71. package/dist/resume.d.ts.map +1 -0
  72. package/dist/resume.js +30 -0
  73. package/dist/resume.js.map +1 -0
  74. package/dist/run.d.ts +73 -0
  75. package/dist/run.d.ts.map +1 -0
  76. package/dist/run.js +568 -0
  77. package/dist/run.js.map +1 -0
  78. package/dist/runtime.d.ts +94 -0
  79. package/dist/runtime.d.ts.map +1 -0
  80. package/dist/runtime.js +82 -0
  81. package/dist/runtime.js.map +1 -0
  82. package/dist/snapshot-build.d.ts +48 -0
  83. package/dist/snapshot-build.d.ts.map +1 -0
  84. package/dist/snapshot-build.js +72 -0
  85. package/dist/snapshot-build.js.map +1 -0
  86. package/dist/snapshot.d.ts +596 -0
  87. package/dist/snapshot.d.ts.map +1 -0
  88. package/dist/snapshot.js +612 -0
  89. package/dist/snapshot.js.map +1 -0
  90. package/dist/types.d.ts +1010 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +853 -0
  93. package/dist/types.js.map +1 -0
  94. package/dist/util.d.ts +296 -0
  95. package/dist/util.d.ts.map +1 -0
  96. package/dist/util.js +234 -0
  97. package/dist/util.js.map +1 -0
  98. package/package.json +7 -2
  99. package/src/api-reference.ts +1094 -0
  100. package/src/base64.ts +14 -0
  101. package/src/client.ts +998 -0
  102. package/src/create.ts +273 -0
  103. package/src/destroy.ts +43 -0
  104. package/src/disk-checkpoint.ts +184 -0
  105. package/src/events.ts +72 -0
  106. package/src/execute.ts +167 -0
  107. package/src/execution.ts +152 -0
  108. package/src/files.ts +637 -0
  109. package/src/get.ts +291 -0
  110. package/src/getStatus.ts +72 -0
  111. package/src/index.ts +252 -18
  112. package/src/job.ts +161 -0
  113. package/src/list.ts +239 -0
  114. package/src/pause.ts +75 -0
  115. package/src/resolve.ts +96 -0
  116. package/src/resume.ts +41 -0
  117. package/src/run.ts +783 -0
  118. package/src/runtime.ts +106 -0
  119. package/src/snapshot-build.ts +94 -0
  120. package/src/snapshot.ts +791 -0
  121. package/src/types.ts +1033 -0
  122. package/src/util.ts +280 -0
package/src/util.ts ADDED
@@ -0,0 +1,280 @@
1
+ import { StructuredError } from '@agentuity/core';
2
+ import type { EventEmitter } from 'node:events';
3
+ import { z } from 'zod';
4
+
5
+ interface WritableWithDrain extends EventEmitter {
6
+ write(chunk: Uint8Array): boolean;
7
+ }
8
+
9
+ /**
10
+ * Machine-readable error codes for sandbox operations.
11
+ * These codes allow programmatic error handling without fragile string matching.
12
+ */
13
+ export const SandboxErrorCodeSchema = z.enum([
14
+ 'SANDBOX_NOT_FOUND',
15
+ 'SANDBOX_TERMINATED',
16
+ 'SANDBOX_BUSY',
17
+ 'EXECUTION_NOT_FOUND',
18
+ 'EXECUTION_TIMEOUT',
19
+ 'EXECUTION_CANCELLED',
20
+ 'SNAPSHOT_NOT_FOUND',
21
+ ]);
22
+ export type SandboxErrorCode = z.infer<typeof SandboxErrorCodeSchema>;
23
+
24
+ /**
25
+ * Error thrown when a sandbox API request fails.
26
+ *
27
+ * Includes optional context about which sandbox or execution caused the error.
28
+ */
29
+ export const SandboxResponseError = StructuredError('SandboxResponseError')<{
30
+ /** The sandbox ID associated with the error, if applicable */
31
+ sandboxId?: string;
32
+ /** The execution ID associated with the error, if applicable */
33
+ executionId?: string;
34
+ /** The session ID (trace ID) from the x-session-id response header for OTel correlation */
35
+ sessionId?: string | null;
36
+ /** Machine-readable error code for programmatic error handling */
37
+ code?: SandboxErrorCode;
38
+ }>();
39
+
40
+ /**
41
+ * Error thrown when a sandbox is not found.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * try {
46
+ * await sandboxGet(client, { sandboxId: 'non-existent' });
47
+ * } catch (error) {
48
+ * if (error._tag === 'SandboxNotFoundError') {
49
+ * console.error(`Sandbox not found: ${error.sandboxId}`);
50
+ * }
51
+ * }
52
+ * ```
53
+ */
54
+ export const SandboxNotFoundError = StructuredError('SandboxNotFoundError')<{
55
+ sandboxId: string;
56
+ }>();
57
+
58
+ /**
59
+ * Error thrown when a sandbox has already terminated.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * try {
64
+ * await sandboxExecute(client, { sandboxId: 'terminated-sandbox', command: ['ls'] });
65
+ * } catch (error) {
66
+ * if (error._tag === 'SandboxTerminatedError') {
67
+ * console.error(`Sandbox terminated: ${error.sandboxId}`);
68
+ * }
69
+ * }
70
+ * ```
71
+ */
72
+ export const SandboxTerminatedError = StructuredError('SandboxTerminatedError')<{
73
+ sandboxId: string;
74
+ }>();
75
+
76
+ /**
77
+ * Error thrown when a sandbox is currently busy executing another command.
78
+ *
79
+ * This typically occurs when a second execute request is sent before the
80
+ * previous execution has completed. Sandbox executions are serialized -
81
+ * wait for the current execution to complete before sending a new one.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * try {
86
+ * await sandbox.execute({ command: ['ls'] });
87
+ * } catch (error) {
88
+ * if (error._tag === 'SandboxBusyError') {
89
+ * console.error('Sandbox is busy, waiting for current execution to finish');
90
+ * // Wait and retry, or use executionGet with long-polling to wait for completion
91
+ * }
92
+ * }
93
+ * ```
94
+ */
95
+ export const SandboxBusyError = StructuredError('SandboxBusyError')<{
96
+ sandboxId?: string;
97
+ }>();
98
+
99
+ /**
100
+ * Error thrown when an execution is not found.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * try {
105
+ * await executionGet(client, { executionId: 'non-existent' });
106
+ * } catch (error) {
107
+ * if (error._tag === 'ExecutionNotFoundError') {
108
+ * console.error(`Execution not found: ${error.executionId}`);
109
+ * }
110
+ * }
111
+ * ```
112
+ */
113
+ export const ExecutionNotFoundError = StructuredError('ExecutionNotFoundError')<{
114
+ executionId: string;
115
+ sandboxId?: string;
116
+ }>();
117
+
118
+ /**
119
+ * Error thrown when an execution times out.
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * try {
124
+ * await sandboxExecute(client, { sandboxId, command: ['long-running'], timeout: '30s' });
125
+ * } catch (error) {
126
+ * if (error._tag === 'ExecutionTimeoutError') {
127
+ * console.error('Execution timed out');
128
+ * }
129
+ * }
130
+ * ```
131
+ */
132
+ export const ExecutionTimeoutError = StructuredError('ExecutionTimeoutError')<{
133
+ executionId?: string;
134
+ sandboxId?: string;
135
+ }>();
136
+
137
+ /**
138
+ * Error thrown when an execution is cancelled.
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * try {
143
+ * await sandboxRun(client, params, { signal: controller.signal });
144
+ * } catch (error) {
145
+ * if (error._tag === 'ExecutionCancelledError') {
146
+ * console.error('Execution was cancelled');
147
+ * }
148
+ * }
149
+ * ```
150
+ */
151
+ export const ExecutionCancelledError = StructuredError('ExecutionCancelledError')<{
152
+ sandboxId?: string;
153
+ }>();
154
+
155
+ /**
156
+ * Error thrown when a snapshot is not found.
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * try {
161
+ * await snapshotGet(client, { snapshotId: 'non-existent' });
162
+ * } catch (error) {
163
+ * if (error._tag === 'SnapshotNotFoundError') {
164
+ * console.error(`Snapshot not found: ${error.snapshotId}`);
165
+ * }
166
+ * }
167
+ * ```
168
+ */
169
+ export const SnapshotNotFoundError = StructuredError('SnapshotNotFoundError')<{
170
+ snapshotId?: string;
171
+ }>();
172
+
173
+ /**
174
+ * Context for throwing sandbox errors.
175
+ */
176
+ export const SandboxErrorContextSchema = z.object({
177
+ sandboxId: z.string().optional().describe('sandbox id'),
178
+ executionId: z.string().optional().describe('execution id'),
179
+ jobId: z.string().optional().describe('job id'),
180
+ sessionId: z.string().nullish().describe('session id'),
181
+ snapshotId: z.string().optional().describe('snapshot id'),
182
+ });
183
+ export type SandboxErrorContext = z.infer<typeof SandboxErrorContextSchema>;
184
+
185
+ /**
186
+ * Throws the appropriate sandbox error based on the response code.
187
+ *
188
+ * This helper centralizes error mapping logic, throwing specific error types
189
+ * when the backend returns a known error code, and falling back to
190
+ * SandboxResponseError for unknown codes.
191
+ *
192
+ * Note: Pause and resume operations use standard error codes. The backend
193
+ * returns SANDBOX_NOT_FOUND when the sandbox doesn't exist, and HTTP 409
194
+ * Conflict (handled by APIClient retries) for invalid state transitions
195
+ * (e.g. pausing a non-running sandbox, resuming a non-suspended sandbox).
196
+ * No additional error codes are needed for pause/resume.
197
+ *
198
+ * @param resp - The API response containing message and optional code
199
+ * @param context - Context about the operation (sandbox ID, execution ID, etc.)
200
+ * @throws {SandboxNotFoundError} When code is 'SANDBOX_NOT_FOUND'
201
+ * @throws {SandboxTerminatedError} When code is 'SANDBOX_TERMINATED'
202
+ * @throws {SandboxBusyError} When code is 'SANDBOX_BUSY'
203
+ * @throws {ExecutionNotFoundError} When code is 'EXECUTION_NOT_FOUND'
204
+ * @throws {ExecutionTimeoutError} When code is 'EXECUTION_TIMEOUT'
205
+ * @throws {ExecutionCancelledError} When code is 'EXECUTION_CANCELLED'
206
+ * @throws {SnapshotNotFoundError} When code is 'SNAPSHOT_NOT_FOUND'
207
+ * @throws {SandboxResponseError} For unknown codes or when no code is provided
208
+ */
209
+ export function throwSandboxError(
210
+ resp: { message?: string; code?: string },
211
+ context: SandboxErrorContext
212
+ ): never {
213
+ const { sandboxId, executionId, sessionId, snapshotId } = context;
214
+ const code = resp.code as SandboxErrorCode | undefined;
215
+
216
+ switch (code) {
217
+ case 'SANDBOX_NOT_FOUND':
218
+ throw new SandboxNotFoundError({ message: resp.message, sandboxId: sandboxId ?? '' });
219
+ case 'SANDBOX_TERMINATED':
220
+ throw new SandboxTerminatedError({ message: resp.message, sandboxId: sandboxId ?? '' });
221
+ case 'SANDBOX_BUSY':
222
+ throw new SandboxBusyError({ message: resp.message, sandboxId });
223
+ case 'EXECUTION_NOT_FOUND':
224
+ throw new ExecutionNotFoundError({
225
+ message: resp.message,
226
+ executionId: executionId ?? '',
227
+ sandboxId,
228
+ });
229
+ case 'EXECUTION_TIMEOUT':
230
+ throw new ExecutionTimeoutError({ message: resp.message, executionId, sandboxId });
231
+ case 'EXECUTION_CANCELLED':
232
+ throw new ExecutionCancelledError({ message: resp.message, sandboxId });
233
+ case 'SNAPSHOT_NOT_FOUND':
234
+ throw new SnapshotNotFoundError({ message: resp.message, snapshotId });
235
+ default:
236
+ throw new SandboxResponseError({
237
+ message: resp.message,
238
+ sandboxId,
239
+ executionId,
240
+ sessionId,
241
+ code,
242
+ });
243
+ }
244
+ }
245
+
246
+ /** Current sandbox API version */
247
+
248
+ /**
249
+ * Write a chunk to a writable stream and wait for it to drain if necessary.
250
+ * Properly cleans up event listeners to avoid memory leaks.
251
+ */
252
+ export function writeAndDrain(writable: WritableWithDrain, chunk: Uint8Array): Promise<void> {
253
+ return new Promise((resolve, reject) => {
254
+ let needsDrain: boolean;
255
+ try {
256
+ needsDrain = !writable.write(chunk);
257
+ } catch (err) {
258
+ reject(err);
259
+ return;
260
+ }
261
+ if (needsDrain) {
262
+ const cleanup = () => {
263
+ writable.removeListener('drain', onDrain);
264
+ writable.removeListener('error', onError);
265
+ };
266
+ const onDrain = () => {
267
+ cleanup();
268
+ resolve();
269
+ };
270
+ const onError = (err: Error) => {
271
+ cleanup();
272
+ reject(err);
273
+ };
274
+ writable.once('drain', onDrain);
275
+ writable.once('error', onError);
276
+ } else {
277
+ resolve();
278
+ }
279
+ });
280
+ }