deepline 0.3.30 → 0.3.32
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/app-runtime-api.ts +17 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +17 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +2 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +2 -1
- package/dist/bundling-sources/shared_libs/play-runtime/vercel-protection.ts +44 -3
- package/dist/cli/index.js +5 -4
- package/dist/cli/index.mjs +5 -4
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
|
@@ -192,7 +192,7 @@ export const SDK_RELEASE = {
|
|
|
192
192
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
193
193
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
194
194
|
// getters keep their established compatibility behavior.
|
|
195
|
-
version: '0.3.
|
|
195
|
+
version: '0.3.32',
|
|
196
196
|
updateSummary:
|
|
197
197
|
'New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.',
|
|
198
198
|
contracts: {
|
|
@@ -49,7 +49,10 @@ import {
|
|
|
49
49
|
PLAY_RUNTIME_TEST_FAULT_HEADER,
|
|
50
50
|
recognizedRuntimeTestFaultCount,
|
|
51
51
|
} from '@shared_libs/play-runtime/test-runtime-seams';
|
|
52
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
isVercelSecurityCheckpointResponse,
|
|
54
|
+
vercelProtectionBypassHeaders,
|
|
55
|
+
} from '@shared_libs/play-runtime/vercel-protection';
|
|
53
56
|
import type { RuntimeReceiptAction } from '@shared_libs/play-runtime/runtime-actions';
|
|
54
57
|
import { RUNTIME_CAPACITY_POLICY } from '@shared_libs/play-runtime/runtime-capacity-policy';
|
|
55
58
|
import { RUNTIME_RELIABILITY_POLICY } from '@shared_libs/play-runtime/runtime-reliability-policy';
|
|
@@ -478,10 +481,20 @@ function isRetryableAppRuntimeResponse(input: {
|
|
|
478
481
|
action: RuntimeApiRequest['action'];
|
|
479
482
|
status: number;
|
|
480
483
|
body: string;
|
|
484
|
+
contentType: string | null;
|
|
481
485
|
}): boolean {
|
|
482
486
|
if (!isRetryableAppRuntimeAction(input.action)) {
|
|
483
487
|
return false;
|
|
484
488
|
}
|
|
489
|
+
// A WAF checkpoint is not a statement from the app about the idempotent
|
|
490
|
+
// projection. Do not make every 403 retryable: only this exact Vercel HTML
|
|
491
|
+
// response on a replay-safe action is an intermediary failure.
|
|
492
|
+
if (
|
|
493
|
+
input.action === 'append_run_events' &&
|
|
494
|
+
isVercelSecurityCheckpointResponse(input)
|
|
495
|
+
) {
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
485
498
|
// The app runtime may explicitly classify an otherwise-client-error status
|
|
486
499
|
// as transient. Keep the structured delivery contract authoritative rather
|
|
487
500
|
// than reducing every 4xx to permanent at the worker boundary.
|
|
@@ -1273,6 +1286,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1273
1286
|
action: body.action,
|
|
1274
1287
|
status: response.status,
|
|
1275
1288
|
body: responseText,
|
|
1289
|
+
contentType: response.headers.get('content-type'),
|
|
1276
1290
|
})
|
|
1277
1291
|
) {
|
|
1278
1292
|
const retryDelayMs = Math.max(
|
|
@@ -1299,6 +1313,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1299
1313
|
action: body.action,
|
|
1300
1314
|
status: response.status,
|
|
1301
1315
|
body: responseText,
|
|
1316
|
+
contentType: response.headers.get('content-type'),
|
|
1302
1317
|
})
|
|
1303
1318
|
? 'retryable_http'
|
|
1304
1319
|
: 'http_error',
|
|
@@ -1328,6 +1343,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1328
1343
|
action: body.action,
|
|
1329
1344
|
status: response.status,
|
|
1330
1345
|
body: responseText,
|
|
1346
|
+
contentType: response.headers.get('content-type'),
|
|
1331
1347
|
}),
|
|
1332
1348
|
detail: summarizeAppRuntimeErrorBody(responseText),
|
|
1333
1349
|
boundaryLabel,
|
package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts
CHANGED
|
@@ -255,6 +255,7 @@ async function createOneShotDaytonaSandbox(input: {
|
|
|
255
255
|
orgId: string;
|
|
256
256
|
context: DaytonaExecutionContext;
|
|
257
257
|
sandboxName: string;
|
|
258
|
+
providerOperationToken?: string;
|
|
258
259
|
}): Promise<DaytonaSandbox> {
|
|
259
260
|
const limits = validatePlaySandboxRuntimeLimits(
|
|
260
261
|
input.context.sandboxRuntimeLimits ?? {
|
|
@@ -271,6 +272,7 @@ async function createOneShotDaytonaSandbox(input: {
|
|
|
271
272
|
workflowId?: string;
|
|
272
273
|
playId?: string;
|
|
273
274
|
runId?: string;
|
|
275
|
+
operationKey?: string;
|
|
274
276
|
} = {
|
|
275
277
|
source: DAYTONA_PLAY_RUNNER_LABEL_SOURCE,
|
|
276
278
|
};
|
|
@@ -278,6 +280,8 @@ async function createOneShotDaytonaSandbox(input: {
|
|
|
278
280
|
if (workflowId) labels.workflowId = workflowId;
|
|
279
281
|
if (playId) labels.playId = playId;
|
|
280
282
|
if (runId) labels.runId = runId;
|
|
283
|
+
if (input.providerOperationToken)
|
|
284
|
+
labels.operationKey = input.providerOperationToken;
|
|
281
285
|
const { networkAllowList } = resolveDaytonaSandboxNetworkPolicy({
|
|
282
286
|
runtimeSchedulerSchema: input.context.runtimeSchedulerSchema ?? null,
|
|
283
287
|
});
|
|
@@ -357,7 +361,9 @@ async function createRetriedOneShotDaytonaSandbox(input: {
|
|
|
357
361
|
context: DaytonaExecutionContext;
|
|
358
362
|
emitStage: DaytonaStageEmitter;
|
|
359
363
|
observeCreateCall?: DaytonaCreateCallObserver;
|
|
360
|
-
reserveSandboxCapacity?: (
|
|
364
|
+
reserveSandboxCapacity?: (
|
|
365
|
+
providerAttempt: number,
|
|
366
|
+
) => Promise<{ leaseId: string; providerOperationToken: string | null }>;
|
|
361
367
|
releaseSandboxCapacity?: (leaseId: string) => Promise<void>;
|
|
362
368
|
nextProviderAttempt: () => number;
|
|
363
369
|
startedAt: number;
|
|
@@ -381,7 +387,7 @@ async function createRetriedOneShotDaytonaSandbox(input: {
|
|
|
381
387
|
occurredAtMs: attemptStartedAt,
|
|
382
388
|
providerAttempt: attempt,
|
|
383
389
|
});
|
|
384
|
-
const reservation = await input.reserveSandboxCapacity?.();
|
|
390
|
+
const reservation = await input.reserveSandboxCapacity?.(attempt);
|
|
385
391
|
let sandbox: DaytonaSandbox;
|
|
386
392
|
try {
|
|
387
393
|
sandbox = await createOneShotDaytonaSandbox({
|
|
@@ -389,6 +395,8 @@ async function createRetriedOneShotDaytonaSandbox(input: {
|
|
|
389
395
|
orgId: input.orgId,
|
|
390
396
|
context: input.context,
|
|
391
397
|
sandboxName,
|
|
398
|
+
providerOperationToken:
|
|
399
|
+
reservation?.providerOperationToken ?? undefined,
|
|
392
400
|
});
|
|
393
401
|
} catch (error) {
|
|
394
402
|
// A normal rejected create has no durable provider resource. A timeout
|
|
@@ -500,7 +508,9 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
500
508
|
context: DaytonaExecutionContext;
|
|
501
509
|
emitStage: DaytonaStageEmitter;
|
|
502
510
|
observeCreateCall?: DaytonaCreateCallObserver;
|
|
503
|
-
reserveSandboxCapacity?: (
|
|
511
|
+
reserveSandboxCapacity?: (
|
|
512
|
+
providerAttempt: number,
|
|
513
|
+
) => Promise<{ leaseId: string; providerOperationToken: string | null }>;
|
|
504
514
|
releaseSandboxCapacity?: (leaseId: string) => Promise<void>;
|
|
505
515
|
nextProviderAttempt: () => number;
|
|
506
516
|
startedAt: number;
|
|
@@ -607,7 +617,10 @@ export function createOneShotDaytonaSandboxLifecycle(input: {
|
|
|
607
617
|
context: DaytonaExecutionContext;
|
|
608
618
|
emitStage: DaytonaStageEmitter;
|
|
609
619
|
observeCreateCall?: DaytonaCreateCallObserver;
|
|
610
|
-
reserveSandboxCapacity?: () => Promise<{
|
|
620
|
+
reserveSandboxCapacity?: (providerAttempt: number) => Promise<{
|
|
621
|
+
leaseId: string;
|
|
622
|
+
providerOperationToken: string | null;
|
|
623
|
+
}>;
|
|
611
624
|
releaseSandboxCapacity?: (leaseId: string) => Promise<void>;
|
|
612
625
|
startedAt?: number;
|
|
613
626
|
}): OneShotDaytonaSandboxLifecycle {
|
|
@@ -953,7 +953,8 @@ function prepareDaytonaExecution(
|
|
|
953
953
|
emitDaytonaStage(callbacks, input.context, stage, extra),
|
|
954
954
|
observeCreateCall: callbacks?.onRuntimeLifecycleEvent,
|
|
955
955
|
reserveSandboxCapacity: callbacks?.reserveSandboxCapacity
|
|
956
|
-
? () =>
|
|
956
|
+
? (providerAttempt) =>
|
|
957
|
+
callbacks.reserveSandboxCapacity!('daytona', providerAttempt)
|
|
957
958
|
: undefined,
|
|
958
959
|
releaseSandboxCapacity: callbacks?.releaseSandboxCapacity,
|
|
959
960
|
});
|
|
@@ -131,7 +131,8 @@ export interface PlayRunnerCallbacks {
|
|
|
131
131
|
/** Reserve one physical-sandbox slot immediately before a provider create. */
|
|
132
132
|
reserveSandboxCapacity?: (
|
|
133
133
|
provider: 'daytona' | 'modal',
|
|
134
|
-
|
|
134
|
+
providerAttempt?: number,
|
|
135
|
+
) => Promise<{ leaseId: string; providerOperationToken: string | null }>;
|
|
135
136
|
/** Release an unbound reservation when provider create never produced a resource. */
|
|
136
137
|
releaseSandboxCapacity?: (leaseId: string) => Promise<void>;
|
|
137
138
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type FetchLike = typeof fetch;
|
|
2
2
|
|
|
3
3
|
const vercelProtectionCookieCache = new Map<string, Promise<string | null>>();
|
|
4
|
+
const VERCEL_PROTECTION_COOKIE_PREFLIGHT_TIMEOUT_MS = 5_000;
|
|
4
5
|
|
|
5
6
|
async function awaitWithSignal<T>(
|
|
6
7
|
promise: Promise<T>,
|
|
@@ -45,6 +46,25 @@ export function vercelProtectionBypassHeader(
|
|
|
45
46
|
return normalized ? { 'x-vercel-protection-bypass': normalized } : {};
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* A Vercel Firewall checkpoint is a transport/intermediary response, not an
|
|
51
|
+
* application authorization decision. Keep this deliberately narrow: callers
|
|
52
|
+
* still decide which replay-safe internal action may retry it.
|
|
53
|
+
*/
|
|
54
|
+
export function isVercelSecurityCheckpointResponse(input: {
|
|
55
|
+
status: number;
|
|
56
|
+
contentType: string | null | undefined;
|
|
57
|
+
body: string;
|
|
58
|
+
}): boolean {
|
|
59
|
+
if (input.status !== 403) return false;
|
|
60
|
+
if (!/^text\/html(?:\s*;|$)/i.test(input.contentType?.trim() ?? '')) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return /<title\b[^>]*>\s*Vercel Security Checkpoint\s*<\/title>/i.test(
|
|
64
|
+
input.body,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
48
68
|
function setCookieHeaders(headers: Headers): string[] {
|
|
49
69
|
const getter = (headers as Headers & { getSetCookie?: () => string[] })
|
|
50
70
|
.getSetCookie;
|
|
@@ -75,14 +95,27 @@ export async function resolveVercelProtectionBypassCookie(input: {
|
|
|
75
95
|
|
|
76
96
|
const cacheKey = `${baseUrl}\n${token}`;
|
|
77
97
|
const cached = vercelProtectionCookieCache.get(cacheKey);
|
|
78
|
-
if (cached)
|
|
98
|
+
if (cached) {
|
|
99
|
+
try {
|
|
100
|
+
return await awaitWithSignal(cached, input.signal);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (input.signal?.aborted) throw input.signal.reason ?? error;
|
|
103
|
+
// A concurrent caller must retain the same header-only fallback as the
|
|
104
|
+
// caller that created the shared preflight promise.
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
79
108
|
|
|
80
109
|
const promise = (async () => {
|
|
81
110
|
const url = new URL(`${baseUrl}/api/v2/health`);
|
|
82
111
|
addVercelProtectionBypassSearchParams(url, token);
|
|
112
|
+
// Keep the cache independent from an individual outbox lease. A caller
|
|
113
|
+
// may stop waiting, but the shared bootstrap is always bounded and either
|
|
114
|
+
// supplies a reusable cookie or evicts itself for a later retry.
|
|
83
115
|
const response = await (input.fetchImpl ?? fetch)(url.toString(), {
|
|
84
116
|
headers: { 'x-vercel-protection-bypass': token },
|
|
85
|
-
|
|
117
|
+
signal: AbortSignal.timeout(VERCEL_PROTECTION_COOKIE_PREFLIGHT_TIMEOUT_MS),
|
|
118
|
+
});
|
|
86
119
|
return response ? cookieHeaderFromSetCookie(response.headers) : null;
|
|
87
120
|
})();
|
|
88
121
|
vercelProtectionCookieCache.set(cacheKey, promise);
|
|
@@ -91,7 +124,15 @@ export async function resolveVercelProtectionBypassCookie(input: {
|
|
|
91
124
|
vercelProtectionCookieCache.delete(cacheKey);
|
|
92
125
|
}
|
|
93
126
|
});
|
|
94
|
-
|
|
127
|
+
try {
|
|
128
|
+
return await awaitWithSignal(promise, input.signal);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (input.signal?.aborted) throw input.signal.reason ?? error;
|
|
131
|
+
// The header-only bypass remains useful when cookie bootstrap is briefly
|
|
132
|
+
// unavailable; do not turn a bounded preflight failure into a permanent
|
|
133
|
+
// delivery failure. The rejection handler above evicts this cache entry.
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
95
136
|
}
|
|
96
137
|
|
|
97
138
|
export async function vercelProtectionBypassHeaders(input: {
|
package/dist/cli/index.js
CHANGED
|
@@ -1042,7 +1042,7 @@ var SDK_RELEASE = {
|
|
|
1042
1042
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
1043
1043
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
1044
1044
|
// getters keep their established compatibility behavior.
|
|
1045
|
-
version: "0.3.
|
|
1045
|
+
version: "0.3.32",
|
|
1046
1046
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
1047
1047
|
contracts: {
|
|
1048
1048
|
api: {
|
|
@@ -34243,9 +34243,10 @@ Notes:
|
|
|
34243
34243
|
for a patch-style change.
|
|
34244
34244
|
Repeating the exact saved definition resumes an incomplete deploy cleanup:
|
|
34245
34245
|
Deepline keeps the replacement, removes only the stored previous binding after
|
|
34246
|
-
provider confirmation, and never creates or charges another monitor.
|
|
34247
|
-
|
|
34248
|
-
|
|
34246
|
+
provider confirmation, and never creates or charges another monitor. There is
|
|
34247
|
+
no separate repair command or retry queue: rerun the same deploy/update.
|
|
34248
|
+
Scheduled reconciliation can also finish that exact saved cleanup; it never
|
|
34249
|
+
creates another monitor.
|
|
34249
34250
|
For a bounded urgent Deepline Native preview, set
|
|
34250
34251
|
controls.execution_type="priority". Deepline injects the provider custom
|
|
34251
34252
|
field and enforces a ten-slot per-org cap; do not use it for regular or bulk
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1028,7 +1028,7 @@ var SDK_RELEASE = {
|
|
|
1028
1028
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
1029
1029
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
1030
1030
|
// getters keep their established compatibility behavior.
|
|
1031
|
-
version: "0.3.
|
|
1031
|
+
version: "0.3.32",
|
|
1032
1032
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
1033
1033
|
contracts: {
|
|
1034
1034
|
api: {
|
|
@@ -34313,9 +34313,10 @@ Notes:
|
|
|
34313
34313
|
for a patch-style change.
|
|
34314
34314
|
Repeating the exact saved definition resumes an incomplete deploy cleanup:
|
|
34315
34315
|
Deepline keeps the replacement, removes only the stored previous binding after
|
|
34316
|
-
provider confirmation, and never creates or charges another monitor.
|
|
34317
|
-
|
|
34318
|
-
|
|
34316
|
+
provider confirmation, and never creates or charges another monitor. There is
|
|
34317
|
+
no separate repair command or retry queue: rerun the same deploy/update.
|
|
34318
|
+
Scheduled reconciliation can also finish that exact saved cleanup; it never
|
|
34319
|
+
creates another monitor.
|
|
34319
34320
|
For a bounded urgent Deepline Native preview, set
|
|
34320
34321
|
controls.execution_type="priority". Deepline injects the provider custom
|
|
34321
34322
|
field and enforces a ten-slot per-org cap; do not use it for regular or bulk
|
package/dist/index.js
CHANGED
|
@@ -778,7 +778,7 @@ var SDK_RELEASE = {
|
|
|
778
778
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
779
779
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
780
780
|
// getters keep their established compatibility behavior.
|
|
781
|
-
version: "0.3.
|
|
781
|
+
version: "0.3.32",
|
|
782
782
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
783
783
|
contracts: {
|
|
784
784
|
api: {
|
package/dist/index.mjs
CHANGED
|
@@ -701,7 +701,7 @@ var SDK_RELEASE = {
|
|
|
701
701
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
702
702
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
703
703
|
// getters keep their established compatibility behavior.
|
|
704
|
-
version: "0.3.
|
|
704
|
+
version: "0.3.32",
|
|
705
705
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
706
706
|
contracts: {
|
|
707
707
|
api: {
|