@sentry/junior 0.263.0 → 0.265.0

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 (67) hide show
  1. package/README.md +1 -1
  2. package/dist/{adapter-BIacI0Bt.js → adapter-cUkPH3Om.js} +1 -1
  3. package/dist/{admin-C7vUk7s9.js → admin-Cf0rDmWV.js} +1 -1
  4. package/dist/{agent-runner-BUBas7Yy.js → agent-runner-C6MwHJEs.js} +296 -238
  5. package/dist/api/schema.d.ts +213 -16
  6. package/dist/api/schema.js +3 -3
  7. package/dist/api.js +107 -40
  8. package/dist/{app-Bo0kB987.d.ts → app-Csbi9dW7.d.ts} +4 -3
  9. package/dist/app.d.ts +1 -1
  10. package/dist/app.js +101 -393
  11. package/dist/{assistant-reply-BFmjgJjD.js → assistant-reply-UPA8vKlC.js} +1 -1
  12. package/dist/{automation-CWX0qL8U.js → automation-2RbKzpMT.js} +30 -9
  13. package/dist/{briefs-h55v5lX9.js → briefs-DRjx8kQ9.js} +3 -3
  14. package/dist/{catalog-runtime-Suf-aLlh.js → catalog-runtime-D3qbJlYz.js} +1 -1
  15. package/dist/cli/chat.js +15 -15
  16. package/dist/cli/check.js +3 -3
  17. package/dist/cli/init.js +4 -18
  18. package/dist/cli/main.js +4 -4
  19. package/dist/cli/plugins.js +4 -4
  20. package/dist/cli/snapshot-create.js +4 -4
  21. package/dist/cli/upgrade.js +3 -3
  22. package/dist/{client-DZBCBj1M.js → client-DX34VNtH.js} +3 -8
  23. package/dist/{config-Dnv8KmNs.js → config-BayQ9hHH.js} +100 -3
  24. package/dist/{connections-Br0_xv27.js → connections-mpN5Lsnj.js} +11 -10
  25. package/dist/{conversation-privacy-FY8yc2q3.js → conversation-privacy-frjQCJa9.js} +1 -1
  26. package/dist/{credential-sync-BvkiyWF5.js → credential-sync-CZmyDNdR.js} +20 -20
  27. package/dist/{agent-hooks-CqnZfuCt.js → credentialed-C6pKlB1p.js} +3197 -66
  28. package/dist/{executor-J9k2sp9m.js → executor-CAr7xQr2.js} +10 -300
  29. package/dist/{images-DV2FlEGL.js → images-B2yA3HWE.js} +2 -2
  30. package/dist/instrumentation.js +41 -25
  31. package/dist/{job-queue-B4yTgklN.js → job-queue-Cjo6Yg_b.js} +53 -14
  32. package/dist/{logging-Dk_FpF36.js → logging-D2QBriUo.js} +19 -32
  33. package/dist/{message-BzseAFH1.js → message-C9ymnpBS.js} +4 -4
  34. package/dist/nitro.d.ts +17 -2
  35. package/dist/nitro.js +26 -27
  36. package/dist/{oauth-callback-server-bTspTMCC.js → oauth-callback-server-BtMf3TPZ.js} +2 -2
  37. package/dist/{prompt-CxatQVaj.js → prompt-CvaDdSX3.js} +1 -1
  38. package/dist/{registry-BFzQFV_y.js → registry-COTeZ2cV.js} +1 -1
  39. package/dist/{resolve-CnRHib0U.js → resolve-g9PKz9pH.js} +7 -106
  40. package/dist/{result-DczfmNBa.js → result-COnC42GR.js} +8 -8
  41. package/dist/{runner-CJd9Wec-.js → runner-BnTNS7x_.js} +10 -10
  42. package/dist/{sandbox-egress-signals-DUmVkOx4.js → sandbox-egress-signals-C_IIdLFk.js} +1 -1
  43. package/dist/{schema-QidbykYl.js → schema-D5Amlzkp.js} +44 -3
  44. package/dist/{sentry-BBUlHih9.js → sentry-MDkYAVPV.js} +3 -6
  45. package/dist/{service-account-BI2kQDrb.js → service-account-CwvToidT.js} +3 -3
  46. package/dist/{service-account-x2QZXm4X.js → service-account-YdYftwV2.js} +5 -5
  47. package/dist/{session-stop-CtyJsnEj.js → session-stop-CpCUy1ZJ.js} +121 -81
  48. package/dist/{skills-Dbzx9-rS.js → skills-Bb2KaJ3m.js} +2 -2
  49. package/dist/{spawn-D8WU99UV.js → spawn-mbTcrLb3.js} +2 -2
  50. package/dist/{store-TKg1HVKx.js → store-CqRnaZSq.js} +3 -3
  51. package/dist/{store-UlOrW6JC.js → store-DdT74Lvq.js} +18 -9
  52. package/dist/{task-queue-BRB49H-J.js → task-queue-8XjDIORF.js} +14 -19
  53. package/dist/{task-runner-C575f2Dd.js → task-runner-6mLmG6g_.js} +46 -28
  54. package/dist/{tasks-LOItiYgL.js → tasks-DGbrur6x.js} +14 -2075
  55. package/dist/{thread-state-IDHdZA7R.js → thread-state-D45lz-oC.js} +5 -5
  56. package/dist/{turn-failure-response-h_LSXJ-M.js → turn-failure-response-CaEPLSBn.js} +7 -7
  57. package/dist/{turn-lifecycle-Cvi6Sb8Y.js → turn-lifecycle-C35IyRGp.js} +1 -1
  58. package/dist/{turn-session-routing-5Nyv4fso.js → turn-session-routing-BKx6rfVd.js} +1 -1
  59. package/dist/{validation-BeTriWC2.js → validation-C0X3CX51.js} +1 -1
  60. package/dist/{version-BQX_hLDC.js → version-BPHgJwnm.js} +1 -1
  61. package/dist/version.js +1 -1
  62. package/dist/{viewer-DmNRM88V.js → viewer-B2wXNspU.js} +21 -5
  63. package/dist/{web-input-BHcGXSox.js → web-input-Y6aXvmfa.js} +145 -46
  64. package/dist/{work-DLJMV02b.js → work-DMbqK0kT.js} +10 -10
  65. package/dist/{worker-vfG2ESg1.js → worker-B-rDJ3XN.js} +8 -6
  66. package/package.json +10 -9
  67. package/dist/profile-Dq5I7TV1.js +0 -582
@@ -1,582 +0,0 @@
1
- import { C as toOptionalTrimmed } from "./client-DZBCBj1M.js";
2
- import { b as setSpanStatus, y as setSpanAttributes } from "./logging-Dk_FpF36.js";
3
- import { n as pluginCatalogRuntime } from "./catalog-runtime-Suf-aLlh.js";
4
- import { createHash } from "node:crypto";
5
- // src/chat/sandbox/paths.ts
6
- function normalizeWorkspaceRoot(input) {
7
- const candidate = (input ?? "").trim();
8
- if (!candidate) return "/vercel/sandbox";
9
- const normalized = candidate.replace(/\/+$/, "");
10
- return normalized.startsWith("/") ? normalized : `/${normalized}`;
11
- }
12
- const SANDBOX_WORKSPACE_ROOT = normalizeWorkspaceRoot(process.env.VERCEL_SANDBOX_WORKSPACE_DIR);
13
- const SANDBOX_REPOS_ROOT = `${SANDBOX_WORKSPACE_ROOT}/repos`;
14
- const SANDBOX_SKILLS_ROOT = `${SANDBOX_WORKSPACE_ROOT}/skills`;
15
- const SANDBOX_DATA_ROOT = `${SANDBOX_WORKSPACE_ROOT}/data`;
16
- /**
17
- * PATH for sandbox commands. Plugin runtime binaries come last so they never
18
- * shadow repo or Workspace tools with the same name. Repo tools often start
19
- * child processes through PATH, so only PATH order can protect them. Plugin
20
- * binaries stay reachable by name or by absolute path.
21
- */
22
- const SANDBOX_COMMAND_PATH = `$PATH:${`${SANDBOX_WORKSPACE_ROOT}/.junior/bin`}`;
23
- function sandboxSkillDir(skillName) {
24
- return `${SANDBOX_SKILLS_ROOT}/${skillName}`;
25
- }
26
- function sandboxSkillFile(skillName) {
27
- return `${sandboxSkillDir(skillName)}/SKILL.md`;
28
- }
29
- /** Explain how the agent should resolve files and commands owned by a skill. */
30
- function sandboxSkillPathResolution(skillName) {
31
- const skillDir = sandboxSkillDir(skillName);
32
- return `Resolve relative paths in this skill against ${skillDir}. For bash commands from this skill, cd to ${skillDir} first or use absolute paths.`;
33
- }
34
- // src/chat/sandbox/noninteractive-command.ts
35
- const NON_INTERACTIVE_ENV = {
36
- CI: "1",
37
- TERM: "dumb",
38
- NO_COLOR: "1",
39
- PAGER: "cat",
40
- GIT_PAGER: "cat",
41
- GH_PROMPT_DISABLED: "1",
42
- GH_NO_UPDATE_NOTIFIER: "1",
43
- GH_NO_EXTENSION_UPDATE_NOTIFIER: "1",
44
- GH_SPINNER_DISABLED: "1",
45
- GIT_TERMINAL_PROMPT: "0",
46
- GCM_INTERACTIVE: "never",
47
- DEBIAN_FRONTEND: "noninteractive",
48
- GIT_ASKPASS: "/bin/true",
49
- GIT_CONFIG_NOSYSTEM: "1",
50
- GIT_CONFIG_COUNT: "2",
51
- GIT_CONFIG_KEY_0: "credential.helper",
52
- GIT_CONFIG_VALUE_0: "",
53
- GIT_CONFIG_KEY_1: "http.emptyAuth",
54
- GIT_CONFIG_VALUE_1: "true"
55
- };
56
- function shellQuote(value) {
57
- return `'${value.replace(/'/g, "'\\''")}'`;
58
- }
59
- function buildEnvExports(options) {
60
- const lines = [];
61
- if (options.path) lines.push(`export PATH="${options.path}"`);
62
- for (const [key, value] of Object.entries(NON_INTERACTIVE_ENV)) lines.push(`export ${key}=${shellQuote(value)}`);
63
- for (const [key, value] of Object.entries(options.env ?? {})) lines.push(`export ${key}=${shellQuote(value)}`);
64
- return lines;
65
- }
66
- /** Render one argv command as a safely quoted shell script. */
67
- function buildCommandScript(input) {
68
- return [shellQuote(input.cmd), ...(input.args ?? []).map(shellQuote)].join(" ");
69
- }
70
- /** Build one shell entrypoint that never waits on terminal input. */
71
- function buildNonInteractiveShellScript(script, options = {}) {
72
- return [
73
- ...buildEnvExports(options),
74
- "exec </dev/null",
75
- script
76
- ].join(" && ");
77
- }
78
- /** Wrap argv-style commands so every sandbox subprocess runs in non-interactive mode. */
79
- function buildNonInteractiveCommand(input) {
80
- return {
81
- cmd: "bash",
82
- args: [input.login ? "-lc" : "-c", buildNonInteractiveShellScript(buildCommandScript(input), {
83
- env: input.env,
84
- path: input.path
85
- })]
86
- };
87
- }
88
- /** Run a subprocess through one enforced non-interactive entrypoint. */
89
- async function runNonInteractiveCommand(sandbox, input) {
90
- const command = {
91
- ...buildNonInteractiveCommand(input),
92
- ...input.cwd ? { cwd: input.cwd } : void 0,
93
- ...input.signal ? { signal: input.signal } : void 0,
94
- ...input.sudo !== void 0 ? { sudo: input.sudo } : void 0,
95
- ...input.timeoutMs !== void 0 ? { timeoutMs: input.timeoutMs } : void 0
96
- };
97
- return await sandbox.runCommand(command);
98
- }
99
- // src/chat/sleep.ts
100
- /** Wait for a duration, rejecting with the owning signal reason when cancelled. */
101
- function sleep(ms, signal) {
102
- if (!signal) return new Promise((resolve) => setTimeout(resolve, ms));
103
- return new Promise((resolve, reject) => {
104
- if (signal.aborted) {
105
- reject(signal.reason);
106
- return;
107
- }
108
- const timeoutId = setTimeout(() => {
109
- signal.removeEventListener("abort", onAbort);
110
- resolve();
111
- }, ms);
112
- const onAbort = () => {
113
- clearTimeout(timeoutId);
114
- reject(signal.reason);
115
- };
116
- signal.addEventListener("abort", onAbort, { once: true });
117
- });
118
- }
119
- // src/chat/sandbox/credentials.ts
120
- /**
121
- * Resolve explicit Vercel Sandbox credentials, or return undefined to let
122
- * the SDK read ambient Vercel auth from its own environment detection.
123
- */
124
- function getVercelSandboxCredentials() {
125
- const token = toOptionalTrimmed(process.env.VERCEL_TOKEN);
126
- const teamId = toOptionalTrimmed(process.env.VERCEL_TEAM_ID);
127
- const projectId = toOptionalTrimmed(process.env.VERCEL_PROJECT_ID);
128
- if (token && teamId && projectId) return {
129
- token,
130
- teamId,
131
- projectId
132
- };
133
- }
134
- // src/chat/sandbox/http-error-details.ts
135
- const DEFAULT_PREVIEW_LIMIT = 512;
136
- function toTrimmedString(value, maxChars) {
137
- if (typeof value !== "string") return;
138
- const normalized = value.trim();
139
- if (!normalized) return;
140
- return normalized.length > maxChars ? `${normalized.slice(0, maxChars)}…` : normalized;
141
- }
142
- /** Extract structured HTTP error metadata (status, headers, body preview) for logging. */
143
- function extractHttpErrorDetails(error, options = {}) {
144
- const prefix = options.attributePrefix ?? "app.http_error";
145
- const previewLimit = options.previewLimit ?? DEFAULT_PREVIEW_LIMIT;
146
- const normalizedError = error instanceof Error ? error : new Error(String(error));
147
- const err = error ?? {};
148
- const attributes = {
149
- "error.type": normalizedError.name || "Error",
150
- "exception.message": toTrimmedString(normalizedError.message, previewLimit) ?? "HTTP error"
151
- };
152
- const response = err.response;
153
- const statusCode = typeof response?.status === "number" ? response.status : void 0;
154
- const statusText = toTrimmedString(response?.statusText, previewLimit);
155
- const responseUrl = toTrimmedString(response?.url, previewLimit);
156
- const responseText = toTrimmedString(err.text, previewLimit);
157
- const responseJson = toTrimmedString(err.json && typeof err.json === "object" ? JSON.stringify(err.json) : void 0, previewLimit);
158
- const contentType = toTrimmedString(response?.headers?.get?.("content-type"), previewLimit);
159
- const requestIdHeader = toTrimmedString(response?.headers?.get?.("x-request-id"), previewLimit);
160
- const vercelIdHeader = toTrimmedString(response?.headers?.get?.("x-vercel-id"), previewLimit);
161
- const requestId = requestIdHeader ?? vercelIdHeader;
162
- if (statusCode !== void 0) attributes["http.response.status_code"] = statusCode;
163
- if (statusText) attributes[`${prefix}.status_text`] = statusText;
164
- if (responseUrl) attributes["url.full"] = responseUrl;
165
- if (contentType) attributes["http.response.header.content-type"] = [contentType];
166
- if (requestIdHeader) attributes["http.response.header.x-request-id"] = [requestIdHeader];
167
- if (vercelIdHeader) attributes["http.response.header.x-vercel-id"] = [vercelIdHeader];
168
- if (responseText) {
169
- attributes[`${prefix}.response_text_preview`] = responseText;
170
- attributes[`${prefix}.response_text_length`] = responseText.length;
171
- }
172
- if (responseJson) attributes[`${prefix}.response_json_preview`] = responseJson;
173
- const summaryParts = [];
174
- if (statusCode !== void 0) summaryParts.push(`status=${statusCode}`);
175
- if (statusText) summaryParts.push(`statusText=${statusText}`);
176
- if (responseUrl) summaryParts.push(`url=${responseUrl}`);
177
- if (requestId) summaryParts.push(`requestId=${requestId}`);
178
- for (const field of options.extraFields ?? []) {
179
- const value = toTrimmedString(err[field.sourceKey], previewLimit);
180
- if (!value) continue;
181
- attributes[`${prefix}.${field.attributeKey}`] = value;
182
- summaryParts.push(`${field.summaryKey ?? field.attributeKey}=${value}`);
183
- }
184
- if (responseJson) summaryParts.push(`json=${responseJson}`);
185
- else if (responseText) summaryParts.push(`text=${responseText}`);
186
- const searchableText = `${normalizedError.message} ${responseText ?? ""} ${responseJson ?? ""}`.toLowerCase();
187
- return {
188
- attributes,
189
- summary: summaryParts.join(", "),
190
- searchableText
191
- };
192
- }
193
- // src/chat/sandbox/snapshot/not-ready-error.ts
194
- /** Report that a Workspace snapshot is not ready to use. */
195
- var WorkspaceSnapshotNotReadyError = class extends Error {
196
- workspaceName;
197
- constructor(workspaceName) {
198
- super(`Workspace ${workspaceName} snapshot is not ready`);
199
- this.name = "WorkspaceSnapshotNotReadyError";
200
- this.workspaceName = workspaceName;
201
- }
202
- };
203
- /** Return WorkspaceSnapshotNotReadyError from this error or its causes. */
204
- function getWorkspaceSnapshotNotReadyError(error) {
205
- const seen = /* @__PURE__ */ new Set();
206
- let current = error;
207
- while (current instanceof Error && !seen.has(current)) {
208
- if (current instanceof WorkspaceSnapshotNotReadyError) return current;
209
- seen.add(current);
210
- current = current.cause;
211
- }
212
- }
213
- /** True when this error or a cause is WorkspaceSnapshotNotReadyError. */
214
- function isWorkspaceSnapshotNotReadyError(error) {
215
- return getWorkspaceSnapshotNotReadyError(error) !== void 0;
216
- }
217
- /** User message when a Workspace sandbox is still preparing. */
218
- function workspaceSnapshotNotReadyUserMessage(error) {
219
- return `The ${error.workspaceName} workspace is still preparing its sandbox. Wait for that preparation to finish, then try again.`;
220
- }
221
- // src/chat/sandbox/errors.ts
222
- const SANDBOX_ERROR_FIELDS = [{
223
- sourceKey: "sandboxId",
224
- attributeKey: "sandbox_id",
225
- summaryKey: "sandboxId"
226
- }];
227
- function getSandboxErrorDetails(error) {
228
- return extractHttpErrorDetails(error, {
229
- attributePrefix: "app.sandbox.api_error",
230
- extraFields: [...SANDBOX_ERROR_FIELDS]
231
- });
232
- }
233
- function findInErrorChain(error, predicate) {
234
- const seen = /* @__PURE__ */ new Set();
235
- let current = error;
236
- while (current && !seen.has(current)) {
237
- if (predicate(current)) return true;
238
- seen.add(current);
239
- current = typeof current === "object" ? current.cause : void 0;
240
- }
241
- return false;
242
- }
243
- function isRecord(value) {
244
- return typeof value === "object" && value !== null;
245
- }
246
- function isInvalidSandboxSessionError(error) {
247
- if (!isRecord(error)) return false;
248
- if (isRecord(error.json) && error.json.invalidToken === true) return true;
249
- const response = error.response;
250
- return isRecord(response) && response.status === 403 && typeof response.url === "string" && response.url.includes("/sandboxes/sessions");
251
- }
252
- function getFirstErrorMessage(error) {
253
- const seen = /* @__PURE__ */ new Set();
254
- let current = error;
255
- while (current && !seen.has(current)) {
256
- if (current instanceof Error) {
257
- const message = current.message.trim();
258
- if (message) return message;
259
- }
260
- seen.add(current);
261
- current = typeof current === "object" ? current.cause : void 0;
262
- }
263
- }
264
- /** Detect sandbox filesystem operations that can safely ignore existing directories. */
265
- function isAlreadyExistsError(error) {
266
- const details = getSandboxErrorDetails(error);
267
- return details.searchableText.includes("already exists") || details.searchableText.includes("file exists") || details.searchableText.includes("eexist");
268
- }
269
- /** Detect when the current sandbox session is unavailable and must be reacquired. */
270
- function isSandboxUnavailableError(error) {
271
- return findInErrorChain(error, (candidate) => {
272
- if (isInvalidSandboxSessionError(candidate)) return true;
273
- const details = getSandboxErrorDetails(candidate);
274
- const searchable = `${details.searchableText} ${details.summary}`.toLowerCase();
275
- return searchable.includes("sandbox_stopped") || searchable.includes("status=410") || searchable.includes("status code 410") || searchable.includes("no longer available") || searchable.includes("stream was closed and is not accepting commands");
276
- });
277
- }
278
- /** Detect a durable sandbox reference that can no longer be restored. */
279
- function isSandboxMissingError(error) {
280
- return findInErrorChain(error, (candidate) => {
281
- const details = getSandboxErrorDetails(candidate);
282
- const searchable = `${details.searchableText} ${details.summary}`.toLowerCase();
283
- return searchable.includes("status=404") || searchable.includes("status code 404") || searchable.includes("snapshot_not_found");
284
- });
285
- }
286
- /** Detect transient snapshot boot races so sandbox creation can retry. */
287
- function isSnapshottingError(error) {
288
- return findInErrorChain(error, (candidate) => {
289
- const details = getSandboxErrorDetails(candidate);
290
- const searchable = `${details.searchableText} ${details.summary}`.toLowerCase();
291
- return searchable.includes("sandbox_snapshotting") || searchable.includes("creating a snapshot") || searchable.includes("stopped shortly");
292
- });
293
- }
294
- /** Detect cancellation so a durable builder stays available for check-in. */
295
- function isAbortError(error) {
296
- return findInErrorChain(error, (candidate) => {
297
- if (typeof DOMException !== "undefined" && candidate instanceof DOMException) return candidate.name === "AbortError";
298
- return candidate instanceof Error && candidate.name === "AbortError";
299
- });
300
- }
301
- /** Detect transient Vercel Sandbox API failures that keep a job resumable. */
302
- function isSandboxApiTransientError(error) {
303
- return findInErrorChain(error, (candidate) => {
304
- const details = getSandboxErrorDetails(candidate);
305
- const searchable = `${details.searchableText} ${details.summary}`.toLowerCase();
306
- if (searchable.includes("internal_server_error") || searchable.includes("status=500") || searchable.includes("status code 500") || searchable.includes("status=502") || searchable.includes("status code 502") || searchable.includes("status=503") || searchable.includes("status code 503") || searchable.includes("status=504") || searchable.includes("status code 504")) return true;
307
- if (!isRecord(candidate)) return false;
308
- const response = candidate.response;
309
- return isRecord(response) && typeof response.status === "number" && response.status >= 500 && response.status < 600;
310
- });
311
- }
312
- /** Detect interrupted command streams where no reliable exit status is available. */
313
- function isSandboxCommandStreamInterruptedError(error) {
314
- return findInErrorChain(error, (candidate) => {
315
- if (!(candidate instanceof Error)) return false;
316
- return candidate.name === "StreamError" && candidate.message.toLowerCase().includes("stream ended before command finished");
317
- });
318
- }
319
- /** Wrap setup failures. Keep WorkspaceSnapshotNotReadyError as-is. */
320
- function wrapSandboxSetupError(error) {
321
- const notReady = getWorkspaceSnapshotNotReadyError(error);
322
- if (notReady) return notReady;
323
- try {
324
- const details = getSandboxErrorDetails(error);
325
- if (details.summary) return new Error(`sandbox setup failed (${details.summary})`, { cause: error });
326
- } catch {}
327
- let causeMessage;
328
- try {
329
- causeMessage = getFirstErrorMessage(error);
330
- } catch (cause) {
331
- causeMessage = cause instanceof Error ? cause.message : void 0;
332
- }
333
- if (causeMessage && causeMessage.trim() && causeMessage !== "sandbox setup failed") {
334
- const oneLine = causeMessage.replace(/\s+/g, " ").trim();
335
- return new Error(`sandbox setup failed (${oneLine})`, { cause: error });
336
- }
337
- return new Error("sandbox setup failed", { cause: error });
338
- }
339
- /** Record span data and throw one stable sandbox operation error. */
340
- function throwSandboxOperationError(action, error, includeMissingPath = false) {
341
- const details = getSandboxErrorDetails(error);
342
- setSpanAttributes({
343
- ...details.attributes,
344
- ...includeMissingPath ? { "app.sandbox.api_error.missing_path": details.searchableText.includes("no such file") || details.searchableText.includes("enoent") } : void 0,
345
- "app.sandbox.success": false
346
- });
347
- setSpanStatus("error");
348
- throw new Error(details.summary ? `${action} failed (${details.summary})` : `${action} failed`, { cause: error });
349
- }
350
- // src/chat/sandbox/docker.ts
351
- /** Docker daemon config for Vercel sandbox guests (nested containers). */
352
- const DOCKER_DAEMON_JSON = `{
353
- "storage-driver": "overlay2",
354
- "live-restore": false
355
- }
356
- `;
357
- /**
358
- * Start dockerd when the socket is missing and wait until `docker info` works.
359
- * Re-execs through sudo so the sandbox user can call it without root.
360
- */
361
- const DOCKER_ENSURE_SCRIPT = `#!/bin/sh
362
- set -eu
363
-
364
- if [ "$(id -u)" -ne 0 ]; then
365
- exec sudo -n "$0" "$@"
366
- fi
367
-
368
- SOCK=/var/run/docker.sock
369
- PIDFILE=/var/run/docker.pid
370
- LOG=/var/log/junior-dockerd.log
371
-
372
- is_ready() {
373
- [ -S "$SOCK" ] && /usr/bin/docker info >/dev/null 2>&1
374
- }
375
-
376
- if is_ready; then
377
- chmod 666 "$SOCK" 2>/dev/null || true
378
- exit 0
379
- fi
380
-
381
- mkdir -p /etc/docker /var/lib/docker /var/run /var/log
382
- if [ ! -f /etc/docker/daemon.json ]; then
383
- cat > /etc/docker/daemon.json <<'EOF'
384
- {
385
- "storage-driver": "overlay2",
386
- "live-restore": false
387
- }
388
- EOF
389
- fi
390
-
391
- if [ -f "$PIDFILE" ]; then
392
- oldpid="$(cat "$PIDFILE" 2>/dev/null || true)"
393
- if [ -n "\${oldpid:-}" ] && ! kill -0 "$oldpid" 2>/dev/null; then
394
- rm -f "$PIDFILE"
395
- fi
396
- fi
397
-
398
- if [ ! -f "$PIDFILE" ] || ! kill -0 "$(cat "$PIDFILE" 2>/dev/null)" 2>/dev/null; then
399
- # dockerd must outlive this helper; nohup + background is intentional.
400
- nohup dockerd --host="unix://$SOCK" --pidfile="$PIDFILE" >"$LOG" 2>&1 &
401
- fi
402
-
403
- i=0
404
- while [ "$i" -lt 50 ]; do
405
- if is_ready; then
406
- chmod 666 "$SOCK" 2>/dev/null || true
407
- exit 0
408
- fi
409
- i=$((i + 1))
410
- sleep 0.1
411
- done
412
-
413
- echo "junior-ensure-docker: dockerd failed to become ready" >&2
414
- if [ -f "$LOG" ]; then
415
- tail -n 50 "$LOG" >&2 || true
416
- fi
417
- exit 1
418
- `;
419
- /** PATH wrapper so plain `docker` starts the daemon on first use. */
420
- const DOCKER_CLI_WRAPPER_SCRIPT = `#!/bin/sh
421
- set -eu
422
- if command -v junior-ensure-docker >/dev/null 2>&1; then
423
- junior-ensure-docker
424
- fi
425
- exec /usr/bin/docker "$@"
426
- `;
427
- /** PATH wrapper so plain `docker-compose` starts the daemon on first use. */
428
- const DOCKER_COMPOSE_CLI_WRAPPER_SCRIPT = `#!/bin/sh
429
- set -eu
430
- if command -v junior-ensure-docker >/dev/null 2>&1; then
431
- junior-ensure-docker
432
- fi
433
- exec /usr/local/libexec/junior-docker-compose "$@"
434
- `;
435
- /**
436
- * Best-effort dockerd start during sandbox prepare.
437
- * Missing helper is a no-op so tests and base sandboxes stay green.
438
- */
439
- async function ensureDockerDaemon(sandbox, signal) {
440
- await runNonInteractiveCommand(sandbox, {
441
- cmd: "sh",
442
- args: ["-c", "command -v junior-ensure-docker >/dev/null 2>&1 && junior-ensure-docker || true"],
443
- signal
444
- });
445
- }
446
- // src/chat/sandbox/runtime-dependencies.ts
447
- const GLOBAL_RUNTIME_DEPENDENCIES = [
448
- {
449
- type: "system",
450
- package: "docker"
451
- },
452
- {
453
- type: "system",
454
- package: "spal-release"
455
- },
456
- {
457
- type: "system",
458
- package: "ripgrep"
459
- }
460
- ];
461
- function writeFileCommand(path, contents, mode = "0644") {
462
- return [`printf '%s' '${Buffer.from(contents, "utf8").toString("base64")}' | base64 -d > ${path}`, `chmod ${mode} ${path}`].join("\n");
463
- }
464
- function writeExecutableCommand(path, contents) {
465
- return writeFileCommand(path, contents, "0755");
466
- }
467
- const GLOBAL_RUNTIME_POSTINSTALL = [{
468
- cmd: "sh",
469
- args: ["-c", [
470
- "set -eu",
471
- "case \"$(uname -m)\" in",
472
- " x86_64) asset=\"x86_64\"; sha256=\"7af95166a730b87e172d4fc9aefea8725d3c6c7327d59149267b452114ddb7d4\" ;;",
473
- " aarch64) asset=\"aarch64\"; sha256=\"49082844b87f03cdcd5f5bbef1ba8c9c897b7a2dfb80cea18d61ec8ca6117e0c\" ;;",
474
- " *) echo \"unsupported Docker Compose architecture: $(uname -m)\" >&2; exit 1 ;;",
475
- "esac",
476
- "tmp=\"$(mktemp)\"",
477
- "trap 'rm -f \"$tmp\"' EXIT",
478
- "curl --fail --location --silent --show-error \"https://github.com/docker/compose/releases/download/v2.39.4/docker-compose-linux-${asset}\" --output \"$tmp\"",
479
- "echo \"${sha256} ${tmp}\" | sha256sum --check --status",
480
- "install -D -m 0755 \"$tmp\" /usr/local/libexec/junior-docker-compose",
481
- "mkdir -p /usr/local/lib/docker/cli-plugins /usr/local/libexec /etc/docker /usr/local/bin",
482
- `mkdir -p ${SANDBOX_WORKSPACE_ROOT}/.junior/bin`,
483
- writeFileCommand("/etc/docker/daemon.json", DOCKER_DAEMON_JSON),
484
- writeExecutableCommand("/usr/local/bin/junior-ensure-docker", DOCKER_ENSURE_SCRIPT),
485
- "ln -sfn /usr/local/libexec/junior-docker-compose /usr/local/lib/docker/cli-plugins/docker-compose",
486
- writeExecutableCommand("/usr/local/bin/docker-compose", DOCKER_COMPOSE_CLI_WRAPPER_SCRIPT),
487
- writeExecutableCommand(`${SANDBOX_WORKSPACE_ROOT}/.junior/bin/docker`, DOCKER_CLI_WRAPPER_SCRIPT),
488
- writeExecutableCommand(`${SANDBOX_WORKSPACE_ROOT}/.junior/bin/docker-compose`, DOCKER_COMPOSE_CLI_WRAPPER_SCRIPT),
489
- writeExecutableCommand("/usr/local/bin/docker", DOCKER_CLI_WRAPPER_SCRIPT)
490
- ].join("\n")],
491
- sudo: true
492
- }];
493
- // src/chat/sandbox/snapshot/profile.ts
494
- const VERSION = 1;
495
- const DEFAULT_FLOATING_MAX_AGE_MS = 6048e5;
496
- function isExactNpmVersion(version) {
497
- return /^\d+\.\d+\.\d+(?:[-+][a-z0-9.]+)?$/i.test(version.trim());
498
- }
499
- function isFloating(dep) {
500
- return dep.type === "npm" && !isExactNpmVersion(dep.version);
501
- }
502
- /**
503
- * Merge dependencies for one global install, rejecting competing npm versions.
504
- */
505
- function mergeDependencies(dependencies) {
506
- const seen = /* @__PURE__ */ new Set();
507
- const npmVersions = /* @__PURE__ */ new Map();
508
- const merged = [];
509
- for (const dependency of dependencies) {
510
- const key = dependency.type === "npm" ? `${dependency.type}:${dependency.package}:${dependency.version}` : "package" in dependency ? `${dependency.type}:package:${dependency.package}` : `${dependency.type}:url:${dependency.url}:${dependency.sha256}`;
511
- if (seen.has(key)) continue;
512
- seen.add(key);
513
- if (dependency.type === "npm") {
514
- const version = npmVersions.get(dependency.package);
515
- if (version !== void 0 && version !== dependency.version) throw new Error(`Conflicting runtime dependency versions for ${dependency.package}: ${version} and ${dependency.version}`);
516
- npmVersions.set(dependency.package, dependency.version);
517
- }
518
- merged.push(dependency);
519
- }
520
- return merged;
521
- }
522
- function floatingMaxAgeMs() {
523
- const raw = process.env.SANDBOX_SNAPSHOT_FLOATING_MAX_AGE_MS;
524
- if (!raw?.trim()) return DEFAULT_FLOATING_MAX_AGE_MS;
525
- const parsed = Number.parseInt(raw, 10);
526
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_FLOATING_MAX_AGE_MS;
527
- }
528
- /** Locale-independent string order for stable profile hashes. */
529
- function compareCodePoints(left, right) {
530
- if (left < right) return -1;
531
- if (left > right) return 1;
532
- return 0;
533
- }
534
- function workspaceRecipe(workspace) {
535
- const repos = [...workspace.repos].map(({ provider, repo }) => ({
536
- provider,
537
- repo
538
- })).sort((left, right) => {
539
- const provider = compareCodePoints(left.provider, right.provider);
540
- if (provider !== 0) return provider;
541
- return compareCodePoints(left.repo, right.repo);
542
- });
543
- return {
544
- id: workspace.id,
545
- repos,
546
- setupScript: workspace.setupScript
547
- };
548
- }
549
- /** Build the complete profile that selects one reusable sandbox snapshot. */
550
- function create(runtime, workspace) {
551
- const dependencies = mergeDependencies([...GLOBAL_RUNTIME_DEPENDENCIES, ...pluginCatalogRuntime.getRuntimeDependencies()]);
552
- const pluginPostinstall = pluginCatalogRuntime.getRuntimePostinstall();
553
- const postinstall = [...GLOBAL_RUNTIME_POSTINSTALL, ...pluginPostinstall];
554
- if (dependencies.length === 0 && postinstall.length === 0 && !workspace) return null;
555
- const rebuildEpoch = process.env.SANDBOX_SNAPSHOT_REBUILD_EPOCH?.trim() ?? "";
556
- const floating = dependencies.some((dependency) => isFloating(dependency)) || pluginPostinstall.length > 0 || Boolean(workspace);
557
- return {
558
- hash: createHash("sha256").update(JSON.stringify({
559
- version: VERSION,
560
- runtime,
561
- rebuildEpoch,
562
- dependencies,
563
- postinstall,
564
- ...workspace ? { workspace: workspaceRecipe(workspace) } : void 0
565
- })).digest("hex"),
566
- dependencyCount: dependencies.length,
567
- floating,
568
- dependencies,
569
- postinstall
570
- };
571
- }
572
- /** Return the current dependency profile hash without building its snapshot. */
573
- function hash(runtime, workspace) {
574
- return create(runtime, workspace)?.hash;
575
- }
576
- /** Decide whether a cached snapshot has outlived a floating profile. */
577
- function isStale(profile, createdAtMs) {
578
- if (!profile.floating) return false;
579
- const maxAgeMs = floatingMaxAgeMs();
580
- return maxAgeMs === 0 || Date.now() - createdAtMs > maxAgeMs;
581
- }
582
- export { sandboxSkillPathResolution as A, SANDBOX_COMMAND_PATH as C, SANDBOX_WORKSPACE_ROOT as D, SANDBOX_SKILLS_ROOT as E, sandboxSkillDir as O, runNonInteractiveCommand as S, SANDBOX_REPOS_ROOT as T, workspaceSnapshotNotReadyUserMessage as _, isAbortError as a, buildCommandScript as b, isSandboxCommandStreamInterruptedError as c, isSnapshottingError as d, throwSandboxOperationError as f, isWorkspaceSnapshotNotReadyError as g, getWorkspaceSnapshotNotReadyError as h, ensureDockerDaemon as i, sandboxSkillFile as k, isSandboxMissingError as l, WorkspaceSnapshotNotReadyError as m, hash as n, isAlreadyExistsError as o, wrapSandboxSetupError as p, isStale as r, isSandboxApiTransientError as s, create as t, isSandboxUnavailableError as u, getVercelSandboxCredentials as v, SANDBOX_DATA_ROOT as w, buildNonInteractiveShellScript as x, sleep as y };