@mastra/deployer-sandbox 0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150

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 (63) hide show
  1. package/CHANGELOG.md +257 -0
  2. package/LICENSE.md +30 -0
  3. package/README.md +104 -0
  4. package/dist/alias.d.ts +10 -0
  5. package/dist/alias.d.ts.map +1 -0
  6. package/dist/client/index.cjs +174 -0
  7. package/dist/client/index.cjs.map +1 -0
  8. package/dist/client/index.d.ts +98 -0
  9. package/dist/client/index.d.ts.map +1 -0
  10. package/dist/client/index.js +171 -0
  11. package/dist/client/index.js.map +1 -0
  12. package/dist/deployer.d.ts +57 -0
  13. package/dist/deployer.d.ts.map +1 -0
  14. package/dist/engine.d.ts +34 -0
  15. package/dist/engine.d.ts.map +1 -0
  16. package/dist/index.cjs +832 -0
  17. package/dist/index.cjs.map +1 -0
  18. package/dist/index.d.ts +7 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +824 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/manifest.d.ts +7 -0
  23. package/dist/manifest.d.ts.map +1 -0
  24. package/dist/shared-C-piQAcU.js +115 -0
  25. package/dist/shared-C-piQAcU.js.map +1 -0
  26. package/dist/shared-H9GioaPM.cjs +192 -0
  27. package/dist/shared-H9GioaPM.cjs.map +1 -0
  28. package/dist/shared.d.ts +63 -0
  29. package/dist/shared.d.ts.map +1 -0
  30. package/dist/studio/assets/CommitMono-400-Regular-DzkyLZ26.woff2 +0 -0
  31. package/dist/studio/assets/CommitMono-700-Regular-DmOSN4kd.woff2 +0 -0
  32. package/dist/studio/assets/MonaSans-VariableFont_wdth-wght-CX-7s9jm.ttf +0 -0
  33. package/dist/studio/assets/babel-lWBcy2sH.js +16 -0
  34. package/dist/studio/assets/bash-atvbtKCR.js +1 -0
  35. package/dist/studio/assets/core-B-AlWOWa.js +12 -0
  36. package/dist/studio/assets/engine-compile-BkERmzkH.js +137 -0
  37. package/dist/studio/assets/engine-javascript-CSIo_1eZ.js +1 -0
  38. package/dist/studio/assets/estree-Dd9JfUIE.js +44 -0
  39. package/dist/studio/assets/github-dark-DHJKELXO.js +1 -0
  40. package/dist/studio/assets/github-light-DAi9KRSo.js +1 -0
  41. package/dist/studio/assets/index-yUiD89B4.js +1 -0
  42. package/dist/studio/assets/index-znlQ7_ex.js +2 -0
  43. package/dist/studio/assets/javascript-ySlJ1b_l.js +1 -0
  44. package/dist/studio/assets/json-BQoSv7ci.js +1 -0
  45. package/dist/studio/assets/jsx-BAng5TT0.js +1 -0
  46. package/dist/studio/assets/livekit-client.esm-CKIgC2IJ.js +39 -0
  47. package/dist/studio/assets/main-D5Yq7Fjg.js +769 -0
  48. package/dist/studio/assets/markdown-UIAJJxZW.js +1 -0
  49. package/dist/studio/assets/preload-helper-PPVm8Dsz.js +1 -0
  50. package/dist/studio/assets/python-DhUJRlN_.js +1 -0
  51. package/dist/studio/assets/shell-CjFT_Tl9.js +1 -0
  52. package/dist/studio/assets/standalone-Dfz2oS5J.js +29 -0
  53. package/dist/studio/assets/style-CAUk37Fq.css +1 -0
  54. package/dist/studio/assets/tsx-B6W0miNI.js +1 -0
  55. package/dist/studio/assets/typescript-Dj6nwHGl.js +1 -0
  56. package/dist/studio/index.html +100 -0
  57. package/dist/studio/mastra.svg +17 -0
  58. package/dist/studio/routes-manifest.json +27 -0
  59. package/dist/types.d.ts +215 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/worker.d.ts +3 -0
  62. package/dist/worker.d.ts.map +1 -0
  63. package/package.json +79 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,832 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_shared = require("./shared-H9GioaPM.cjs");
3
+ let fs_promises = require("fs/promises");
4
+ let path = require("path");
5
+ let url = require("url");
6
+ let _mastra_deployer = require("@mastra/deployer");
7
+ let fs_extra_esm = require("fs-extra/esm");
8
+ let child_process = require("child_process");
9
+ let crypto = require("crypto");
10
+ let fs = require("fs");
11
+ let os = require("os");
12
+ let util = require("util");
13
+ let _mastra_core_workspace = require("@mastra/core/workspace");
14
+ //#region src/alias.ts
15
+ /**
16
+ * Upsert a Vercel Edge Config item so a stable key always points at the
17
+ * current sandbox URL. Used for Tier 3 routing: apps read the key from Edge
18
+ * Config (e.g. in middleware) instead of hardcoding the rotating sandbox URL.
19
+ */
20
+ async function updateEdgeConfigAlias(options) {
21
+ const { token, teamId } = options;
22
+ if (!token) throw new Error("Updating the Edge Config alias requires a Vercel API token. Pass `alias.token`.");
23
+ const endpoint = new URL(`https://api.vercel.com/v1/edge-config/${options.edgeConfigId}/items`);
24
+ if (teamId) endpoint.searchParams.set("teamId", teamId);
25
+ const res = await fetch(endpoint, {
26
+ method: "PATCH",
27
+ headers: {
28
+ Authorization: `Bearer ${token}`,
29
+ "Content-Type": "application/json"
30
+ },
31
+ body: JSON.stringify({ items: [{
32
+ operation: "upsert",
33
+ key: options.key,
34
+ value: options.url
35
+ }] }),
36
+ signal: AbortSignal.timeout(3e4)
37
+ });
38
+ if (!res.ok) {
39
+ const body = await res.text().catch(() => "");
40
+ throw new Error(`Failed to update Edge Config alias "${options.key}" (${res.status}): ${body}`);
41
+ }
42
+ }
43
+ //#endregion
44
+ //#region src/engine.ts
45
+ const execFileAsync = (0, util.promisify)(child_process.execFile);
46
+ const noopLogger = {
47
+ debug: () => {},
48
+ info: () => {},
49
+ warn: () => {},
50
+ error: () => {}
51
+ };
52
+ /** Max shell-command payload per chunk for the base64 upload fallback. */
53
+ const UPLOAD_CHUNK_SIZE = 96e3;
54
+ /**
55
+ * Deploy a prebuilt Mastra server directory into any workspace sandbox that
56
+ * supports networking. Provider-agnostic: only uses the core WorkspaceSandbox
57
+ * contract (`executeCommand` + `networking`, with `writeFiles` / `processes`
58
+ * as fast paths).
59
+ */
60
+ async function deployToSandbox(options) {
61
+ const { sandbox, dir, port = require_shared.DEFAULT_PORT, env = {}, studio = false, healthCheckPath = "/api", healthCheckTimeoutMs = 6e4, healthCheckIntervalMs = 1e3, installCommand = "npm install --omit=dev", logger = noopLogger } = options;
62
+ if (!(0, fs.existsSync)((0, path.join)(dir, "index.mjs"))) throw new Error(`No index.mjs found in "${dir}" — did the build succeed?`);
63
+ logger.info(`Starting ${sandbox.provider} sandbox...`);
64
+ await sandbox.start?.();
65
+ if (!(0, _mastra_core_workspace.supportsNetworking)(sandbox)) throw new Error(`Sandbox provider "${sandbox.provider}" does not support networking (public port URLs), which is required for sandbox deploys.`);
66
+ if (!sandbox.executeCommand) throw new Error(`Sandbox provider "${sandbox.provider}" does not support executeCommand, which is required for sandbox deploys.`);
67
+ const url = await sandbox.networking.getPortUrl(port);
68
+ if (!url) throw new Error(`Sandbox provider "${sandbox.provider}" did not expose a public URL for port ${port}. Make sure the port is declared when constructing the sandbox (e.g. \`ports: [${port}]\`).`);
69
+ const remoteDir = await require_shared.resolveRemoteDir(sandbox, options.remoteDir);
70
+ const mergedEnv = { ...env };
71
+ if (studio && mergedEnv.MASTRA_STUDIO_PATH === void 0) mergedEnv.MASTRA_STUDIO_PATH = `${remoteDir}/studio`;
72
+ logger.info(`Uploading build output from ${dir}...`);
73
+ const tarball = await createTarball(dir);
74
+ logger.debug(`Tarball size: ${(tarball.length / 1024 / 1024).toFixed(2)} MB`);
75
+ const remoteTarball = `${remoteDir}/.deploy.tgz`;
76
+ await require_shared.runInSandbox(sandbox, `mkdir -p ${require_shared.shellQuote(remoteDir)}`);
77
+ await uploadFile(sandbox, remoteTarball, tarball);
78
+ await require_shared.killPreviousServer(sandbox, remoteDir);
79
+ await require_shared.runInSandbox(sandbox, `cd ${require_shared.shellQuote(remoteDir)} && tar -xzf .deploy.tgz && rm -f .deploy.tgz`, { timeout: 12e4 });
80
+ const installHash = await hashInstallInputs(dir, installCommand);
81
+ const marker = `${remoteDir}/${require_shared.INSTALL_MARKER}`;
82
+ const markerCheck = await require_shared.runInSandbox(sandbox, `cat ${require_shared.shellQuote(marker)} 2>/dev/null || true`, { allowFailure: true });
83
+ if (installHash && markerCheck.stdout.trim() === installHash) logger.info("Dependencies unchanged — skipping install.");
84
+ else {
85
+ logger.info(`Installing dependencies (${installCommand})...`);
86
+ await require_shared.runInSandbox(sandbox, `cd ${require_shared.shellQuote(remoteDir)} && ${installCommand}`, {
87
+ timeout: 6e5,
88
+ label: `install dependencies (${installCommand})`
89
+ });
90
+ if (installHash) await require_shared.runInSandbox(sandbox, `printf '%s' ${require_shared.shellQuote(installHash)} > ${require_shared.shellQuote(marker)}`);
91
+ }
92
+ const launchScript = buildLaunchScript({
93
+ remoteDir,
94
+ port,
95
+ env: mergedEnv
96
+ });
97
+ await uploadFile(sandbox, `${remoteDir}/${require_shared.SERVER_SCRIPT}`, Buffer.from(launchScript));
98
+ await require_shared.runInSandbox(sandbox, `chmod 700 ${require_shared.shellQuote(`${remoteDir}/${require_shared.SERVER_SCRIPT}`)}`);
99
+ logger.info("Starting Mastra server...");
100
+ await require_shared.launchServer(sandbox, remoteDir);
101
+ if (!await require_shared.waitForHealthy(url, {
102
+ path: healthCheckPath,
103
+ timeoutMs: healthCheckTimeoutMs,
104
+ intervalMs: healthCheckIntervalMs
105
+ })) {
106
+ const log = await require_shared.tailServerLog(sandbox, remoteDir).catch(() => "");
107
+ throw new Error(`Mastra server did not become healthy at ${url}${healthCheckPath} within ${healthCheckTimeoutMs}ms.` + (log ? `\n\nServer log:\n${log}` : "\n\n(no server log output captured)"));
108
+ }
109
+ const info = await require_shared.getInfoSafe(sandbox);
110
+ return {
111
+ url,
112
+ sandboxId: info?.id ?? sandbox.id,
113
+ expiresAt: info?.timeoutAt,
114
+ stop: async () => {
115
+ await sandbox.stop?.();
116
+ },
117
+ destroy: async () => {
118
+ await sandbox.destroy?.();
119
+ },
120
+ logs: (lines) => require_shared.tailServerLog(sandbox, remoteDir, lines)
121
+ };
122
+ }
123
+ /**
124
+ * Build the POSIX launch script. Re-running the script restarts the server —
125
+ * the wake path uses this after a snapshot resume (which restores the
126
+ * filesystem but not processes).
127
+ */
128
+ function buildLaunchScript(opts) {
129
+ const lines = ["#!/bin/sh", `cd ${require_shared.shellQuote(opts.remoteDir)}`];
130
+ const env = {
131
+ MASTRA_AUTO_DETECT_URL: "true",
132
+ ...opts.env,
133
+ PORT: String(opts.port),
134
+ MASTRA_HOST: "0.0.0.0"
135
+ };
136
+ for (const [key, value] of Object.entries(env)) {
137
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`Invalid environment variable name: "${key}"`);
138
+ lines.push(`export ${key}=${require_shared.shellQuote(value)}`);
139
+ }
140
+ lines.push(`echo $$ > ${require_shared.shellQuote(require_shared.SERVER_PIDFILE)}`);
141
+ lines.push(`exec node index.mjs >> ${require_shared.shellQuote(require_shared.SERVER_LOGFILE)} 2>&1`);
142
+ return lines.join("\n") + "\n";
143
+ }
144
+ /** Create a gzipped tarball of the directory contents (excluding node_modules). */
145
+ async function createTarball(dir) {
146
+ const tmp = await (0, fs_promises.mkdtemp)((0, path.join)((0, os.tmpdir)(), "mastra-sandbox-"));
147
+ const tarPath = (0, path.join)(tmp, "deploy.tgz");
148
+ try {
149
+ await execFileAsync("tar", [
150
+ "-czf",
151
+ tarPath,
152
+ "--exclude=node_modules",
153
+ "-C",
154
+ dir,
155
+ "."
156
+ ]);
157
+ return await (0, fs_promises.readFile)(tarPath);
158
+ } finally {
159
+ await (0, fs_promises.rm)(tmp, {
160
+ recursive: true,
161
+ force: true
162
+ });
163
+ }
164
+ }
165
+ /**
166
+ * Upload a file into the sandbox. Uses the provider's native `writeFiles` fast
167
+ * path when available, otherwise falls back to base64 chunks over
168
+ * `executeCommand` — so `executeCommand` + `networking` is the minimum contract.
169
+ */
170
+ async function uploadFile(sandbox, remotePath, content) {
171
+ if (sandbox.writeFiles) {
172
+ await sandbox.writeFiles([{
173
+ path: remotePath,
174
+ content
175
+ }]);
176
+ return;
177
+ }
178
+ const b64 = content.toString("base64");
179
+ const tmpPath = `${remotePath}.b64`;
180
+ await require_shared.runInSandbox(sandbox, `rm -f ${require_shared.shellQuote(tmpPath)}`);
181
+ for (let i = 0; i < b64.length; i += UPLOAD_CHUNK_SIZE) await require_shared.runInSandbox(sandbox, `printf '%s' ${require_shared.shellQuote(b64.slice(i, i + UPLOAD_CHUNK_SIZE))} >> ${require_shared.shellQuote(tmpPath)}`, { label: `upload chunk to ${remotePath}` });
182
+ await require_shared.runInSandbox(sandbox, `base64 -d ${require_shared.shellQuote(tmpPath)} > ${require_shared.shellQuote(remotePath)} && rm -f ${require_shared.shellQuote(tmpPath)}`, { label: `decode upload at ${remotePath}` });
183
+ }
184
+ /** Lockfiles that, when present in the build output, participate in the install-skip hash. */
185
+ const LOCKFILES = [
186
+ "package-lock.json",
187
+ "npm-shrinkwrap.json",
188
+ "pnpm-lock.yaml",
189
+ "yarn.lock",
190
+ "bun.lock"
191
+ ];
192
+ /**
193
+ * Hash everything that determines the outcome of a dependency install:
194
+ * package.json, any bundled lockfile, and the install command itself. A
195
+ * matching hash means the previous `node_modules` can be reused.
196
+ */
197
+ async function hashInstallInputs(dir, installCommand) {
198
+ const hash = (0, crypto.createHash)("sha256");
199
+ try {
200
+ hash.update(await (0, fs_promises.readFile)((0, path.join)(dir, "package.json")));
201
+ } catch {
202
+ return null;
203
+ }
204
+ for (const lockfile of LOCKFILES) {
205
+ let content;
206
+ try {
207
+ content = await (0, fs_promises.readFile)((0, path.join)(dir, lockfile));
208
+ } catch {
209
+ continue;
210
+ }
211
+ hash.update(lockfile).update(content);
212
+ }
213
+ hash.update(installCommand);
214
+ return hash.digest("hex");
215
+ }
216
+ //#endregion
217
+ //#region src/manifest.ts
218
+ const MANIFEST_FILENAME = "sandbox-deployment.json";
219
+ /** Write `sandbox-deployment.json` into the build output directory. */
220
+ async function writeDeploymentManifest(outputDir, manifest) {
221
+ await (0, fs_promises.writeFile)((0, path.join)(outputDir, MANIFEST_FILENAME), JSON.stringify(manifest, null, 2));
222
+ }
223
+ /** Read `sandbox-deployment.json` from the build output directory, or null when absent. */
224
+ async function readDeploymentManifest(outputDir) {
225
+ let raw;
226
+ try {
227
+ raw = await (0, fs_promises.readFile)((0, path.join)(outputDir, MANIFEST_FILENAME), "utf-8");
228
+ } catch (error) {
229
+ if (error.code === "ENOENT") return null;
230
+ throw error;
231
+ }
232
+ return JSON.parse(raw);
233
+ }
234
+ //#endregion
235
+ //#region src/deployer.ts
236
+ /**
237
+ * Deploy a full Mastra server into any workspace sandbox that supports
238
+ * networking (Vercel Sandbox, E2B, ...) and get a live public URL.
239
+ *
240
+ * Positioning: ephemeral environments — instant previews, PR/CI smoke deploys,
241
+ * agent-built-app verification. Not production hosting.
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * import { SandboxDeployer } from '@mastra/deployer-sandbox';
246
+ * import { VercelSandbox } from '@mastra/vercel';
247
+ *
248
+ * export const mastra = new Mastra({
249
+ * deployer: new SandboxDeployer({
250
+ * sandbox: new VercelSandbox({ sandboxName: 'my-preview', timeout: 3_600_000, ports: [4111] }),
251
+ * }),
252
+ * });
253
+ * ```
254
+ */
255
+ var SandboxDeployer = class extends _mastra_deployer.Deployer {
256
+ /** Sandbox deploys are push-style: `mastra build` runs `deploy()` after bundling. */
257
+ deployOnBuild = true;
258
+ sandbox;
259
+ port;
260
+ studio;
261
+ /** Explicit remote dir, when configured. The engine defaults to `$HOME/mastra-app` inside the sandbox. */
262
+ remoteDir;
263
+ env;
264
+ alias;
265
+ healthCheckTimeoutMs;
266
+ constructor(options) {
267
+ super({ name: "SANDBOX" });
268
+ this.sandbox = options.sandbox;
269
+ this.port = options.port ?? 4111;
270
+ this.studio = options.studio ?? true;
271
+ this.remoteDir = options.remoteDir;
272
+ this.env = options.env ?? {};
273
+ this.alias = options.alias;
274
+ this.healthCheckTimeoutMs = options.healthCheckTimeoutMs;
275
+ }
276
+ /**
277
+ * Merge all existing env files instead of only the first one (base behavior).
278
+ * Later files win in `loadEnvVars()`, so order least → most specific: a
279
+ * `.env.local` written by `vercel env pull` shouldn't shadow the `.env` that
280
+ * holds the app's own keys.
281
+ */
282
+ async getEnvFiles() {
283
+ const candidates = [
284
+ ".env",
285
+ ".env.production",
286
+ ".env.local"
287
+ ];
288
+ const existing = [];
289
+ for (const file of candidates) try {
290
+ await (0, fs_promises.access)(file);
291
+ existing.push(file);
292
+ } catch {}
293
+ return existing;
294
+ }
295
+ async getUserBundlerOptions(mastraEntryFile, outputDirectory) {
296
+ return {
297
+ ...await super.getUserBundlerOptions(mastraEntryFile, outputDirectory),
298
+ externals: true
299
+ };
300
+ }
301
+ getEntry() {
302
+ return `
303
+ // @ts-expect-error
304
+ import { scoreTracesWorkflow } from '@mastra/core/evals/scoreTraces';
305
+ import { mastra } from '#mastra';
306
+ import { createNodeServer, getToolExports } from '#server';
307
+ import { tools } from '#tools';
308
+
309
+ // @ts-expect-error
310
+ await createNodeServer(mastra, { tools: getToolExports(tools), studio: ${this.studio} });
311
+
312
+ const storage = mastra.getStorage();
313
+ if (storage) {
314
+ if (!storage.disableInit) {
315
+ storage.init();
316
+ }
317
+ mastra.__registerInternalWorkflow(scoreTracesWorkflow);
318
+ }
319
+ `;
320
+ }
321
+ async prepare(outputDirectory) {
322
+ await super.prepare(outputDirectory);
323
+ if (this.studio) {
324
+ const studioSource = (0, path.join)((0, path.dirname)((0, path.dirname)((0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href))), "dist", "studio");
325
+ const studioServePath = (0, path.join)(outputDirectory, this.outputDir, "studio");
326
+ try {
327
+ await (0, fs_extra_esm.copy)(studioSource, studioServePath, { overwrite: true });
328
+ } catch (err) {
329
+ throw new Error(`Failed to copy studio assets from "${studioSource}" to "${studioServePath}": ${err instanceof Error ? err.message : err}`);
330
+ }
331
+ }
332
+ }
333
+ async bundle(entryFile, outputDirectory, { toolsPaths, projectRoot }) {
334
+ return this._bundle(this.getEntry(), entryFile, {
335
+ outputDirectory,
336
+ projectRoot
337
+ }, toolsPaths);
338
+ }
339
+ /**
340
+ * Deploy the built output into the sandbox and wait for the server to come
341
+ * up on its public URL. Writes `sandbox-deployment.json` into the output
342
+ * directory and updates the Edge Config alias when configured.
343
+ */
344
+ async deploy(outputDirectory) {
345
+ const dir = (0, path.join)(outputDirectory, this.outputDir);
346
+ const envVars = await this.loadEnvVars();
347
+ const env = {
348
+ ...Object.fromEntries(envVars),
349
+ ...this.env
350
+ };
351
+ if (envVars.size > 0) this.logger.warn("Environment variables from your .env file are injected into the remote sandbox. Anyone with access to the sandbox can read them.");
352
+ const deployment = await deployToSandbox({
353
+ sandbox: this.sandbox,
354
+ dir,
355
+ port: this.port,
356
+ env,
357
+ studio: this.studio,
358
+ remoteDir: this.remoteDir,
359
+ healthCheckTimeoutMs: this.healthCheckTimeoutMs,
360
+ logger: this.logger
361
+ });
362
+ await writeDeploymentManifest(dir, {
363
+ provider: this.sandbox.provider,
364
+ sandboxId: deployment.sandboxId,
365
+ url: deployment.url,
366
+ port: this.port,
367
+ deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
368
+ expiresAt: deployment.expiresAt?.toISOString()
369
+ });
370
+ if (this.alias) {
371
+ await updateEdgeConfigAlias({
372
+ ...this.alias,
373
+ url: deployment.url
374
+ });
375
+ this.logger.info(`Edge Config alias "${this.alias.key}" now points at ${deployment.url}`);
376
+ }
377
+ this.logger.info(`Mastra server deployed: ${deployment.url}/api`);
378
+ if (this.studio) this.logger.info(`Studio: ${deployment.url}`);
379
+ if (deployment.expiresAt) this.logger.warn(`Sandbox expires at ${deployment.expiresAt.toISOString()} (provider runtime cap).`);
380
+ }
381
+ };
382
+ //#endregion
383
+ //#region src/worker.ts
384
+ const ARCHIVE = ".mastra-worker.tar.gz";
385
+ const RUNTIME_DIR = ".mastra/executions";
386
+ const INSTALL_MARKER = ".mastra-install-hash";
387
+ const INSTALL_LOCK = ".mastra-install-lock";
388
+ const ARTIFACT_LOCK = ".mastra-artifact-lock";
389
+ const EXECUTION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
390
+ const DEFAULT_INPUT_LIMIT = 16 * 1024 * 1024;
391
+ const DEFAULT_OUTPUT_READ_LIMIT = 1024 * 1024;
392
+ async function deployWorkerToSandbox(options) {
393
+ validateOptions(options);
394
+ const { sandbox, dir, executionId, command, mode = "worker", args = [], env = {}, workingDirectory = ".", installCommand = "npm install --omit=dev", startupTimeoutMs = 1e4, executionTimeoutMs, terminationGraceMs = 5e3, inputLimitBytes = DEFAULT_INPUT_LIMIT } = options;
395
+ const remoteDir = await require_shared.resolveRemoteDir(sandbox, options.remoteDir);
396
+ const config = {
397
+ sandbox,
398
+ remoteDir,
399
+ command,
400
+ args,
401
+ env,
402
+ workingDirectory,
403
+ mode,
404
+ startupTimeoutMs,
405
+ executionTimeoutMs,
406
+ terminationGraceMs,
407
+ inputLimitBytes
408
+ };
409
+ const archive = `${remoteDir}/${ARCHIVE}`;
410
+ const tarball = await createTarball(dir);
411
+ const installHash = await hashInstallInputs(dir, installCommand);
412
+ const artifactLock = `${remoteDir}/${ARTIFACT_LOCK}`;
413
+ let artifactLockAcquired = false;
414
+ try {
415
+ await require_shared.runInSandbox(sandbox, `mkdir -p ${require_shared.shellQuote(remoteDir)}`);
416
+ await acquireLock(sandbox, artifactLock, options.installTimeoutMs, "worker artifact");
417
+ artifactLockAcquired = true;
418
+ await uploadFile(sandbox, archive, tarball);
419
+ await require_shared.runInSandbox(sandbox, `tar -xzf ${require_shared.shellQuote(archive)} -C ${require_shared.shellQuote(remoteDir)} && rm -f ${require_shared.shellQuote(archive)}`, { label: "extract worker artifact" });
420
+ } catch (error) {
421
+ throw workerPhaseError("upload", error);
422
+ } finally {
423
+ if (artifactLockAcquired) await require_shared.runInSandbox(sandbox, `rm -rf ${require_shared.shellQuote(artifactLock)}`, {
424
+ allowFailure: true,
425
+ label: "release worker artifact lock"
426
+ });
427
+ }
428
+ try {
429
+ await installDependencies(sandbox, remoteDir, installHash ?? void 0, installCommand, options.installTimeoutMs);
430
+ } catch (error) {
431
+ throw workerPhaseError("install", error);
432
+ }
433
+ return createExecution(config, executionId, options.input);
434
+ }
435
+ function validateOptions(options) {
436
+ if (!options.sandbox.executeCommand) throw new Error(`Sandbox provider "${options.sandbox.provider}" does not support executeCommand, which is required for worker deploys.`);
437
+ validateExecutionId(options.executionId);
438
+ if (!options.command || /[\0\r\n]/.test(options.command)) throw new Error("Worker command must be a non-empty executable path.");
439
+ if (options.args?.some((arg) => arg.includes("\0"))) throw new Error("Worker arguments must not contain NUL bytes.");
440
+ for (const key of Object.keys(options.env ?? {})) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`Invalid worker environment variable name: ${key}`);
441
+ validateRelativePath(options.workingDirectory ?? ".", "workingDirectory");
442
+ validateInput(options.input);
443
+ for (const [name, value] of [
444
+ ["inputLimitBytes", options.inputLimitBytes],
445
+ ["startupTimeoutMs", options.startupTimeoutMs],
446
+ ["executionTimeoutMs", options.executionTimeoutMs],
447
+ ["terminationGraceMs", options.terminationGraceMs]
448
+ ]) if (value !== void 0 && (!Number.isFinite(value) || value <= 0)) throw new Error(`${name} must be greater than zero.`);
449
+ }
450
+ function validateRelativePath(value, label) {
451
+ if (!value || path.posix.isAbsolute(value) || path.posix.normalize(value).startsWith("..")) throw new Error(`Worker ${label} must stay within the deployed artifact root.`);
452
+ }
453
+ function validateInput(input) {
454
+ if (input?.type === "file") validateRelativePath(input.path, "input file path");
455
+ }
456
+ async function acquireLock(sandbox, lock, timeout, label) {
457
+ const timeoutMs = timeout ?? 6e5;
458
+ const attempts = Math.max(1, Math.ceil(timeoutMs / 1e3));
459
+ await require_shared.runInSandbox(sandbox, [
460
+ "i=0",
461
+ `while ! mkdir ${require_shared.shellQuote(lock)} 2>/dev/null; do`,
462
+ ` if [ "$i" -ge ${attempts} ]; then echo ${require_shared.shellQuote(`${label} lock timeout`)} >&2; exit 1; fi`,
463
+ " sleep 1; i=$((i + 1))",
464
+ "done"
465
+ ].join("\n"), {
466
+ timeout: timeoutMs,
467
+ label: `acquire ${label} lock`
468
+ });
469
+ }
470
+ async function installDependencies(sandbox, remoteDir, installHash, installCommand, timeout) {
471
+ if (!installHash) return;
472
+ const marker = `${remoteDir}/${INSTALL_MARKER}`;
473
+ const lock = `${remoteDir}/${INSTALL_LOCK}`;
474
+ await acquireLock(sandbox, lock, timeout, "dependency install");
475
+ try {
476
+ await require_shared.runInSandbox(sandbox, [
477
+ `current="$(cat ${require_shared.shellQuote(marker)} 2>/dev/null || true)"`,
478
+ `if [ "$current" != ${require_shared.shellQuote(installHash)} ]; then`,
479
+ ` cd ${require_shared.shellQuote(remoteDir)} && ${installCommand}`,
480
+ ` printf %s ${require_shared.shellQuote(installHash)} > ${require_shared.shellQuote(`${marker}.tmp`)}`,
481
+ ` mv ${require_shared.shellQuote(`${marker}.tmp`)} ${require_shared.shellQuote(marker)}`,
482
+ "fi"
483
+ ].join("\n"), {
484
+ timeout: timeout ?? 6e5,
485
+ label: "install worker dependencies"
486
+ });
487
+ } finally {
488
+ await require_shared.runInSandbox(sandbox, `rm -rf ${require_shared.shellQuote(lock)}`, {
489
+ allowFailure: true,
490
+ label: "release dependency install lock"
491
+ });
492
+ }
493
+ }
494
+ async function createExecution(config, executionId, input) {
495
+ validateExecutionId(executionId);
496
+ const paths = executionPaths(config.remoteDir, executionId);
497
+ await require_shared.runInSandbox(config.sandbox, `mkdir -p ${require_shared.shellQuote(`${config.remoteDir}/${RUNTIME_DIR}`)} && mkdir -m 700 ${require_shared.shellQuote(paths.dir)}`, { label: "create worker execution namespace" });
498
+ const script = buildExecutionScript(config, paths, await stageInput(config, paths, input));
499
+ await uploadFile(config.sandbox, paths.script, Buffer.from(script));
500
+ await require_shared.runInSandbox(config.sandbox, `chmod 700 ${require_shared.shellQuote(paths.script)}`);
501
+ try {
502
+ await launchExecution(config.sandbox, paths);
503
+ } catch (error) {
504
+ await writeFailedStatus(config.sandbox, paths, executionId, "launch", error);
505
+ throw workerPhaseError("launch", error);
506
+ }
507
+ const startup = await waitForStartup(config, executionId, paths);
508
+ if (startup.state === "timed_out") await cancelExecution(config, executionId, paths, "startup");
509
+ if (startup.state === "failed" || startup.state === "timed_out" || startup.state === "provider_unavailable") throw new Error(`Worker ${startup.state} during startup${"message" in startup && startup.message ? `: ${startup.message}` : ""}.`);
510
+ const info = await require_shared.getInfoSafe(config.sandbox);
511
+ return deployment(config, executionId, paths, info?.id ?? config.sandbox.id ?? "unknown", info?.timeoutAt);
512
+ }
513
+ function deployment(config, executionId, paths, sandboxId, expiresAt) {
514
+ return {
515
+ sandboxId,
516
+ executionId,
517
+ expiresAt,
518
+ status: (options) => readWorkerStatus(config.sandbox, executionId, paths, options),
519
+ readOutput: (stream, options) => readOutput(config.sandbox, executionId, paths, stream, options),
520
+ cancel: () => cancelExecution(config, executionId, paths),
521
+ stop: async () => {
522
+ if (!config.sandbox.stop) throw new Error(`Sandbox provider "${config.sandbox.provider}" does not support stop.`);
523
+ await config.sandbox.stop();
524
+ },
525
+ destroy: (options) => destroyWithRetry(config.sandbox, options),
526
+ relaunch: async (options) => {
527
+ if (options.executionId === executionId) throw new Error("Relaunch requires a new executionId.");
528
+ validateInput(options.input);
529
+ return createExecution(config, options.executionId, options.input);
530
+ }
531
+ };
532
+ }
533
+ async function stageInput(config, paths, input) {
534
+ if (!input) return void 0;
535
+ const data = typeof input.data === "string" ? Buffer.from(input.data) : Buffer.from(input.data);
536
+ if (data.byteLength > config.inputLimitBytes) throw new Error(`Worker input exceeds inputLimitBytes (${data.byteLength} > ${config.inputLimitBytes}).`);
537
+ const path$1 = input.type === "stdin" ? paths.stdin : path.posix.resolve(config.remoteDir, input.path);
538
+ await uploadFile(config.sandbox, path$1, data);
539
+ await require_shared.runInSandbox(config.sandbox, `chmod 600 ${require_shared.shellQuote(path$1)}`);
540
+ return input.type === "stdin" ? path$1 : void 0;
541
+ }
542
+ function buildExecutionScript(config, paths, stdinPath) {
543
+ const cwd = path.posix.resolve(config.remoteDir, config.workingDirectory);
544
+ const envPrefix = Object.entries(config.env).map(([key, value]) => `${key}=${require_shared.shellQuote(value)}`).join(" ");
545
+ const executable = [require_shared.shellQuote(config.command), ...config.args.map(require_shared.shellQuote)].join(" ");
546
+ const target = `${envPrefix ? `env ${envPrefix} ` : ""}${executable}`;
547
+ const graceAttempts = Math.max(1, Math.ceil(config.terminationGraceMs / 1e3));
548
+ const state = (value) => `tmp=${require_shared.shellQuote(`${paths.status}.tmp.$$`)}; printf '%s\\n' ${require_shared.shellQuote(value)} > "$tmp"; mv "$tmp" ${require_shared.shellQuote(paths.status)};`;
549
+ return [
550
+ "#!/bin/sh",
551
+ `cd ${require_shared.shellQuote(cwd)}`,
552
+ `execution_id=${require_shared.shellQuote(paths.executionId)}`,
553
+ `stdout=${require_shared.shellQuote(paths.stdout)}`,
554
+ `stderr=${require_shared.shellQuote(paths.stderr)}`,
555
+ `pidfile=${require_shared.shellQuote(paths.pid)}`,
556
+ `tokenfile=${require_shared.shellQuote(paths.pidToken)}`,
557
+ `: > "$stdout"; : > "$stderr"`,
558
+ state(`starting|${paths.executionId}`),
559
+ `setsid sh -c ${require_shared.shellQuote(`exec ${target}${stdinPath ? ` < ${require_shared.shellQuote(stdinPath)}` : ""}`)} > "$stdout" 2> "$stderr" &`,
560
+ "child=$!",
561
+ "printf %s \"$child\" > \"$pidfile\"",
562
+ `if [ -r "/proc/$child/stat" ]; then awk '{print $22}' "/proc/$child/stat" > "$tokenfile"; else : > "$tokenfile"; fi`,
563
+ state(`running|${paths.executionId}`),
564
+ "cancelled=0",
565
+ `trap 'cancelled=1; kill -TERM -"$child" 2>/dev/null || kill -TERM "$child" 2>/dev/null || true' TERM INT`,
566
+ ...config.executionTimeoutMs ? [`(sleep ${Math.max(1, Math.ceil(config.executionTimeoutMs / 1e3))}; if kill -0 "$child" 2>/dev/null; then ${state(`timed_out|${paths.executionId}|execution`)} kill -TERM -"$child" 2>/dev/null || kill -TERM "$child" 2>/dev/null || true; i=0; while kill -0 "$child" 2>/dev/null && [ "$i" -lt ${graceAttempts} ]; do sleep 1; i=$((i + 1)); done; kill -KILL -"$child" 2>/dev/null || kill -KILL "$child" 2>/dev/null || true; fi) &`, "watchdog=$!"] : [],
567
+ "wait \"$child\"",
568
+ "code=$?",
569
+ ...config.executionTimeoutMs ? ["kill \"$watchdog\" 2>/dev/null || true"] : [],
570
+ `current="$(cat ${require_shared.shellQuote(paths.status)} 2>/dev/null || true)"`,
571
+ `case "$current" in timed_out*) ;; *) if [ "$cancelled" -eq 1 ]; then ${state(`cancelled|${paths.executionId}|TERM`)} else signal=''; if [ "$code" -gt 128 ]; then signal="SIG$((code - 128))"; fi; tmp=${require_shared.shellQuote(`${paths.status}.tmp.$$`)}; printf 'exited|%s|%s|%s\\n' "$execution_id" "$code" "$signal" > "$tmp"; mv "$tmp" ${require_shared.shellQuote(paths.status)}; fi ;; esac`,
572
+ "rm -f \"$pidfile\" \"$tokenfile\"",
573
+ "exit \"$code\""
574
+ ].join("\n");
575
+ }
576
+ async function launchExecution(sandbox, paths) {
577
+ await require_shared.runInSandbox(sandbox, `setsid nohup sh ${require_shared.shellQuote(paths.script)} >/dev/null 2>&1 & echo $!`, { label: "launch worker execution" });
578
+ }
579
+ async function waitForStartup(config, executionId, paths) {
580
+ const deadline = Date.now() + config.startupTimeoutMs;
581
+ while (Date.now() < deadline) {
582
+ const status = await readWorkerStatus(config.sandbox, executionId, paths);
583
+ if (status.state !== "unknown" && status.state !== "starting") return status;
584
+ await new Promise((resolve) => setTimeout(resolve, 100));
585
+ }
586
+ return {
587
+ state: "timed_out",
588
+ executionId,
589
+ phase: "startup"
590
+ };
591
+ }
592
+ async function readWorkerStatus(sandbox, executionId, paths, options) {
593
+ const providerState = sandbox.status;
594
+ if (providerState === "destroyed" || providerState === "destroying") return {
595
+ state: "provider_unavailable",
596
+ executionId,
597
+ providerState
598
+ };
599
+ if (providerState === "stopped" || providerState === "stopping") {
600
+ if (!options?.wake || !sandbox.start) return {
601
+ state: "provider_unavailable",
602
+ executionId,
603
+ providerState
604
+ };
605
+ try {
606
+ await sandbox.start();
607
+ } catch (error) {
608
+ return {
609
+ state: "provider_unavailable",
610
+ executionId,
611
+ providerState,
612
+ message: errorMessage(error)
613
+ };
614
+ }
615
+ }
616
+ try {
617
+ const result = await require_shared.runInSandbox(sandbox, [
618
+ `status="$(cat ${require_shared.shellQuote(paths.status)} 2>/dev/null || true)"`,
619
+ `if [ -f ${require_shared.shellQuote(paths.pid)} ]; then`,
620
+ ` pid="$(cat ${require_shared.shellQuote(paths.pid)})"`,
621
+ ` expected="$(cat ${require_shared.shellQuote(paths.pidToken)} 2>/dev/null || true)"`,
622
+ ` actual="$(if [ -r "/proc/$pid/stat" ]; then awk '{print $22}' "/proc/$pid/stat"; fi)"`,
623
+ ` if kill -0 "$pid" 2>/dev/null && { [ -z "$expected" ] || [ "$expected" = "$actual" ]; }; then echo "running|${executionId}"; exit 0; fi`,
624
+ ` if kill -0 "$pid" 2>/dev/null; then echo "stale|${executionId}"; exit 0; fi`,
625
+ "fi",
626
+ `if [ -n "$status" ]; then printf '%s\\n' "$status"; else echo "unknown|${executionId}"; fi`
627
+ ].join("\n"), {
628
+ allowFailure: true,
629
+ label: "read worker status"
630
+ });
631
+ if (result.exitCode !== 0) return {
632
+ state: "provider_unavailable",
633
+ executionId,
634
+ providerState: sandbox.status,
635
+ message: result.stderr || result.stdout || "Sandbox status inspection failed."
636
+ };
637
+ return parseStatus(executionId, result.stdout.trim());
638
+ } catch (error) {
639
+ return {
640
+ state: "provider_unavailable",
641
+ executionId,
642
+ providerState: sandbox.status,
643
+ message: errorMessage(error)
644
+ };
645
+ }
646
+ }
647
+ function parseStatus(executionId, value) {
648
+ const [state, recordedId, first, second] = value.split("|");
649
+ if (recordedId !== executionId || state === "stale") return {
650
+ state: "unknown",
651
+ executionId
652
+ };
653
+ if (state === "starting") return {
654
+ state,
655
+ executionId
656
+ };
657
+ if (state === "running") return {
658
+ state,
659
+ executionId
660
+ };
661
+ if (state === "exited") {
662
+ const exitCode = Number(first);
663
+ return Number.isInteger(exitCode) ? {
664
+ state,
665
+ executionId,
666
+ exitCode,
667
+ ...second ? { signal: second } : {}
668
+ } : {
669
+ state: "unknown",
670
+ executionId
671
+ };
672
+ }
673
+ if (state === "cancelled") return {
674
+ state,
675
+ executionId,
676
+ ...first ? { signal: first } : {}
677
+ };
678
+ if (state === "timed_out" && (first === "startup" || first === "execution")) return {
679
+ state,
680
+ executionId,
681
+ phase: first
682
+ };
683
+ if (state === "failed" && (first === "upload" || first === "install" || first === "launch")) return {
684
+ state,
685
+ executionId,
686
+ phase: first,
687
+ message: second ?? ""
688
+ };
689
+ return {
690
+ state: "unknown",
691
+ executionId
692
+ };
693
+ }
694
+ async function cancelExecution(config, executionId, paths, timeoutPhase) {
695
+ const current = await readWorkerStatus(config.sandbox, executionId, paths);
696
+ if (current.state !== "running" && current.state !== "starting") return current;
697
+ const attempts = Math.max(1, Math.ceil(config.terminationGraceMs / 1e3));
698
+ const terminal = timeoutPhase ? `timed_out|${executionId}|startup` : `cancelled|${executionId}|TERM`;
699
+ await require_shared.runInSandbox(config.sandbox, [
700
+ `pid="$(cat ${require_shared.shellQuote(paths.pid)} 2>/dev/null || true)"`,
701
+ "[ -n \"$pid\" ] || exit 0",
702
+ `expected="$(cat ${require_shared.shellQuote(paths.pidToken)} 2>/dev/null || true)"`,
703
+ `actual="$(if [ -r "/proc/$pid/stat" ]; then awk '{print $22}' "/proc/$pid/stat"; fi)"`,
704
+ "[ -n \"$expected\" ] && [ \"$expected\" != \"$actual\" ] && exit 0",
705
+ "kill -TERM -\"$pid\" 2>/dev/null || kill -TERM \"$pid\" 2>/dev/null || true",
706
+ `i=0; while kill -0 "$pid" 2>/dev/null && [ "$i" -lt ${attempts} ]; do sleep 1; i=$((i + 1)); done`,
707
+ "kill -KILL -\"$pid\" 2>/dev/null || kill -KILL \"$pid\" 2>/dev/null || true",
708
+ `tmp=${require_shared.shellQuote(`${paths.status}.tmp.$$`)}; printf '%s\\n' ${require_shared.shellQuote(terminal)} > "$tmp"; mv "$tmp" ${require_shared.shellQuote(paths.status)}`,
709
+ `rm -f ${require_shared.shellQuote(paths.pid)} ${require_shared.shellQuote(paths.pidToken)}`
710
+ ].join("\n"), {
711
+ allowFailure: true,
712
+ timeout: config.terminationGraceMs + 5e3,
713
+ label: "cancel worker execution"
714
+ });
715
+ return parseStatus(executionId, terminal);
716
+ }
717
+ async function readOutput(sandbox, executionId, paths, stream, options) {
718
+ const offset = Math.max(0, Math.floor(options?.offset ?? 0));
719
+ const maxBytes = Math.max(1, Math.floor(options?.maxBytes ?? DEFAULT_OUTPUT_READ_LIMIT));
720
+ const path$2 = stream === "stdout" ? paths.stdout : paths.stderr;
721
+ try {
722
+ const result = await require_shared.runInSandbox(sandbox, `size=$(wc -c < ${require_shared.shellQuote(path$2)} 2>/dev/null || echo 0); printf '%s\\n' "$size"; tail -c +${offset + 1} ${require_shared.shellQuote(path$2)} 2>/dev/null | head -c ${maxBytes} | base64`, {
723
+ allowFailure: true,
724
+ label: `read worker ${stream}`
725
+ });
726
+ if (result.exitCode !== 0) throw new Error(result.stderr || result.stdout || `Unable to read worker ${stream}.`);
727
+ const newline = result.stdout.indexOf("\n");
728
+ const totalBytes = Number((newline === -1 ? result.stdout : result.stdout.slice(0, newline)).trim()) || 0;
729
+ const encoded = newline === -1 ? "" : result.stdout.slice(newline + 1).replace(/\s/g, "");
730
+ const data = Buffer.from(encoded, "base64");
731
+ const nextOffset = offset + data.byteLength;
732
+ const status = await readWorkerStatus(sandbox, executionId, paths);
733
+ const terminal = [
734
+ "exited",
735
+ "cancelled",
736
+ "timed_out",
737
+ "failed"
738
+ ].includes(status.state);
739
+ const interrupted = status.state === "provider_unavailable" || status.state === "unknown";
740
+ return {
741
+ stream,
742
+ data,
743
+ offset,
744
+ nextOffset,
745
+ totalBytes,
746
+ eof: terminal && nextOffset >= totalBytes,
747
+ truncated: nextOffset < totalBytes,
748
+ interrupted
749
+ };
750
+ } catch {
751
+ return {
752
+ stream,
753
+ data: /* @__PURE__ */ new Uint8Array(),
754
+ offset,
755
+ nextOffset: offset,
756
+ totalBytes: offset,
757
+ eof: false,
758
+ truncated: false,
759
+ interrupted: true
760
+ };
761
+ }
762
+ }
763
+ async function destroyWithRetry(sandbox, options) {
764
+ if (!sandbox.destroy) return {
765
+ state: "unsupported",
766
+ attempts: 0
767
+ };
768
+ const attempts = Math.max(1, Math.floor(options?.attempts ?? 3));
769
+ const delayMs = Math.max(0, Math.floor(options?.delayMs ?? 250));
770
+ let lastError;
771
+ for (let attempt = 1; attempt <= attempts; attempt++) try {
772
+ await sandbox.destroy();
773
+ return {
774
+ state: "destroyed",
775
+ attempts: attempt
776
+ };
777
+ } catch (error) {
778
+ lastError = error;
779
+ if (attempt < attempts) await new Promise((resolve) => setTimeout(resolve, delayMs));
780
+ }
781
+ return {
782
+ state: "exhausted",
783
+ attempts,
784
+ error: lastError
785
+ };
786
+ }
787
+ async function writeFailedStatus(sandbox, paths, executionId, phase, error) {
788
+ await writeStatus(sandbox, paths.status, `failed|${executionId}|${phase}|${sanitizeStatusValue(errorMessage(error))}`);
789
+ }
790
+ async function writeStatus(sandbox, path$3, value) {
791
+ await require_shared.runInSandbox(sandbox, `tmp=${require_shared.shellQuote(`${path$3}.tmp.$$`)}; printf '%s\\n' ${require_shared.shellQuote(value)} > "$tmp"; mv "$tmp" ${require_shared.shellQuote(path$3)}`, {
792
+ allowFailure: true,
793
+ label: "write worker status"
794
+ });
795
+ }
796
+ function executionPaths(remoteDir, executionId) {
797
+ const dir = `${remoteDir}/${RUNTIME_DIR}/${executionId}`;
798
+ return {
799
+ executionId,
800
+ dir,
801
+ script: `${dir}/launch.sh`,
802
+ pid: `${dir}/pid`,
803
+ pidToken: `${dir}/pid-start`,
804
+ status: `${dir}/status`,
805
+ stdin: `${dir}/stdin`,
806
+ stdout: `${dir}/stdout`,
807
+ stderr: `${dir}/stderr`
808
+ };
809
+ }
810
+ function validateExecutionId(executionId) {
811
+ if (!executionId || !EXECUTION_ID_PATTERN.test(executionId)) throw new Error("Worker executionId must contain only letters, numbers, dots, underscores, and hyphens.");
812
+ }
813
+ function workerPhaseError(phase, error) {
814
+ return new Error(`Worker ${phase} failed: ${errorMessage(error)}`, { cause: error });
815
+ }
816
+ function errorMessage(error) {
817
+ return error instanceof Error ? error.message : String(error);
818
+ }
819
+ function sanitizeStatusValue(value) {
820
+ return value.replace(/[|\r\n]/g, " ").slice(0, 500);
821
+ }
822
+ //#endregion
823
+ exports.MANIFEST_FILENAME = MANIFEST_FILENAME;
824
+ exports.SandboxDeployer = SandboxDeployer;
825
+ exports.buildLaunchScript = buildLaunchScript;
826
+ exports.deployToSandbox = deployToSandbox;
827
+ exports.deployWorkerToSandbox = deployWorkerToSandbox;
828
+ exports.readDeploymentManifest = readDeploymentManifest;
829
+ exports.updateEdgeConfigAlias = updateEdgeConfigAlias;
830
+ exports.writeDeploymentManifest = writeDeploymentManifest;
831
+
832
+ //# sourceMappingURL=index.cjs.map