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,1330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Play definition, execution, and lifecycle management.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the high-level API for defining and running plays —
|
|
5
|
+
* composable TypeScript workflows that orchestrate tool calls into repeatable
|
|
6
|
+
* pipelines. Plays run on Temporal for durable execution with automatic retries,
|
|
7
|
+
* timeouts, and full observability.
|
|
8
|
+
*
|
|
9
|
+
* ## Core concepts
|
|
10
|
+
*
|
|
11
|
+
* - **{@link definePlay}** — Define a play as a typed async function. The returned
|
|
12
|
+
* value is both callable (for server-side execution) and has methods for
|
|
13
|
+
* remote lifecycle management (run, get, publish, tail).
|
|
14
|
+
*
|
|
15
|
+
* - **{@link Deepline.connect}** — Create a client context for programmatic SDK
|
|
16
|
+
* usage. Returns a {@link DeeplineContext} with tool and play handles.
|
|
17
|
+
*
|
|
18
|
+
* - **{@link PlayJob}** — Handle to a running play execution. Poll for status,
|
|
19
|
+
* stream logs, wait for completion, or cancel.
|
|
20
|
+
*
|
|
21
|
+
* ## Usage patterns
|
|
22
|
+
*
|
|
23
|
+
* ### Define and run a play (file-backed)
|
|
24
|
+
*
|
|
25
|
+
* ```typescript
|
|
26
|
+
* // my-play.play.ts
|
|
27
|
+
* import { definePlay } from 'deepline';
|
|
28
|
+
*
|
|
29
|
+
* export default definePlay('my-play', async (ctx, input: { domain: string }) => {
|
|
30
|
+
* ctx.log(`Looking up ${input.domain}`);
|
|
31
|
+
* const company = await ctx.tools.execute({
|
|
32
|
+
* id: 'company_search',
|
|
33
|
+
* tool: 'test_company_search',
|
|
34
|
+
* input: { domain: input.domain },
|
|
35
|
+
* description: 'Look up company details by domain.',
|
|
36
|
+
* });
|
|
37
|
+
* return { company: company.result };
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* Then run via CLI: `deepline play run --file my-play.play.ts --watch`
|
|
42
|
+
*
|
|
43
|
+
* ### Programmatic usage
|
|
44
|
+
*
|
|
45
|
+
* ```typescript
|
|
46
|
+
* import { Deepline } from 'deepline';
|
|
47
|
+
*
|
|
48
|
+
* const ctx = await Deepline.connect();
|
|
49
|
+
* const job = await ctx.play('my-play').run({ domain: 'stripe.com' });
|
|
50
|
+
* const result = await job.get(); // Polls until complete
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* ### With bindings (cron, webhook)
|
|
54
|
+
*
|
|
55
|
+
* ```typescript
|
|
56
|
+
* import { definePlay } from 'deepline';
|
|
57
|
+
*
|
|
58
|
+
* export default definePlay('daily-sync', async (ctx) => {
|
|
59
|
+
* const data = await ctx.tools.execute({
|
|
60
|
+
* id: 'crm_export',
|
|
61
|
+
* tool: 'crm_export',
|
|
62
|
+
* input: {},
|
|
63
|
+
* description: 'Export CRM records for the daily sync.',
|
|
64
|
+
* });
|
|
65
|
+
* return data;
|
|
66
|
+
* }, {
|
|
67
|
+
* cron: { schedule: '0 9 * * *', timezone: 'America/New_York' },
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @module
|
|
72
|
+
*/
|
|
73
|
+
import { DeeplineClient } from './client.js';
|
|
74
|
+
import { DeeplineError } from './errors.js';
|
|
75
|
+
import { createToolCallResult } from './tool-output.js';
|
|
76
|
+
import type { ToolCallResult } from './tool-output.js';
|
|
77
|
+
import type {
|
|
78
|
+
PlayDataset,
|
|
79
|
+
PlayDatasetInput,
|
|
80
|
+
} from '../../shared_libs/plays/dataset.js';
|
|
81
|
+
import type {
|
|
82
|
+
DeeplineClientOptions,
|
|
83
|
+
PlayDetail,
|
|
84
|
+
ClearPlayHistoryResult,
|
|
85
|
+
PlayRevisionSummary,
|
|
86
|
+
PlayRunListItem,
|
|
87
|
+
PlayStatus,
|
|
88
|
+
PublishPlayVersionResult,
|
|
89
|
+
StopPlayRunResult,
|
|
90
|
+
ToolDefinition,
|
|
91
|
+
ToolMetadata,
|
|
92
|
+
} from './types.js';
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Optional trigger bindings for a play.
|
|
96
|
+
*
|
|
97
|
+
* Plays can be triggered by webhooks (with HMAC signature verification)
|
|
98
|
+
* or cron schedules. Bindings are declared as the third argument to
|
|
99
|
+
* {@link definePlay}.
|
|
100
|
+
*
|
|
101
|
+
* @example Webhook with HMAC verification
|
|
102
|
+
* ```typescript
|
|
103
|
+
* definePlay('webhook-handler', handler, {
|
|
104
|
+
* webhook: {
|
|
105
|
+
* hmac: {
|
|
106
|
+
* algorithm: 'sha256',
|
|
107
|
+
* header: 'X-Hub-Signature-256',
|
|
108
|
+
* secretEnv: 'WEBHOOK_SECRET',
|
|
109
|
+
* },
|
|
110
|
+
* },
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* @example Cron schedule
|
|
115
|
+
* ```typescript
|
|
116
|
+
* definePlay('nightly-sync', handler, {
|
|
117
|
+
* cron: { schedule: '0 2 * * *', timezone: 'UTC' },
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export type PlayBindings = {
|
|
122
|
+
/** Optional per-run billing controls enforced by the runtime. */
|
|
123
|
+
billing?: {
|
|
124
|
+
/** Stop the run before a billed action would push total run credits above this cap. */
|
|
125
|
+
maxCreditsPerRun?: number;
|
|
126
|
+
};
|
|
127
|
+
/** Webhook trigger with optional HMAC signature verification. */
|
|
128
|
+
webhook?: {
|
|
129
|
+
hmac?: {
|
|
130
|
+
/** Hash algorithm. Currently only `'sha256'` is supported. */
|
|
131
|
+
algorithm?: 'sha256';
|
|
132
|
+
/** HTTP header containing the signature (e.g. `'X-Hub-Signature-256'`). */
|
|
133
|
+
header?: string;
|
|
134
|
+
/** Environment variable name holding the HMAC secret. */
|
|
135
|
+
secretEnv: string;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
/** Cron schedule trigger. */
|
|
139
|
+
cron?: {
|
|
140
|
+
/** Cron expression (e.g. `'0 9 * * *'` for daily at 9am). */
|
|
141
|
+
schedule: string;
|
|
142
|
+
/** IANA timezone (e.g. `'America/New_York'`). Defaults to UTC. */
|
|
143
|
+
timezone?: string;
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export type LoosePlayObject = {
|
|
148
|
+
[key: string]: LoosePlayObject;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export type ToolExecutionRequest = {
|
|
152
|
+
id: string;
|
|
153
|
+
tool: string;
|
|
154
|
+
input: Record<string, unknown>;
|
|
155
|
+
description?: string;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export type SdkToolExecutionRequest = {
|
|
159
|
+
tool: string;
|
|
160
|
+
input: Record<string, unknown>;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export type ToolExecuteResult<TResult = unknown> = {
|
|
164
|
+
status: string;
|
|
165
|
+
result: TResult;
|
|
166
|
+
_metadata: {
|
|
167
|
+
toolId: string;
|
|
168
|
+
execution: {
|
|
169
|
+
idempotent: true;
|
|
170
|
+
cached: boolean;
|
|
171
|
+
source: 'live' | 'checkpoint' | 'cache';
|
|
172
|
+
cacheKey?: string;
|
|
173
|
+
};
|
|
174
|
+
targets: Record<string, { value: unknown; path: string }>;
|
|
175
|
+
lists: Record<
|
|
176
|
+
string,
|
|
177
|
+
{
|
|
178
|
+
path: string;
|
|
179
|
+
count: number | null;
|
|
180
|
+
keys: Record<string, string>;
|
|
181
|
+
}
|
|
182
|
+
>;
|
|
183
|
+
};
|
|
184
|
+
get<T = unknown>(target: string): T | null;
|
|
185
|
+
getEmail(): string | null;
|
|
186
|
+
getPhone(): string | null;
|
|
187
|
+
getLinkedin(): string | null;
|
|
188
|
+
list<T = Record<string, unknown>>(name?: string): T[] | null;
|
|
189
|
+
listPick<const TKeys extends readonly string[]>(
|
|
190
|
+
keys: TKeys,
|
|
191
|
+
name?: string,
|
|
192
|
+
): Array<Record<TKeys[number], unknown>> | null;
|
|
193
|
+
listKeys(name?: string): Record<string, string>;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export type StepResolver<Row, Value> = (
|
|
197
|
+
row: Row,
|
|
198
|
+
ctx: DeeplinePlayRuntimeContext,
|
|
199
|
+
index: number,
|
|
200
|
+
) => Value | Promise<Value>;
|
|
201
|
+
|
|
202
|
+
export type ConditionalStepResolver<Row, Value, Else = null> = {
|
|
203
|
+
readonly kind: 'conditional';
|
|
204
|
+
readonly when: (row: Row, index: number) => boolean | Promise<boolean>;
|
|
205
|
+
readonly run: StepResolver<Row, Value>;
|
|
206
|
+
readonly elseValue: Else;
|
|
207
|
+
else<ValueElse>(
|
|
208
|
+
value: ValueElse,
|
|
209
|
+
): ConditionalStepResolver<Row, Value, ValueElse>;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export type StepProgram<Input, Output, Return = Output> = {
|
|
213
|
+
readonly kind: 'steps';
|
|
214
|
+
readonly steps: readonly PlayStepProgramStep[];
|
|
215
|
+
readonly returnResolver?: StepResolver<Output, Return>;
|
|
216
|
+
readonly __inputType?: (input: Input) => void;
|
|
217
|
+
step<Name extends string, Value>(
|
|
218
|
+
name: Name,
|
|
219
|
+
resolver:
|
|
220
|
+
| StepResolver<Output, Value>
|
|
221
|
+
| ConditionalStepResolver<Output, Value>
|
|
222
|
+
| StepProgramResolver<Output, Value>,
|
|
223
|
+
): StepProgram<Input, Output & Record<Name, Value>, Return>;
|
|
224
|
+
return<Value>(
|
|
225
|
+
resolver: StepResolver<Output, Value>,
|
|
226
|
+
): StepProgram<Input, Output, Value>;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export type StepProgramResolver<Input, Return> = {
|
|
230
|
+
readonly kind: 'steps';
|
|
231
|
+
readonly steps: readonly PlayStepProgramStep[];
|
|
232
|
+
readonly returnResolver?: StepResolver<never, Return>;
|
|
233
|
+
readonly __inputType?: (input: Input) => void;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export type PlayStepProgramStep = {
|
|
237
|
+
readonly name: string;
|
|
238
|
+
readonly resolver:
|
|
239
|
+
| StepResolver<Record<string, unknown>, unknown>
|
|
240
|
+
| ConditionalStepResolver<Record<string, unknown>, unknown>
|
|
241
|
+
| StepProgramResolver<Record<string, unknown>, unknown>;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
export type MapStepResolver<Row, Value> =
|
|
245
|
+
| StepResolver<Row, Value>
|
|
246
|
+
| ConditionalStepResolver<Row, Value>
|
|
247
|
+
| StepProgramResolver<Row, Value>;
|
|
248
|
+
|
|
249
|
+
export type MapRowKey<InputRow extends object> =
|
|
250
|
+
| (keyof InputRow & string)
|
|
251
|
+
| readonly (keyof InputRow & string)[]
|
|
252
|
+
| ((row: InputRow, index: number) => string | number | readonly unknown[]);
|
|
253
|
+
|
|
254
|
+
export type MapDefinitionOptions<InputRow extends object> = {
|
|
255
|
+
staleAfterSeconds?: number;
|
|
256
|
+
key?: MapRowKey<InputRow>;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export type MapRunOptions = {
|
|
260
|
+
description?: string;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
export type MapStepBuilder<
|
|
264
|
+
InputRow extends object,
|
|
265
|
+
OutputRow extends object,
|
|
266
|
+
> = {
|
|
267
|
+
step<Name extends string, Value>(
|
|
268
|
+
name: Name,
|
|
269
|
+
resolver: MapStepResolver<OutputRow, Value>,
|
|
270
|
+
): MapStepBuilder<InputRow, OutputRow & Record<Name, Value>>;
|
|
271
|
+
run(options?: MapRunOptions): Promise<PlayDataset<OutputRow>>;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Runtime context available inside a play function.
|
|
276
|
+
*
|
|
277
|
+
* Provides methods for calling tools, processing data, and emitting logs.
|
|
278
|
+
* This context is injected by the Temporal worker — you never construct it directly.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```typescript
|
|
282
|
+
* definePlay('example', async (ctx, input: { domain: string }) => {
|
|
283
|
+
* // Call a tool
|
|
284
|
+
* const company = await ctx.tools.execute({
|
|
285
|
+
* id: 'company_search',
|
|
286
|
+
* tool: 'test_company_search',
|
|
287
|
+
* input: { domain: input.domain },
|
|
288
|
+
* description: 'Look up company details by domain.',
|
|
289
|
+
* });
|
|
290
|
+
*
|
|
291
|
+
* // Fan-out: process items with named steps
|
|
292
|
+
* const enriched = await ctx
|
|
293
|
+
* .map('companies', [{ domain: 'a.com' }, { domain: 'b.com' }], { key: 'domain' })
|
|
294
|
+
* .step('company', (row, rowCtx) =>
|
|
295
|
+
* rowCtx.tool({
|
|
296
|
+
* id: 'company_search',
|
|
297
|
+
* tool: 'test_company_search',
|
|
298
|
+
* input: { domain: row.domain },
|
|
299
|
+
* description: 'Look up company details by domain.',
|
|
300
|
+
* }))
|
|
301
|
+
* .run({ description: 'Look up company details.' });
|
|
302
|
+
*
|
|
303
|
+
* // Load CSV data
|
|
304
|
+
* const leads = await ctx.csv('leads.csv');
|
|
305
|
+
*
|
|
306
|
+
* // Emit a log line (visible in `play tail`)
|
|
307
|
+
* ctx.log(`Loaded ${await leads.count()} leads`);
|
|
308
|
+
*
|
|
309
|
+
* // Pause execution
|
|
310
|
+
* await ctx.sleep(1000);
|
|
311
|
+
*
|
|
312
|
+
* // Access the raw input object
|
|
313
|
+
* console.log(ctx.input);
|
|
314
|
+
*
|
|
315
|
+
* return { company, enriched };
|
|
316
|
+
* });
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export interface DeeplinePlayRuntimeContext {
|
|
320
|
+
/**
|
|
321
|
+
* Load a CSV file as a dataset handle.
|
|
322
|
+
*
|
|
323
|
+
* The CSV must be staged or available at the given path. Each row becomes
|
|
324
|
+
* an object keyed by column headers.
|
|
325
|
+
*
|
|
326
|
+
* @typeParam T - Row type (defaults to `Record<string, unknown>`)
|
|
327
|
+
* @param path - Relative path to the CSV file
|
|
328
|
+
* The returned dataset supports `for await`, `peek()`, `count()`, and
|
|
329
|
+
* explicit `materialize()` for small result sets.
|
|
330
|
+
*
|
|
331
|
+
* @returns Parsed dataset rows
|
|
332
|
+
*/
|
|
333
|
+
csv<T = Record<string, unknown>>(
|
|
334
|
+
path: string,
|
|
335
|
+
options?: { description?: string },
|
|
336
|
+
): Promise<PlayDataset<T>>;
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Fan-out: process each item through one or more named columns.
|
|
340
|
+
*
|
|
341
|
+
* Each key in `columns` becomes an output column. Each value is an async
|
|
342
|
+
* callback `(row, ctx) => result` that receives the current row and the
|
|
343
|
+
* play context — call tools, run waterfalls, do arbitrary logic.
|
|
344
|
+
*
|
|
345
|
+
* Items are processed in parallel (paced by the rate-limit scheduler).
|
|
346
|
+
* The first argument identifies the logical map/table namespace. Use
|
|
347
|
+
* `options.key` to pin durable row identity to stable input fields instead of
|
|
348
|
+
* hashing the full input row.
|
|
349
|
+
* `options.staleAfterSeconds` intentionally partitions the durable cache on a
|
|
350
|
+
* relative time window. Use `86400` for daily reruns; retries inside the same
|
|
351
|
+
* window still replay safely.
|
|
352
|
+
*
|
|
353
|
+
* Returns a dataset handle containing the original rows merged with the new
|
|
354
|
+
* columns. Input may be a normal array, iterable, async iterable, or another
|
|
355
|
+
* play dataset handle.
|
|
356
|
+
*
|
|
357
|
+
* @typeParam T - Row type
|
|
358
|
+
* @param key - Logical map key used to isolate row identities (e.g. `'main_table'`, `'email_lookup'`)
|
|
359
|
+
* @param items - Input rows or dataset handle
|
|
360
|
+
* @returns Dataset of rows merged with the computed column values
|
|
361
|
+
*
|
|
362
|
+
* @example Single tool per row
|
|
363
|
+
* ```typescript
|
|
364
|
+
* const results = await ctx
|
|
365
|
+
* .map('leads', leads, { key: 'domain' })
|
|
366
|
+
* .step('company', (row, ctx) =>
|
|
367
|
+
* ctx.tools.execute({
|
|
368
|
+
* id: 'company_search',
|
|
369
|
+
* tool: 'test_company_search',
|
|
370
|
+
* input: { domain: row.domain },
|
|
371
|
+
* description: 'Look up company details by domain.',
|
|
372
|
+
* }))
|
|
373
|
+
* .run({ description: 'Look up companies.' });
|
|
374
|
+
* // [{ domain: 'stripe.com', company: { name: 'Stripe', ... } }, ...]
|
|
375
|
+
* ```
|
|
376
|
+
*
|
|
377
|
+
* @example Multiple columns with pre/post logic
|
|
378
|
+
* ```typescript
|
|
379
|
+
* const results = await ctx
|
|
380
|
+
* .map('leads', leads, { key: 'lead_id' })
|
|
381
|
+
* .step('company', (row, ctx) =>
|
|
382
|
+
* ctx.tools.execute({
|
|
383
|
+
* id: 'company_search',
|
|
384
|
+
* tool: 'test_company_search',
|
|
385
|
+
* input: { domain: row.domain },
|
|
386
|
+
* description: 'Look up company details by domain.',
|
|
387
|
+
* }))
|
|
388
|
+
* .step('score', (row) =>
|
|
389
|
+
* row.company?.employeeCount > 100 ? 'enterprise' : 'smb')
|
|
390
|
+
* .run({ description: 'Enrich leads.' });
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
map<TItem extends object>(
|
|
394
|
+
key: string,
|
|
395
|
+
items: PlayDatasetInput<TItem>,
|
|
396
|
+
options?: MapDefinitionOptions<TItem>,
|
|
397
|
+
): MapStepBuilder<TItem, TItem>;
|
|
398
|
+
|
|
399
|
+
/** Tool execution namespace. */
|
|
400
|
+
tools: {
|
|
401
|
+
/**
|
|
402
|
+
* Execute a single tool by stable durable execution id and tool ID.
|
|
403
|
+
*
|
|
404
|
+
* @param request - Tool execution request. `id` is the durable execution id;
|
|
405
|
+
* omit it in row/map steps when the surrounding step id is the execution id.
|
|
406
|
+
* @returns The tool's output
|
|
407
|
+
*/
|
|
408
|
+
execute<TOutput = LoosePlayObject>(
|
|
409
|
+
request: ToolExecutionRequest,
|
|
410
|
+
): Promise<ToolExecuteResult<TOutput>>;
|
|
411
|
+
};
|
|
412
|
+
/**
|
|
413
|
+
* Execute a single tool by stable durable execution id and tool ID.
|
|
414
|
+
*
|
|
415
|
+
* Shorthand for `ctx.tools.execute({ id, tool, input })`; this is the
|
|
416
|
+
* preferred spelling in row-level step programs.
|
|
417
|
+
*/
|
|
418
|
+
tool<TOutput = LoosePlayObject>(
|
|
419
|
+
request: ToolExecutionRequest,
|
|
420
|
+
): Promise<ToolExecuteResult<TOutput>>;
|
|
421
|
+
step<T>(id: string, run: () => T | Promise<T>): Promise<T>;
|
|
422
|
+
fetch(
|
|
423
|
+
key: string,
|
|
424
|
+
url: string | URL,
|
|
425
|
+
init?: RequestInit,
|
|
426
|
+
): Promise<{
|
|
427
|
+
ok: boolean;
|
|
428
|
+
status: number;
|
|
429
|
+
statusText: string;
|
|
430
|
+
url: string;
|
|
431
|
+
headers: Record<string, string>;
|
|
432
|
+
bodyText: string;
|
|
433
|
+
json: unknown | null;
|
|
434
|
+
}>;
|
|
435
|
+
runPlay(
|
|
436
|
+
key: string,
|
|
437
|
+
playRef: string | PlayReferenceLike,
|
|
438
|
+
input: Record<string, unknown>,
|
|
439
|
+
options: { description?: string },
|
|
440
|
+
): Promise<Record<string, unknown>>;
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Emit a log line visible in `play tail` and the play's progress logs.
|
|
444
|
+
*
|
|
445
|
+
* @param message - Log message (plain text)
|
|
446
|
+
*/
|
|
447
|
+
log(message: string): void;
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Pause play execution for the specified duration.
|
|
451
|
+
*
|
|
452
|
+
* Uses Temporal's durable timer — safe across worker restarts.
|
|
453
|
+
*
|
|
454
|
+
* @param ms - Duration in milliseconds
|
|
455
|
+
*/
|
|
456
|
+
sleep(ms: number): Promise<void>;
|
|
457
|
+
|
|
458
|
+
/** The raw input object passed when the play was started. */
|
|
459
|
+
readonly input: Record<string, unknown>;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Handle to a running play execution.
|
|
464
|
+
*
|
|
465
|
+
* Provides methods to check status, stream logs, wait for completion,
|
|
466
|
+
* or cancel the execution.
|
|
467
|
+
*
|
|
468
|
+
* This handle is the SDK-context equivalent of `deepline play run --watch` and
|
|
469
|
+
* `POST /api/v2/plays/run`: every surface returns a run id first, then exposes
|
|
470
|
+
* the completed user output through `PlayJob.get()` or the status endpoint's
|
|
471
|
+
* `result` field. Runtime logs are available from `status().progress.logs` and
|
|
472
|
+
* are intentionally separate from the returned output object.
|
|
473
|
+
*
|
|
474
|
+
* @typeParam TOutput - The play's return type
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* ```typescript
|
|
478
|
+
* const job: PlayJob = await ctx.play('my-play').run({ domain: 'stripe.com' });
|
|
479
|
+
*
|
|
480
|
+
* // Poll status
|
|
481
|
+
* const status = await job.status();
|
|
482
|
+
* console.log(status.temporalStatus); // 'RUNNING'
|
|
483
|
+
*
|
|
484
|
+
* // Stream logs until completion
|
|
485
|
+
* const finalStatus = await job.tail({
|
|
486
|
+
* onLog: (line) => console.log(`[play] ${line}`),
|
|
487
|
+
* });
|
|
488
|
+
*
|
|
489
|
+
* // Or just wait for the result
|
|
490
|
+
* const output = await job.get();
|
|
491
|
+
*
|
|
492
|
+
* // Cancel if needed
|
|
493
|
+
* await job.cancel();
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
export interface PlayJob<TOutput = unknown> {
|
|
497
|
+
/** Temporal workflow ID for this execution. */
|
|
498
|
+
id: string;
|
|
499
|
+
|
|
500
|
+
/** Get the current execution status (single poll). */
|
|
501
|
+
status(): Promise<PlayStatus>;
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Stream logs and wait for completion.
|
|
505
|
+
*
|
|
506
|
+
* Polls until the play reaches a terminal state, invoking `onLog` for
|
|
507
|
+
* each new log line. Returns the final status.
|
|
508
|
+
*
|
|
509
|
+
* @param options.intervalMs - Poll interval in ms. Default: `500`.
|
|
510
|
+
* @param options.onLog - Callback for each log line. Default: `console.log`.
|
|
511
|
+
*/
|
|
512
|
+
tail(options?: {
|
|
513
|
+
intervalMs?: number;
|
|
514
|
+
onLog?: (line: string) => void;
|
|
515
|
+
}): Promise<PlayStatus>;
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Wait for the play to complete and return its output.
|
|
519
|
+
*
|
|
520
|
+
* Polls until terminal state. Throws {@link DeeplineError} if the play
|
|
521
|
+
* fails, is cancelled, or times out.
|
|
522
|
+
*
|
|
523
|
+
* @param options.intervalMs - Poll interval in ms. Default: `500`.
|
|
524
|
+
* @returns The play's return value
|
|
525
|
+
* @throws {@link DeeplineError} if the play did not complete successfully
|
|
526
|
+
*/
|
|
527
|
+
get(options?: { intervalMs?: number }): Promise<TOutput>;
|
|
528
|
+
|
|
529
|
+
/** Cancel this play execution. */
|
|
530
|
+
cancel(): Promise<void>;
|
|
531
|
+
|
|
532
|
+
/** Deep-stop this play execution, including open HITL waits. */
|
|
533
|
+
stop(options?: { reason?: string }): Promise<StopPlayRunResult>;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Handle to a named play for remote lifecycle operations.
|
|
538
|
+
*
|
|
539
|
+
* Returned by {@link DeeplineContext.play} and attached to {@link DefinedPlay}.
|
|
540
|
+
* Provides methods to run, inspect, list runs, and publish a play by name.
|
|
541
|
+
*
|
|
542
|
+
* @typeParam TInput - The play's input type
|
|
543
|
+
* @typeParam TOutput - The play's return type
|
|
544
|
+
*
|
|
545
|
+
* @example
|
|
546
|
+
* ```typescript
|
|
547
|
+
* const ctx = await Deepline.connect();
|
|
548
|
+
* const play = ctx.play<{ domain: string }, Company>('company-lookup');
|
|
549
|
+
*
|
|
550
|
+
* // Get play definition
|
|
551
|
+
* const detail = await play.get();
|
|
552
|
+
* console.log(`Live: v${detail.play.currentPublishedVersion}`);
|
|
553
|
+
*
|
|
554
|
+
* // Run and wait
|
|
555
|
+
* const result = await play.runSync({ domain: 'stripe.com' });
|
|
556
|
+
*
|
|
557
|
+
* // Run async
|
|
558
|
+
* const job = await play.run({ domain: 'stripe.com' });
|
|
559
|
+
* const output = await job.get();
|
|
560
|
+
*
|
|
561
|
+
* // List recent runs
|
|
562
|
+
* const runs = await play.runs();
|
|
563
|
+
*
|
|
564
|
+
* // List saved versions
|
|
565
|
+
* const versions = await play.versions();
|
|
566
|
+
*
|
|
567
|
+
* // Publish the current draft
|
|
568
|
+
* await play.publish();
|
|
569
|
+
* ```
|
|
570
|
+
*/
|
|
571
|
+
export interface DeeplineNamedPlay<
|
|
572
|
+
TInput = Record<string, unknown>,
|
|
573
|
+
TOutput = unknown,
|
|
574
|
+
> {
|
|
575
|
+
/** The play's name. */
|
|
576
|
+
readonly name: string;
|
|
577
|
+
|
|
578
|
+
/** Fetch the full play definition with revision history and run stats. */
|
|
579
|
+
get(): Promise<PlayDetail>;
|
|
580
|
+
|
|
581
|
+
/** List recent runs for this play. */
|
|
582
|
+
runs(): Promise<PlayRunListItem[]>;
|
|
583
|
+
|
|
584
|
+
/** List saved versions for this play (newest first). */
|
|
585
|
+
versions(): Promise<PlayRevisionSummary[]>;
|
|
586
|
+
|
|
587
|
+
/** Publish a play revision. Defaults to the current working revision. */
|
|
588
|
+
publish(options?: { revisionId?: string }): Promise<PublishPlayVersionResult>;
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Clear run history and durable sheet/result data for this play while keeping
|
|
592
|
+
* the play definition and revisions.
|
|
593
|
+
*/
|
|
594
|
+
clearHistory(options?: {
|
|
595
|
+
tableNamespaces?: string[];
|
|
596
|
+
}): Promise<ClearPlayHistoryResult>;
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Start a new run of this play. Returns a {@link PlayJob} for monitoring.
|
|
600
|
+
*
|
|
601
|
+
* @param input - Runtime input passed to the play function
|
|
602
|
+
*/
|
|
603
|
+
run(
|
|
604
|
+
input: TInput,
|
|
605
|
+
options?: { revisionId?: string },
|
|
606
|
+
): Promise<PlayJob<TOutput>>;
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Run this play and wait for completion.
|
|
610
|
+
*
|
|
611
|
+
* Equivalent to `play.run(input).then(job => job.get())`.
|
|
612
|
+
*
|
|
613
|
+
* @param input - Runtime input
|
|
614
|
+
* @returns The play's return value
|
|
615
|
+
*/
|
|
616
|
+
runSync(input: TInput, options?: { revisionId?: string }): Promise<TOutput>;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export type PrebuiltPlayRef = {
|
|
620
|
+
readonly playName: string;
|
|
621
|
+
readonly name: string;
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
export type PlayReferenceLike = {
|
|
625
|
+
readonly playName?: string;
|
|
626
|
+
readonly name?: string;
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
export type {
|
|
630
|
+
PlayDataset,
|
|
631
|
+
PlayDatasetInput,
|
|
632
|
+
} from '../../shared_libs/plays/dataset.js';
|
|
633
|
+
|
|
634
|
+
export type PlayReturnObject = Record<string, unknown> & {
|
|
635
|
+
readonly _metadata?: never;
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
export type PlayInputContract<TInput> = {
|
|
639
|
+
readonly schema: Record<string, unknown>;
|
|
640
|
+
readonly __inputType?: TInput;
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
export type DefinePlayConfig<TInput, TOutput extends PlayReturnObject> = {
|
|
644
|
+
id: string;
|
|
645
|
+
input: PlayInputContract<TInput>;
|
|
646
|
+
run: (ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>;
|
|
647
|
+
bindings?: PlayBindings;
|
|
648
|
+
billing?: PlayBindings['billing'];
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
class DeeplineConditionalStepResolver<
|
|
652
|
+
Row,
|
|
653
|
+
Value,
|
|
654
|
+
ElseValue,
|
|
655
|
+
> implements ConditionalStepResolver<Row, Value, ElseValue> {
|
|
656
|
+
readonly kind = 'conditional' as const;
|
|
657
|
+
|
|
658
|
+
constructor(
|
|
659
|
+
readonly when: (row: Row, index: number) => boolean | Promise<boolean>,
|
|
660
|
+
readonly run: StepResolver<Row, Value>,
|
|
661
|
+
readonly elseValue: ElseValue,
|
|
662
|
+
) {}
|
|
663
|
+
|
|
664
|
+
else<ValueElse>(
|
|
665
|
+
value: ValueElse,
|
|
666
|
+
): ConditionalStepResolver<Row, Value, ValueElse> {
|
|
667
|
+
return new DeeplineConditionalStepResolver(this.when, this.run, value);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
class DeeplineStepProgram<Input, Output, ReturnValue> implements StepProgram<
|
|
672
|
+
Input,
|
|
673
|
+
Output,
|
|
674
|
+
ReturnValue
|
|
675
|
+
> {
|
|
676
|
+
readonly kind = 'steps' as const;
|
|
677
|
+
declare readonly __inputType?: (input: Input) => void;
|
|
678
|
+
|
|
679
|
+
constructor(
|
|
680
|
+
readonly steps: readonly PlayStepProgramStep[],
|
|
681
|
+
readonly returnResolver?: StepResolver<Output, ReturnValue>,
|
|
682
|
+
) {}
|
|
683
|
+
|
|
684
|
+
step<Name extends string, Value>(
|
|
685
|
+
name: Name,
|
|
686
|
+
resolver:
|
|
687
|
+
| StepResolver<Output, Value>
|
|
688
|
+
| ConditionalStepResolver<Output, Value>
|
|
689
|
+
| StepProgramResolver<Output, Value>,
|
|
690
|
+
): StepProgram<Input, Output & Record<Name, Value>, ReturnValue> {
|
|
691
|
+
if (!name.trim()) {
|
|
692
|
+
throw new Error(
|
|
693
|
+
'steps().step(name, ...) requires a non-empty step name.',
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
return new DeeplineStepProgram(
|
|
697
|
+
[
|
|
698
|
+
...this.steps,
|
|
699
|
+
{
|
|
700
|
+
name,
|
|
701
|
+
resolver: resolver as PlayStepProgramStep['resolver'],
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
this.returnResolver as StepResolver<
|
|
705
|
+
Output & Record<Name, Value>,
|
|
706
|
+
ReturnValue
|
|
707
|
+
>,
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return<Value>(
|
|
712
|
+
resolver: StepResolver<Output, Value>,
|
|
713
|
+
): StepProgram<Input, Output, Value> {
|
|
714
|
+
return new DeeplineStepProgram(this.steps, resolver);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
export function steps<TInput>(): StepProgram<TInput, TInput, TInput> {
|
|
719
|
+
return new DeeplineStepProgram<TInput, TInput, TInput>([]);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
export function when<Row, Value>(
|
|
723
|
+
predicate: (row: Row, index: number) => boolean | Promise<boolean>,
|
|
724
|
+
resolver: StepResolver<Row, Value>,
|
|
725
|
+
): ConditionalStepResolver<Row, Value, null> {
|
|
726
|
+
return new DeeplineConditionalStepResolver(predicate, resolver, null);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* A defined play: both a callable function and a named play handle.
|
|
731
|
+
*
|
|
732
|
+
* Created by {@link definePlay}. Can be:
|
|
733
|
+
* 1. Called directly as a function (for server-side Temporal execution)
|
|
734
|
+
* 2. Used as a {@link DeeplineNamedPlay} for remote lifecycle operations
|
|
735
|
+
*
|
|
736
|
+
* @typeParam TInput - The play's input type
|
|
737
|
+
* @typeParam TOutput - The play's return type
|
|
738
|
+
*
|
|
739
|
+
* @example
|
|
740
|
+
* ```typescript
|
|
741
|
+
* import { definePlay } from 'deepline';
|
|
742
|
+
*
|
|
743
|
+
* const myPlay = definePlay('my-play', async (ctx, input: { domain: string }) => {
|
|
744
|
+
* const company = await ctx.tools.execute({
|
|
745
|
+
* id: 'company_search',
|
|
746
|
+
* tool: 'test_company_search',
|
|
747
|
+
* input: { domain: input.domain },
|
|
748
|
+
* description: 'Look up company details by domain.',
|
|
749
|
+
* });
|
|
750
|
+
* return { company: company.result };
|
|
751
|
+
* });
|
|
752
|
+
*
|
|
753
|
+
* // Type is: DefinedPlay<{ domain: string }, unknown>
|
|
754
|
+
*
|
|
755
|
+
* // Use as named play handle:
|
|
756
|
+
* const detail = await myPlay.get();
|
|
757
|
+
* const result = await myPlay.runSync({ domain: 'stripe.com' });
|
|
758
|
+
*
|
|
759
|
+
* // Access metadata:
|
|
760
|
+
* console.log(myPlay.playName); // "my-play"
|
|
761
|
+
* console.log(myPlay.bindings); // undefined (no cron/webhook)
|
|
762
|
+
* ```
|
|
763
|
+
*/
|
|
764
|
+
export type DefinedPlay<TInput, TOutput extends PlayReturnObject> = ((
|
|
765
|
+
ctx: DeeplinePlayRuntimeContext,
|
|
766
|
+
input: TInput,
|
|
767
|
+
) => Promise<TOutput>) &
|
|
768
|
+
DeeplineNamedPlay<TInput, TOutput> & {
|
|
769
|
+
/** Optional trigger bindings (cron, webhook). */
|
|
770
|
+
readonly bindings?: PlayBindings;
|
|
771
|
+
/** The play's name (same as `.name`). */
|
|
772
|
+
readonly playName: string;
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
type PlayMetadata = {
|
|
776
|
+
name: string;
|
|
777
|
+
bindings?: PlayBindings;
|
|
778
|
+
inputSchema?: Record<string, unknown>;
|
|
779
|
+
billing?: PlayBindings['billing'];
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
const PLAY_METADATA_SYMBOL = Symbol.for('deepline.play.metadata');
|
|
783
|
+
|
|
784
|
+
class DeeplinePlayJobImpl<TOutput = unknown> implements PlayJob<TOutput> {
|
|
785
|
+
readonly id: string;
|
|
786
|
+
|
|
787
|
+
constructor(
|
|
788
|
+
private readonly client: DeeplineClient,
|
|
789
|
+
runId: string,
|
|
790
|
+
) {
|
|
791
|
+
this.id = runId;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
async status(): Promise<PlayStatus> {
|
|
795
|
+
return this.client.getPlayStatus(this.id);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
async tail(options?: {
|
|
799
|
+
intervalMs?: number;
|
|
800
|
+
onLog?: (line: string) => void;
|
|
801
|
+
}): Promise<PlayStatus> {
|
|
802
|
+
const intervalMs = options?.intervalMs ?? 500;
|
|
803
|
+
const onLog = options?.onLog ?? ((line: string) => console.log(line));
|
|
804
|
+
const terminalStates = new Set(['completed', 'failed', 'cancelled']);
|
|
805
|
+
let lastLogIndex = 0;
|
|
806
|
+
|
|
807
|
+
while (true) {
|
|
808
|
+
const status = await this.status();
|
|
809
|
+
const logs = status.progress?.logs ?? [];
|
|
810
|
+
for (let index = lastLogIndex; index < logs.length; index += 1) {
|
|
811
|
+
onLog(logs[index]!);
|
|
812
|
+
}
|
|
813
|
+
lastLogIndex = logs.length;
|
|
814
|
+
|
|
815
|
+
if (terminalStates.has(status.status)) {
|
|
816
|
+
return status;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
async get(options?: { intervalMs?: number }): Promise<TOutput> {
|
|
824
|
+
const intervalMs = options?.intervalMs ?? 500;
|
|
825
|
+
const terminalStates = new Set(['completed', 'failed', 'cancelled']);
|
|
826
|
+
|
|
827
|
+
while (true) {
|
|
828
|
+
const status = await this.status();
|
|
829
|
+
if (terminalStates.has(status.status)) {
|
|
830
|
+
if (status.status !== 'completed') {
|
|
831
|
+
throw new DeeplineError(
|
|
832
|
+
status.progress?.error ||
|
|
833
|
+
`Play run ${this.id} ended with ${status.status}.`,
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
const payload = status.result as { output?: unknown } | undefined;
|
|
837
|
+
return ((payload && 'output' in payload
|
|
838
|
+
? payload.output
|
|
839
|
+
: status.result) ?? null) as TOutput;
|
|
840
|
+
}
|
|
841
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
async cancel(): Promise<void> {
|
|
846
|
+
await this.client.cancelPlay(this.id);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
async stop(options?: { reason?: string }): Promise<StopPlayRunResult> {
|
|
850
|
+
return this.client.stopPlay(this.id, options);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function createNamedPlayHandle<
|
|
855
|
+
TInput = Record<string, unknown>,
|
|
856
|
+
TOutput = unknown,
|
|
857
|
+
>(
|
|
858
|
+
clientFactory: () => DeeplineClient,
|
|
859
|
+
name: string,
|
|
860
|
+
): DeeplineNamedPlay<TInput, TOutput> {
|
|
861
|
+
return {
|
|
862
|
+
name,
|
|
863
|
+
get: () => clientFactory().getPlay(name),
|
|
864
|
+
runs: () => clientFactory().listPlayRuns(name),
|
|
865
|
+
versions: () => clientFactory().listPlayVersions(name),
|
|
866
|
+
publish: (options) => clientFactory().publishPlayVersion(name, options),
|
|
867
|
+
clearHistory: (options) => clientFactory().clearPlayHistory(name, options),
|
|
868
|
+
async run(
|
|
869
|
+
input: TInput,
|
|
870
|
+
options?: { revisionId?: string },
|
|
871
|
+
): Promise<PlayJob<TOutput>> {
|
|
872
|
+
const client = clientFactory();
|
|
873
|
+
const started = await client.startPlayRun({
|
|
874
|
+
name,
|
|
875
|
+
...(options?.revisionId ? { revisionId: options.revisionId } : {}),
|
|
876
|
+
input: input as Record<string, unknown>,
|
|
877
|
+
});
|
|
878
|
+
return new DeeplinePlayJobImpl<TOutput>(client, started.workflowId);
|
|
879
|
+
},
|
|
880
|
+
async runSync(
|
|
881
|
+
input: TInput,
|
|
882
|
+
options?: { revisionId?: string },
|
|
883
|
+
): Promise<TOutput> {
|
|
884
|
+
const job = await this.run(input, options);
|
|
885
|
+
return job.get();
|
|
886
|
+
},
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* High-level SDK context with tool shortcuts and play handles.
|
|
892
|
+
*
|
|
893
|
+
* Created by {@link Deepline.connect}. Wraps a {@link DeeplineClient} with
|
|
894
|
+
* a friendlier API for common operations.
|
|
895
|
+
*
|
|
896
|
+
* @example
|
|
897
|
+
* ```typescript
|
|
898
|
+
* const ctx = await Deepline.connect();
|
|
899
|
+
*
|
|
900
|
+
* // Tools
|
|
901
|
+
* const tools = await ctx.tools.list();
|
|
902
|
+
* const result = await ctx.tools.execute({
|
|
903
|
+
* tool: 'test_company_search',
|
|
904
|
+
* input: { domain: 'stripe.com' },
|
|
905
|
+
* });
|
|
906
|
+
*
|
|
907
|
+
* // Plays
|
|
908
|
+
* const job = await ctx.play('email-waterfall').run({ domain: 'stripe.com' });
|
|
909
|
+
* const output = await job.get();
|
|
910
|
+
* ```
|
|
911
|
+
*/
|
|
912
|
+
export class DeeplineContext {
|
|
913
|
+
private readonly client: DeeplineClient;
|
|
914
|
+
|
|
915
|
+
constructor(options?: DeeplineClientOptions) {
|
|
916
|
+
this.client = new DeeplineClient(options);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Tool operations namespace.
|
|
921
|
+
*
|
|
922
|
+
* @example
|
|
923
|
+
* ```typescript
|
|
924
|
+
* const tools = await ctx.tools.list();
|
|
925
|
+
* const meta = await ctx.tools.get('apollo_people_search');
|
|
926
|
+
* const result = await ctx.tools.execute({
|
|
927
|
+
* tool: 'test_company_search',
|
|
928
|
+
* input: { domain: 'stripe.com' },
|
|
929
|
+
* });
|
|
930
|
+
* const rows = result.tryList({ listExtractorPaths: ['people'] });
|
|
931
|
+
* const email = result.getEmail();
|
|
932
|
+
* ```
|
|
933
|
+
*/
|
|
934
|
+
get tools() {
|
|
935
|
+
return {
|
|
936
|
+
/** List all available tools. */
|
|
937
|
+
list: (): Promise<ToolDefinition[]> => this.client.listTools(),
|
|
938
|
+
/** Get detailed metadata for a tool. */
|
|
939
|
+
get: (toolId: string): Promise<ToolMetadata> =>
|
|
940
|
+
this.client.getTool(toolId),
|
|
941
|
+
/** Execute a tool and return an ergonomic result wrapper. */
|
|
942
|
+
execute: async (
|
|
943
|
+
request: SdkToolExecutionRequest,
|
|
944
|
+
): Promise<ToolCallResult> =>
|
|
945
|
+
createToolCallResult(
|
|
946
|
+
await this.client.executeTool(request.tool, request.input),
|
|
947
|
+
),
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
get plays() {
|
|
952
|
+
return {
|
|
953
|
+
list: () => this.client.listPlays(),
|
|
954
|
+
get: <TInput = Record<string, unknown>, TOutput = unknown>(
|
|
955
|
+
name: string,
|
|
956
|
+
) => this.play<TInput, TOutput>(name),
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
get prebuilt(): Record<string, PrebuiltPlayRef> {
|
|
961
|
+
const explicit = {
|
|
962
|
+
companyToContact: {
|
|
963
|
+
playName: 'prebuilt/company-to-contact',
|
|
964
|
+
name: 'prebuilt/company-to-contact',
|
|
965
|
+
},
|
|
966
|
+
personToPhone: {
|
|
967
|
+
playName: 'prebuilt/person-to-phone',
|
|
968
|
+
name: 'prebuilt/person-to-phone',
|
|
969
|
+
},
|
|
970
|
+
personToEmail: {
|
|
971
|
+
playName: 'prebuilt/person-to-email',
|
|
972
|
+
name: 'prebuilt/person-to-email',
|
|
973
|
+
},
|
|
974
|
+
personLinkedinToEmail: {
|
|
975
|
+
playName: 'prebuilt/person-linkedin-to-email',
|
|
976
|
+
name: 'prebuilt/person-linkedin-to-email',
|
|
977
|
+
},
|
|
978
|
+
} satisfies Record<string, PrebuiltPlayRef>;
|
|
979
|
+
return new Proxy(
|
|
980
|
+
{},
|
|
981
|
+
{
|
|
982
|
+
get: (_target, prop) => {
|
|
983
|
+
if (typeof prop !== 'string') return undefined;
|
|
984
|
+
if (prop in explicit) {
|
|
985
|
+
return explicit[prop as keyof typeof explicit];
|
|
986
|
+
}
|
|
987
|
+
const playName = prop.startsWith('prebuilt/')
|
|
988
|
+
? prop
|
|
989
|
+
: `prebuilt/${prop}`;
|
|
990
|
+
return {
|
|
991
|
+
playName,
|
|
992
|
+
name: playName,
|
|
993
|
+
} satisfies PrebuiltPlayRef;
|
|
994
|
+
},
|
|
995
|
+
},
|
|
996
|
+
) as Record<string, PrebuiltPlayRef>;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Get a named play handle for remote lifecycle operations.
|
|
1001
|
+
*
|
|
1002
|
+
* @typeParam TInput - Expected input type
|
|
1003
|
+
* @typeParam TOutput - Expected output type
|
|
1004
|
+
* @param name - Play name (as registered on the server)
|
|
1005
|
+
* @returns Named play handle with run, versions, get, publish, etc.
|
|
1006
|
+
*
|
|
1007
|
+
* @example
|
|
1008
|
+
* ```typescript
|
|
1009
|
+
* const play = ctx.play<{ domain: string }>('email-waterfall');
|
|
1010
|
+
* const job = await play.run({ domain: 'stripe.com' });
|
|
1011
|
+
* const result = await job.get();
|
|
1012
|
+
* ```
|
|
1013
|
+
*/
|
|
1014
|
+
play<TInput = Record<string, unknown>, TOutput = unknown>(
|
|
1015
|
+
name: string,
|
|
1016
|
+
): DeeplineNamedPlay<TInput, TOutput> {
|
|
1017
|
+
return createNamedPlayHandle<TInput, TOutput>(() => this.client, name);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
async runPlay<TInput = Record<string, unknown>, TOutput = unknown>(
|
|
1021
|
+
playOrRef: string | PlayReferenceLike,
|
|
1022
|
+
input: TInput,
|
|
1023
|
+
): Promise<TOutput> {
|
|
1024
|
+
const name =
|
|
1025
|
+
typeof playOrRef === 'string'
|
|
1026
|
+
? playOrRef
|
|
1027
|
+
: (playOrRef.playName ?? playOrRef.name ?? '');
|
|
1028
|
+
return await this.play<TInput, TOutput>(name).runSync(input);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Static entry point for the Deepline SDK.
|
|
1034
|
+
*
|
|
1035
|
+
* @example
|
|
1036
|
+
* ```typescript
|
|
1037
|
+
* import { Deepline } from 'deepline';
|
|
1038
|
+
*
|
|
1039
|
+
* const ctx = await Deepline.connect();
|
|
1040
|
+
* const tools = await ctx.tools.list();
|
|
1041
|
+
* const result = await ctx.tools.execute({
|
|
1042
|
+
* tool: 'test_company_search',
|
|
1043
|
+
* input: { domain: 'stripe.com' },
|
|
1044
|
+
* });
|
|
1045
|
+
* ```
|
|
1046
|
+
*/
|
|
1047
|
+
export class Deepline {
|
|
1048
|
+
/**
|
|
1049
|
+
* Create a connected SDK context.
|
|
1050
|
+
*
|
|
1051
|
+
* Resolves configuration from options, environment variables, and CLI config
|
|
1052
|
+
* files. See {@link resolveConfig} for the resolution order.
|
|
1053
|
+
*
|
|
1054
|
+
* @param options - Optional overrides for API key, base URL, etc.
|
|
1055
|
+
* @returns Ready-to-use SDK context
|
|
1056
|
+
* @throws {@link ConfigError} if no API key can be resolved
|
|
1057
|
+
*
|
|
1058
|
+
* @example
|
|
1059
|
+
* ```typescript
|
|
1060
|
+
* // Auto-config (uses env vars / CLI auth):
|
|
1061
|
+
* const ctx = await Deepline.connect();
|
|
1062
|
+
*
|
|
1063
|
+
* // Explicit config:
|
|
1064
|
+
* const ctx2 = await Deepline.connect({
|
|
1065
|
+
* apiKey: 'dl_test_...',
|
|
1066
|
+
* baseUrl: 'http://localhost:3000',
|
|
1067
|
+
* });
|
|
1068
|
+
* ```
|
|
1069
|
+
*/
|
|
1070
|
+
static async connect(
|
|
1071
|
+
options?: DeeplineClientOptions,
|
|
1072
|
+
): Promise<DeeplineContext> {
|
|
1073
|
+
return new DeeplineContext(options);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
export function defineInput<TInput>(
|
|
1078
|
+
schema: Record<string, unknown>,
|
|
1079
|
+
): PlayInputContract<TInput> {
|
|
1080
|
+
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
|
|
1081
|
+
throw new Error(
|
|
1082
|
+
'defineInput<T>(schema) requires a JSON-schema-like object.',
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
return { schema };
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Define a play — a composable TypeScript workflow for the Deepline platform.
|
|
1090
|
+
*
|
|
1091
|
+
* The returned value is both:
|
|
1092
|
+
* 1. **A callable function** — invoked by the Temporal worker with a runtime context
|
|
1093
|
+
* 2. **A named play handle** — with `.run()`, `.versions()`, `.get()`, `.publish()`, etc. for remote lifecycle management
|
|
1094
|
+
*
|
|
1095
|
+
* Plays are the primary abstraction for building repeatable data pipelines.
|
|
1096
|
+
* They run on Temporal for durable execution with automatic retries and timeouts.
|
|
1097
|
+
*
|
|
1098
|
+
* @typeParam TInput - The input type accepted by the play
|
|
1099
|
+
* @typeParam TOutput - The return type of the play
|
|
1100
|
+
* @param name - Unique play name (used for publishing, running by name, and CLI reference)
|
|
1101
|
+
* @param fn - Async function that receives a {@link DeeplinePlayRuntimeContext} and typed input
|
|
1102
|
+
* @param bindings - Optional trigger bindings (cron schedule, webhook with HMAC)
|
|
1103
|
+
* @returns A {@link DefinedPlay} that is both callable and has lifecycle methods
|
|
1104
|
+
*
|
|
1105
|
+
* @example Basic play
|
|
1106
|
+
* ```typescript
|
|
1107
|
+
* import { definePlay } from 'deepline';
|
|
1108
|
+
*
|
|
1109
|
+
* export default definePlay('company-lookup', async (ctx, input: { domain: string }) => {
|
|
1110
|
+
* ctx.log(`Searching for ${input.domain}`);
|
|
1111
|
+
* const company = await ctx.tools.execute({
|
|
1112
|
+
* id: 'company_search',
|
|
1113
|
+
* tool: 'test_company_search',
|
|
1114
|
+
* input: { domain: input.domain },
|
|
1115
|
+
* description: 'Look up company details by domain.',
|
|
1116
|
+
* });
|
|
1117
|
+
* return { company: company.result };
|
|
1118
|
+
* });
|
|
1119
|
+
* ```
|
|
1120
|
+
*
|
|
1121
|
+
* @example CSV processing play
|
|
1122
|
+
* ```typescript
|
|
1123
|
+
* export default definePlay('bulk-enrich', async (ctx) => {
|
|
1124
|
+
* const leads = await ctx.csv('leads.csv');
|
|
1125
|
+
* ctx.log(`Processing ${leads.length} rows`);
|
|
1126
|
+
* const results = await ctx
|
|
1127
|
+
* .map('domain', leads)
|
|
1128
|
+
* .step('company', (row, ctx) =>
|
|
1129
|
+
* ctx.tools.execute({
|
|
1130
|
+
* id: 'company_search',
|
|
1131
|
+
* tool: 'test_company_search',
|
|
1132
|
+
* input: { domain: row.domain },
|
|
1133
|
+
* description: 'Look up company details by domain.',
|
|
1134
|
+
* }))
|
|
1135
|
+
* .run({ description: 'Enrich lead companies.' });
|
|
1136
|
+
* return results;
|
|
1137
|
+
* });
|
|
1138
|
+
* ```
|
|
1139
|
+
*
|
|
1140
|
+
* @example With cron binding
|
|
1141
|
+
* ```typescript
|
|
1142
|
+
* export default definePlay('daily-report', async (ctx) => {
|
|
1143
|
+
* const data = await ctx.tools.execute({
|
|
1144
|
+
* id: 'crm_export',
|
|
1145
|
+
* tool: 'crm_export',
|
|
1146
|
+
* input: { since: 'yesterday' },
|
|
1147
|
+
* description: 'Export yesterday CRM records.',
|
|
1148
|
+
* });
|
|
1149
|
+
* return data;
|
|
1150
|
+
* }, {
|
|
1151
|
+
* cron: { schedule: '0 9 * * *', timezone: 'America/New_York' },
|
|
1152
|
+
* });
|
|
1153
|
+
* ```
|
|
1154
|
+
*
|
|
1155
|
+
* @example Programmatic lifecycle
|
|
1156
|
+
* ```typescript
|
|
1157
|
+
* const myPlay = definePlay('my-play', handler);
|
|
1158
|
+
*
|
|
1159
|
+
* // Get play definition:
|
|
1160
|
+
* const detail = await myPlay.get();
|
|
1161
|
+
*
|
|
1162
|
+
* // Run remotely:
|
|
1163
|
+
* const result = await myPlay.runSync({ domain: 'stripe.com' });
|
|
1164
|
+
*
|
|
1165
|
+
* // Make the current draft live:
|
|
1166
|
+
* await myPlay.publish();
|
|
1167
|
+
* ```
|
|
1168
|
+
*/
|
|
1169
|
+
export function definePlay<TInput, TOutput extends PlayReturnObject>(
|
|
1170
|
+
config: DefinePlayConfig<TInput, TOutput>,
|
|
1171
|
+
): DefinedPlay<TInput, TOutput>;
|
|
1172
|
+
export function definePlay<TInput, TOutput extends PlayReturnObject>(
|
|
1173
|
+
name: string,
|
|
1174
|
+
fn: (ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>,
|
|
1175
|
+
bindings?: PlayBindings,
|
|
1176
|
+
): DefinedPlay<TInput, TOutput>;
|
|
1177
|
+
export function definePlay<TInput, TOutput extends PlayReturnObject>(
|
|
1178
|
+
nameOrConfig: string | DefinePlayConfig<TInput, TOutput>,
|
|
1179
|
+
maybeFn?: (
|
|
1180
|
+
ctx: DeeplinePlayRuntimeContext,
|
|
1181
|
+
input: TInput,
|
|
1182
|
+
) => Promise<TOutput>,
|
|
1183
|
+
maybeBindings?: PlayBindings,
|
|
1184
|
+
): DefinedPlay<TInput, TOutput> {
|
|
1185
|
+
const config =
|
|
1186
|
+
typeof nameOrConfig === 'string'
|
|
1187
|
+
? {
|
|
1188
|
+
name: nameOrConfig,
|
|
1189
|
+
fn: maybeFn,
|
|
1190
|
+
bindings: maybeBindings,
|
|
1191
|
+
inputSchema: undefined,
|
|
1192
|
+
billing: maybeBindings?.billing,
|
|
1193
|
+
}
|
|
1194
|
+
: {
|
|
1195
|
+
name: nameOrConfig.id,
|
|
1196
|
+
fn: nameOrConfig.run,
|
|
1197
|
+
bindings: nameOrConfig.bindings,
|
|
1198
|
+
inputSchema: nameOrConfig.input.schema,
|
|
1199
|
+
billing: nameOrConfig.billing,
|
|
1200
|
+
};
|
|
1201
|
+
const name = config.name;
|
|
1202
|
+
const fn = config.fn;
|
|
1203
|
+
const bindings = config.bindings;
|
|
1204
|
+
const billing = config.billing;
|
|
1205
|
+
const inputSchema = config.inputSchema;
|
|
1206
|
+
if (typeof fn !== 'function') {
|
|
1207
|
+
throw new Error('definePlay(...) requires an async run function.');
|
|
1208
|
+
}
|
|
1209
|
+
if (name.includes('/')) {
|
|
1210
|
+
throw new Error(
|
|
1211
|
+
'definePlay(name, ...) play names cannot contain "/". Slash is reserved for qualified references like "prebuilt/example" or "self/example".',
|
|
1212
|
+
);
|
|
1213
|
+
}
|
|
1214
|
+
const normalizedName = name
|
|
1215
|
+
.trim()
|
|
1216
|
+
.replace(/[^a-z0-9]+/gi, '_')
|
|
1217
|
+
.replace(/_+/g, '_')
|
|
1218
|
+
.replace(/^_+|_+$/g, '')
|
|
1219
|
+
.toLowerCase();
|
|
1220
|
+
if (!normalizedName) {
|
|
1221
|
+
throw new Error(
|
|
1222
|
+
'definePlay(name, ...) requires a play name with at least one letter or number. ' +
|
|
1223
|
+
'Use only letters, numbers, underscores, or hyphens.',
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
if (normalizedName.length > 63) {
|
|
1227
|
+
throw new Error(
|
|
1228
|
+
`definePlay("${name}", ...) is too long after normalization (${normalizedName.length}/63). ` +
|
|
1229
|
+
`Shorten the play name to 63 characters or fewer. Normalized value: "${normalizedName}".`,
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
const metadata: PlayMetadata = {
|
|
1234
|
+
name,
|
|
1235
|
+
...(bindings ? { bindings } : {}),
|
|
1236
|
+
...(inputSchema ? { inputSchema } : {}),
|
|
1237
|
+
...(billing ? { billing } : {}),
|
|
1238
|
+
};
|
|
1239
|
+
const play = fn as DefinedPlay<TInput, TOutput>;
|
|
1240
|
+
|
|
1241
|
+
Object.defineProperty(play, PLAY_METADATA_SYMBOL, {
|
|
1242
|
+
value: metadata,
|
|
1243
|
+
enumerable: false,
|
|
1244
|
+
configurable: false,
|
|
1245
|
+
writable: false,
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
Object.defineProperty(play, 'playName', {
|
|
1249
|
+
value: name,
|
|
1250
|
+
enumerable: true,
|
|
1251
|
+
configurable: false,
|
|
1252
|
+
writable: false,
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
Object.defineProperty(play, 'bindings', {
|
|
1256
|
+
value: bindings,
|
|
1257
|
+
enumerable: true,
|
|
1258
|
+
configurable: false,
|
|
1259
|
+
writable: false,
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
const handle = createNamedPlayHandle<TInput, TOutput>(
|
|
1263
|
+
() => new DeeplineClient(),
|
|
1264
|
+
name,
|
|
1265
|
+
);
|
|
1266
|
+
for (const key of [
|
|
1267
|
+
'name',
|
|
1268
|
+
'get',
|
|
1269
|
+
'runs',
|
|
1270
|
+
'versions',
|
|
1271
|
+
'publish',
|
|
1272
|
+
'run',
|
|
1273
|
+
'runSync',
|
|
1274
|
+
] as const) {
|
|
1275
|
+
Object.defineProperty(play, key, {
|
|
1276
|
+
value: handle[key],
|
|
1277
|
+
enumerable: false,
|
|
1278
|
+
configurable: false,
|
|
1279
|
+
writable: false,
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
return play;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* Alias for {@link definePlay}. Workflows and plays share the same public
|
|
1288
|
+
* Deepline SDK contract; the selected execution profile decides whether the
|
|
1289
|
+
* run is backed by local Node, Temporal/Daytona, or Cloudflare Dynamic
|
|
1290
|
+
* Workflows.
|
|
1291
|
+
*/
|
|
1292
|
+
export const defineWorkflow = definePlay;
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* Extract play metadata from a value that may be a defined play.
|
|
1296
|
+
*
|
|
1297
|
+
* Used internally by the CLI and bundler to detect `definePlay()` exports
|
|
1298
|
+
* and extract the play name and bindings.
|
|
1299
|
+
*
|
|
1300
|
+
* @param value - Any value (typically a module's default export)
|
|
1301
|
+
* @returns Play metadata if the value is a defined play, `null` otherwise
|
|
1302
|
+
*
|
|
1303
|
+
* @example
|
|
1304
|
+
* ```typescript
|
|
1305
|
+
* import { getDefinedPlayMetadata } from 'deepline';
|
|
1306
|
+
*
|
|
1307
|
+
* const mod = await import('./my-play.play.ts');
|
|
1308
|
+
* const meta = getDefinedPlayMetadata(mod.default);
|
|
1309
|
+
* if (meta) {
|
|
1310
|
+
* console.log(`Play name: ${meta.name}`);
|
|
1311
|
+
* console.log(`Bindings:`, meta.bindings);
|
|
1312
|
+
* }
|
|
1313
|
+
* ```
|
|
1314
|
+
*/
|
|
1315
|
+
export function getDefinedPlayMetadata(value: unknown): PlayMetadata | null {
|
|
1316
|
+
if (typeof value !== 'function') {
|
|
1317
|
+
return null;
|
|
1318
|
+
}
|
|
1319
|
+
const metadata = (value as unknown as Record<PropertyKey, unknown>)[
|
|
1320
|
+
PLAY_METADATA_SYMBOL
|
|
1321
|
+
];
|
|
1322
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
1323
|
+
return null;
|
|
1324
|
+
}
|
|
1325
|
+
const candidate = metadata as PlayMetadata;
|
|
1326
|
+
if (!candidate.name || typeof candidate.name !== 'string') {
|
|
1327
|
+
return null;
|
|
1328
|
+
}
|
|
1329
|
+
return candidate;
|
|
1330
|
+
}
|