deepline 0.2.34 → 0.2.35
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.
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +204 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
|
@@ -160,7 +160,7 @@ export const SDK_RELEASE = {
|
|
|
160
160
|
// 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
|
|
161
161
|
// exposed storage-dependent synchronous access. This deliberate minor
|
|
162
162
|
// release keeps lazy paging semantics independent of row residency.
|
|
163
|
-
version: '0.2.
|
|
163
|
+
version: '0.2.35',
|
|
164
164
|
contracts: {
|
|
165
165
|
api: {
|
|
166
166
|
name: 'sdk-http-api',
|
|
@@ -43,6 +43,9 @@ const DAYTONA_COMMAND_RECOVERY_POLL_MS = 2_000;
|
|
|
43
43
|
const DAYTONA_INFRASTRUCTURE_MAX_ATTEMPTS = 2;
|
|
44
44
|
const DAYTONA_UPLOAD_MAX_ATTEMPTS = 2;
|
|
45
45
|
const DAYTONA_UPLOAD_ATTEMPT_DEADLINE_MS = 90_000;
|
|
46
|
+
const DAYTONA_CRASH_DIAGNOSTIC_TAIL_MAX_BYTES = 64 * 1_024;
|
|
47
|
+
const DAYTONA_CRASH_DIAGNOSTIC_MAX_LINES = 40;
|
|
48
|
+
const DAYTONA_CRASH_DIAGNOSTIC_MAX_TEXT_BYTES = 12 * 1_024;
|
|
46
49
|
|
|
47
50
|
const RUNTIME_POSTGRES_CONNECT_RETRY_PATTERN =
|
|
48
51
|
/\bRuntime Postgres\b.*\b(connection timed out|connect timeout|ETIMEDOUT|ECONNRESET|ECONNREFUSED|Connection terminated|Connection ended unexpectedly)\b/i;
|
|
@@ -54,13 +57,25 @@ const DAYTONA_INFRASTRUCTURE_RETRY_PATTERN =
|
|
|
54
57
|
/\b(?:Request failed with status code (?:408|429|500|502|503|504)|ECONNRESET|ECONNREFUSED|ETIMEDOUT|UND_ERR_CONNECT_TIMEOUT|fetch failed|socket hang up|Daytona sandbox create failed across|Daytona sandbox create did not complete|Daytona readiness baseline)\b/i;
|
|
55
58
|
|
|
56
59
|
type DaytonaExecution = { exitCode: number | null; result: string };
|
|
57
|
-
type DaytonaLookupFailure = {
|
|
60
|
+
export type DaytonaLookupFailure = {
|
|
58
61
|
errorName: string;
|
|
59
62
|
errorCode: string | null;
|
|
60
63
|
httpStatus: number | null;
|
|
61
64
|
requestId: string | null;
|
|
62
65
|
detail: string;
|
|
63
66
|
};
|
|
67
|
+
export type DaytonaCrashDiagnostic = {
|
|
68
|
+
schemaVersion: 1;
|
|
69
|
+
collectionStatus: 'collected' | 'unavailable' | 'wrong_routing_domain';
|
|
70
|
+
exitCodeFile: number | null;
|
|
71
|
+
sessionExitCode: number | null;
|
|
72
|
+
outputBytes: number | null;
|
|
73
|
+
tailBytes: number;
|
|
74
|
+
tailTruncated: boolean;
|
|
75
|
+
signals: string[];
|
|
76
|
+
unclassifiedLineCount: number;
|
|
77
|
+
collectionFailure: DaytonaLookupFailure | null;
|
|
78
|
+
};
|
|
64
79
|
type DaytonaUploadAttemptTiming = {
|
|
65
80
|
attempt: number;
|
|
66
81
|
sandboxId: string;
|
|
@@ -165,6 +180,194 @@ export function describeDaytonaLookupFailure(
|
|
|
165
180
|
};
|
|
166
181
|
}
|
|
167
182
|
|
|
183
|
+
function shellQuoteDaytonaDiagnostic(value: string): string {
|
|
184
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const DAYTONA_CRASH_DIAGNOSTIC_LINE_PATTERN =
|
|
188
|
+
/(?:\b(?:Type|Range|Reference|Syntax|Aggregate|Eval|URI)?Error(?:\s*\[[^\]]+\])?:|\bERR_[A-Z0-9_]+\b|\b(?:uncaught|unhandled(?:promiserejection)?)\b|\bFATAL ERROR\b|heap out of memory|\b(?:ENOMEM|EPIPE|ECONNRESET|ETIMEDOUT)\b|\bSIG(?:ABRT|BUS|FPE|ILL|KILL|SEGV|TERM)\b|^node:|^\s*at\s+(?:async\s+)?)/i;
|
|
189
|
+
|
|
190
|
+
function daytonaCrashDiagnosticSignature(line: string): string | null {
|
|
191
|
+
const signals = new Set<string>();
|
|
192
|
+
const errorName = line.match(
|
|
193
|
+
/\b((?:Type|Range|Reference|Syntax|Aggregate|Eval|URI)?Error)\b/i,
|
|
194
|
+
)?.[1];
|
|
195
|
+
if (errorName) signals.add(`error:${errorName}`);
|
|
196
|
+
|
|
197
|
+
if (/\buncaught\b/i.test(line)) signals.add('exception:uncaught');
|
|
198
|
+
if (/\bunhandled(?:promiserejection)?\b/i.test(line))
|
|
199
|
+
signals.add('exception:unhandled_rejection');
|
|
200
|
+
if (/\bFATAL ERROR\b/i.test(line)) signals.add('runtime:fatal_error');
|
|
201
|
+
if (/heap out of memory/i.test(line)) signals.add('runtime:heap_out_of_memory');
|
|
202
|
+
|
|
203
|
+
for (const code of line.matchAll(/\b(ENOMEM|EPIPE|ECONNRESET|ETIMEDOUT)\b/gi))
|
|
204
|
+
signals.add(`transport:${code[1]!.toUpperCase()}`);
|
|
205
|
+
for (const signal of line.matchAll(
|
|
206
|
+
/\b(SIG(?:ABRT|BUS|FPE|ILL|KILL|SEGV|TERM))\b/gi,
|
|
207
|
+
))
|
|
208
|
+
signals.add(`signal:${signal[1]!.toUpperCase()}`);
|
|
209
|
+
|
|
210
|
+
const nodeErrorCode = line.match(/\bERR_[A-Z0-9_]+\b/i)?.[0];
|
|
211
|
+
if (nodeErrorCode) signals.add('runtime:node_error_code');
|
|
212
|
+
if (/^node:/i.test(line)) signals.add('runtime:node_error');
|
|
213
|
+
if (/^\s*at\s+(?:async\s+)?/i.test(line)) signals.add('stack:frame');
|
|
214
|
+
|
|
215
|
+
return signals.size > 0 ? [...signals].join(' ') : null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Reduce a captured runner-output tail to error and stack signals only.
|
|
220
|
+
* Compact Daytona stdout contains JSON result envelopes which can contain
|
|
221
|
+
* customer rows, so every JSON-looking line is excluded even when truncated.
|
|
222
|
+
*/
|
|
223
|
+
export function extractDaytonaCrashDiagnosticSignals(outputTail: string): {
|
|
224
|
+
signals: string[];
|
|
225
|
+
unclassifiedLineCount: number;
|
|
226
|
+
} {
|
|
227
|
+
const candidates: string[] = [];
|
|
228
|
+
let unclassifiedLineCount = 0;
|
|
229
|
+
for (const rawLine of outputTail.split(/\r?\n/)) {
|
|
230
|
+
const line = rawLine.trimEnd();
|
|
231
|
+
if (!line.trim()) continue;
|
|
232
|
+
if (/^\s*[\[{]/.test(line)) {
|
|
233
|
+
unclassifiedLineCount += 1;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (!DAYTONA_CRASH_DIAGNOSTIC_LINE_PATTERN.test(line)) {
|
|
237
|
+
unclassifiedLineCount += 1;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const signature = daytonaCrashDiagnosticSignature(line);
|
|
241
|
+
if (signature) candidates.push(signature);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const selected = candidates.slice(-DAYTONA_CRASH_DIAGNOSTIC_MAX_LINES);
|
|
245
|
+
const signals: string[] = [];
|
|
246
|
+
let retainedBytes = 0;
|
|
247
|
+
for (const line of selected) {
|
|
248
|
+
const lineBytes = Buffer.byteLength(line, 'utf8');
|
|
249
|
+
if (
|
|
250
|
+
retainedBytes + lineBytes + (signals.length > 0 ? 1 : 0) >
|
|
251
|
+
DAYTONA_CRASH_DIAGNOSTIC_MAX_TEXT_BYTES
|
|
252
|
+
) {
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
signals.push(line);
|
|
256
|
+
retainedBytes += lineBytes + (signals.length > 1 ? 1 : 0);
|
|
257
|
+
}
|
|
258
|
+
return { signals, unclassifiedLineCount };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Read bounded crash evidence while the failed sandbox still exists. This is
|
|
263
|
+
* operator telemetry only: it never becomes a run result or customer log.
|
|
264
|
+
*/
|
|
265
|
+
export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
266
|
+
sandboxId: string;
|
|
267
|
+
sessionId: string;
|
|
268
|
+
cmdId: string;
|
|
269
|
+
outputPath: string;
|
|
270
|
+
exitCodePath: string;
|
|
271
|
+
expectedOrganizationId?: string | null;
|
|
272
|
+
}): Promise<DaytonaCrashDiagnostic> {
|
|
273
|
+
const unavailable = (
|
|
274
|
+
collectionFailure: DaytonaLookupFailure,
|
|
275
|
+
): DaytonaCrashDiagnostic => ({
|
|
276
|
+
schemaVersion: 1,
|
|
277
|
+
collectionStatus: 'unavailable',
|
|
278
|
+
exitCodeFile: null,
|
|
279
|
+
sessionExitCode: null,
|
|
280
|
+
outputBytes: null,
|
|
281
|
+
tailBytes: 0,
|
|
282
|
+
tailTruncated: false,
|
|
283
|
+
signals: [],
|
|
284
|
+
unclassifiedLineCount: 0,
|
|
285
|
+
collectionFailure,
|
|
286
|
+
});
|
|
287
|
+
try {
|
|
288
|
+
const { clientOptions } = loadDaytonaRequiredConfig();
|
|
289
|
+
const sandbox = (await daytonaSdkClientFactory
|
|
290
|
+
.createFull(clientOptions)
|
|
291
|
+
.get(input.sandboxId)) as DaytonaSandbox;
|
|
292
|
+
const expectedOrganizationId = input.expectedOrganizationId?.trim() || null;
|
|
293
|
+
const observedOrganizationId = sandbox.organizationId?.trim() || null;
|
|
294
|
+
if (
|
|
295
|
+
expectedOrganizationId &&
|
|
296
|
+
observedOrganizationId !== expectedOrganizationId
|
|
297
|
+
) {
|
|
298
|
+
return {
|
|
299
|
+
...unavailable({
|
|
300
|
+
errorName: 'DaytonaRoutingDomainMismatch',
|
|
301
|
+
errorCode: 'wrong_routing_domain',
|
|
302
|
+
httpStatus: null,
|
|
303
|
+
requestId: null,
|
|
304
|
+
detail: 'Sandbox belongs to a different Daytona organization.',
|
|
305
|
+
}),
|
|
306
|
+
collectionStatus: 'wrong_routing_domain',
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const script = `const fs=require('node:fs');const output=process.argv[1];const exit=process.argv[2];const max=Number(process.argv[3]);const stat=fs.statSync(output);const start=Math.max(0,stat.size-max);const fd=fs.openSync(output,'r');const tail=Buffer.alloc(stat.size-start);fs.readSync(fd,tail,0,tail.length,start);fs.closeSync(fd);let exitCode=null;try{const value=Number.parseInt(fs.readFileSync(exit,'utf8').trim(),10);if(Number.isFinite(value))exitCode=value}catch{}process.stdout.write(JSON.stringify({outputBytes:stat.size,tail:tail.toString('base64'),exitCode}));`;
|
|
311
|
+
const [snapshotResult, sessionResult] = await Promise.allSettled([
|
|
312
|
+
sandbox.process.executeCommand(
|
|
313
|
+
`node -e ${shellQuoteDaytonaDiagnostic(script)} ${shellQuoteDaytonaDiagnostic(input.outputPath)} ${shellQuoteDaytonaDiagnostic(input.exitCodePath)} ${DAYTONA_CRASH_DIAGNOSTIC_TAIL_MAX_BYTES}`,
|
|
314
|
+
undefined,
|
|
315
|
+
{},
|
|
316
|
+
8,
|
|
317
|
+
),
|
|
318
|
+
sandbox.process.getSessionCommand(input.sessionId, input.cmdId),
|
|
319
|
+
]);
|
|
320
|
+
if (snapshotResult.status !== 'fulfilled') {
|
|
321
|
+
return unavailable(describeDaytonaLookupFailure(snapshotResult.reason));
|
|
322
|
+
}
|
|
323
|
+
const snapshot = JSON.parse(snapshotResult.value.result) as {
|
|
324
|
+
outputBytes?: unknown;
|
|
325
|
+
tail?: unknown;
|
|
326
|
+
exitCode?: unknown;
|
|
327
|
+
};
|
|
328
|
+
const tailBuffer =
|
|
329
|
+
typeof snapshot.tail === 'string'
|
|
330
|
+
? Buffer.from(snapshot.tail, 'base64')
|
|
331
|
+
: Buffer.alloc(0);
|
|
332
|
+
const tail = tailBuffer.toString('utf8');
|
|
333
|
+
const outputBytes =
|
|
334
|
+
typeof snapshot.outputBytes === 'number' &&
|
|
335
|
+
Number.isFinite(snapshot.outputBytes)
|
|
336
|
+
? snapshot.outputBytes
|
|
337
|
+
: null;
|
|
338
|
+
const { signals, unclassifiedLineCount } =
|
|
339
|
+
extractDaytonaCrashDiagnosticSignals(tail);
|
|
340
|
+
const sessionExitCode =
|
|
341
|
+
sessionResult.status === 'fulfilled' &&
|
|
342
|
+
typeof sessionResult.value?.exitCode === 'number'
|
|
343
|
+
? sessionResult.value.exitCode
|
|
344
|
+
: null;
|
|
345
|
+
return {
|
|
346
|
+
schemaVersion: 1,
|
|
347
|
+
collectionStatus: 'collected',
|
|
348
|
+
exitCodeFile:
|
|
349
|
+
typeof snapshot.exitCode === 'number' &&
|
|
350
|
+
Number.isFinite(snapshot.exitCode)
|
|
351
|
+
? snapshot.exitCode
|
|
352
|
+
: null,
|
|
353
|
+
sessionExitCode,
|
|
354
|
+
outputBytes,
|
|
355
|
+
tailBytes: tailBuffer.byteLength,
|
|
356
|
+
tailTruncated:
|
|
357
|
+
outputBytes !== null &&
|
|
358
|
+
outputBytes > DAYTONA_CRASH_DIAGNOSTIC_TAIL_MAX_BYTES,
|
|
359
|
+
signals,
|
|
360
|
+
unclassifiedLineCount,
|
|
361
|
+
collectionFailure:
|
|
362
|
+
sessionResult.status === 'rejected'
|
|
363
|
+
? describeDaytonaLookupFailure(sessionResult.reason)
|
|
364
|
+
: null,
|
|
365
|
+
};
|
|
366
|
+
} catch (error) {
|
|
367
|
+
return unavailable(describeDaytonaLookupFailure(error));
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
168
371
|
/**
|
|
169
372
|
* Best-effort deletion of an orphaned Daytona sandbox by id (push-execution B4).
|
|
170
373
|
*
|
package/dist/cli/index.js
CHANGED
|
@@ -1044,7 +1044,7 @@ var SDK_RELEASE = {
|
|
|
1044
1044
|
// 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
|
|
1045
1045
|
// exposed storage-dependent synchronous access. This deliberate minor
|
|
1046
1046
|
// release keeps lazy paging semantics independent of row residency.
|
|
1047
|
-
version: "0.2.
|
|
1047
|
+
version: "0.2.35",
|
|
1048
1048
|
contracts: {
|
|
1049
1049
|
api: {
|
|
1050
1050
|
name: "sdk-http-api",
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1030,7 +1030,7 @@ var SDK_RELEASE = {
|
|
|
1030
1030
|
// 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
|
|
1031
1031
|
// exposed storage-dependent synchronous access. This deliberate minor
|
|
1032
1032
|
// release keeps lazy paging semantics independent of row residency.
|
|
1033
|
-
version: "0.2.
|
|
1033
|
+
version: "0.2.35",
|
|
1034
1034
|
contracts: {
|
|
1035
1035
|
api: {
|
|
1036
1036
|
name: "sdk-http-api",
|
package/dist/index.js
CHANGED
|
@@ -763,7 +763,7 @@ var SDK_RELEASE = {
|
|
|
763
763
|
// 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
|
|
764
764
|
// exposed storage-dependent synchronous access. This deliberate minor
|
|
765
765
|
// release keeps lazy paging semantics independent of row residency.
|
|
766
|
-
version: "0.2.
|
|
766
|
+
version: "0.2.35",
|
|
767
767
|
contracts: {
|
|
768
768
|
api: {
|
|
769
769
|
name: "sdk-http-api",
|
package/dist/index.mjs
CHANGED
|
@@ -689,7 +689,7 @@ var SDK_RELEASE = {
|
|
|
689
689
|
// 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
|
|
690
690
|
// exposed storage-dependent synchronous access. This deliberate minor
|
|
691
691
|
// release keeps lazy paging semantics independent of row residency.
|
|
692
|
-
version: "0.2.
|
|
692
|
+
version: "0.2.35",
|
|
693
693
|
contracts: {
|
|
694
694
|
api: {
|
|
695
695
|
name: "sdk-http-api",
|