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,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workers-only Deepline SDK entry.
|
|
3
|
+
*
|
|
4
|
+
* The normal `deepline` package entry exports the REST client, config loading,
|
|
5
|
+
* file writers, and CLI-oriented helpers. Per-graphHash Workers only need the
|
|
6
|
+
* tiny play-definition DSL at runtime. The esm_workers bundler aliases
|
|
7
|
+
* `import { definePlay } from "deepline"` to this file so user plays do not
|
|
8
|
+
* compile the full SDK client into every dynamic Worker isolate.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
ConditionalStepResolver,
|
|
13
|
+
DefinePlayConfig,
|
|
14
|
+
DeeplineNamedPlay,
|
|
15
|
+
DeeplinePlayRuntimeContext,
|
|
16
|
+
DefinedPlay,
|
|
17
|
+
PlayBindings,
|
|
18
|
+
PlayInputContract,
|
|
19
|
+
PlayReturnObject,
|
|
20
|
+
PlayStepProgramStep,
|
|
21
|
+
StepProgram,
|
|
22
|
+
StepProgramResolver,
|
|
23
|
+
StepResolver,
|
|
24
|
+
} from './play.js';
|
|
25
|
+
|
|
26
|
+
export type {
|
|
27
|
+
ConditionalStepResolver,
|
|
28
|
+
DefinePlayConfig,
|
|
29
|
+
DeeplineNamedPlay,
|
|
30
|
+
DeeplinePlayRuntimeContext,
|
|
31
|
+
DefinedPlay,
|
|
32
|
+
MapStepBuilder,
|
|
33
|
+
MapStepResolver,
|
|
34
|
+
PlayBindings,
|
|
35
|
+
PlayDataset,
|
|
36
|
+
PlayDatasetInput,
|
|
37
|
+
PlayInputContract,
|
|
38
|
+
PlayJob,
|
|
39
|
+
PlayReturnObject,
|
|
40
|
+
PlayStepProgramStep,
|
|
41
|
+
PrebuiltPlayRef,
|
|
42
|
+
StepProgram,
|
|
43
|
+
StepProgramResolver,
|
|
44
|
+
StepResolver,
|
|
45
|
+
ToolExecuteResult,
|
|
46
|
+
} from './play.js';
|
|
47
|
+
|
|
48
|
+
const PLAY_METADATA_SYMBOL = Symbol.for('deepline.play.metadata');
|
|
49
|
+
|
|
50
|
+
type PlayMetadata = {
|
|
51
|
+
name: string;
|
|
52
|
+
bindings?: PlayBindings;
|
|
53
|
+
inputSchema?: Record<string, unknown>;
|
|
54
|
+
billing?: PlayBindings['billing'];
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
class WorkerConditionalStepResolver<Row, Value, ElseValue>
|
|
58
|
+
implements ConditionalStepResolver<Row, Value, ElseValue>
|
|
59
|
+
{
|
|
60
|
+
readonly kind = 'conditional' as const;
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
readonly when: (row: Row, index: number) => boolean | Promise<boolean>,
|
|
64
|
+
readonly run: StepResolver<Row, Value>,
|
|
65
|
+
readonly elseValue: ElseValue,
|
|
66
|
+
) {}
|
|
67
|
+
|
|
68
|
+
else<ValueElse>(
|
|
69
|
+
value: ValueElse,
|
|
70
|
+
): ConditionalStepResolver<Row, Value, ValueElse> {
|
|
71
|
+
return new WorkerConditionalStepResolver(this.when, this.run, value);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class WorkerStepProgram<Input, Output, ReturnValue>
|
|
76
|
+
implements StepProgram<Input, Output, ReturnValue>
|
|
77
|
+
{
|
|
78
|
+
readonly kind = 'steps' as const;
|
|
79
|
+
declare readonly __inputType?: (input: Input) => void;
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
readonly steps: readonly PlayStepProgramStep[],
|
|
83
|
+
readonly returnResolver?: StepResolver<Output, ReturnValue>,
|
|
84
|
+
) {}
|
|
85
|
+
|
|
86
|
+
step<Name extends string, Value>(
|
|
87
|
+
name: Name,
|
|
88
|
+
resolver:
|
|
89
|
+
| StepResolver<Output, Value>
|
|
90
|
+
| ConditionalStepResolver<Output, Value>
|
|
91
|
+
| StepProgramResolver<Output, Value>,
|
|
92
|
+
): StepProgram<Input, Output & Record<Name, Value>, ReturnValue> {
|
|
93
|
+
if (!name.trim()) {
|
|
94
|
+
throw new Error('steps().step(name, ...) requires a non-empty step name.');
|
|
95
|
+
}
|
|
96
|
+
return new WorkerStepProgram(
|
|
97
|
+
[
|
|
98
|
+
...this.steps,
|
|
99
|
+
{
|
|
100
|
+
name,
|
|
101
|
+
resolver: resolver as PlayStepProgramStep['resolver'],
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
this.returnResolver as StepResolver<
|
|
105
|
+
Output & Record<Name, Value>,
|
|
106
|
+
ReturnValue
|
|
107
|
+
>,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return<Value>(
|
|
112
|
+
resolver: StepResolver<Output, Value>,
|
|
113
|
+
): StepProgram<Input, Output, Value> {
|
|
114
|
+
return new WorkerStepProgram(this.steps, resolver);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function createUnavailableNamedPlayHandle<TInput, TOutput extends PlayReturnObject>(
|
|
119
|
+
name: string,
|
|
120
|
+
): DeeplineNamedPlay<TInput, TOutput> {
|
|
121
|
+
const unavailable = () => {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`definePlay("${name}") remote lifecycle methods are unavailable inside workers_edge play execution. Use ctx.runPlay(...) for runtime composition.`,
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
name,
|
|
128
|
+
get: unavailable,
|
|
129
|
+
runs: unavailable,
|
|
130
|
+
versions: unavailable,
|
|
131
|
+
publish: unavailable,
|
|
132
|
+
clearHistory: unavailable,
|
|
133
|
+
run: unavailable,
|
|
134
|
+
runSync: unavailable,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function steps<TInput>(): StepProgram<TInput, TInput, TInput> {
|
|
139
|
+
return new WorkerStepProgram<TInput, TInput, TInput>([]);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function when<Row, Value>(
|
|
143
|
+
predicate: (row: Row, index: number) => boolean | Promise<boolean>,
|
|
144
|
+
resolver: StepResolver<Row, Value>,
|
|
145
|
+
): ConditionalStepResolver<Row, Value, null> {
|
|
146
|
+
return new WorkerConditionalStepResolver(predicate, resolver, null);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function defineInput<TInput>(
|
|
150
|
+
schema: Record<string, unknown>,
|
|
151
|
+
): PlayInputContract<TInput> {
|
|
152
|
+
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
'defineInput<T>(schema) requires a JSON-schema-like object.',
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
return { schema };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function definePlay<TInput, TOutput extends PlayReturnObject>(
|
|
161
|
+
config: DefinePlayConfig<TInput, TOutput>,
|
|
162
|
+
): DefinedPlay<TInput, TOutput>;
|
|
163
|
+
export function definePlay<TInput, TOutput extends PlayReturnObject>(
|
|
164
|
+
name: string,
|
|
165
|
+
fn: (ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>,
|
|
166
|
+
bindings?: PlayBindings,
|
|
167
|
+
): DefinedPlay<TInput, TOutput>;
|
|
168
|
+
export function definePlay<TInput, TOutput extends PlayReturnObject>(
|
|
169
|
+
nameOrConfig: string | DefinePlayConfig<TInput, TOutput>,
|
|
170
|
+
maybeFn?: (
|
|
171
|
+
ctx: DeeplinePlayRuntimeContext,
|
|
172
|
+
input: TInput,
|
|
173
|
+
) => Promise<TOutput>,
|
|
174
|
+
maybeBindings?: PlayBindings,
|
|
175
|
+
): DefinedPlay<TInput, TOutput> {
|
|
176
|
+
const config =
|
|
177
|
+
typeof nameOrConfig === 'string'
|
|
178
|
+
? {
|
|
179
|
+
name: nameOrConfig,
|
|
180
|
+
fn: maybeFn,
|
|
181
|
+
bindings: maybeBindings,
|
|
182
|
+
inputSchema: undefined,
|
|
183
|
+
billing: maybeBindings?.billing,
|
|
184
|
+
}
|
|
185
|
+
: {
|
|
186
|
+
name: nameOrConfig.id,
|
|
187
|
+
fn: nameOrConfig.run,
|
|
188
|
+
bindings: nameOrConfig.bindings,
|
|
189
|
+
inputSchema: nameOrConfig.input.schema,
|
|
190
|
+
billing: nameOrConfig.billing,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const name = config.name;
|
|
194
|
+
const fn = config.fn;
|
|
195
|
+
if (typeof fn !== 'function') {
|
|
196
|
+
throw new Error('definePlay(...) requires an async run function.');
|
|
197
|
+
}
|
|
198
|
+
if (name.includes('/')) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
'definePlay(name, ...) play names cannot contain "/". Slash is reserved for qualified references like "prebuilt/example" or "self/example".',
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const normalizedName = name
|
|
204
|
+
.trim()
|
|
205
|
+
.replace(/[^a-z0-9]+/gi, '_')
|
|
206
|
+
.replace(/_+/g, '_')
|
|
207
|
+
.replace(/^_+|_+$/g, '')
|
|
208
|
+
.toLowerCase();
|
|
209
|
+
if (!normalizedName) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
'definePlay(name, ...) requires a play name with at least one letter or number. Use only letters, numbers, underscores, or hyphens.',
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
if (normalizedName.length > 63) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`definePlay("${name}", ...) is too long after normalization (${normalizedName.length}/63). Shorten the play name to 63 characters or fewer. Normalized value: "${normalizedName}".`,
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const metadata: PlayMetadata = {
|
|
221
|
+
name,
|
|
222
|
+
...(config.bindings ? { bindings: config.bindings } : {}),
|
|
223
|
+
...(config.inputSchema ? { inputSchema: config.inputSchema } : {}),
|
|
224
|
+
...(config.billing ? { billing: config.billing } : {}),
|
|
225
|
+
};
|
|
226
|
+
const play = fn as DefinedPlay<TInput, TOutput>;
|
|
227
|
+
|
|
228
|
+
Object.defineProperty(play, PLAY_METADATA_SYMBOL, {
|
|
229
|
+
value: metadata,
|
|
230
|
+
enumerable: false,
|
|
231
|
+
configurable: false,
|
|
232
|
+
writable: false,
|
|
233
|
+
});
|
|
234
|
+
Object.defineProperty(play, 'playName', {
|
|
235
|
+
value: name,
|
|
236
|
+
enumerable: true,
|
|
237
|
+
configurable: false,
|
|
238
|
+
writable: false,
|
|
239
|
+
});
|
|
240
|
+
Object.defineProperty(play, 'bindings', {
|
|
241
|
+
value: config.bindings,
|
|
242
|
+
enumerable: true,
|
|
243
|
+
configurable: false,
|
|
244
|
+
writable: false,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const handle = createUnavailableNamedPlayHandle<TInput, TOutput>(name);
|
|
248
|
+
for (const key of [
|
|
249
|
+
'name',
|
|
250
|
+
'get',
|
|
251
|
+
'runs',
|
|
252
|
+
'versions',
|
|
253
|
+
'publish',
|
|
254
|
+
'clearHistory',
|
|
255
|
+
'run',
|
|
256
|
+
'runSync',
|
|
257
|
+
] as const) {
|
|
258
|
+
Object.defineProperty(play, key, {
|
|
259
|
+
value: handle[key],
|
|
260
|
+
enumerable: false,
|
|
261
|
+
configurable: false,
|
|
262
|
+
writable: false,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return play;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export const defineWorkflow = definePlay;
|
|
270
|
+
|
|
271
|
+
export function getDefinedPlayMetadata(value: unknown): PlayMetadata | null {
|
|
272
|
+
if (typeof value !== 'function') {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
const metadata = (value as unknown as Record<PropertyKey, unknown>)[
|
|
276
|
+
PLAY_METADATA_SYMBOL
|
|
277
|
+
];
|
|
278
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
const candidate = metadata as PlayMetadata;
|
|
282
|
+
if (!candidate.name || typeof candidate.name !== 'string') {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
return candidate;
|
|
286
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { mkdir, appendFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import type { NodeSDK } from '@opentelemetry/sdk-node';
|
|
4
|
+
import type {
|
|
5
|
+
ReadableSpan,
|
|
6
|
+
SpanExporter,
|
|
7
|
+
} from '@opentelemetry/sdk-trace-base';
|
|
8
|
+
import type { ExportResult } from '@opentelemetry/core';
|
|
9
|
+
|
|
10
|
+
const EXPORT_RESULT_SUCCESS = 0;
|
|
11
|
+
const EXPORT_RESULT_FAILED = 1;
|
|
12
|
+
|
|
13
|
+
declare global {
|
|
14
|
+
var __deeplineTracingInitPromise: Promise<boolean> | undefined;
|
|
15
|
+
var __deeplineTracingSdk: NodeSDK | undefined;
|
|
16
|
+
var __deeplineTracingShutdownPromise: Promise<void> | undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class JsonlFileSpanExporter implements SpanExporter {
|
|
20
|
+
constructor(private readonly filePath: string) {}
|
|
21
|
+
|
|
22
|
+
async export(
|
|
23
|
+
spans: ReadableSpan[],
|
|
24
|
+
resultCallback: (result: ExportResult) => void,
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
try {
|
|
27
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
28
|
+
const lines = spans
|
|
29
|
+
.map((span) =>
|
|
30
|
+
JSON.stringify({
|
|
31
|
+
traceId: span.spanContext().traceId,
|
|
32
|
+
spanId: span.spanContext().spanId,
|
|
33
|
+
parentSpanId: span.parentSpanContext?.spanId ?? null,
|
|
34
|
+
name: span.name,
|
|
35
|
+
kind: span.kind,
|
|
36
|
+
startTime: span.startTime,
|
|
37
|
+
endTime: span.endTime,
|
|
38
|
+
attributes: span.attributes,
|
|
39
|
+
status: span.status,
|
|
40
|
+
resource: span.resource.attributes,
|
|
41
|
+
}),
|
|
42
|
+
)
|
|
43
|
+
.join('\n');
|
|
44
|
+
if (lines.length > 0) {
|
|
45
|
+
await appendFile(this.filePath, `${lines}\n`, 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
resultCallback({ code: EXPORT_RESULT_SUCCESS });
|
|
48
|
+
} catch (error) {
|
|
49
|
+
resultCallback({
|
|
50
|
+
code: EXPORT_RESULT_FAILED,
|
|
51
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async shutdown(): Promise<void> {}
|
|
57
|
+
async forceFlush(): Promise<void> {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function resolveTraceExporters(): Promise<SpanExporter[]> {
|
|
61
|
+
const exporters: SpanExporter[] = [];
|
|
62
|
+
const otlpEndpoint =
|
|
63
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT?.trim() ||
|
|
64
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim() ||
|
|
65
|
+
'';
|
|
66
|
+
if (otlpEndpoint) {
|
|
67
|
+
const { OTLPTraceExporter } = await import(
|
|
68
|
+
'@opentelemetry/exporter-trace-otlp-http'
|
|
69
|
+
);
|
|
70
|
+
exporters.push(new OTLPTraceExporter());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const traceFilePath = process.env.DEEPLINE_TRACE_FILE?.trim() || '';
|
|
74
|
+
if (traceFilePath) {
|
|
75
|
+
exporters.push(new JsonlFileSpanExporter(traceFilePath));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return exporters;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function startNodeTracing(serviceName: string): Promise<boolean> {
|
|
82
|
+
const exporters = await resolveTraceExporters();
|
|
83
|
+
if (exporters.length === 0) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!process.env.OTEL_SERVICE_NAME) {
|
|
88
|
+
process.env.OTEL_SERVICE_NAME = serviceName;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const [{ NodeSDK }, { BatchSpanProcessor }] = await Promise.all([
|
|
92
|
+
import('@opentelemetry/sdk-node'),
|
|
93
|
+
import('@opentelemetry/sdk-trace-base'),
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
const sdk = new NodeSDK({
|
|
97
|
+
instrumentations: [],
|
|
98
|
+
spanProcessors: exporters.map(
|
|
99
|
+
(exporter) => new BatchSpanProcessor(exporter),
|
|
100
|
+
),
|
|
101
|
+
});
|
|
102
|
+
await sdk.start();
|
|
103
|
+
globalThis.__deeplineTracingSdk = sdk;
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function ensureNodeTracing(serviceName: string): Promise<boolean> {
|
|
108
|
+
globalThis.__deeplineTracingInitPromise ??= startNodeTracing(serviceName);
|
|
109
|
+
return await globalThis.__deeplineTracingInitPromise;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function shutdownNodeTracing(): Promise<void> {
|
|
113
|
+
const initialized = await (globalThis.__deeplineTracingInitPromise ??
|
|
114
|
+
Promise.resolve(false));
|
|
115
|
+
if (!initialized) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const sdk = globalThis.__deeplineTracingSdk;
|
|
120
|
+
if (!sdk) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
globalThis.__deeplineTracingShutdownPromise ??= sdk.shutdown().finally(() => {
|
|
125
|
+
globalThis.__deeplineTracingSdk = undefined;
|
|
126
|
+
globalThis.__deeplineTracingInitPromise = undefined;
|
|
127
|
+
});
|
|
128
|
+
await globalThis.__deeplineTracingShutdownPromise;
|
|
129
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SpanKind,
|
|
3
|
+
SpanStatusCode,
|
|
4
|
+
trace,
|
|
5
|
+
type Attributes,
|
|
6
|
+
type Span,
|
|
7
|
+
} from '@opentelemetry/api';
|
|
8
|
+
|
|
9
|
+
type PrimitiveAttribute =
|
|
10
|
+
| string
|
|
11
|
+
| number
|
|
12
|
+
| boolean
|
|
13
|
+
| null
|
|
14
|
+
| undefined;
|
|
15
|
+
|
|
16
|
+
type TraceAttributes = Record<string, PrimitiveAttribute>;
|
|
17
|
+
|
|
18
|
+
function normalizeAttributes(
|
|
19
|
+
attributes: TraceAttributes | undefined,
|
|
20
|
+
): Attributes | undefined {
|
|
21
|
+
if (!attributes) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const normalized: Attributes = {};
|
|
26
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
27
|
+
if (value === undefined || value === null) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (
|
|
31
|
+
typeof value === 'string' ||
|
|
32
|
+
typeof value === 'number' ||
|
|
33
|
+
typeof value === 'boolean'
|
|
34
|
+
) {
|
|
35
|
+
normalized[key] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function recordError(span: Span, error: unknown): void {
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
span.recordException(error);
|
|
44
|
+
span.setStatus({
|
|
45
|
+
code: SpanStatusCode.ERROR,
|
|
46
|
+
message: error.message,
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const message = String(error);
|
|
52
|
+
span.recordException({ name: 'Error', message });
|
|
53
|
+
span.setStatus({
|
|
54
|
+
code: SpanStatusCode.ERROR,
|
|
55
|
+
message,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function withActiveSpan<T>(
|
|
60
|
+
name: string,
|
|
61
|
+
options: {
|
|
62
|
+
tracer?: string;
|
|
63
|
+
kind?: SpanKind;
|
|
64
|
+
attributes?: TraceAttributes;
|
|
65
|
+
},
|
|
66
|
+
fn: (span: Span) => Promise<T> | T,
|
|
67
|
+
): Promise<T> {
|
|
68
|
+
const tracer = trace.getTracer(options.tracer ?? 'deepline');
|
|
69
|
+
return await tracer.startActiveSpan(
|
|
70
|
+
name,
|
|
71
|
+
{
|
|
72
|
+
kind: options.kind,
|
|
73
|
+
attributes: normalizeAttributes(options.attributes),
|
|
74
|
+
},
|
|
75
|
+
async (span) => {
|
|
76
|
+
try {
|
|
77
|
+
const result = await fn(span);
|
|
78
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
79
|
+
return result;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
recordError(span, error);
|
|
82
|
+
throw error;
|
|
83
|
+
} finally {
|
|
84
|
+
span.end();
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function setSpanAttributes(
|
|
91
|
+
span: Span,
|
|
92
|
+
attributes: TraceAttributes,
|
|
93
|
+
): void {
|
|
94
|
+
const normalized = normalizeAttributes(attributes);
|
|
95
|
+
if (normalized) {
|
|
96
|
+
span.setAttributes(normalized);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
export const PLAY_RUNTIME_BACKENDS = {
|
|
2
|
+
localProcess: 'local_process',
|
|
3
|
+
daytona: 'daytona',
|
|
4
|
+
// Artifact/runtime contract for Cloudflare Dynamic Workers. This is no
|
|
5
|
+
// longer a standalone runner backend; use profile=workers_edge so the
|
|
6
|
+
// cf-workflows scheduler owns loading and execution.
|
|
7
|
+
cloudflareWorkers: 'cloudflare_workers',
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export type PlayRuntimeBackendId =
|
|
11
|
+
(typeof PLAY_RUNTIME_BACKENDS)[keyof typeof PLAY_RUNTIME_BACKENDS];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The runtime shape of a built play artifact. Different runner backends need
|
|
15
|
+
* different bundle outputs:
|
|
16
|
+
* - cjs_node20: Daytona + local-process (full Node.js sandbox).
|
|
17
|
+
* - esm_workers: Cloudflare Workers (V8 isolate, no `fs`/`child_process`).
|
|
18
|
+
*
|
|
19
|
+
* The CLI selects which kind to build based on the target backend; the worker
|
|
20
|
+
* validates the kind matches the backend it's about to dispatch to.
|
|
21
|
+
*/
|
|
22
|
+
export const PLAY_ARTIFACT_KINDS = {
|
|
23
|
+
cjsNode20: 'cjs_node20',
|
|
24
|
+
esmWorkers: 'esm_workers',
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
export type PlayArtifactKind =
|
|
28
|
+
(typeof PLAY_ARTIFACT_KINDS)[keyof typeof PLAY_ARTIFACT_KINDS];
|
|
29
|
+
|
|
30
|
+
export type PlayBackendDescriptor = {
|
|
31
|
+
id: PlayRuntimeBackendId;
|
|
32
|
+
/** What artifact kind this backend's runner consumes. */
|
|
33
|
+
artifactKind: PlayArtifactKind;
|
|
34
|
+
/** Human label for logs. */
|
|
35
|
+
label: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const PLAY_BACKEND_DESCRIPTORS: Record<
|
|
39
|
+
PlayRuntimeBackendId,
|
|
40
|
+
PlayBackendDescriptor
|
|
41
|
+
> = {
|
|
42
|
+
[PLAY_RUNTIME_BACKENDS.localProcess]: {
|
|
43
|
+
id: PLAY_RUNTIME_BACKENDS.localProcess,
|
|
44
|
+
artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
|
|
45
|
+
label: 'Local node subprocess',
|
|
46
|
+
},
|
|
47
|
+
[PLAY_RUNTIME_BACKENDS.daytona]: {
|
|
48
|
+
id: PLAY_RUNTIME_BACKENDS.daytona,
|
|
49
|
+
artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
|
|
50
|
+
label: 'Daytona sandbox',
|
|
51
|
+
},
|
|
52
|
+
[PLAY_RUNTIME_BACKENDS.cloudflareWorkers]: {
|
|
53
|
+
id: PLAY_RUNTIME_BACKENDS.cloudflareWorkers,
|
|
54
|
+
artifactKind: PLAY_ARTIFACT_KINDS.esmWorkers,
|
|
55
|
+
label: 'Cloudflare Dynamic Workers',
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export function describePlayBackend(
|
|
60
|
+
id: PlayRuntimeBackendId,
|
|
61
|
+
): PlayBackendDescriptor {
|
|
62
|
+
return PLAY_BACKEND_DESCRIPTORS[id];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function artifactKindForBackend(
|
|
66
|
+
id: PlayRuntimeBackendId,
|
|
67
|
+
): PlayArtifactKind {
|
|
68
|
+
return PLAY_BACKEND_DESCRIPTORS[id].artifactKind;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function normalizePlayRuntimeBackend(
|
|
72
|
+
value?: string | null,
|
|
73
|
+
): PlayRuntimeBackendId {
|
|
74
|
+
const normalized = value?.trim().toLowerCase();
|
|
75
|
+
if (!normalized) {
|
|
76
|
+
return defaultPlayRuntimeBackend();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (normalized === PLAY_RUNTIME_BACKENDS.daytona) {
|
|
80
|
+
return PLAY_RUNTIME_BACKENDS.daytona;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (normalized === PLAY_RUNTIME_BACKENDS.localProcess) {
|
|
84
|
+
return PLAY_RUNTIME_BACKENDS.localProcess;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (normalized === PLAY_RUNTIME_BACKENDS.cloudflareWorkers) {
|
|
88
|
+
return PLAY_RUNTIME_BACKENDS.cloudflareWorkers;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Unsupported play runtime backend "${normalized}". Expected one of: ${Object.values(
|
|
93
|
+
PLAY_RUNTIME_BACKENDS,
|
|
94
|
+
).join(', ')}.`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function defaultPlayRuntimeBackend(input?: {
|
|
99
|
+
deeplineEnv?: string | null;
|
|
100
|
+
vercelEnv?: string | null;
|
|
101
|
+
nodeEnv?: string | null;
|
|
102
|
+
previewPlaysDeployment?: boolean;
|
|
103
|
+
}): PlayRuntimeBackendId {
|
|
104
|
+
if (input?.previewPlaysDeployment) {
|
|
105
|
+
return PLAY_RUNTIME_BACKENDS.daytona;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const candidates = [
|
|
109
|
+
input?.deeplineEnv ?? process.env.DEEPLINE_ENV,
|
|
110
|
+
input?.vercelEnv ?? process.env.VERCEL_ENV,
|
|
111
|
+
input?.nodeEnv ?? process.env.NODE_ENV,
|
|
112
|
+
]
|
|
113
|
+
.filter(Boolean)
|
|
114
|
+
.map((value) => String(value).trim().toLowerCase());
|
|
115
|
+
|
|
116
|
+
for (const value of candidates) {
|
|
117
|
+
if (
|
|
118
|
+
value === 'production' ||
|
|
119
|
+
value === 'prod' ||
|
|
120
|
+
value === 'live' ||
|
|
121
|
+
value === 'preview' ||
|
|
122
|
+
value === 'staging' ||
|
|
123
|
+
value === 'stage'
|
|
124
|
+
) {
|
|
125
|
+
return PLAY_RUNTIME_BACKENDS.daytona;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return PLAY_RUNTIME_BACKENDS.localProcess;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function isPlayRuntimeBackendId(
|
|
133
|
+
value: unknown,
|
|
134
|
+
): value is PlayRuntimeBackendId {
|
|
135
|
+
return (
|
|
136
|
+
typeof value === 'string' &&
|
|
137
|
+
Object.values(PLAY_RUNTIME_BACKENDS).includes(value as PlayRuntimeBackendId)
|
|
138
|
+
);
|
|
139
|
+
}
|