deepline 0.0.1 → 0.1.1
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/README.md +324 -0
- package/dist/cli/index.js +6750 -503
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +6735 -512
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +2349 -32
- package/dist/index.d.ts +2349 -32
- package/dist/index.js +1631 -82
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1617 -83
- package/dist/index.mjs.map +1 -1
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +3256 -0
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +710 -0
- package/dist/repo/apps/play-runner-workers/src/entry.ts +5070 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/README.md +21 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +177 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +52 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +100 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +184 -0
- package/dist/repo/sdk/src/cli/commands/auth.ts +482 -0
- package/dist/repo/sdk/src/cli/commands/billing.ts +188 -0
- package/dist/repo/sdk/src/cli/commands/csv.ts +123 -0
- package/dist/repo/sdk/src/cli/commands/db.ts +119 -0
- package/dist/repo/sdk/src/cli/commands/feedback.ts +40 -0
- package/dist/repo/sdk/src/cli/commands/org.ts +117 -0
- package/dist/repo/sdk/src/cli/commands/play.ts +3200 -0
- package/dist/repo/sdk/src/cli/commands/tools.ts +687 -0
- package/dist/repo/sdk/src/cli/dataset-stats.ts +341 -0
- package/dist/repo/sdk/src/cli/index.ts +138 -0
- package/dist/repo/sdk/src/cli/progress.ts +135 -0
- package/dist/repo/sdk/src/cli/trace.ts +61 -0
- package/dist/repo/sdk/src/cli/utils.ts +145 -0
- package/dist/repo/sdk/src/client.ts +1188 -0
- package/dist/repo/sdk/src/compat.ts +77 -0
- package/dist/repo/sdk/src/config.ts +285 -0
- package/dist/repo/sdk/src/errors.ts +125 -0
- package/dist/repo/sdk/src/http.ts +391 -0
- package/dist/repo/sdk/src/index.ts +139 -0
- package/dist/repo/sdk/src/play.ts +1330 -0
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +133 -0
- package/dist/repo/sdk/src/plays/harness-stub.ts +210 -0
- package/dist/repo/sdk/src/plays/local-file-discovery.ts +326 -0
- package/dist/repo/sdk/src/tool-output.ts +489 -0
- package/dist/repo/sdk/src/types.ts +669 -0
- package/dist/repo/sdk/src/version.ts +2 -0
- package/dist/repo/sdk/src/worker-play-entry.ts +286 -0
- package/dist/repo/shared_libs/observability/node-tracing.ts +129 -0
- package/dist/repo/shared_libs/observability/tracing.ts +98 -0
- package/dist/repo/shared_libs/play-runtime/backend.ts +139 -0
- package/dist/repo/shared_libs/play-runtime/batch-runtime.ts +182 -0
- package/dist/repo/shared_libs/play-runtime/batching-types.ts +91 -0
- package/dist/repo/shared_libs/play-runtime/context.ts +3999 -0
- package/dist/repo/shared_libs/play-runtime/coordinator-headers.ts +78 -0
- package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +250 -0
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +713 -0
- package/dist/repo/shared_libs/play-runtime/dataset-id.ts +10 -0
- package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +304 -0
- package/dist/repo/shared_libs/play-runtime/db-session.ts +462 -0
- package/dist/repo/shared_libs/play-runtime/dedup-backend.ts +0 -0
- package/dist/repo/shared_libs/play-runtime/default-batch-strategies.ts +124 -0
- package/dist/repo/shared_libs/play-runtime/execution-plan.ts +262 -0
- package/dist/repo/shared_libs/play-runtime/live-events.ts +214 -0
- package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +50 -0
- package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +114 -0
- package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +158 -0
- package/dist/repo/shared_libs/play-runtime/profiles.ts +90 -0
- package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +172 -0
- package/dist/repo/shared_libs/play-runtime/protocol.ts +121 -0
- package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +42 -0
- package/dist/repo/shared_libs/play-runtime/result-normalization.ts +33 -0
- package/dist/repo/shared_libs/play-runtime/runtime-actions.ts +208 -0
- package/dist/repo/shared_libs/play-runtime/runtime-api.ts +1873 -0
- package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +2 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +201 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +48 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +84 -0
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +174 -0
- package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +147 -0
- package/dist/repo/shared_libs/play-runtime/suspension.ts +68 -0
- package/dist/repo/shared_libs/play-runtime/tool-batch-executor.ts +146 -0
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +387 -0
- package/dist/repo/shared_libs/play-runtime/tracing.ts +31 -0
- package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +75 -0
- package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +140 -0
- package/dist/repo/shared_libs/plays/artifact-transport.ts +14 -0
- package/dist/repo/shared_libs/plays/artifact-types.ts +49 -0
- package/dist/repo/shared_libs/plays/bundling/index.ts +1346 -0
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +186 -0
- package/dist/repo/shared_libs/plays/contracts.ts +51 -0
- package/dist/repo/shared_libs/plays/dataset.ts +308 -0
- package/dist/repo/shared_libs/plays/definition.ts +264 -0
- package/dist/repo/shared_libs/plays/file-refs.ts +11 -0
- package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +206 -0
- package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +164 -0
- package/dist/repo/shared_libs/plays/row-identity.ts +302 -0
- package/dist/repo/shared_libs/plays/runtime-validation.ts +415 -0
- package/dist/repo/shared_libs/plays/static-pipeline.ts +560 -0
- package/dist/repo/shared_libs/temporal/constants.ts +39 -0
- package/dist/repo/shared_libs/temporal/preview-config.ts +153 -0
- package/package.json +14 -12
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import {
|
|
2
|
+
derivePlayRowIdentity,
|
|
3
|
+
derivePlayRowIdentityFromKey,
|
|
4
|
+
} from '@shared_libs/plays/row-identity';
|
|
5
|
+
import type { MapExecutionScope } from './ctx-types';
|
|
6
|
+
|
|
7
|
+
export type ExplicitMapKeyResolver = (
|
|
8
|
+
row: Record<string, unknown>,
|
|
9
|
+
index: number,
|
|
10
|
+
) => string;
|
|
11
|
+
|
|
12
|
+
export type ExplicitMapKeyInput =
|
|
13
|
+
| string
|
|
14
|
+
| readonly string[]
|
|
15
|
+
| ((row: Record<string, unknown>, index: number) => unknown);
|
|
16
|
+
|
|
17
|
+
export function stripMapFieldOutputs(
|
|
18
|
+
row: Record<string, unknown>,
|
|
19
|
+
fieldNames: readonly string[],
|
|
20
|
+
): Record<string, unknown> {
|
|
21
|
+
return Object.fromEntries(
|
|
22
|
+
Object.entries(row).filter(
|
|
23
|
+
([fieldName]) => !fieldNames.includes(fieldName),
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createExplicitMapKeyResolver(input: {
|
|
29
|
+
mapNamespace: string;
|
|
30
|
+
fieldNames: readonly string[];
|
|
31
|
+
key: ExplicitMapKeyInput | null | undefined;
|
|
32
|
+
}): ExplicitMapKeyResolver | null {
|
|
33
|
+
if (!input.key) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const key = input.key;
|
|
37
|
+
return (row, index) => {
|
|
38
|
+
const stableRow = stripMapFieldOutputs(row, input.fieldNames);
|
|
39
|
+
let raw: unknown;
|
|
40
|
+
if (typeof key === 'function') {
|
|
41
|
+
raw = key(stableRow, index);
|
|
42
|
+
} else if (typeof key === 'string') {
|
|
43
|
+
raw = stableRow[key];
|
|
44
|
+
} else {
|
|
45
|
+
raw = key.map((fieldName) => stableRow[fieldName]);
|
|
46
|
+
}
|
|
47
|
+
if (raw === null || raw === undefined) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`ctx.map("${input.mapNamespace}") key function returned ${raw === null ? 'null' : 'undefined'} for row ${index}. ` +
|
|
50
|
+
'Use a non-empty stable input column (e.g. { key: "email" }) or return a non-empty string, number, or tuple.',
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const asString = normalizeExplicitMapKey(raw);
|
|
54
|
+
if (!asString) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`ctx.map("${input.mapNamespace}") key function returned an empty value for row ${index}. ` +
|
|
57
|
+
'Use non-empty stable input columns or return a non-empty string, number, or tuple.',
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return asString;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeExplicitMapKey(value: unknown): string {
|
|
65
|
+
if (Array.isArray(value)) {
|
|
66
|
+
const parts = value.map((entry) => normalizeExplicitMapKeyPart(entry));
|
|
67
|
+
return parts.every(Boolean) ? JSON.stringify(parts) : '';
|
|
68
|
+
}
|
|
69
|
+
return normalizeExplicitMapKeyPart(value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeExplicitMapKeyPart(value: unknown): string {
|
|
73
|
+
if (typeof value === 'number') {
|
|
74
|
+
return Number.isFinite(value) ? String(value) : '';
|
|
75
|
+
}
|
|
76
|
+
return String(value ?? '').trim();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function assertUniqueExplicitMapKeys(input: {
|
|
80
|
+
mapNamespace: string;
|
|
81
|
+
rows: readonly Record<string, unknown>[];
|
|
82
|
+
resolver: ExplicitMapKeyResolver | null;
|
|
83
|
+
}): void {
|
|
84
|
+
if (!input.resolver) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const seenKeys = new Map<string, number>();
|
|
88
|
+
for (let index = 0; index < input.rows.length; index += 1) {
|
|
89
|
+
const keyValue = input.resolver(input.rows[index]!, index);
|
|
90
|
+
const previousIndex = seenKeys.get(keyValue);
|
|
91
|
+
if (previousIndex !== undefined) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`ctx.map("${input.mapNamespace}") key function produced duplicate value "${keyValue}" for rows ${previousIndex} and ${index}. ` +
|
|
94
|
+
'Each row must produce a unique key. Combine columns (e.g. `${row.email}|${row.company}`) or pick a column that is unique per row.',
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
seenKeys.set(keyValue, index);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function deriveMapRowIdentity(input: {
|
|
102
|
+
row: Record<string, unknown>;
|
|
103
|
+
index?: number;
|
|
104
|
+
artifactTableNamespace: string;
|
|
105
|
+
fieldNames?: readonly string[];
|
|
106
|
+
explicitKey?: ExplicitMapKeyResolver | null;
|
|
107
|
+
}): string {
|
|
108
|
+
const stableRow = stripMapFieldOutputs(input.row, input.fieldNames ?? []);
|
|
109
|
+
return input.explicitKey
|
|
110
|
+
? derivePlayRowIdentityFromKey(
|
|
111
|
+
input.explicitKey(stableRow, input.index ?? 0),
|
|
112
|
+
input.artifactTableNamespace,
|
|
113
|
+
)
|
|
114
|
+
: derivePlayRowIdentity(stableRow, input.artifactTableNamespace);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export class MapRowIdentity {
|
|
118
|
+
private nextInvocationIndex: number;
|
|
119
|
+
|
|
120
|
+
constructor(startInvocationIndex = 0) {
|
|
121
|
+
this.nextInvocationIndex = startInvocationIndex;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get invocationIndex(): number {
|
|
125
|
+
return this.nextInvocationIndex;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
set invocationIndex(value: number) {
|
|
129
|
+
this.nextInvocationIndex = value;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
createScope(input: {
|
|
133
|
+
logicalNamespace: string;
|
|
134
|
+
artifactTableNamespace: string;
|
|
135
|
+
mapNodeId?: string | null;
|
|
136
|
+
fieldNames?: readonly string[];
|
|
137
|
+
explicitKey?: ExplicitMapKeyResolver | null;
|
|
138
|
+
}): MapExecutionScope {
|
|
139
|
+
const mapInvocationId = `${input.logicalNamespace}:${this.nextInvocationIndex}`;
|
|
140
|
+
this.nextInvocationIndex += 1;
|
|
141
|
+
const explicitKey = input.explicitKey ?? null;
|
|
142
|
+
const fieldNames = input.fieldNames ?? [];
|
|
143
|
+
return {
|
|
144
|
+
mapInvocationId,
|
|
145
|
+
mapNodeId: input.mapNodeId ?? null,
|
|
146
|
+
logicalNamespace: input.logicalNamespace,
|
|
147
|
+
artifactTableNamespace: input.artifactTableNamespace,
|
|
148
|
+
rowIdentity: (row, index) =>
|
|
149
|
+
deriveMapRowIdentity({
|
|
150
|
+
row,
|
|
151
|
+
index,
|
|
152
|
+
artifactTableNamespace: input.artifactTableNamespace,
|
|
153
|
+
fieldNames,
|
|
154
|
+
explicitKey,
|
|
155
|
+
}),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution profile = the (scheduler, runner, dedup) tuple selected for a run.
|
|
3
|
+
*
|
|
4
|
+
* Profiles let us hot-swap whole architectures for benchmarking and migration:
|
|
5
|
+
* - `workers_edge` — Full-CF: Dynamic Workers + Cloudflare Workflows + DO dedup (default)
|
|
6
|
+
* - `legacy` — Daytona + Temporal + in-memory dedup (deprecated; kept for benchmarking)
|
|
7
|
+
* - `local` — Local subprocess + in-process scheduler (for tests)
|
|
8
|
+
*
|
|
9
|
+
* Selection: the API hardcodes `workers_edge` as the default. Per-run
|
|
10
|
+
* overrides happen via the `profile` field on POST /api/v2/plays/run
|
|
11
|
+
* — never via env vars. The CLI does not decide profiles.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { PLAY_RUNTIME_BACKENDS, type PlayRuntimeBackendId } from './backend';
|
|
15
|
+
import {
|
|
16
|
+
PLAY_SCHEDULER_BACKENDS,
|
|
17
|
+
type PlaySchedulerBackendId,
|
|
18
|
+
} from './scheduler-backend';
|
|
19
|
+
import {
|
|
20
|
+
PLAY_DEDUP_BACKENDS,
|
|
21
|
+
type PlayDedupBackendId,
|
|
22
|
+
} from './dedup-backend';
|
|
23
|
+
|
|
24
|
+
export type PlayExecutionProfile = {
|
|
25
|
+
id: PlayExecutionProfileId;
|
|
26
|
+
scheduler: PlaySchedulerBackendId;
|
|
27
|
+
runner: PlayRuntimeBackendId;
|
|
28
|
+
dedup: PlayDedupBackendId;
|
|
29
|
+
label: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const PLAY_EXECUTION_PROFILE_IDS = {
|
|
33
|
+
legacy: 'legacy',
|
|
34
|
+
workersEdge: 'workers_edge',
|
|
35
|
+
local: 'local',
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
export type PlayExecutionProfileId =
|
|
39
|
+
(typeof PLAY_EXECUTION_PROFILE_IDS)[keyof typeof PLAY_EXECUTION_PROFILE_IDS];
|
|
40
|
+
|
|
41
|
+
export const PLAY_EXECUTION_PROFILES: Record<
|
|
42
|
+
PlayExecutionProfileId,
|
|
43
|
+
PlayExecutionProfile
|
|
44
|
+
> = {
|
|
45
|
+
legacy: {
|
|
46
|
+
id: 'legacy',
|
|
47
|
+
scheduler: PLAY_SCHEDULER_BACKENDS.temporal,
|
|
48
|
+
runner: PLAY_RUNTIME_BACKENDS.daytona,
|
|
49
|
+
dedup: PLAY_DEDUP_BACKENDS.inMemory,
|
|
50
|
+
label: 'Daytona + Temporal (production today)',
|
|
51
|
+
},
|
|
52
|
+
workers_edge: {
|
|
53
|
+
id: 'workers_edge',
|
|
54
|
+
scheduler: PLAY_SCHEDULER_BACKENDS.cfWorkflows,
|
|
55
|
+
runner: PLAY_RUNTIME_BACKENDS.cloudflareWorkers,
|
|
56
|
+
dedup: PLAY_DEDUP_BACKENDS.durableObject,
|
|
57
|
+
label: 'Cloudflare Dynamic Workflows + Dynamic Workers + DO dedup',
|
|
58
|
+
},
|
|
59
|
+
local: {
|
|
60
|
+
id: 'local',
|
|
61
|
+
scheduler: PLAY_SCHEDULER_BACKENDS.temporal,
|
|
62
|
+
runner: PLAY_RUNTIME_BACKENDS.localProcess,
|
|
63
|
+
dedup: PLAY_DEDUP_BACKENDS.inMemory,
|
|
64
|
+
label: 'Local Temporal scheduler + local subprocess runner (tests)',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export function defaultExecutionProfile(): PlayExecutionProfile {
|
|
69
|
+
// Hardcoded. The full-Cloudflare path is the only production execution
|
|
70
|
+
// story — Dynamic Workflows + Dynamic Workers + DO dedup. legacy/local
|
|
71
|
+
// remain selectable via the per-run `profile` body field on
|
|
72
|
+
// POST /api/v2/plays/run for benchmarking and tests; do NOT reintroduce
|
|
73
|
+
// env-var-based switching here.
|
|
74
|
+
return PLAY_EXECUTION_PROFILES.workers_edge;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function resolveExecutionProfile(
|
|
78
|
+
override?: string | null,
|
|
79
|
+
): PlayExecutionProfile {
|
|
80
|
+
if (override?.trim()) {
|
|
81
|
+
const id = override.trim();
|
|
82
|
+
if (id in PLAY_EXECUTION_PROFILES) {
|
|
83
|
+
return PLAY_EXECUTION_PROFILES[id as PlayExecutionProfileId];
|
|
84
|
+
}
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Unknown execution profile "${id}". Expected one of: ${Object.keys(PLAY_EXECUTION_PROFILES).join(', ')}.`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return defaultExecutionProfile();
|
|
90
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { sqlSafePlayColumnName } from '@shared_libs/plays/static-pipeline';
|
|
2
|
+
import type {
|
|
3
|
+
InlineWaterfallSpec,
|
|
4
|
+
MapExecutionScope,
|
|
5
|
+
PlayExecutionEvent,
|
|
6
|
+
PlayRowUpdate,
|
|
7
|
+
} from './ctx-types';
|
|
8
|
+
|
|
9
|
+
type RowContextSnapshot = {
|
|
10
|
+
fieldName?: string;
|
|
11
|
+
mapScope?: MapExecutionScope;
|
|
12
|
+
} | null;
|
|
13
|
+
|
|
14
|
+
type CellStatus =
|
|
15
|
+
| 'queued'
|
|
16
|
+
| 'running'
|
|
17
|
+
| 'completed'
|
|
18
|
+
| 'failed'
|
|
19
|
+
| 'cached'
|
|
20
|
+
| 'missed'
|
|
21
|
+
| 'skipped';
|
|
22
|
+
|
|
23
|
+
export class PlayProgressEmitter {
|
|
24
|
+
constructor(
|
|
25
|
+
private readonly onRowUpdate:
|
|
26
|
+
| ((update: PlayRowUpdate) => void | Promise<void>)
|
|
27
|
+
| undefined,
|
|
28
|
+
private readonly onExecutionEvent:
|
|
29
|
+
| ((event: PlayExecutionEvent) => void | Promise<void>)
|
|
30
|
+
| undefined,
|
|
31
|
+
private readonly getRowContext: () => RowContextSnapshot,
|
|
32
|
+
private readonly isInlineWaterfallToolStep: (
|
|
33
|
+
step: InlineWaterfallSpec['steps'][number],
|
|
34
|
+
) => boolean,
|
|
35
|
+
) {}
|
|
36
|
+
|
|
37
|
+
executionEvent(event: PlayExecutionEvent): void {
|
|
38
|
+
if (!this.onExecutionEvent) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
void this.onExecutionEvent(event);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
rowUpdate(
|
|
45
|
+
key: string | null,
|
|
46
|
+
tableNamespace: string | null,
|
|
47
|
+
update: Omit<PlayRowUpdate, 'key'>,
|
|
48
|
+
): void {
|
|
49
|
+
const rowContext = this.getRowContext();
|
|
50
|
+
const rowScope = rowContext?.mapScope;
|
|
51
|
+
if (rowScope && key) {
|
|
52
|
+
this.executionEvent({
|
|
53
|
+
type: 'map.row.updated',
|
|
54
|
+
mapInvocationId: rowScope.mapInvocationId,
|
|
55
|
+
mapNodeId: rowScope.mapNodeId ?? null,
|
|
56
|
+
logicalNamespace: rowScope.logicalNamespace,
|
|
57
|
+
artifactTableNamespace: rowScope.artifactTableNamespace,
|
|
58
|
+
rowKey: key,
|
|
59
|
+
rowStatus: update.status,
|
|
60
|
+
fieldName: rowContext?.fieldName ?? null,
|
|
61
|
+
stage: update.stage ?? null,
|
|
62
|
+
provider: update.provider ?? null,
|
|
63
|
+
at: Date.now(),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (!key || !this.onRowUpdate) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
void this.onRowUpdate({
|
|
70
|
+
...update,
|
|
71
|
+
key,
|
|
72
|
+
tableNamespace,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fieldMetaUpdate(input: {
|
|
77
|
+
rowId: number;
|
|
78
|
+
key: string | null;
|
|
79
|
+
tableNamespace: string | null;
|
|
80
|
+
fieldName?: string | null;
|
|
81
|
+
status: CellStatus;
|
|
82
|
+
rowStatus?: PlayRowUpdate['status'];
|
|
83
|
+
stage?: string | null;
|
|
84
|
+
provider?: string | null;
|
|
85
|
+
error?: string | null;
|
|
86
|
+
reused?: boolean;
|
|
87
|
+
dataPatch?: Record<string, unknown>;
|
|
88
|
+
}): void {
|
|
89
|
+
if (!input.fieldName) {
|
|
90
|
+
this.rowUpdate(input.key, input.tableNamespace, {
|
|
91
|
+
rowId: input.rowId,
|
|
92
|
+
status: input.rowStatus,
|
|
93
|
+
stage: input.stage ?? null,
|
|
94
|
+
provider: input.provider ?? null,
|
|
95
|
+
error: input.error ?? null,
|
|
96
|
+
dataPatch: input.dataPatch ?? {},
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.rowUpdate(input.key, input.tableNamespace, {
|
|
102
|
+
rowId: input.rowId,
|
|
103
|
+
status: input.rowStatus,
|
|
104
|
+
stage: input.stage ?? null,
|
|
105
|
+
provider: input.provider ?? null,
|
|
106
|
+
error: input.error ?? null,
|
|
107
|
+
dataPatch: input.dataPatch ?? {},
|
|
108
|
+
cellMetaPatch: {
|
|
109
|
+
[input.fieldName]: {
|
|
110
|
+
status: input.status,
|
|
111
|
+
stage: input.stage ?? null,
|
|
112
|
+
provider: input.provider ?? null,
|
|
113
|
+
error: input.error ?? null,
|
|
114
|
+
...(input.reused !== undefined ? { reused: input.reused } : {}),
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
cellUpdate(input: {
|
|
121
|
+
rowId: number;
|
|
122
|
+
key: string | null;
|
|
123
|
+
tableNamespace: string | null;
|
|
124
|
+
columnName: string;
|
|
125
|
+
status: CellStatus;
|
|
126
|
+
rowStatus?: PlayRowUpdate['status'];
|
|
127
|
+
stage?: string | null;
|
|
128
|
+
provider?: string | null;
|
|
129
|
+
error?: string | null;
|
|
130
|
+
value?: unknown;
|
|
131
|
+
}): void {
|
|
132
|
+
this.rowUpdate(input.key, input.tableNamespace, {
|
|
133
|
+
rowId: input.rowId,
|
|
134
|
+
status: input.rowStatus,
|
|
135
|
+
stage: input.stage ?? null,
|
|
136
|
+
provider: input.provider ?? null,
|
|
137
|
+
error: input.error ?? null,
|
|
138
|
+
dataPatch:
|
|
139
|
+
input.value === undefined ? {} : { [input.columnName]: input.value },
|
|
140
|
+
cellMetaPatch: {
|
|
141
|
+
[input.columnName]: {
|
|
142
|
+
status: input.status,
|
|
143
|
+
stage: input.stage ?? null,
|
|
144
|
+
provider: input.provider ?? null,
|
|
145
|
+
error: input.error ?? null,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
queuedInlineWaterfallSteps(input: {
|
|
152
|
+
rowId: number;
|
|
153
|
+
key: string | null;
|
|
154
|
+
tableNamespace: string | null;
|
|
155
|
+
spec: InlineWaterfallSpec;
|
|
156
|
+
}): void {
|
|
157
|
+
for (const step of input.spec.steps) {
|
|
158
|
+
this.cellUpdate({
|
|
159
|
+
rowId: input.rowId,
|
|
160
|
+
key: input.key,
|
|
161
|
+
tableNamespace: input.tableNamespace,
|
|
162
|
+
columnName: sqlSafePlayColumnName(`${input.spec.id}.${step.id}`),
|
|
163
|
+
status: 'queued',
|
|
164
|
+
stage: step.id,
|
|
165
|
+
provider: this.isInlineWaterfallToolStep(step)
|
|
166
|
+
? (step as Extract<InlineWaterfallSpec['steps'][number], { toolId: string }>).toolId
|
|
167
|
+
: 'code',
|
|
168
|
+
value: null,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { PlayBundleArtifact } from '../plays/artifact-types';
|
|
2
|
+
import type {
|
|
3
|
+
PlayCheckpoint,
|
|
4
|
+
PlayExecutionEvent,
|
|
5
|
+
PlayRowUpdate,
|
|
6
|
+
PlayStep,
|
|
7
|
+
} from './ctx-types';
|
|
8
|
+
import type { PlayExecutionSuspension } from './suspension';
|
|
9
|
+
import type { PlayStaticPipeline } from '../plays/static-pipeline';
|
|
10
|
+
import type { PlayLiveEventSource } from './live-events';
|
|
11
|
+
import type { PreloadedRuntimeDbSession } from './db-session';
|
|
12
|
+
|
|
13
|
+
export interface PlayRunnerContextConfig {
|
|
14
|
+
executorToken?: string;
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
orgId?: string;
|
|
17
|
+
workflowId?: string;
|
|
18
|
+
playId?: string;
|
|
19
|
+
runId?: string;
|
|
20
|
+
playName?: string;
|
|
21
|
+
userEmail?: string;
|
|
22
|
+
convexUrl?: string;
|
|
23
|
+
staticPipeline?: PlayStaticPipeline | null;
|
|
24
|
+
/**
|
|
25
|
+
* Short-lived scoped Postgres sessions minted by the worker before the runner
|
|
26
|
+
* starts. They are only an initial fast path: the runner still renews through
|
|
27
|
+
* the app runtime API when a session is absent, too close to expiry, or not an
|
|
28
|
+
* exact-enough match for the requested play/table/operation scope.
|
|
29
|
+
*/
|
|
30
|
+
preloadedDbSessions?: PreloadedRuntimeDbSession[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PlayRunnerExecutionConfig {
|
|
34
|
+
artifact: PlayBundleArtifact;
|
|
35
|
+
input: Record<string, unknown>;
|
|
36
|
+
checkpoint?: PlayCheckpoint | null;
|
|
37
|
+
csvSourcePath?: string | null;
|
|
38
|
+
csvSourceUrl?: string | null;
|
|
39
|
+
csvSourceContentBase64?: string | null;
|
|
40
|
+
runtimeInputAlias?: string | null;
|
|
41
|
+
materializedFiles: Record<string, string>;
|
|
42
|
+
workspaceRoot: string;
|
|
43
|
+
context: PlayRunnerContextConfig;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type PlayRunnerLogEvent = {
|
|
47
|
+
type: 'log';
|
|
48
|
+
at: string;
|
|
49
|
+
source: PlayLiveEventSource;
|
|
50
|
+
line: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type PlayRunnerEvent =
|
|
54
|
+
| PlayRunnerLogEvent
|
|
55
|
+
| {
|
|
56
|
+
type: 'checkpoint';
|
|
57
|
+
checkpoint: PlayCheckpoint;
|
|
58
|
+
}
|
|
59
|
+
| {
|
|
60
|
+
type: 'row_update';
|
|
61
|
+
update: PlayRowUpdate;
|
|
62
|
+
}
|
|
63
|
+
| {
|
|
64
|
+
type: 'execution_event';
|
|
65
|
+
event: PlayExecutionEvent;
|
|
66
|
+
}
|
|
67
|
+
| {
|
|
68
|
+
type: 'result';
|
|
69
|
+
result: PlayRunnerResult;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export type PlayRunnerDeferredRuntimeTask = {
|
|
73
|
+
kind: 'daytona_sandbox_cleanup';
|
|
74
|
+
sandboxId: string;
|
|
75
|
+
billingStartedAt: number;
|
|
76
|
+
cpu: number;
|
|
77
|
+
memoryGiB: number;
|
|
78
|
+
diskGiB: number;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type PlayRunnerResult =
|
|
82
|
+
| {
|
|
83
|
+
status: 'completed';
|
|
84
|
+
output: unknown;
|
|
85
|
+
outputRowCount?: number;
|
|
86
|
+
logs: string[];
|
|
87
|
+
stats: Record<string, unknown>;
|
|
88
|
+
steps: PlayStep[];
|
|
89
|
+
checkpoint: PlayCheckpoint;
|
|
90
|
+
tableNamespace?: string | null;
|
|
91
|
+
totalRows?: number;
|
|
92
|
+
inserted?: number;
|
|
93
|
+
skipped?: number;
|
|
94
|
+
deferredRuntimeTasks?: PlayRunnerDeferredRuntimeTask[];
|
|
95
|
+
}
|
|
96
|
+
| {
|
|
97
|
+
status: 'suspended';
|
|
98
|
+
suspension: PlayExecutionSuspension;
|
|
99
|
+
logs: string[];
|
|
100
|
+
stats: Record<string, unknown>;
|
|
101
|
+
steps: PlayStep[];
|
|
102
|
+
checkpoint: PlayCheckpoint;
|
|
103
|
+
tableNamespace?: string | null;
|
|
104
|
+
totalRows?: number;
|
|
105
|
+
inserted?: number;
|
|
106
|
+
skipped?: number;
|
|
107
|
+
deferredRuntimeTasks?: PlayRunnerDeferredRuntimeTask[];
|
|
108
|
+
}
|
|
109
|
+
| {
|
|
110
|
+
status: 'failed';
|
|
111
|
+
error: string;
|
|
112
|
+
logs: string[];
|
|
113
|
+
stats: Record<string, unknown>;
|
|
114
|
+
steps: PlayStep[];
|
|
115
|
+
checkpoint?: PlayCheckpoint | null;
|
|
116
|
+
tableNamespace?: string | null;
|
|
117
|
+
totalRows?: number;
|
|
118
|
+
inserted?: number;
|
|
119
|
+
skipped?: number;
|
|
120
|
+
deferredRuntimeTasks?: PlayRunnerDeferredRuntimeTask[];
|
|
121
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type PublicPlayRunStatus =
|
|
2
|
+
| 'queued'
|
|
3
|
+
| 'running'
|
|
4
|
+
| 'waiting'
|
|
5
|
+
| 'completed'
|
|
6
|
+
| 'failed'
|
|
7
|
+
| 'cancelled';
|
|
8
|
+
|
|
9
|
+
export interface PublicPlayRunResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
runId: string;
|
|
12
|
+
result?: unknown;
|
|
13
|
+
logs: string[];
|
|
14
|
+
durationMs: number;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PublicPlayProgressStatus {
|
|
19
|
+
status: string;
|
|
20
|
+
totalRows?: number;
|
|
21
|
+
logs: string[];
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface PublicPlayWaitState {
|
|
26
|
+
kind: 'integration_event' | 'sleep';
|
|
27
|
+
boundaryId?: string;
|
|
28
|
+
eventKey?: string;
|
|
29
|
+
until?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PublicPlayStatus {
|
|
33
|
+
runId: string;
|
|
34
|
+
apiVersion?: number;
|
|
35
|
+
status: PublicPlayRunStatus;
|
|
36
|
+
error?: string;
|
|
37
|
+
progress?: PublicPlayProgressStatus;
|
|
38
|
+
result?: unknown;
|
|
39
|
+
resultView?: unknown;
|
|
40
|
+
contract?: Record<string, unknown> | null;
|
|
41
|
+
wait?: PublicPlayWaitState | null;
|
|
42
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
2
|
+
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Normalize integration responses into the higher-level SDK/play shape.
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* - `{ data: {...} }` => `{...}`
|
|
10
|
+
* - `{ provider, operation, result: { data: {...}, meta } }`
|
|
11
|
+
* => `{ provider, operation, result: {...} }`
|
|
12
|
+
*/
|
|
13
|
+
export function normalizePlayToolResult(value: unknown): unknown {
|
|
14
|
+
if (!isRecord(value)) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if ('data' in value) {
|
|
19
|
+
return normalizePlayToolResult(value.data);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if ('result' in value) {
|
|
23
|
+
const normalizedResult = normalizePlayToolResult(value.result);
|
|
24
|
+
if (normalizedResult !== value.result) {
|
|
25
|
+
return {
|
|
26
|
+
...value,
|
|
27
|
+
result: normalizedResult,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return value;
|
|
33
|
+
}
|