@tangle-network/agent-app 0.45.4 → 0.45.5
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.
|
@@ -100,7 +100,7 @@ import {
|
|
|
100
100
|
flattenHistory,
|
|
101
101
|
readSandboxBinaryBytes,
|
|
102
102
|
statSandboxFileSize
|
|
103
|
-
} from "../chunk-
|
|
103
|
+
} from "../chunk-QGHQ2IRC.js";
|
|
104
104
|
import "../chunk-LWSJK546.js";
|
|
105
105
|
import "../chunk-73F3CKVK.js";
|
|
106
106
|
import "../chunk-ITCINLSU.js";
|
|
@@ -155,6 +155,206 @@ async function readSandboxBinaryBytes(box, absolutePath, expectedSize, options)
|
|
|
155
155
|
return { succeeded: true, value: { bytes, size: expectedSize } };
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
// src/sandbox/diagnostics.ts
|
|
159
|
+
var EGRESS_PROXY_RECOVERY_REQUIRED = "EGRESS_PROXY_RECOVERY_REQUIRED";
|
|
160
|
+
var SAFE_ERROR_FIELDS = [
|
|
161
|
+
"code",
|
|
162
|
+
"status",
|
|
163
|
+
"phase",
|
|
164
|
+
"endpoint",
|
|
165
|
+
"origin",
|
|
166
|
+
"retryAfterMs",
|
|
167
|
+
"sidecarVersion",
|
|
168
|
+
"containerImage"
|
|
169
|
+
];
|
|
170
|
+
function isRecord(value) {
|
|
171
|
+
return typeof value === "object" && value !== null;
|
|
172
|
+
}
|
|
173
|
+
function asSafeScalar(value) {
|
|
174
|
+
if (typeof value === "string" && value.length > 0) return redactDiagnosticText(value);
|
|
175
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
176
|
+
return void 0;
|
|
177
|
+
}
|
|
178
|
+
function redactDiagnosticText(value) {
|
|
179
|
+
const secretKeyPattern = "(?:(?:[A-Z0-9]+_)*(?:api_?key|auth_?token|access_?token|refresh_?token|key|token|password|secret|credential)|apiKey|authToken|accessToken|refreshToken)";
|
|
180
|
+
return value.replace(/(['"]authorization['"]\s*:\s*)(['"])[^'"]*\2/gi, "$1$2[REDACTED]$2").replace(/\b(authorization\s*[:=]\s*)(['"])[^'"]*\2/gi, "$1$2[REDACTED]$2").replace(/\b(authorization\s*[:=]\s*)Digest\b[^;}\n]*(?:\r?\n[ \t]+[^;}\n]*)*/gi, "$1[REDACTED]").replace(/\b(authorization\s*[:=]\s*)(?:Basic|Bearer|Negotiate)\s+[^\s;,&}\n]+(?:\r?\n[ \t]+[^\s;,&}\n]+)*/gi, "$1[REDACTED]").replace(new RegExp(`\\b(authorization\\s*[:=]\\s*)(?!(?:Basic|Bearer|Digest|Negotiate)\\b)([A-Za-z][A-Za-z0-9._-]+)\\s+(?!(?:authorization|${secretKeyPattern})\\s*[:=]|['"](?:authorization|${secretKeyPattern})['"]\\s*:)([^}\\n]*(?:\\r?\\n[ \\t]+[^}\\n]*)*)`, "gi"), (_match, prefix, _scheme, valuePart) => {
|
|
181
|
+
if (valuePart.includes(";") || /\r?\n[ \t]+/.test(valuePart)) return `${prefix}[REDACTED]`;
|
|
182
|
+
const tokenMatch = valuePart.match(/^\S+(.*)$/);
|
|
183
|
+
return `${prefix}[REDACTED]${tokenMatch?.[1] ?? ""}`;
|
|
184
|
+
}).replace(/\b(authorization\s*[:=]\s*)[A-Za-z][\w.-]+\s+[^\s;,&}\n]+(?=[;&}\n]|$)/gi, "$1[REDACTED]").replace(/\b(authorization\s*[:=]\s*)(?!\[REDACTED\])[^'"\s&}]+(?:[;,][^'"\s&}]+)*/gi, "$1[REDACTED]").replace(/\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi, "Bearer [REDACTED]").replace(/\bsk-[A-Za-z0-9_-]{6,}\b/g, "sk-[REDACTED]").replace(
|
|
185
|
+
/([?&][^=&#\s]*(?:token|key|secret|password|authorization)[^=&#\s]*=)[^&#\s]+/gi,
|
|
186
|
+
"$1[REDACTED]"
|
|
187
|
+
).replace(
|
|
188
|
+
new RegExp(`(['"])(${secretKeyPattern})\\1\\s*:\\s*(['"])[^'"]+\\3`, "gi"),
|
|
189
|
+
"$1$2$1:$3[REDACTED]$3"
|
|
190
|
+
).replace(
|
|
191
|
+
new RegExp(`\\b(${secretKeyPattern})\\s*([:=])(\\s*)(['"]?)(?!Bearer\\b)[^'"\\s;,&}]+(?:[;,]\\s*[^'"\\s;,&}]+)*`, "gi"),
|
|
192
|
+
"$1$2$3$4[REDACTED]"
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
function readMessage(value) {
|
|
196
|
+
if (value instanceof Error) return redactDiagnosticText(value.message);
|
|
197
|
+
if (typeof value === "string" && value.length > 0) return redactDiagnosticText(value);
|
|
198
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
199
|
+
if (typeof value === "boolean") return String(value);
|
|
200
|
+
if (!isRecord(value)) return void 0;
|
|
201
|
+
const message = value.message;
|
|
202
|
+
return typeof message === "string" && message.length > 0 ? redactDiagnosticText(message) : void 0;
|
|
203
|
+
}
|
|
204
|
+
function readName(value) {
|
|
205
|
+
if (value instanceof Error) return redactDiagnosticText(value.name);
|
|
206
|
+
if (!isRecord(value)) return void 0;
|
|
207
|
+
const name = value.name;
|
|
208
|
+
return typeof name === "string" && name.length > 0 ? redactDiagnosticText(name) : void 0;
|
|
209
|
+
}
|
|
210
|
+
function readCause(value) {
|
|
211
|
+
if (!isRecord(value)) return void 0;
|
|
212
|
+
return value.cause;
|
|
213
|
+
}
|
|
214
|
+
function serializeSandboxProvisioningError(error, options = {}) {
|
|
215
|
+
const maxDepth = options.maxDepth ?? 6;
|
|
216
|
+
const causes = [];
|
|
217
|
+
const seen = /* @__PURE__ */ new Set();
|
|
218
|
+
let truncatedAtDepth;
|
|
219
|
+
let cycle = false;
|
|
220
|
+
let current = error;
|
|
221
|
+
let depth = 0;
|
|
222
|
+
for (; depth < maxDepth && current !== void 0 && current !== null; depth += 1) {
|
|
223
|
+
if (seen.has(current)) {
|
|
224
|
+
cycle = true;
|
|
225
|
+
causes.push({
|
|
226
|
+
name: "CauseChainCycle",
|
|
227
|
+
message: `cause chain cycle detected after ${depth} entries`
|
|
228
|
+
});
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
if (isRecord(current)) seen.add(current);
|
|
232
|
+
const cause = {};
|
|
233
|
+
const name = readName(current);
|
|
234
|
+
const message = readMessage(current);
|
|
235
|
+
if (name) cause.name = name;
|
|
236
|
+
if (message) cause.message = message;
|
|
237
|
+
if (isRecord(current)) {
|
|
238
|
+
for (const field of SAFE_ERROR_FIELDS) {
|
|
239
|
+
const safeValue = asSafeScalar(current[field]);
|
|
240
|
+
if (safeValue !== void 0) {
|
|
241
|
+
Object.assign(cause, { [field]: safeValue });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (Object.keys(cause).length > 0) causes.push(cause);
|
|
246
|
+
current = readCause(current);
|
|
247
|
+
}
|
|
248
|
+
if (!cycle && isRecord(current) && seen.has(current)) {
|
|
249
|
+
cycle = true;
|
|
250
|
+
causes.push({
|
|
251
|
+
name: "CauseChainCycle",
|
|
252
|
+
message: `cause chain cycle detected after ${depth} entries`
|
|
253
|
+
});
|
|
254
|
+
} else if (current !== void 0 && current !== null && depth >= maxDepth) {
|
|
255
|
+
truncatedAtDepth = maxDepth;
|
|
256
|
+
causes.push({
|
|
257
|
+
name: "CauseChainTruncated",
|
|
258
|
+
message: `cause chain truncated after ${maxDepth} entries`
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
message: readMessage(error) ?? "Sandbox unavailable",
|
|
263
|
+
causes,
|
|
264
|
+
truncated: truncatedAtDepth !== void 0,
|
|
265
|
+
truncatedAtDepth,
|
|
266
|
+
cycle
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function formatSandboxProvisioningSupportDetails(diagnostics) {
|
|
270
|
+
const actionableCause = diagnostics.causes.find(
|
|
271
|
+
(cause) => cause.code !== void 0 || cause.status !== void 0 || cause.phase !== void 0 || cause.endpoint !== void 0 || cause.origin !== void 0 || cause.retryAfterMs !== void 0 || cause.sidecarVersion !== void 0 || cause.containerImage !== void 0
|
|
272
|
+
) ?? diagnostics.causes[1];
|
|
273
|
+
if (!actionableCause) return "Support details: no nested sandbox cause details were available.";
|
|
274
|
+
const details = [
|
|
275
|
+
typeof actionableCause.name === "string" ? redactDiagnosticText(actionableCause.name) : void 0,
|
|
276
|
+
actionableCause.code !== void 0 ? `code=${actionableCause.code}` : void 0,
|
|
277
|
+
actionableCause.status !== void 0 ? `status=${actionableCause.status}` : void 0,
|
|
278
|
+
actionableCause.phase !== void 0 ? `phase=${redactDiagnosticText(actionableCause.phase)}` : void 0,
|
|
279
|
+
actionableCause.endpoint !== void 0 ? `endpoint=${redactDiagnosticText(actionableCause.endpoint)}` : void 0,
|
|
280
|
+
actionableCause.origin !== void 0 ? `origin=${redactDiagnosticText(actionableCause.origin)}` : void 0,
|
|
281
|
+
actionableCause.retryAfterMs !== void 0 ? `retryAfterMs=${actionableCause.retryAfterMs}` : void 0,
|
|
282
|
+
actionableCause.sidecarVersion !== void 0 ? `sidecarVersion=${redactDiagnosticText(actionableCause.sidecarVersion)}` : void 0,
|
|
283
|
+
actionableCause.containerImage !== void 0 ? `containerImage=${redactDiagnosticText(actionableCause.containerImage)}` : void 0,
|
|
284
|
+
typeof actionableCause.message === "string" ? redactDiagnosticText(actionableCause.message) : void 0
|
|
285
|
+
].filter(Boolean);
|
|
286
|
+
if (details.length === 0) return "Support details: no nested sandbox cause details were available.";
|
|
287
|
+
return `Support details: ${details.join("; ")}`;
|
|
288
|
+
}
|
|
289
|
+
function isSandboxAuthFailure(diagnostics) {
|
|
290
|
+
return diagnostics.causes.some((cause) => {
|
|
291
|
+
const code = typeof cause.code === "string" ? cause.code.toUpperCase() : void 0;
|
|
292
|
+
const status = typeof cause.status === "number" ? cause.status : typeof cause.status === "string" ? Number.parseInt(cause.status, 10) : void 0;
|
|
293
|
+
const name = typeof cause.name === "string" ? cause.name.toLowerCase() : "";
|
|
294
|
+
const message = typeof cause.message === "string" ? cause.message.toLowerCase() : "";
|
|
295
|
+
return code === "AUTH_ERROR" || status === 401 || name.includes("autherror") || message.includes("missing or invalid authentication");
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function isSandboxApiBearerAuthFailure(diagnostics) {
|
|
299
|
+
return diagnostics.causes.some((cause) => {
|
|
300
|
+
const status = typeof cause.status === "number" ? cause.status : typeof cause.status === "string" ? Number.parseInt(cause.status, 10) : void 0;
|
|
301
|
+
if (status !== 401) return false;
|
|
302
|
+
if (cause.origin !== "sandbox-api") return false;
|
|
303
|
+
if (typeof cause.endpoint !== "string") return false;
|
|
304
|
+
const endpointPath = sandboxApiEndpointPath(cause.endpoint);
|
|
305
|
+
if (!endpointPath) return false;
|
|
306
|
+
return /^\/v1\/sandboxes\/[^/?#]+(?:\/(?!runtime(?:[/?#]|$))[^?#]*)?(?:[?#].*)?$/.test(endpointPath);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function isSandboxApiSandboxMissingFailure(diagnostics) {
|
|
310
|
+
return diagnostics.causes.some((cause) => {
|
|
311
|
+
const status = typeof cause.status === "number" ? cause.status : typeof cause.status === "string" ? Number.parseInt(cause.status, 10) : void 0;
|
|
312
|
+
if (status !== 404) return false;
|
|
313
|
+
if (cause.origin !== "sandbox-api") return false;
|
|
314
|
+
if (typeof cause.endpoint !== "string") return false;
|
|
315
|
+
const endpointPath = sandboxApiEndpointPath(cause.endpoint);
|
|
316
|
+
if (!endpointPath) return false;
|
|
317
|
+
return /^\/v1\/sandboxes\/[^/?#]+(?:\/(?!runtime(?:[/?#]|$))[^?#]*)?(?:[?#].*)?$/.test(endpointPath);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
function isSandboxHostCapacityFailure(diagnostics) {
|
|
321
|
+
return diagnostics.causes.some((cause) => {
|
|
322
|
+
if (cause.origin !== "sandbox-api") return false;
|
|
323
|
+
if (typeof cause.message !== "string") return false;
|
|
324
|
+
return /host has no available slot|host capacity reservation failed/i.test(cause.message);
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
function sandboxApiEndpointPath(endpoint) {
|
|
328
|
+
if (endpoint.startsWith("/")) return endpoint;
|
|
329
|
+
try {
|
|
330
|
+
const url = new URL(endpoint);
|
|
331
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
332
|
+
} catch {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function formatSandboxProvisioningUserMessage(diagnostics) {
|
|
337
|
+
if (diagnostics.causes.some((cause) => cause.code === EGRESS_PROXY_RECOVERY_REQUIRED)) {
|
|
338
|
+
return "Sandbox recovery is required before chat can continue. The stopped sandbox has not been deleted.";
|
|
339
|
+
}
|
|
340
|
+
if (diagnostics.causes.some((cause) => cause.code === "vault.hydration_incomplete")) {
|
|
341
|
+
return "I couldn't finish copying your Vault into the sandbox, so I stopped rather than work from a partial copy. The copy resumes where it left off \u2014 try again in a moment.";
|
|
342
|
+
}
|
|
343
|
+
if (isSandboxApiBearerAuthFailure(diagnostics)) {
|
|
344
|
+
return "I'm unable to reconnect to the sandbox because its sandbox API credential was rejected. Another request may have rotated the bearer used for this operation.";
|
|
345
|
+
}
|
|
346
|
+
if (isSandboxAuthFailure(diagnostics)) {
|
|
347
|
+
return "I'm unable to reconnect to the sandbox because its runtime authentication failed. This can happen when an existing sandbox is reused with stale credentials.";
|
|
348
|
+
}
|
|
349
|
+
if (diagnostics.causes.some((cause) => cause.code === "PAYLOAD_TOO_LARGE" || cause.code === "FILE_TOO_LARGE" || cause.status === 413 || cause.status === "413")) {
|
|
350
|
+
return "An attachment is too large for the sandbox to accept. Use a smaller file and try again.";
|
|
351
|
+
}
|
|
352
|
+
if (diagnostics.causes.some((cause) => cause.code === "UPLOAD_BUDGET_EXHAUSTED")) {
|
|
353
|
+
return "Too much attachment data is staged in the sandbox at once. Retry shortly.";
|
|
354
|
+
}
|
|
355
|
+
return "I'm unable to connect to the sandbox right now. This usually means the sandbox service is not configured or is temporarily unavailable.";
|
|
356
|
+
}
|
|
357
|
+
|
|
158
358
|
// src/sandbox/workspace-sandbox-manager.ts
|
|
159
359
|
function createWorkspaceSandboxManager(opts) {
|
|
160
360
|
return {
|
|
@@ -1250,10 +1450,14 @@ async function ensureWorkspaceSandbox(shell, options) {
|
|
|
1250
1450
|
} catch (err) {
|
|
1251
1451
|
if (!shell.replaceUnbringableBox) throw err;
|
|
1252
1452
|
if (options.forceNew) throw err;
|
|
1253
|
-
if (!(err
|
|
1453
|
+
if (!isUnbringableBoxError(err)) throw err;
|
|
1254
1454
|
return await provisionWorkspaceSandbox(shell, { ...options, forceNew: true });
|
|
1255
1455
|
}
|
|
1256
1456
|
}
|
|
1457
|
+
function isUnbringableBoxError(error) {
|
|
1458
|
+
if (error instanceof SandboxRecoveryFailedError) return true;
|
|
1459
|
+
return isSandboxHostCapacityFailure(serializeSandboxProvisioningError(error));
|
|
1460
|
+
}
|
|
1257
1461
|
async function provisionWorkspaceSandbox(shell, options) {
|
|
1258
1462
|
const { workspaceId, userId, harness, forceNew, onProgress, billingOwnerId } = options;
|
|
1259
1463
|
const resolved = await resolveWorkspaceSandboxClient(shell, workspaceId, userId);
|
|
@@ -1735,6 +1939,14 @@ export {
|
|
|
1735
1939
|
shellQuote,
|
|
1736
1940
|
statSandboxFileSize,
|
|
1737
1941
|
readSandboxBinaryBytes,
|
|
1942
|
+
EGRESS_PROXY_RECOVERY_REQUIRED,
|
|
1943
|
+
serializeSandboxProvisioningError,
|
|
1944
|
+
formatSandboxProvisioningSupportDetails,
|
|
1945
|
+
isSandboxAuthFailure,
|
|
1946
|
+
isSandboxApiBearerAuthFailure,
|
|
1947
|
+
isSandboxApiSandboxMissingFailure,
|
|
1948
|
+
isSandboxHostCapacityFailure,
|
|
1949
|
+
formatSandboxProvisioningUserMessage,
|
|
1738
1950
|
createWorkspaceSandboxManager,
|
|
1739
1951
|
createSandboxTerminalConnectionRoute,
|
|
1740
1952
|
createSandboxPrewarmer,
|
|
@@ -1786,4 +1998,4 @@ export {
|
|
|
1786
1998
|
isTerminalPromptEvent,
|
|
1787
1999
|
detectInteractiveQuestion
|
|
1788
2000
|
};
|
|
1789
|
-
//# sourceMappingURL=chunk-
|
|
2001
|
+
//# sourceMappingURL=chunk-QGHQ2IRC.js.map
|