deepline 0.1.214 → 0.1.216
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/apps/play-runner-workers/src/child-manifest-resolver.ts +108 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +85 -10
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-runtime-proxy.ts +34 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +50 -36
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +12 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry.ts +4 -0
- package/dist/bundling-sources/sdk/src/client.ts +1 -2
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +21 -3
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +95 -144
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +14 -3
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +37 -2
- package/dist/bundling-sources/shared_libs/play-runtime/profiles.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +32 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +13 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +5 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +66 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +18 -55
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +59 -27
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +17 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +3 -2
- package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +11 -1
- package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +10 -0
- package/dist/cli/index.js +72 -21
- package/dist/cli/index.mjs +72 -21
- package/dist/index.d.mts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +7 -6
- package/dist/index.mjs +7 -6
- package/package.json +1 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PlayRuntimeManifest,
|
|
3
|
+
PlayRuntimeManifestMap,
|
|
4
|
+
} from '../../../shared_libs/plays/compiler-manifest';
|
|
5
|
+
import { createSingleFlight } from '../../../shared_libs/play-runtime/single-flight';
|
|
6
|
+
|
|
7
|
+
type ChildManifestResolverInput = {
|
|
8
|
+
preloaded?: PlayRuntimeManifestMap | null;
|
|
9
|
+
resolveManifest: (playRef: string) => Promise<PlayRuntimeManifest | null>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type ChildManifestResolutionState = {
|
|
13
|
+
resolved: Map<string, PlayRuntimeManifest>;
|
|
14
|
+
flights: ReturnType<
|
|
15
|
+
typeof createSingleFlight<string, PlayRuntimeManifest | null>
|
|
16
|
+
>;
|
|
17
|
+
lastAccessedAt: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const RUN_RESOLUTION_CACHE_TTL_MS = 10 * 60_000;
|
|
21
|
+
const RUN_RESOLUTION_CACHE_MAX_ENTRIES = 128;
|
|
22
|
+
const runResolutionStates = new Map<string, ChildManifestResolutionState>();
|
|
23
|
+
|
|
24
|
+
function seedPreloadedManifests(
|
|
25
|
+
resolved: Map<string, PlayRuntimeManifest>,
|
|
26
|
+
preloaded?: PlayRuntimeManifestMap | null,
|
|
27
|
+
): void {
|
|
28
|
+
for (const [playRef, manifest] of Object.entries(preloaded ?? {})) {
|
|
29
|
+
resolved.set(playRef, manifest);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createResolutionState(
|
|
34
|
+
preloaded?: PlayRuntimeManifestMap | null,
|
|
35
|
+
): ChildManifestResolutionState {
|
|
36
|
+
const resolved = new Map<string, PlayRuntimeManifest>(
|
|
37
|
+
Object.entries(preloaded ?? {}),
|
|
38
|
+
);
|
|
39
|
+
return {
|
|
40
|
+
resolved,
|
|
41
|
+
flights: createSingleFlight<string, PlayRuntimeManifest | null>(),
|
|
42
|
+
lastAccessedAt: Date.now(),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function resolveWithState(
|
|
47
|
+
state: ChildManifestResolutionState,
|
|
48
|
+
input: ChildManifestResolverInput,
|
|
49
|
+
playRef: string,
|
|
50
|
+
): Promise<PlayRuntimeManifest | null> {
|
|
51
|
+
state.lastAccessedAt = Date.now();
|
|
52
|
+
seedPreloadedManifests(state.resolved, input.preloaded);
|
|
53
|
+
const cached = state.resolved.get(playRef);
|
|
54
|
+
if (cached) return Promise.resolve(cached);
|
|
55
|
+
|
|
56
|
+
return state.flights.run(playRef, async () => {
|
|
57
|
+
const cachedAfterAdmission = state.resolved.get(playRef);
|
|
58
|
+
if (cachedAfterAdmission) return cachedAfterAdmission;
|
|
59
|
+
|
|
60
|
+
const manifest = await input.resolveManifest(playRef);
|
|
61
|
+
if (manifest) state.resolved.set(playRef, manifest);
|
|
62
|
+
return manifest;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function pruneRunResolutionStates(now: number): void {
|
|
67
|
+
for (const [scopeKey, state] of runResolutionStates) {
|
|
68
|
+
if (
|
|
69
|
+
state.flights.activeCount() === 0 &&
|
|
70
|
+
now - state.lastAccessedAt >= RUN_RESOLUTION_CACHE_TTL_MS
|
|
71
|
+
) {
|
|
72
|
+
runResolutionStates.delete(scopeKey);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (runResolutionStates.size <= RUN_RESOLUTION_CACHE_MAX_ENTRIES) return;
|
|
76
|
+
|
|
77
|
+
const evictable = [...runResolutionStates.entries()]
|
|
78
|
+
.filter(([, state]) => state.flights.activeCount() === 0)
|
|
79
|
+
.sort((a, b) => a[1].lastAccessedAt - b[1].lastAccessedAt);
|
|
80
|
+
for (const [scopeKey] of evictable) {
|
|
81
|
+
if (runResolutionStates.size <= RUN_RESOLUTION_CACHE_MAX_ENTRIES) break;
|
|
82
|
+
runResolutionStates.delete(scopeKey);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function createChildManifestResolver(
|
|
87
|
+
input: ChildManifestResolverInput,
|
|
88
|
+
): (playRef: string) => Promise<PlayRuntimeManifest | null> {
|
|
89
|
+
const state = createResolutionState(input.preloaded);
|
|
90
|
+
|
|
91
|
+
return (playRef) => resolveWithState(state, input, playRef);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function createRunScopedChildManifestResolver(
|
|
95
|
+
input: ChildManifestResolverInput & { scopeKey: string },
|
|
96
|
+
): (playRef: string) => Promise<PlayRuntimeManifest | null> {
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
pruneRunResolutionStates(now);
|
|
99
|
+
let state = runResolutionStates.get(input.scopeKey);
|
|
100
|
+
if (!state) {
|
|
101
|
+
state = createResolutionState(input.preloaded);
|
|
102
|
+
runResolutionStates.set(input.scopeKey, state);
|
|
103
|
+
pruneRunResolutionStates(now);
|
|
104
|
+
}
|
|
105
|
+
state.lastAccessedAt = now;
|
|
106
|
+
|
|
107
|
+
return (playRef) => resolveWithState(state, input, playRef);
|
|
108
|
+
}
|
|
@@ -57,9 +57,13 @@ import {
|
|
|
57
57
|
PLAY_RUNTIME_CONTRACT_HEADER,
|
|
58
58
|
} from '../../../shared_libs/play-runtime/runtime-contract';
|
|
59
59
|
import {
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
PLAY_RUNTIME_API_CURRENT_PATH,
|
|
61
|
+
PLAY_RUNTIME_TAIL_LOG_CURRENT_PATH,
|
|
62
62
|
} from '../../../shared_libs/play-runtime/runtime-api-paths';
|
|
63
|
+
import {
|
|
64
|
+
executorTokenFromAuthorizationHeader,
|
|
65
|
+
isCoordinatorRuntimeProxyPath,
|
|
66
|
+
} from './coordinator-runtime-proxy';
|
|
63
67
|
import {
|
|
64
68
|
decideWorkflowPlatformRetry,
|
|
65
69
|
PLATFORM_DEPLOY_WORKFLOW_RETRY_LIMIT,
|
|
@@ -1331,7 +1335,7 @@ async function markWorkflowRuntimeFailure(input: {
|
|
|
1331
1335
|
} satisfies PlayRunLedgerEvent,
|
|
1332
1336
|
],
|
|
1333
1337
|
});
|
|
1334
|
-
const url = `${baseUrl.replace(/\/$/, '')}${
|
|
1338
|
+
const url = `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_CURRENT_PATH}`;
|
|
1335
1339
|
const backoffMs = [200, 500, 1500];
|
|
1336
1340
|
let lastError: unknown = null;
|
|
1337
1341
|
for (let attempt = 0; attempt <= backoffMs.length; attempt += 1) {
|
|
@@ -1420,7 +1424,7 @@ async function appendWorkflowChildCompletedRunEvent(input: {
|
|
|
1420
1424
|
} satisfies PlayRunLedgerEvent,
|
|
1421
1425
|
],
|
|
1422
1426
|
});
|
|
1423
|
-
const url = `${baseUrl.replace(/\/$/, '')}${
|
|
1427
|
+
const url = `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_CURRENT_PATH}`;
|
|
1424
1428
|
const response = await fetch(url, { method: 'POST', headers, body });
|
|
1425
1429
|
if (!response.ok) {
|
|
1426
1430
|
throw new Error(
|
|
@@ -1840,7 +1844,7 @@ async function markRegisteredChildRunFailed(input: {
|
|
|
1840
1844
|
contract: PLAY_RUNTIME_CONTRACT,
|
|
1841
1845
|
executorToken: input.childExecutorToken,
|
|
1842
1846
|
baseUrl: input.baseUrl,
|
|
1843
|
-
path:
|
|
1847
|
+
path: PLAY_RUNTIME_API_CURRENT_PATH,
|
|
1844
1848
|
headers: {
|
|
1845
1849
|
'x-deepline-request-id': crypto.randomUUID(),
|
|
1846
1850
|
[PLAY_RUNTIME_CONTRACT_HEADER]: String(PLAY_RUNTIME_CONTRACT),
|
|
@@ -1897,7 +1901,7 @@ async function appendRegisteredChildRunTerminal(input: {
|
|
|
1897
1901
|
contract: PLAY_RUNTIME_CONTRACT,
|
|
1898
1902
|
executorToken: input.childExecutorToken,
|
|
1899
1903
|
baseUrl: input.baseUrl,
|
|
1900
|
-
path:
|
|
1904
|
+
path: PLAY_RUNTIME_API_CURRENT_PATH,
|
|
1901
1905
|
headers: {
|
|
1902
1906
|
'x-deepline-request-id': crypto.randomUUID(),
|
|
1903
1907
|
[PLAY_RUNTIME_CONTRACT_HEADER]: String(PLAY_RUNTIME_CONTRACT),
|
|
@@ -1979,7 +1983,7 @@ async function callRuntimeApiFromCoordinator(input: {
|
|
|
1979
1983
|
contract: PLAY_RUNTIME_CONTRACT,
|
|
1980
1984
|
executorToken: input.executorToken,
|
|
1981
1985
|
baseUrl: input.baseUrl,
|
|
1982
|
-
path:
|
|
1986
|
+
path: PLAY_RUNTIME_API_CURRENT_PATH,
|
|
1983
1987
|
body,
|
|
1984
1988
|
headers: {
|
|
1985
1989
|
'x-deepline-request-id': requestId,
|
|
@@ -3118,7 +3122,8 @@ async function submitChildWorkflowThroughCoordinator(input: {
|
|
|
3118
3122
|
* Workflows serializes the dynamic Worker's `env` map when it persists
|
|
3119
3123
|
* workflow state. WorkerEntrypoint stubs ARE cloneable.
|
|
3120
3124
|
*
|
|
3121
|
-
* Path allowlist:
|
|
3125
|
+
* Path allowlist: the current runtime endpoint plus `/api/v2/plays/*` and
|
|
3126
|
+
* `/api/v2/integrations/*`.
|
|
3122
3127
|
* Anything else is a sandbox-escape attempt and gets a loud 403 — the
|
|
3123
3128
|
* coordinator must NOT proxy a play's request to internal admin routes
|
|
3124
3129
|
* even if the play tries to construct such a URL.
|
|
@@ -3127,6 +3132,7 @@ export class RuntimeApi extends WorkerEntrypoint<CoordinatorEnv, undefined> {
|
|
|
3127
3132
|
async fetch(request: Request): Promise<Response> {
|
|
3128
3133
|
const incoming = new URL(request.url);
|
|
3129
3134
|
const allowed =
|
|
3135
|
+
incoming.pathname === PLAY_RUNTIME_API_CURRENT_PATH ||
|
|
3130
3136
|
incoming.pathname.startsWith('/api/v2/plays/') ||
|
|
3131
3137
|
incoming.pathname.startsWith('/api/v2/integrations/');
|
|
3132
3138
|
if (!allowed) {
|
|
@@ -3759,6 +3765,62 @@ const coordinatorEntrypoint = {
|
|
|
3759
3765
|
|
|
3760
3766
|
export default coordinatorEntrypoint;
|
|
3761
3767
|
|
|
3768
|
+
const COORDINATOR_RUNTIME_PROXY_MAX_BODY_BYTES = 16 * 1024 * 1024;
|
|
3769
|
+
|
|
3770
|
+
async function proxyCoordinatorRuntimeApiRequest(input: {
|
|
3771
|
+
request: Request;
|
|
3772
|
+
env: CoordinatorEnv;
|
|
3773
|
+
url: URL;
|
|
3774
|
+
}): Promise<Response> {
|
|
3775
|
+
const { request, env, url } = input;
|
|
3776
|
+
if (request.method !== 'GET' && request.method !== 'POST') {
|
|
3777
|
+
return new Response('method not allowed', { status: 405 });
|
|
3778
|
+
}
|
|
3779
|
+
const executorToken = executorTokenFromAuthorizationHeader(
|
|
3780
|
+
request.headers.get('authorization'),
|
|
3781
|
+
);
|
|
3782
|
+
if (!executorToken) {
|
|
3783
|
+
return Response.json(
|
|
3784
|
+
{ error: 'Runtime gateway requires a bearer executor token.' },
|
|
3785
|
+
{ status: 401 },
|
|
3786
|
+
);
|
|
3787
|
+
}
|
|
3788
|
+
const contentLength = Number(request.headers.get('content-length') ?? '0');
|
|
3789
|
+
if (
|
|
3790
|
+
Number.isFinite(contentLength) &&
|
|
3791
|
+
contentLength > COORDINATOR_RUNTIME_PROXY_MAX_BODY_BYTES
|
|
3792
|
+
) {
|
|
3793
|
+
return Response.json(
|
|
3794
|
+
{ error: 'Runtime gateway request body exceeds the 16 MiB limit.' },
|
|
3795
|
+
{ status: 413 },
|
|
3796
|
+
);
|
|
3797
|
+
}
|
|
3798
|
+
let body: unknown = {};
|
|
3799
|
+
if (request.method === 'POST') {
|
|
3800
|
+
try {
|
|
3801
|
+
body = await request.json();
|
|
3802
|
+
} catch {
|
|
3803
|
+
return Response.json(
|
|
3804
|
+
{ error: 'Runtime gateway requires a JSON request body.' },
|
|
3805
|
+
{ status: 400 },
|
|
3806
|
+
);
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
const upstream = await env.HARNESS.runtimeApiCall({
|
|
3810
|
+
contract: PLAY_RUNTIME_CONTRACT,
|
|
3811
|
+
executorToken,
|
|
3812
|
+
method: request.method,
|
|
3813
|
+
path: `${url.pathname}${url.search}`,
|
|
3814
|
+
body,
|
|
3815
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
3816
|
+
});
|
|
3817
|
+
const headers = new Headers(upstream.headers);
|
|
3818
|
+
// The harness materializes the upstream response body as a string. Let this
|
|
3819
|
+
// Response calculate its own byte length instead of forwarding a stale one.
|
|
3820
|
+
headers.delete('content-length');
|
|
3821
|
+
return new Response(upstream.body, { status: upstream.status, headers });
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3762
3824
|
/**
|
|
3763
3825
|
* Route dispatcher for the coordinator fetch handler. Extracted as a standalone
|
|
3764
3826
|
* function so the outer {@link coordinatorEntrypoint.fetch} try/catch backstop
|
|
@@ -3787,6 +3849,19 @@ async function coordinatorRouteFetch(
|
|
|
3787
3849
|
}
|
|
3788
3850
|
return new Response('ok', { status: 200, headers });
|
|
3789
3851
|
}
|
|
3852
|
+
if (isCoordinatorRuntimeProxyPath(url.pathname)) {
|
|
3853
|
+
try {
|
|
3854
|
+
return await proxyCoordinatorRuntimeApiRequest({ request, env, url });
|
|
3855
|
+
} catch (error) {
|
|
3856
|
+
return coordinatorRouteErrorResponse({
|
|
3857
|
+
logTag: '[coordinator.runtime_proxy.error]',
|
|
3858
|
+
code: 'COORDINATOR_RUNTIME_PROXY_FAILED',
|
|
3859
|
+
phase: 'coordinator.runtime_proxy',
|
|
3860
|
+
runId: null,
|
|
3861
|
+
error,
|
|
3862
|
+
});
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3790
3865
|
if (url.pathname === '/internal-token/probe') {
|
|
3791
3866
|
const authError = authorizeCoordinatorControlRequest({ request, env });
|
|
3792
3867
|
if (authError) return authError;
|
|
@@ -4035,7 +4110,7 @@ async function flushTailRunLogs(
|
|
|
4035
4110
|
return;
|
|
4036
4111
|
}
|
|
4037
4112
|
await fetch(
|
|
4038
|
-
`${env.DEEPLINE_API_BASE_URL}${
|
|
4113
|
+
`${env.DEEPLINE_API_BASE_URL}${PLAY_RUNTIME_TAIL_LOG_CURRENT_PATH}`,
|
|
4039
4114
|
{
|
|
4040
4115
|
method: 'POST',
|
|
4041
4116
|
headers: {
|
|
@@ -5455,7 +5530,7 @@ export class TenantWorkflow extends WorkflowEntrypoint {
|
|
|
5455
5530
|
const runId = payload && typeof payload.runId === "string" ? payload.runId : "warmup";
|
|
5456
5531
|
const startedAt = Date.now();
|
|
5457
5532
|
if (this.env.RUNTIME_API) {
|
|
5458
|
-
await this.env.RUNTIME_API.fetch(new Request("https://deepline.runtime.internal${
|
|
5533
|
+
await this.env.RUNTIME_API.fetch(new Request("https://deepline.runtime.internal${PLAY_RUNTIME_API_CURRENT_PATH}", {
|
|
5459
5534
|
method: "POST",
|
|
5460
5535
|
headers: { "content-type": "application/json" },
|
|
5461
5536
|
body: "{}"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PLAY_RUNTIME_API_CURRENT_PATH,
|
|
3
|
+
PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_CURRENT_PATH,
|
|
4
|
+
PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH,
|
|
5
|
+
} from '../../../shared_libs/play-runtime/runtime-api-paths';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Paths a credential-isolated Daytona sandbox may send through the
|
|
9
|
+
* coordinator. The sandbox receives the coordinator as its runtime origin so
|
|
10
|
+
* it never holds the Vercel protection-bypass credential; the harness owns the
|
|
11
|
+
* protected upstream hop. Keep this deliberately narrower than the harness
|
|
12
|
+
* allowlist: a coordinator URL is public, while the harness is a service
|
|
13
|
+
* binding.
|
|
14
|
+
*/
|
|
15
|
+
export function isCoordinatorRuntimeProxyPath(pathname: string): boolean {
|
|
16
|
+
return (
|
|
17
|
+
pathname === PLAY_RUNTIME_API_CURRENT_PATH ||
|
|
18
|
+
pathname === PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_CURRENT_PATH ||
|
|
19
|
+
pathname === PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH ||
|
|
20
|
+
pathname === '/api/v2/plays/run' ||
|
|
21
|
+
/^\/api\/v2\/plays\/runtime-tools\/[^/]+(?:\/(?:auth-scope|event-wait))?$/.test(
|
|
22
|
+
pathname,
|
|
23
|
+
) ||
|
|
24
|
+
/^\/api\/v2\/integrations\/[^/]+\/(?:execute|stream)$/.test(pathname)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function executorTokenFromAuthorizationHeader(
|
|
29
|
+
authorization: string | null,
|
|
30
|
+
): string | null {
|
|
31
|
+
const match = /^Bearer\s+(.+)$/i.exec(authorization?.trim() ?? '');
|
|
32
|
+
const token = match?.[1]?.trim();
|
|
33
|
+
return token || null;
|
|
34
|
+
}
|
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
type WorkflowStepLike,
|
|
68
68
|
} from './child-play-await';
|
|
69
69
|
import { submitChildPlayThroughCoordinator } from './child-play-submit';
|
|
70
|
+
import { createRunScopedChildManifestResolver } from './child-manifest-resolver';
|
|
70
71
|
import { isRetryableWorkerRuntimeApiError } from './runtime-api-retry';
|
|
71
72
|
import { formatCustomerConsoleValue } from './runtime/customer-console';
|
|
72
73
|
import {
|
|
@@ -184,8 +185,8 @@ import {
|
|
|
184
185
|
PLAY_RUNTIME_CONTRACT_HEADER,
|
|
185
186
|
} from '../../../shared_libs/play-runtime/runtime-contract';
|
|
186
187
|
import {
|
|
187
|
-
|
|
188
|
-
|
|
188
|
+
PLAY_RUNTIME_API_CURRENT_PATH,
|
|
189
|
+
PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH,
|
|
189
190
|
} from '../../../shared_libs/play-runtime/runtime-api-paths';
|
|
190
191
|
import { DYNAMIC_PLAY_WORKER_ARTIFACT_VERSION } from '../../../shared_libs/play-runtime/dynamic-worker-version';
|
|
191
192
|
import {
|
|
@@ -859,7 +860,7 @@ async function fetchRuntimeApi(
|
|
|
859
860
|
? Math.max(1, Math.ceil(options.timeoutMsOverride))
|
|
860
861
|
: path === '/api/v2/plays/run'
|
|
861
862
|
? RUNTIME_API_PLAY_RUN_TIMEOUT_MS
|
|
862
|
-
: path ===
|
|
863
|
+
: path === PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH
|
|
863
864
|
? RUNTIME_API_EGRESS_FETCH_TIMEOUT_MS
|
|
864
865
|
: /^\/api\/v2\/integrations\/[^/]+\/execute$/.test(path)
|
|
865
866
|
? RUNTIME_API_INTEGRATION_EXECUTE_TIMEOUT_MS
|
|
@@ -1315,7 +1316,7 @@ async function postRuntimeApi<T>(
|
|
|
1315
1316
|
try {
|
|
1316
1317
|
res = await fetchRuntimeApi(
|
|
1317
1318
|
baseUrl,
|
|
1318
|
-
|
|
1319
|
+
PLAY_RUNTIME_API_CURRENT_PATH,
|
|
1319
1320
|
{
|
|
1320
1321
|
method: 'POST',
|
|
1321
1322
|
headers: {
|
|
@@ -1619,15 +1620,7 @@ type RuntimeResolvedPlayResponse = {
|
|
|
1619
1620
|
async function resolveAuthorizedWorkerChildManifest(input: {
|
|
1620
1621
|
req: RunRequest;
|
|
1621
1622
|
playRef: string;
|
|
1622
|
-
cache: Map<string, PlayRuntimeManifest>;
|
|
1623
1623
|
}): Promise<PlayRuntimeManifest | null> {
|
|
1624
|
-
const cached = input.cache.get(input.playRef);
|
|
1625
|
-
if (cached) return cached;
|
|
1626
|
-
const preloaded = input.req.childPlayManifests?.[input.playRef];
|
|
1627
|
-
if (preloaded) {
|
|
1628
|
-
input.cache.set(input.playRef, preloaded);
|
|
1629
|
-
return preloaded;
|
|
1630
|
-
}
|
|
1631
1624
|
const response = await postRuntimeApi<RuntimeResolvedPlayResponse>(
|
|
1632
1625
|
input.req.baseUrl,
|
|
1633
1626
|
input.req.executorToken,
|
|
@@ -1641,11 +1634,7 @@ async function resolveAuthorizedWorkerChildManifest(input: {
|
|
|
1641
1634
|
timeoutMsOverride: 30_000,
|
|
1642
1635
|
},
|
|
1643
1636
|
);
|
|
1644
|
-
|
|
1645
|
-
if (manifest) {
|
|
1646
|
-
input.cache.set(input.playRef, manifest);
|
|
1647
|
-
}
|
|
1648
|
-
return manifest;
|
|
1637
|
+
return response.manifest ?? null;
|
|
1649
1638
|
}
|
|
1650
1639
|
|
|
1651
1640
|
async function childPlayEventKey(input: {
|
|
@@ -2497,6 +2486,10 @@ function deserializeDurableStepValue<T>(value: T, depth = 0): T {
|
|
|
2497
2486
|
) as T;
|
|
2498
2487
|
}
|
|
2499
2488
|
|
|
2489
|
+
function decodeLegacyWorkflowStepCheckpoint<T>(checkpoint: unknown): T {
|
|
2490
|
+
return deserializeDurableStepValue(checkpoint) as T;
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2500
2493
|
type WorkerStepResolver = (
|
|
2501
2494
|
row: Record<string, unknown>,
|
|
2502
2495
|
ctx: unknown,
|
|
@@ -2897,7 +2890,7 @@ async function postRuntimeEgressFetch(
|
|
|
2897
2890
|
) {
|
|
2898
2891
|
const response = await fetchRuntimeApi(
|
|
2899
2892
|
req.baseUrl,
|
|
2900
|
-
|
|
2893
|
+
PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH,
|
|
2901
2894
|
{
|
|
2902
2895
|
method: 'POST',
|
|
2903
2896
|
headers: {
|
|
@@ -4258,14 +4251,19 @@ function createMinimalWorkerCtx(
|
|
|
4258
4251
|
// lineage snapshot are owned by the Governor (createGovernorForRun above).
|
|
4259
4252
|
// The worker keeps only substrate mechanism here.
|
|
4260
4253
|
const stepCallCounts: Record<string, number> = {};
|
|
4261
|
-
const
|
|
4254
|
+
const resolveChildManifest = createRunScopedChildManifestResolver({
|
|
4255
|
+
scopeKey: `${req.orgId}:${req.runId}:${req.runAttempt}`,
|
|
4256
|
+
preloaded: req.childPlayManifests,
|
|
4257
|
+
resolveManifest: (playRef) =>
|
|
4258
|
+
resolveAuthorizedWorkerChildManifest({ req, playRef }),
|
|
4259
|
+
});
|
|
4262
4260
|
const secretRedactor = createSecretRedactionContext();
|
|
4263
4261
|
|
|
4264
4262
|
const resolveSecretAuth = async (auth?: SecretAuth) => {
|
|
4265
4263
|
if (!auth) return {};
|
|
4266
4264
|
const response = await fetchRuntimeApi(
|
|
4267
4265
|
req.baseUrl,
|
|
4268
|
-
|
|
4266
|
+
PLAY_RUNTIME_API_CURRENT_PATH,
|
|
4269
4267
|
{
|
|
4270
4268
|
method: 'POST',
|
|
4271
4269
|
headers: {
|
|
@@ -4375,22 +4373,42 @@ function createMinimalWorkerCtx(
|
|
|
4375
4373
|
});
|
|
4376
4374
|
return deserializeDurableStepValue(serialized) as T;
|
|
4377
4375
|
};
|
|
4378
|
-
const
|
|
4376
|
+
const executeWithDurableStepReceipt = async <T>(
|
|
4379
4377
|
name: string,
|
|
4380
4378
|
execute: () => Promise<T> | T,
|
|
4381
4379
|
): Promise<T> => {
|
|
4382
4380
|
if (!workflowStep) {
|
|
4383
4381
|
return await executeWithRuntimeReceipt(name, execute);
|
|
4384
4382
|
}
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
) => Promise<
|
|
4391
|
-
)
|
|
4392
|
-
|
|
4383
|
+
let liveValue: T | undefined;
|
|
4384
|
+
let executedLive = false;
|
|
4385
|
+
const checkpoint = await (
|
|
4386
|
+
workflowStep.do as unknown as (
|
|
4387
|
+
name: string,
|
|
4388
|
+
callback: () => Promise<{ receiptCompleted: true }>,
|
|
4389
|
+
) => Promise<unknown>
|
|
4390
|
+
)(name, async () => {
|
|
4391
|
+
liveValue = await executeWithRuntimeReceipt(name, execute);
|
|
4392
|
+
executedLive = true;
|
|
4393
|
+
return { receiptCompleted: true };
|
|
4393
4394
|
});
|
|
4395
|
+
if (executedLive) return liveValue as T;
|
|
4396
|
+
if (
|
|
4397
|
+
checkpoint !== null &&
|
|
4398
|
+
typeof checkpoint === 'object' &&
|
|
4399
|
+
!Array.isArray(checkpoint) &&
|
|
4400
|
+
(checkpoint as { receiptCompleted?: unknown }).receiptCompleted === true
|
|
4401
|
+
) {
|
|
4402
|
+
return await executeWithRuntimeReceipt(name, () => {
|
|
4403
|
+
throw new Error(
|
|
4404
|
+
`Workflow step ${name} replayed without its completed runtime receipt.`,
|
|
4405
|
+
);
|
|
4406
|
+
});
|
|
4407
|
+
}
|
|
4408
|
+
// Runs started before runtime contract 3 checkpointed the full serialized
|
|
4409
|
+
// value in Cloudflare Workflow. Accept that legacy value only on replay;
|
|
4410
|
+
// new executions above always persist the compact receipt marker.
|
|
4411
|
+
return decodeLegacyWorkflowStepCheckpoint<T>(checkpoint);
|
|
4394
4412
|
};
|
|
4395
4413
|
const nextCtxStepReceiptKey = (
|
|
4396
4414
|
name: string,
|
|
@@ -6694,7 +6712,7 @@ function createMinimalWorkerCtx(
|
|
|
6694
6712
|
if (!normalizedName) {
|
|
6695
6713
|
throw new Error('ctx.step(name, callback) requires a name.');
|
|
6696
6714
|
}
|
|
6697
|
-
return await
|
|
6715
|
+
return await executeWithDurableStepReceipt(
|
|
6698
6716
|
nextCtxStepReceiptKey(normalizedName, options?.staleAfterSeconds),
|
|
6699
6717
|
callback,
|
|
6700
6718
|
);
|
|
@@ -6914,11 +6932,7 @@ function createMinimalWorkerCtx(
|
|
|
6914
6932
|
if (!resolvedName) {
|
|
6915
6933
|
throw new Error('ctx.runPlay(...) requires a resolvable play name.');
|
|
6916
6934
|
}
|
|
6917
|
-
const childManifest = await
|
|
6918
|
-
req,
|
|
6919
|
-
playRef: resolvedName,
|
|
6920
|
-
cache: childManifestCache,
|
|
6921
|
-
});
|
|
6935
|
+
const childManifest = await resolveChildManifest(resolvedName);
|
|
6922
6936
|
const rowScope = workerRunPlayRowContext.getStore();
|
|
6923
6937
|
const invocationScope = buildDurableRunPlayInvocationScope({
|
|
6924
6938
|
childPlayName: resolvedName,
|
|
@@ -8928,7 +8942,7 @@ export class TenantWorkflow extends WorkflowEntrypoint<
|
|
|
8928
8942
|
try {
|
|
8929
8943
|
const response = await fetchRuntimeApi(
|
|
8930
8944
|
req.baseUrl,
|
|
8931
|
-
|
|
8945
|
+
PLAY_RUNTIME_API_CURRENT_PATH,
|
|
8932
8946
|
{
|
|
8933
8947
|
method: 'POST',
|
|
8934
8948
|
headers: {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
harnessReleaseRuntimeReceipt,
|
|
12
12
|
} from '../../../../sdk/src/plays/harness-stub';
|
|
13
13
|
import type { PreloadedRuntimeDbSessionInput } from '../../../play-harness-worker/src/rpc-types';
|
|
14
|
+
import { PLAY_RUNTIME_CONTRACT } from '../../../../shared_libs/play-runtime/runtime-contract';
|
|
14
15
|
import type { WorkerRuntimeReceiptStore } from './receipts';
|
|
15
16
|
|
|
16
17
|
const RUNTIME_TEST_FAULT_NAMES = new Set([
|
|
@@ -79,6 +80,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
79
80
|
? Math.max(0, Math.floor(input.runAttempt))
|
|
80
81
|
: null;
|
|
81
82
|
const runAttemptField = runAttempt == null ? {} : { runAttempt };
|
|
83
|
+
const runtimeContractField = { contract: PLAY_RUNTIME_CONTRACT };
|
|
82
84
|
const leaseTtlField =
|
|
83
85
|
typeof input.receiptLeaseTtlMs === 'number' &&
|
|
84
86
|
Number.isFinite(input.receiptLeaseTtlMs) &&
|
|
@@ -92,6 +94,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
92
94
|
getReceipt(command) {
|
|
93
95
|
return harnessGetRuntimeReceipt({
|
|
94
96
|
executorToken: input.executorToken,
|
|
97
|
+
...runtimeContractField,
|
|
95
98
|
orgId: input.orgId,
|
|
96
99
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
97
100
|
userEmail: input.userEmail ?? null,
|
|
@@ -104,6 +107,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
104
107
|
claimReceipt(command) {
|
|
105
108
|
return harnessClaimRuntimeReceipt({
|
|
106
109
|
executorToken: input.executorToken,
|
|
110
|
+
...runtimeContractField,
|
|
107
111
|
orgId: input.orgId,
|
|
108
112
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
109
113
|
userEmail: input.userEmail ?? null,
|
|
@@ -116,6 +120,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
116
120
|
claimReceipts(command) {
|
|
117
121
|
return harnessClaimRuntimeReceipts({
|
|
118
122
|
executorToken: input.executorToken,
|
|
123
|
+
...runtimeContractField,
|
|
119
124
|
orgId: input.orgId,
|
|
120
125
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
121
126
|
userEmail: input.userEmail ?? null,
|
|
@@ -128,6 +133,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
128
133
|
markReceiptsRunning(command) {
|
|
129
134
|
return harnessMarkRuntimeReceiptsRunning({
|
|
130
135
|
executorToken: input.executorToken,
|
|
136
|
+
...runtimeContractField,
|
|
131
137
|
orgId: input.orgId,
|
|
132
138
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
133
139
|
userEmail: input.userEmail ?? null,
|
|
@@ -143,6 +149,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
143
149
|
completeReceipt(command) {
|
|
144
150
|
return harnessCompleteRuntimeReceipt({
|
|
145
151
|
executorToken: input.executorToken,
|
|
152
|
+
...runtimeContractField,
|
|
146
153
|
orgId: input.orgId,
|
|
147
154
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
148
155
|
userEmail: input.userEmail ?? null,
|
|
@@ -154,6 +161,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
154
161
|
completeReceipts(command) {
|
|
155
162
|
return harnessCompleteRuntimeReceipts({
|
|
156
163
|
executorToken: input.executorToken,
|
|
164
|
+
...runtimeContractField,
|
|
157
165
|
orgId: input.orgId,
|
|
158
166
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
159
167
|
userEmail: input.userEmail ?? null,
|
|
@@ -168,6 +176,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
168
176
|
failReceipt(command) {
|
|
169
177
|
return harnessFailRuntimeReceipt({
|
|
170
178
|
executorToken: input.executorToken,
|
|
179
|
+
...runtimeContractField,
|
|
171
180
|
orgId: input.orgId,
|
|
172
181
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
173
182
|
userEmail: input.userEmail ?? null,
|
|
@@ -179,6 +188,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
179
188
|
releaseReceipt(command) {
|
|
180
189
|
return harnessReleaseRuntimeReceipt({
|
|
181
190
|
executorToken: input.executorToken,
|
|
191
|
+
...runtimeContractField,
|
|
182
192
|
orgId: input.orgId,
|
|
183
193
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
184
194
|
userEmail: input.userEmail ?? null,
|
|
@@ -190,6 +200,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
190
200
|
failReceipts(command) {
|
|
191
201
|
return harnessFailRuntimeReceipts({
|
|
192
202
|
executorToken: input.executorToken,
|
|
203
|
+
...runtimeContractField,
|
|
193
204
|
orgId: input.orgId,
|
|
194
205
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
195
206
|
userEmail: input.userEmail ?? null,
|
|
@@ -204,6 +215,7 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
204
215
|
heartbeatReceipts(command) {
|
|
205
216
|
return harnessHeartbeatRuntimeReceipts({
|
|
206
217
|
executorToken: input.executorToken,
|
|
218
|
+
...runtimeContractField,
|
|
207
219
|
orgId: input.orgId,
|
|
208
220
|
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
209
221
|
userEmail: input.userEmail ?? null,
|
|
@@ -19,6 +19,9 @@ function workflowRetryReasonForFailure(failure: {
|
|
|
19
19
|
code: string;
|
|
20
20
|
cause?: string;
|
|
21
21
|
}): string {
|
|
22
|
+
if (failure.code === 'PLATFORM_WORKFLOW_ENDPOINT_INTERRUPTED') {
|
|
23
|
+
return 'cloudflare_workflow_endpoint_unavailable';
|
|
24
|
+
}
|
|
22
25
|
if (failure.code === 'PLATFORM_WORKER_SUBREQUEST_INTERRUPTED') {
|
|
23
26
|
return 'cloudflare_worker_subrequest_limit';
|
|
24
27
|
}
|
|
@@ -51,6 +54,7 @@ export function decideWorkflowPlatformRetry(input: {
|
|
|
51
54
|
const failure = normalizePlayRunFailure(input.error);
|
|
52
55
|
const retryablePlatformReset =
|
|
53
56
|
failure.code === 'PLATFORM_DEPLOY_INTERRUPTED' ||
|
|
57
|
+
failure.code === 'PLATFORM_WORKFLOW_ENDPOINT_INTERRUPTED' ||
|
|
54
58
|
failure.code === 'PLATFORM_WORKER_SUBREQUEST_INTERRUPTED';
|
|
55
59
|
if (
|
|
56
60
|
retryablePlatformReset &&
|
|
@@ -1563,8 +1563,7 @@ export class DeeplineClient {
|
|
|
1563
1563
|
? { waitForCompletionMs: request.waitForCompletionMs }
|
|
1564
1564
|
: {}),
|
|
1565
1565
|
// Profile selection is the API's job, not the CLI's. The server
|
|
1566
|
-
// defaults to absurd; callers
|
|
1567
|
-
// `request.profile` explicitly.
|
|
1566
|
+
// defaults to absurd; callers normally omit this field.
|
|
1568
1567
|
...(request.profile ? { profile: request.profile } : {}),
|
|
1569
1568
|
...(integrationMode ? { integrationMode } : {}),
|
|
1570
1569
|
...(testPolicyOverrides ? { testPolicyOverrides } : {}),
|
|
@@ -106,10 +106,10 @@ export const SDK_RELEASE = {
|
|
|
106
106
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
107
107
|
// fields shipped in 0.1.153.
|
|
108
108
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
109
|
-
version: '0.1.
|
|
109
|
+
version: '0.1.216',
|
|
110
110
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
111
111
|
supportPolicy: {
|
|
112
|
-
latest: '0.1.
|
|
112
|
+
latest: '0.1.216',
|
|
113
113
|
minimumSupported: '0.1.53',
|
|
114
114
|
deprecatedBelow: '0.1.53',
|
|
115
115
|
commandMinimumSupported: [
|
|
@@ -1138,9 +1138,8 @@ export interface StartPlayRunRequest {
|
|
|
1138
1138
|
/** Optionally let the start request wait briefly and return a terminal result. */
|
|
1139
1139
|
waitForCompletionMs?: number;
|
|
1140
1140
|
/**
|
|
1141
|
-
* Per-run execution profile override. The server defaults to absurd
|
|
1142
|
-
*
|
|
1143
|
-
* this unset.
|
|
1141
|
+
* Per-run execution profile override. The server defaults to absurd. The
|
|
1142
|
+
* workers_edge profile is disabled; most callers should leave this unset.
|
|
1144
1143
|
*/
|
|
1145
1144
|
profile?: string;
|
|
1146
1145
|
/** Optional per-run provider execution mode for eval/smoke runs. */
|