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,1188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The main API client for interacting with the Deepline platform.
|
|
3
|
+
*
|
|
4
|
+
* `DeeplineClient` is the low-level workhorse — it maps 1:1 to the REST API
|
|
5
|
+
* and handles authentication, retries, and response parsing. For a higher-level
|
|
6
|
+
* interface with named play handles and tool shortcuts, use {@link Deepline.connect}
|
|
7
|
+
* or {@link DeeplineContext} instead.
|
|
8
|
+
*
|
|
9
|
+
* ## Quick start
|
|
10
|
+
*
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { DeeplineClient } from 'deepline';
|
|
13
|
+
*
|
|
14
|
+
* const client = new DeeplineClient();
|
|
15
|
+
*
|
|
16
|
+
* // List available tools
|
|
17
|
+
* const tools = await client.listTools();
|
|
18
|
+
*
|
|
19
|
+
* // Execute a tool
|
|
20
|
+
* const result = await client.executeTool('test_company_search', { domain: 'stripe.com' });
|
|
21
|
+
*
|
|
22
|
+
* // Run a play end-to-end
|
|
23
|
+
* const playResult = await client.runPlay(bundledCode, null, 'my-play', {
|
|
24
|
+
* onProgress: (status) => console.log(status.progress?.logs),
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* ## Configuration
|
|
29
|
+
*
|
|
30
|
+
* All options are optional — the client resolves API keys and URLs automatically
|
|
31
|
+
* from environment variables and CLI config files. See {@link resolveConfig} for
|
|
32
|
+
* the full resolution order.
|
|
33
|
+
*
|
|
34
|
+
* @module
|
|
35
|
+
*/
|
|
36
|
+
import { resolveConfig } from './config.js';
|
|
37
|
+
import { HttpClient } from './http.js';
|
|
38
|
+
import type {
|
|
39
|
+
DeeplineClientOptions,
|
|
40
|
+
ResolvedConfig,
|
|
41
|
+
PlayRevisionSummary,
|
|
42
|
+
PlayRunListItem,
|
|
43
|
+
PlayDetail,
|
|
44
|
+
PlayCheckResult,
|
|
45
|
+
PlayRunResult,
|
|
46
|
+
PlayRunStart,
|
|
47
|
+
PlayStatus,
|
|
48
|
+
PlayLiveEvent,
|
|
49
|
+
PlayListItem,
|
|
50
|
+
PlayDescription,
|
|
51
|
+
StopPlayRunResult,
|
|
52
|
+
ClearPlayHistoryRequest,
|
|
53
|
+
ClearPlayHistoryResult,
|
|
54
|
+
PublishPlayVersionRequest,
|
|
55
|
+
PublishPlayVersionResult,
|
|
56
|
+
StartPlayRunRequest,
|
|
57
|
+
DeletePlayResult,
|
|
58
|
+
ToolDefinition,
|
|
59
|
+
ToolMetadata,
|
|
60
|
+
CustomerDbQueryResult,
|
|
61
|
+
} from './types.js';
|
|
62
|
+
import type { PlayStagedFileRef } from './plays/local-file-discovery.js';
|
|
63
|
+
import type { PlayCompilerManifest } from '../../shared_libs/plays/compiler-manifest.js';
|
|
64
|
+
|
|
65
|
+
const TERMINAL_PLAY_STATUSES = new Set(['completed', 'failed', 'cancelled']);
|
|
66
|
+
|
|
67
|
+
function normalizePlayStatus(raw: Record<string, unknown>): PlayStatus {
|
|
68
|
+
const status =
|
|
69
|
+
typeof raw.status === 'string'
|
|
70
|
+
? raw.status
|
|
71
|
+
: typeof raw.temporalStatus === 'string'
|
|
72
|
+
? mapLegacyTemporalStatus(raw.temporalStatus)
|
|
73
|
+
: 'running';
|
|
74
|
+
const runId =
|
|
75
|
+
typeof raw.runId === 'string'
|
|
76
|
+
? raw.runId
|
|
77
|
+
: typeof raw.workflowId === 'string'
|
|
78
|
+
? raw.workflowId
|
|
79
|
+
: '';
|
|
80
|
+
return {
|
|
81
|
+
...(raw as unknown as Omit<PlayStatus, 'runId' | 'status'>),
|
|
82
|
+
runId,
|
|
83
|
+
status: status as PlayStatus['status'],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function mapLegacyTemporalStatus(status: string): PlayStatus['status'] {
|
|
88
|
+
switch (status.trim().toUpperCase()) {
|
|
89
|
+
case 'PENDING':
|
|
90
|
+
return 'queued';
|
|
91
|
+
case 'COMPLETED':
|
|
92
|
+
return 'completed';
|
|
93
|
+
case 'FAILED':
|
|
94
|
+
return 'failed';
|
|
95
|
+
case 'CANCELLED':
|
|
96
|
+
case 'TERMINATED':
|
|
97
|
+
case 'TIMED_OUT':
|
|
98
|
+
return 'cancelled';
|
|
99
|
+
case 'RUNNING':
|
|
100
|
+
default:
|
|
101
|
+
return 'running';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Low-level client for the Deepline REST API.
|
|
107
|
+
*
|
|
108
|
+
* Provides typed methods for every API endpoint: tools, plays, auth, and health.
|
|
109
|
+
* Handles authentication, retries, and localhost failover automatically.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* import { DeeplineClient } from 'deepline';
|
|
114
|
+
*
|
|
115
|
+
* // Zero-config (uses env vars / CLI auth):
|
|
116
|
+
* const client = new DeeplineClient();
|
|
117
|
+
*
|
|
118
|
+
* // Explicit config:
|
|
119
|
+
* const client2 = new DeeplineClient({
|
|
120
|
+
* apiKey: 'dl_test_...',
|
|
121
|
+
* baseUrl: 'http://localhost:3000',
|
|
122
|
+
* });
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export class DeeplineClient {
|
|
126
|
+
private readonly http: HttpClient;
|
|
127
|
+
private readonly config: ResolvedConfig;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @param options - Optional overrides for API key, base URL, timeout, and retries.
|
|
131
|
+
* @throws {@link ConfigError} if no API key can be resolved from any source.
|
|
132
|
+
*/
|
|
133
|
+
constructor(options?: DeeplineClientOptions) {
|
|
134
|
+
this.config = resolveConfig(options);
|
|
135
|
+
this.http = new HttpClient(this.config);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
|
|
139
|
+
get baseUrl(): string {
|
|
140
|
+
return this.config.baseUrl;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private compactSchema(schema: Record<string, unknown> | null | undefined) {
|
|
144
|
+
if (!schema) return null;
|
|
145
|
+
const fields = Array.isArray(schema.fields)
|
|
146
|
+
? schema.fields
|
|
147
|
+
.map((field) =>
|
|
148
|
+
field && typeof field === 'object'
|
|
149
|
+
? {
|
|
150
|
+
name: String((field as Record<string, unknown>).name ?? ''),
|
|
151
|
+
type: (field as Record<string, unknown>).type ?? undefined,
|
|
152
|
+
required:
|
|
153
|
+
(field as Record<string, unknown>).required ?? undefined,
|
|
154
|
+
}
|
|
155
|
+
: null,
|
|
156
|
+
)
|
|
157
|
+
.filter((field) => Boolean(field?.name))
|
|
158
|
+
: [];
|
|
159
|
+
return fields.length > 0 ? { fields } : schema;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private playRunCommand(name: string): string {
|
|
163
|
+
return `deepline plays run ${name} --input '{...}' --watch`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private summarizePlayListItem(
|
|
167
|
+
play: PlayListItem,
|
|
168
|
+
options?: { compact?: boolean },
|
|
169
|
+
): PlayDescription {
|
|
170
|
+
const aliases = play.aliases?.length ? play.aliases : [play.name];
|
|
171
|
+
const runCommand = this.playRunCommand(play.name);
|
|
172
|
+
return {
|
|
173
|
+
name: play.name,
|
|
174
|
+
...(play.reference ? { reference: play.reference } : {}),
|
|
175
|
+
...(play.displayName ? { displayName: play.displayName } : {}),
|
|
176
|
+
origin: play.origin,
|
|
177
|
+
ownerType: play.ownerType,
|
|
178
|
+
canEdit: play.canEdit,
|
|
179
|
+
canClone: play.canClone,
|
|
180
|
+
aliases,
|
|
181
|
+
inputSchema: options?.compact
|
|
182
|
+
? this.compactSchema(play.inputSchema)
|
|
183
|
+
: (play.inputSchema ?? null),
|
|
184
|
+
outputSchema: options?.compact
|
|
185
|
+
? this.compactSchema(play.outputSchema)
|
|
186
|
+
: (play.outputSchema ?? null),
|
|
187
|
+
runCommand,
|
|
188
|
+
examples: [runCommand],
|
|
189
|
+
currentPublishedVersion: play.currentPublishedVersion ?? null,
|
|
190
|
+
isDraftDirty: play.isDraftDirty,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private summarizePlayDetail(
|
|
195
|
+
detail: PlayDetail,
|
|
196
|
+
options?: { compact?: boolean },
|
|
197
|
+
): PlayDescription {
|
|
198
|
+
const play = detail.play;
|
|
199
|
+
return {
|
|
200
|
+
...this.summarizePlayListItem(play, options),
|
|
201
|
+
currentPublishedVersion:
|
|
202
|
+
play.currentPublishedVersion ?? play.liveRevision?.version ?? null,
|
|
203
|
+
latestRunId: play.latestRunId ?? detail.latestRuns[0]?.workflowId ?? null,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ——————————————————————————————————————————————————————————
|
|
208
|
+
// Tools
|
|
209
|
+
// ——————————————————————————————————————————————————————————
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* List all available tools.
|
|
213
|
+
*
|
|
214
|
+
* Returns tool definitions including ID, provider, description, input/output schemas,
|
|
215
|
+
* and list extractor paths for automatic CSV conversion.
|
|
216
|
+
*
|
|
217
|
+
* @returns Array of tool definitions
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* const tools = await client.listTools();
|
|
222
|
+
* const searchTools = tools.filter(t => t.categories.includes('search'));
|
|
223
|
+
* console.log(`Found ${searchTools.length} search tools`);
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
async listTools(): Promise<ToolDefinition[]> {
|
|
227
|
+
const res = await this.http.get<{ tools: ToolDefinition[] }>(
|
|
228
|
+
'/api/v2/tools',
|
|
229
|
+
);
|
|
230
|
+
return res.tools;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get detailed metadata for a single tool.
|
|
235
|
+
*
|
|
236
|
+
* Returns everything from {@link ToolDefinition} plus pricing info, sample
|
|
237
|
+
* inputs/outputs, failure modes, and cost estimates.
|
|
238
|
+
*
|
|
239
|
+
* @param toolId - Tool identifier (e.g. `"apollo_people_search"`)
|
|
240
|
+
* @returns Full tool metadata
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```typescript
|
|
244
|
+
* const meta = await client.getTool('apollo_people_search');
|
|
245
|
+
* console.log(`Cost: ${meta.estimatedCreditsRange} credits`);
|
|
246
|
+
* console.log(`Input schema:`, meta.inputSchema);
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
async getTool(toolId: string): Promise<ToolMetadata> {
|
|
250
|
+
return this.http.request<ToolMetadata>(
|
|
251
|
+
`/api/v2/integrations/${encodeURIComponent(toolId)}/get`,
|
|
252
|
+
{
|
|
253
|
+
method: 'GET',
|
|
254
|
+
headers: {
|
|
255
|
+
'x-deepline-tool-meta-only': '1',
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Execute a tool and return the extracted result.
|
|
263
|
+
*
|
|
264
|
+
* Sends the input payload to the tool and returns the `.result` field from the
|
|
265
|
+
* response. For the full response envelope (including job_id, credits, etc.),
|
|
266
|
+
* use {@link executeToolRaw}.
|
|
267
|
+
*
|
|
268
|
+
* @param toolId - Tool identifier (e.g. `"test_company_search"`)
|
|
269
|
+
* @param input - Tool-specific input parameters
|
|
270
|
+
* @returns The tool's output (shape varies by tool)
|
|
271
|
+
* @throws {@link DeeplineError} if the tool execution fails
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```typescript
|
|
275
|
+
* const company = await client.executeTool('test_company_search', {
|
|
276
|
+
* domain: 'stripe.com',
|
|
277
|
+
* });
|
|
278
|
+
* console.log(company); // { name: "Stripe", industry: "Financial Services", ... }
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
async executeTool(
|
|
282
|
+
toolId: string,
|
|
283
|
+
input: Record<string, unknown>,
|
|
284
|
+
): Promise<unknown> {
|
|
285
|
+
const res = await this.http.post<{ result: unknown }>(
|
|
286
|
+
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
287
|
+
{ payload: input },
|
|
288
|
+
);
|
|
289
|
+
return res.result ?? res;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Execute a tool and return the full response envelope.
|
|
294
|
+
*
|
|
295
|
+
* Unlike {@link executeTool}, this returns the complete API response including
|
|
296
|
+
* `job_id`, `status`, `credits`, and the raw `result` object.
|
|
297
|
+
*
|
|
298
|
+
* @param toolId - Tool identifier
|
|
299
|
+
* @param input - Tool-specific input parameters
|
|
300
|
+
* @returns Full response with job metadata and result
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```typescript
|
|
304
|
+
* const raw = await client.executeToolRaw('test_company_search', { domain: 'stripe.com' });
|
|
305
|
+
* console.log(`Job: ${raw.job_id}, Credits: ${raw.credits}`);
|
|
306
|
+
* console.log(`Result:`, raw.result);
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
async executeToolRaw(
|
|
310
|
+
toolId: string,
|
|
311
|
+
input: Record<string, unknown>,
|
|
312
|
+
): Promise<Record<string, unknown>> {
|
|
313
|
+
return this.http.post<Record<string, unknown>>(
|
|
314
|
+
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
315
|
+
{ payload: input },
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async queryCustomerDb(input: {
|
|
320
|
+
sql: string;
|
|
321
|
+
maxRows?: number;
|
|
322
|
+
}): Promise<CustomerDbQueryResult> {
|
|
323
|
+
return this.http.post<CustomerDbQueryResult>('/api/v2/db/query', {
|
|
324
|
+
sql: input.sql,
|
|
325
|
+
...(input.maxRows ? { max_rows: input.maxRows } : {}),
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ——————————————————————————————————————————————————————————
|
|
330
|
+
// Plays — submission and lifecycle
|
|
331
|
+
// ——————————————————————————————————————————————————————————
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Start a play run.
|
|
335
|
+
*
|
|
336
|
+
* Internal/advanced primitive. For normal callers, prefer the public
|
|
337
|
+
* entrypoints: the CLI, {@link Deepline.connect}, {@link submitPlay},
|
|
338
|
+
* or {@link runPlay}.
|
|
339
|
+
*
|
|
340
|
+
* Supported invocation surfaces intentionally share this same run contract:
|
|
341
|
+
* `deepline play run`, repo scripts such as `bun run deepline -- play run`,
|
|
342
|
+
* SDK context calls like `Deepline.connect().play(name).run()`, and direct
|
|
343
|
+
* `POST /api/v2/plays/run` calls all return a workflow/run id. The completed
|
|
344
|
+
* output is always retrievable from `getPlayStatus(runId).result` (or from
|
|
345
|
+
* `PlayJob.get()` for SDK context calls). Execution logs live under
|
|
346
|
+
* `progress.logs`; they are not part of the user output object.
|
|
347
|
+
*
|
|
348
|
+
* @param request - Play run configuration (name, code, input, etc.)
|
|
349
|
+
* @returns Workflow metadata including the `workflowId` for status polling
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* ```typescript
|
|
353
|
+
* // Run a live play by name:
|
|
354
|
+
* const started = await client.startPlayRun({
|
|
355
|
+
* name: 'email-waterfall',
|
|
356
|
+
* input: { linkedin_url: 'https://linkedin.com/in/jdoe', domain: 'acme.com' },
|
|
357
|
+
* });
|
|
358
|
+
* console.log(`Workflow: ${started.workflowId}`);
|
|
359
|
+
*
|
|
360
|
+
* // Run an ad hoc artifact-backed play:
|
|
361
|
+
* const started2 = await client.startPlayRun({
|
|
362
|
+
* artifactStorageKey: 'plays/v1/orgs/acme/plays/my-play/artifacts/playgraph_abc123.json',
|
|
363
|
+
* });
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
async startPlayRun(request: StartPlayRunRequest): Promise<PlayRunStart> {
|
|
367
|
+
return this.http.post<PlayRunStart>('/api/v2/plays/run', {
|
|
368
|
+
...(request.name ? { name: request.name } : {}),
|
|
369
|
+
...(request.revisionId ? { revisionId: request.revisionId } : {}),
|
|
370
|
+
...(request.artifactStorageKey
|
|
371
|
+
? { artifactStorageKey: request.artifactStorageKey }
|
|
372
|
+
: {}),
|
|
373
|
+
...(request.sourceCode ? { sourceCode: request.sourceCode } : {}),
|
|
374
|
+
...('staticPipeline' in request
|
|
375
|
+
? { staticPipeline: request.staticPipeline }
|
|
376
|
+
: {}),
|
|
377
|
+
...(request.artifactHash ? { artifactHash: request.artifactHash } : {}),
|
|
378
|
+
...(request.graphHash ? { graphHash: request.graphHash } : {}),
|
|
379
|
+
...(request.runtimeArtifact
|
|
380
|
+
? { runtimeArtifact: request.runtimeArtifact }
|
|
381
|
+
: {}),
|
|
382
|
+
...(request.compilerManifest
|
|
383
|
+
? { compilerManifest: request.compilerManifest }
|
|
384
|
+
: {}),
|
|
385
|
+
...(request.inputFileUpload
|
|
386
|
+
? { inputFileUpload: request.inputFileUpload }
|
|
387
|
+
: {}),
|
|
388
|
+
...(request.packagedFileUploads?.length
|
|
389
|
+
? { packagedFileUploads: request.packagedFileUploads }
|
|
390
|
+
: {}),
|
|
391
|
+
...(request.input ? { input: request.input } : {}),
|
|
392
|
+
...(request.inputFile ? { inputFile: request.inputFile } : {}),
|
|
393
|
+
...(request.packagedFiles?.length
|
|
394
|
+
? { packagedFiles: request.packagedFiles }
|
|
395
|
+
: {}),
|
|
396
|
+
...(request.force ? { force: true } : {}),
|
|
397
|
+
...(typeof request.waitForCompletionMs === 'number'
|
|
398
|
+
? { waitForCompletionMs: request.waitForCompletionMs }
|
|
399
|
+
: {}),
|
|
400
|
+
// Profile selection is the API's job, not the CLI's. The server
|
|
401
|
+
// hardcodes workers_edge as the default; tests that want a
|
|
402
|
+
// different profile pass `request.profile` explicitly.
|
|
403
|
+
...(request.profile ? { profile: request.profile } : {}),
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async *startPlayRunStream(
|
|
408
|
+
request: StartPlayRunRequest,
|
|
409
|
+
options?: { signal?: AbortSignal },
|
|
410
|
+
): AsyncGenerator<PlayLiveEvent> {
|
|
411
|
+
const body = {
|
|
412
|
+
...(request.name ? { name: request.name } : {}),
|
|
413
|
+
...(request.revisionId ? { revisionId: request.revisionId } : {}),
|
|
414
|
+
...(request.artifactStorageKey
|
|
415
|
+
? { artifactStorageKey: request.artifactStorageKey }
|
|
416
|
+
: {}),
|
|
417
|
+
...(request.sourceCode ? { sourceCode: request.sourceCode } : {}),
|
|
418
|
+
...('staticPipeline' in request
|
|
419
|
+
? { staticPipeline: request.staticPipeline }
|
|
420
|
+
: {}),
|
|
421
|
+
...(request.artifactHash ? { artifactHash: request.artifactHash } : {}),
|
|
422
|
+
...(request.graphHash ? { graphHash: request.graphHash } : {}),
|
|
423
|
+
...(request.runtimeArtifact
|
|
424
|
+
? { runtimeArtifact: request.runtimeArtifact }
|
|
425
|
+
: {}),
|
|
426
|
+
...(request.compilerManifest
|
|
427
|
+
? { compilerManifest: request.compilerManifest }
|
|
428
|
+
: {}),
|
|
429
|
+
...(request.inputFileUpload
|
|
430
|
+
? { inputFileUpload: request.inputFileUpload }
|
|
431
|
+
: {}),
|
|
432
|
+
...(request.packagedFileUploads?.length
|
|
433
|
+
? { packagedFileUploads: request.packagedFileUploads }
|
|
434
|
+
: {}),
|
|
435
|
+
...(request.input ? { input: request.input } : {}),
|
|
436
|
+
...(request.inputFile ? { inputFile: request.inputFile } : {}),
|
|
437
|
+
...(request.packagedFiles?.length
|
|
438
|
+
? { packagedFiles: request.packagedFiles }
|
|
439
|
+
: {}),
|
|
440
|
+
...(request.force ? { force: true } : {}),
|
|
441
|
+
...(request.profile ? { profile: request.profile } : {}),
|
|
442
|
+
};
|
|
443
|
+
for await (const event of this.http.streamSse<PlayLiveEvent>(
|
|
444
|
+
'/api/v2/plays/run?stream=true',
|
|
445
|
+
{
|
|
446
|
+
method: 'POST',
|
|
447
|
+
body,
|
|
448
|
+
signal: options?.signal,
|
|
449
|
+
},
|
|
450
|
+
)) {
|
|
451
|
+
if (event.scope === 'play') {
|
|
452
|
+
yield event;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Register a bundled play artifact.
|
|
459
|
+
*
|
|
460
|
+
* Internal/advanced primitive used by packaging flows. Public callers should
|
|
461
|
+
* prefer the CLI, {@link submitPlay}, or {@link runPlay}.
|
|
462
|
+
*/
|
|
463
|
+
async registerPlayArtifact(input: {
|
|
464
|
+
name: string;
|
|
465
|
+
sourceCode: string;
|
|
466
|
+
artifact: Record<string, unknown>;
|
|
467
|
+
compilerManifest?: PlayCompilerManifest;
|
|
468
|
+
publish?: boolean;
|
|
469
|
+
ownerType?: 'org' | 'deepline';
|
|
470
|
+
scope?: 'org' | 'system';
|
|
471
|
+
userId?: string;
|
|
472
|
+
}): Promise<{
|
|
473
|
+
success?: boolean;
|
|
474
|
+
name?: string;
|
|
475
|
+
artifactStorageKey: string;
|
|
476
|
+
artifactMetadata?: Record<string, unknown> | null;
|
|
477
|
+
staticPipeline?: unknown;
|
|
478
|
+
definitionId?: string | null;
|
|
479
|
+
revisionId?: string | null;
|
|
480
|
+
version?: number | null;
|
|
481
|
+
liveVersion?: number | null;
|
|
482
|
+
triggerMetadata?: unknown;
|
|
483
|
+
triggerBindings?: unknown;
|
|
484
|
+
}> {
|
|
485
|
+
const compilerManifest =
|
|
486
|
+
input.compilerManifest ??
|
|
487
|
+
(await this.compilePlayManifest({
|
|
488
|
+
name: input.name,
|
|
489
|
+
sourceCode: input.sourceCode,
|
|
490
|
+
artifact: input.artifact,
|
|
491
|
+
}));
|
|
492
|
+
return this.http.post('/api/v2/plays/artifacts', {
|
|
493
|
+
...input,
|
|
494
|
+
compilerManifest,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async registerPlayArtifacts(
|
|
499
|
+
artifacts: Array<{
|
|
500
|
+
name: string;
|
|
501
|
+
sourceCode: string;
|
|
502
|
+
artifact: Record<string, unknown>;
|
|
503
|
+
compilerManifest?: PlayCompilerManifest;
|
|
504
|
+
publish?: boolean;
|
|
505
|
+
ownerType?: 'org' | 'deepline';
|
|
506
|
+
scope?: 'org' | 'system';
|
|
507
|
+
userId?: string;
|
|
508
|
+
}>,
|
|
509
|
+
): Promise<{
|
|
510
|
+
success: boolean;
|
|
511
|
+
artifacts: Array<{
|
|
512
|
+
success?: boolean;
|
|
513
|
+
name?: string;
|
|
514
|
+
artifactStorageKey: string;
|
|
515
|
+
artifactMetadata?: Record<string, unknown> | null;
|
|
516
|
+
staticPipeline?: unknown;
|
|
517
|
+
definitionId?: string | null;
|
|
518
|
+
revisionId?: string | null;
|
|
519
|
+
version?: number | null;
|
|
520
|
+
liveVersion?: number | null;
|
|
521
|
+
triggerMetadata?: unknown;
|
|
522
|
+
triggerBindings?: unknown;
|
|
523
|
+
}>;
|
|
524
|
+
}> {
|
|
525
|
+
const compiledArtifacts = await Promise.all(
|
|
526
|
+
artifacts.map(async (artifact) => ({
|
|
527
|
+
...artifact,
|
|
528
|
+
compilerManifest:
|
|
529
|
+
artifact.compilerManifest ??
|
|
530
|
+
(await this.compilePlayManifest({
|
|
531
|
+
name: artifact.name,
|
|
532
|
+
sourceCode: artifact.sourceCode,
|
|
533
|
+
artifact: artifact.artifact,
|
|
534
|
+
})),
|
|
535
|
+
})),
|
|
536
|
+
);
|
|
537
|
+
return this.http.post('/api/v2/plays/artifacts', {
|
|
538
|
+
artifacts: compiledArtifacts,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async compilePlayManifest(input: {
|
|
543
|
+
name: string;
|
|
544
|
+
sourceCode: string;
|
|
545
|
+
artifact: Record<string, unknown>;
|
|
546
|
+
importedPlayDependencies?: PlayCompilerManifest[];
|
|
547
|
+
}): Promise<PlayCompilerManifest> {
|
|
548
|
+
const response = await this.http.post<{
|
|
549
|
+
compilerManifest: PlayCompilerManifest;
|
|
550
|
+
}>('/api/v2/plays/compile-manifest', input);
|
|
551
|
+
return response.compilerManifest;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Check a bundled play artifact against the server's current play compiler.
|
|
556
|
+
*
|
|
557
|
+
* Unlike {@link registerPlayArtifact}, this does not store the artifact,
|
|
558
|
+
* publish a revision, or start a run. It is the authoritative cloud validation
|
|
559
|
+
* path used by `deepline play check`.
|
|
560
|
+
*/
|
|
561
|
+
async checkPlayArtifact(input: {
|
|
562
|
+
name?: string;
|
|
563
|
+
sourceCode: string;
|
|
564
|
+
artifact: Record<string, unknown>;
|
|
565
|
+
}): Promise<PlayCheckResult> {
|
|
566
|
+
return this.http.post('/api/v2/plays/check', input);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async startPlayRunFromBundle(input: {
|
|
570
|
+
name: string;
|
|
571
|
+
sourceCode: string;
|
|
572
|
+
artifact: Record<string, unknown>;
|
|
573
|
+
compilerManifest?: PlayCompilerManifest;
|
|
574
|
+
input?: Record<string, unknown>;
|
|
575
|
+
inputFile?: PlayStagedFileRef | null;
|
|
576
|
+
packagedFiles?: PlayStagedFileRef[];
|
|
577
|
+
force?: boolean;
|
|
578
|
+
}): Promise<PlayRunStart> {
|
|
579
|
+
const compilerManifest =
|
|
580
|
+
input.compilerManifest ??
|
|
581
|
+
(await this.compilePlayManifest({
|
|
582
|
+
name: input.name,
|
|
583
|
+
sourceCode: input.sourceCode,
|
|
584
|
+
artifact: input.artifact,
|
|
585
|
+
}));
|
|
586
|
+
const registeredArtifact = await this.registerPlayArtifact({
|
|
587
|
+
name: input.name,
|
|
588
|
+
sourceCode: input.sourceCode,
|
|
589
|
+
artifact: input.artifact,
|
|
590
|
+
compilerManifest,
|
|
591
|
+
publish: false,
|
|
592
|
+
});
|
|
593
|
+
if (!registeredArtifact.artifactStorageKey) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
'registerPlayArtifact did not return an artifactStorageKey.',
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return this.startPlayRun({
|
|
600
|
+
name: input.name,
|
|
601
|
+
artifactStorageKey: registeredArtifact.artifactStorageKey,
|
|
602
|
+
compilerManifest,
|
|
603
|
+
...(input.input ? { input: input.input } : {}),
|
|
604
|
+
...(input.inputFile ? { inputFile: input.inputFile } : {}),
|
|
605
|
+
...(input.packagedFiles?.length
|
|
606
|
+
? { packagedFiles: input.packagedFiles }
|
|
607
|
+
: {}),
|
|
608
|
+
...(input.force ? { force: true } : {}),
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Register a bundled play artifact and start a run from the live revision.
|
|
614
|
+
*
|
|
615
|
+
* Convenience wrapper around {@link registerPlayArtifact} plus
|
|
616
|
+
* {@link startPlayRun}. This is the canonical file-backed path used by wrappers.
|
|
617
|
+
* The returned id can be passed to {@link getPlayStatus} to retrieve the same
|
|
618
|
+
* durable `{ result }` object that the CLI prints after `--watch` completes.
|
|
619
|
+
*
|
|
620
|
+
* @param code - Source string fallback; the bundled artifact should be passed in `options.artifact`
|
|
621
|
+
* @param csvPath - Path to input CSV file, or `null`
|
|
622
|
+
* @param name - Play name (extracted from source if omitted)
|
|
623
|
+
* @param options - Additional submission options
|
|
624
|
+
* @returns Workflow metadata with `workflowId`
|
|
625
|
+
*
|
|
626
|
+
* @example
|
|
627
|
+
* ```typescript
|
|
628
|
+
* const started = await client.submitPlay(
|
|
629
|
+
* originalSource,
|
|
630
|
+
* './leads.csv',
|
|
631
|
+
* 'bulk-enrich',
|
|
632
|
+
* { artifact: bundledArtifact, input: { limit: 100 } },
|
|
633
|
+
* );
|
|
634
|
+
* ```
|
|
635
|
+
*/
|
|
636
|
+
async submitPlay(
|
|
637
|
+
code: string,
|
|
638
|
+
csvPath: string | null,
|
|
639
|
+
name?: string,
|
|
640
|
+
options?: {
|
|
641
|
+
sourceCode?: string;
|
|
642
|
+
artifact?: Record<string, unknown>;
|
|
643
|
+
compilerManifest?: PlayCompilerManifest;
|
|
644
|
+
input?: Record<string, unknown>;
|
|
645
|
+
inputFile?: PlayStagedFileRef | null;
|
|
646
|
+
packagedFiles?: PlayStagedFileRef[];
|
|
647
|
+
force?: boolean;
|
|
648
|
+
},
|
|
649
|
+
): Promise<PlayRunStart> {
|
|
650
|
+
const runtimeInput = options?.input ? { ...options.input } : {};
|
|
651
|
+
|
|
652
|
+
if (csvPath) {
|
|
653
|
+
runtimeInput.file = csvPath;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const sourceCode = options?.sourceCode ?? code;
|
|
657
|
+
const artifact = options?.artifact;
|
|
658
|
+
if (!name?.trim()) {
|
|
659
|
+
throw new Error('submitPlay requires a play name.');
|
|
660
|
+
}
|
|
661
|
+
if (!artifact) {
|
|
662
|
+
throw new Error('submitPlay requires a bundled play artifact.');
|
|
663
|
+
}
|
|
664
|
+
const compilerManifest =
|
|
665
|
+
options?.compilerManifest ??
|
|
666
|
+
(await this.compilePlayManifest({
|
|
667
|
+
name,
|
|
668
|
+
sourceCode,
|
|
669
|
+
artifact,
|
|
670
|
+
}));
|
|
671
|
+
|
|
672
|
+
const registeredArtifact = await this.registerPlayArtifact({
|
|
673
|
+
name,
|
|
674
|
+
sourceCode,
|
|
675
|
+
artifact,
|
|
676
|
+
compilerManifest,
|
|
677
|
+
publish: false,
|
|
678
|
+
});
|
|
679
|
+
if (!registeredArtifact.artifactStorageKey) {
|
|
680
|
+
throw new Error(
|
|
681
|
+
'registerPlayArtifact did not return an artifactStorageKey.',
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return this.startPlayRun({
|
|
686
|
+
name,
|
|
687
|
+
artifactStorageKey: registeredArtifact.artifactStorageKey,
|
|
688
|
+
sourceCode,
|
|
689
|
+
staticPipeline: registeredArtifact.staticPipeline ?? null,
|
|
690
|
+
artifactHash:
|
|
691
|
+
typeof artifact.artifactHash === 'string'
|
|
692
|
+
? artifact.artifactHash
|
|
693
|
+
: undefined,
|
|
694
|
+
graphHash:
|
|
695
|
+
typeof artifact.graphHash === 'string' ? artifact.graphHash : undefined,
|
|
696
|
+
runtimeArtifact: artifact,
|
|
697
|
+
compilerManifest,
|
|
698
|
+
...(Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {}),
|
|
699
|
+
...(options?.inputFile ? { inputFile: options.inputFile } : {}),
|
|
700
|
+
...(options?.packagedFiles?.length
|
|
701
|
+
? { packagedFiles: options.packagedFiles }
|
|
702
|
+
: {}),
|
|
703
|
+
...(options?.force ? { force: true } : {}),
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Upload files to the staging area for use in play runs.
|
|
709
|
+
*
|
|
710
|
+
* Internal/advanced primitive used by packaging flows. Public callers should
|
|
711
|
+
* prefer the CLI, {@link submitPlay}, or {@link runPlay}.
|
|
712
|
+
*
|
|
713
|
+
* Staged files are referenced by their returned {@link PlayStagedFileRef}
|
|
714
|
+
* in subsequent {@link startPlayRun} calls via `inputFile` or `packagedFiles`.
|
|
715
|
+
*
|
|
716
|
+
* @param files - Array of files to stage (base64-encoded content)
|
|
717
|
+
* @returns Array of staged file references
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* ```typescript
|
|
721
|
+
* const staged = await client.stagePlayFiles([{
|
|
722
|
+
* logicalPath: 'data/leads.csv',
|
|
723
|
+
* contentBase64: Buffer.from(csvContent).toString('base64'),
|
|
724
|
+
* contentHash: sha256(csvContent),
|
|
725
|
+
* contentType: 'text/csv',
|
|
726
|
+
* bytes: csvContent.length,
|
|
727
|
+
* }]);
|
|
728
|
+
* // Use staged[0] as inputFile in startPlayRun
|
|
729
|
+
* ```
|
|
730
|
+
*/
|
|
731
|
+
async stagePlayFiles(
|
|
732
|
+
files: Array<{
|
|
733
|
+
logicalPath: string;
|
|
734
|
+
contentBase64: string;
|
|
735
|
+
contentHash: string;
|
|
736
|
+
contentType: string;
|
|
737
|
+
bytes: number;
|
|
738
|
+
}>,
|
|
739
|
+
): Promise<PlayStagedFileRef[]> {
|
|
740
|
+
const response = await this.http.post<{ files: PlayStagedFileRef[] }>(
|
|
741
|
+
'/api/v2/plays/files/stage',
|
|
742
|
+
{ files },
|
|
743
|
+
);
|
|
744
|
+
return response.files;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
async resolveStagedPlayFiles(
|
|
748
|
+
files: Array<{
|
|
749
|
+
logicalPath: string;
|
|
750
|
+
contentHash: string;
|
|
751
|
+
contentType: string;
|
|
752
|
+
bytes: number;
|
|
753
|
+
}>,
|
|
754
|
+
): Promise<{
|
|
755
|
+
files: PlayStagedFileRef[];
|
|
756
|
+
missing: Array<{ logicalPath: string; contentHash: string }>;
|
|
757
|
+
}> {
|
|
758
|
+
return this.http.post<{
|
|
759
|
+
files: PlayStagedFileRef[];
|
|
760
|
+
missing: Array<{ logicalPath: string; contentHash: string }>;
|
|
761
|
+
}>('/api/v2/plays/files/stage', { files });
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// ——————————————————————————————————————————————————————————
|
|
765
|
+
// Plays — status and monitoring
|
|
766
|
+
// ——————————————————————————————————————————————————————————
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Get the current status of a play execution.
|
|
770
|
+
*
|
|
771
|
+
* Internal/advanced primitive. Public callers should usually prefer
|
|
772
|
+
* {@link runPlay}, {@link PlayJob.get}, or `deepline play run --watch`.
|
|
773
|
+
*
|
|
774
|
+
* Poll this method until `status` reaches a terminal state:
|
|
775
|
+
* `'completed'`, `'failed'`, or `'cancelled'`.
|
|
776
|
+
*
|
|
777
|
+
* @param workflowId - Play-run id from {@link startPlayRun}
|
|
778
|
+
* @returns Current status with progress logs and partial results
|
|
779
|
+
*
|
|
780
|
+
* @example
|
|
781
|
+
* ```typescript
|
|
782
|
+
* const status = await client.getPlayStatus('play-abc123');
|
|
783
|
+
* console.log(`Status: ${status.status}`);
|
|
784
|
+
* console.log(`Logs: ${status.progress?.logs.length ?? 0} lines`);
|
|
785
|
+
* ```
|
|
786
|
+
*/
|
|
787
|
+
async getPlayStatus(workflowId: string): Promise<PlayStatus> {
|
|
788
|
+
const response = await this.http.get<Record<string, unknown>>(
|
|
789
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}`,
|
|
790
|
+
);
|
|
791
|
+
return normalizePlayStatus(response);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Get the lightweight tail-polling status for a play execution.
|
|
796
|
+
*
|
|
797
|
+
* This is intentionally smaller than {@link getPlayStatus}: it returns the
|
|
798
|
+
* fields needed for CLI log tailing while the run is in flight, without
|
|
799
|
+
* forcing the API to rebuild final result views on every poll. Call
|
|
800
|
+
* {@link getPlayStatus} once after a terminal state for the full result.
|
|
801
|
+
*/
|
|
802
|
+
async getPlayTailStatus(
|
|
803
|
+
workflowId: string,
|
|
804
|
+
options?: {
|
|
805
|
+
afterLogIndex?: number;
|
|
806
|
+
waitMs?: number;
|
|
807
|
+
terminalOnly?: boolean;
|
|
808
|
+
},
|
|
809
|
+
): Promise<PlayStatus> {
|
|
810
|
+
const params = new URLSearchParams({ mode: 'tail' });
|
|
811
|
+
if (typeof options?.afterLogIndex === 'number') {
|
|
812
|
+
params.set('afterLogIndex', String(options.afterLogIndex));
|
|
813
|
+
}
|
|
814
|
+
if (typeof options?.waitMs === 'number') {
|
|
815
|
+
params.set('waitMs', String(options.waitMs));
|
|
816
|
+
}
|
|
817
|
+
if (options?.terminalOnly) {
|
|
818
|
+
params.set('terminalOnly', 'true');
|
|
819
|
+
}
|
|
820
|
+
const response = await this.http.get<Record<string, unknown>>(
|
|
821
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`,
|
|
822
|
+
);
|
|
823
|
+
return normalizePlayStatus(response);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Stream semantic play-run events using the same SSE feed as the dashboard.
|
|
828
|
+
*
|
|
829
|
+
* Consumers should still keep a polling fallback: SSE is the fast live-update
|
|
830
|
+
* transport, while the status endpoints remain the authoritative recovery path.
|
|
831
|
+
*/
|
|
832
|
+
async *streamPlayRunEvents(
|
|
833
|
+
workflowId: string,
|
|
834
|
+
options?: {
|
|
835
|
+
signal?: AbortSignal;
|
|
836
|
+
lastEventId?: string;
|
|
837
|
+
mode?: 'cli' | 'ui';
|
|
838
|
+
},
|
|
839
|
+
): AsyncGenerator<PlayLiveEvent> {
|
|
840
|
+
const headers =
|
|
841
|
+
options?.lastEventId && options.lastEventId.trim()
|
|
842
|
+
? { 'Last-Event-ID': options.lastEventId.trim() }
|
|
843
|
+
: undefined;
|
|
844
|
+
const params = new URLSearchParams({ stream: 'true' });
|
|
845
|
+
params.set('mode', options?.mode ?? 'cli');
|
|
846
|
+
for await (const event of this.http.streamSse<PlayLiveEvent>(
|
|
847
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`,
|
|
848
|
+
{ signal: options?.signal, headers },
|
|
849
|
+
)) {
|
|
850
|
+
if (event.scope === 'play') {
|
|
851
|
+
yield event;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Cancel a running play execution.
|
|
858
|
+
*
|
|
859
|
+
* Sends a stop request for the run.
|
|
860
|
+
*
|
|
861
|
+
* @param workflowId - Temporal workflow ID to cancel
|
|
862
|
+
*
|
|
863
|
+
* @example
|
|
864
|
+
* ```typescript
|
|
865
|
+
* await client.cancelPlay('play-abc123');
|
|
866
|
+
* ```
|
|
867
|
+
*/
|
|
868
|
+
async cancelPlay(workflowId: string): Promise<void> {
|
|
869
|
+
await this.http.request(
|
|
870
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}/stop`,
|
|
871
|
+
{ method: 'POST' },
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Stop a running play execution, including open HITL waits.
|
|
877
|
+
*
|
|
878
|
+
* @param workflowId - Temporal workflow ID to stop
|
|
879
|
+
* @param options.reason - Optional audit/debug reason
|
|
880
|
+
*/
|
|
881
|
+
async stopPlay(
|
|
882
|
+
workflowId: string,
|
|
883
|
+
options?: { reason?: string },
|
|
884
|
+
): Promise<StopPlayRunResult> {
|
|
885
|
+
return this.http.post<StopPlayRunResult>(
|
|
886
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}/stop`,
|
|
887
|
+
options?.reason ? { reason: options.reason } : {},
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* List recent runs for a named play.
|
|
893
|
+
*
|
|
894
|
+
* Returns runs sorted by start time (newest first), including workflow IDs,
|
|
895
|
+
* status, timestamps, and metadata.
|
|
896
|
+
*
|
|
897
|
+
* @param playName - The play name to query
|
|
898
|
+
* @returns Array of run summaries (empty array if no runs exist)
|
|
899
|
+
*
|
|
900
|
+
* @example
|
|
901
|
+
* ```typescript
|
|
902
|
+
* const runs = await client.listPlayRuns('email-waterfall');
|
|
903
|
+
* for (const run of runs) {
|
|
904
|
+
* console.log(`${run.workflowId}: ${run.status} (${run.executionTime})`);
|
|
905
|
+
* }
|
|
906
|
+
* ```
|
|
907
|
+
*/
|
|
908
|
+
async listPlayRuns(playName: string): Promise<PlayRunListItem[]> {
|
|
909
|
+
const encodedName = encodeURIComponent(playName);
|
|
910
|
+
const response = await this.http.get<{ runs: PlayRunListItem[] }>(
|
|
911
|
+
`/api/v2/plays/${encodedName}/runs`,
|
|
912
|
+
);
|
|
913
|
+
return response.runs ?? [];
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
async listPlays(): Promise<PlayListItem[]> {
|
|
917
|
+
const response = await this.http.get<{ plays: PlayListItem[] }>(
|
|
918
|
+
'/api/v2/plays',
|
|
919
|
+
);
|
|
920
|
+
return response.plays ?? [];
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
async searchPlays(options: {
|
|
924
|
+
query: string;
|
|
925
|
+
origin?: 'prebuilt' | 'owned';
|
|
926
|
+
compact?: boolean;
|
|
927
|
+
}): Promise<PlayDescription[]> {
|
|
928
|
+
const query = options.query.trim().toLowerCase();
|
|
929
|
+
const terms = query.split(/\s+/).filter(Boolean);
|
|
930
|
+
const plays = await this.listPlays();
|
|
931
|
+
return plays
|
|
932
|
+
.filter((play) => {
|
|
933
|
+
if (options.origin && (play.origin ?? 'owned') !== options.origin) {
|
|
934
|
+
return false;
|
|
935
|
+
}
|
|
936
|
+
const haystack = [
|
|
937
|
+
play.name,
|
|
938
|
+
play.reference,
|
|
939
|
+
play.displayName,
|
|
940
|
+
play.origin,
|
|
941
|
+
...(play.aliases ?? []),
|
|
942
|
+
play.inputSchema ? JSON.stringify(play.inputSchema) : '',
|
|
943
|
+
]
|
|
944
|
+
.filter(Boolean)
|
|
945
|
+
.join(' ')
|
|
946
|
+
.toLowerCase();
|
|
947
|
+
return terms.every((term) => haystack.includes(term));
|
|
948
|
+
})
|
|
949
|
+
.map((play) => this.summarizePlayListItem(play, options));
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* Get the full definition and state of a named play.
|
|
954
|
+
*
|
|
955
|
+
* Returns the play's revision state (draft, live), recent runs,
|
|
956
|
+
* sheet processing summary, and database URL.
|
|
957
|
+
*
|
|
958
|
+
* @param name - Play name
|
|
959
|
+
* @returns Complete play detail
|
|
960
|
+
*
|
|
961
|
+
* @example
|
|
962
|
+
* ```typescript
|
|
963
|
+
* const detail = await client.getPlay('email-waterfall');
|
|
964
|
+
* console.log(`Live: v${detail.play.currentPublishedVersion}`);
|
|
965
|
+
* console.log(`Draft dirty: ${detail.play.isDraftDirty}`);
|
|
966
|
+
* console.log(`Total runs: ${detail.play.runCount}`);
|
|
967
|
+
* ```
|
|
968
|
+
*/
|
|
969
|
+
async getPlay(name: string): Promise<PlayDetail> {
|
|
970
|
+
const encodedName = encodeURIComponent(name);
|
|
971
|
+
return this.http.get<PlayDetail>(`/api/v2/plays/${encodedName}`);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
async describePlay(
|
|
975
|
+
name: string,
|
|
976
|
+
options?: { compact?: boolean },
|
|
977
|
+
): Promise<PlayDescription> {
|
|
978
|
+
const detail = await this.getPlay(name);
|
|
979
|
+
return this.summarizePlayDetail(detail, options);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Clear run history and durable sheet/result data for a play without deleting
|
|
984
|
+
* the play definition or revisions.
|
|
985
|
+
*/
|
|
986
|
+
async clearPlayHistory(
|
|
987
|
+
name: string,
|
|
988
|
+
request: ClearPlayHistoryRequest = {},
|
|
989
|
+
): Promise<ClearPlayHistoryResult> {
|
|
990
|
+
const encodedName = encodeURIComponent(name);
|
|
991
|
+
return this.http.post<ClearPlayHistoryResult>(
|
|
992
|
+
`/api/v2/plays/${encodedName}/history/clear`,
|
|
993
|
+
request,
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* List saved versions for a named play.
|
|
999
|
+
*
|
|
1000
|
+
* Returns immutable revision snapshots newest-first, including the revision
|
|
1001
|
+
* id needed for exact-version runs and live-version switching.
|
|
1002
|
+
*
|
|
1003
|
+
* @param name - Play name
|
|
1004
|
+
* @returns Version list (newest first)
|
|
1005
|
+
*/
|
|
1006
|
+
async listPlayVersions(name: string): Promise<PlayRevisionSummary[]> {
|
|
1007
|
+
const encodedName = encodeURIComponent(name);
|
|
1008
|
+
const response = await this.http.get<{ versions: PlayRevisionSummary[] }>(
|
|
1009
|
+
`/api/v2/plays/${encodedName}/versions`,
|
|
1010
|
+
);
|
|
1011
|
+
return response.versions ?? [];
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* Make a play revision live.
|
|
1016
|
+
*
|
|
1017
|
+
* When `revisionId` is omitted, the current working revision becomes live.
|
|
1018
|
+
* The live version is what executes when the play is run by name without
|
|
1019
|
+
* specifying an explicit revision.
|
|
1020
|
+
*
|
|
1021
|
+
* @param name - Play name
|
|
1022
|
+
* @param request - Optional explicit revision to make live
|
|
1023
|
+
* @returns Result with the new live version number
|
|
1024
|
+
*
|
|
1025
|
+
* @example
|
|
1026
|
+
* ```typescript
|
|
1027
|
+
* const result = await client.publishPlayVersion('email-waterfall');
|
|
1028
|
+
* if (result.success) {
|
|
1029
|
+
* console.log(`Live v${result.liveVersion}`);
|
|
1030
|
+
* }
|
|
1031
|
+
* ```
|
|
1032
|
+
*/
|
|
1033
|
+
async publishPlayVersion(
|
|
1034
|
+
name: string,
|
|
1035
|
+
request: PublishPlayVersionRequest = {},
|
|
1036
|
+
): Promise<PublishPlayVersionResult> {
|
|
1037
|
+
const encodedName = encodeURIComponent(name);
|
|
1038
|
+
return this.http.post<PublishPlayVersionResult>(
|
|
1039
|
+
`/api/v2/plays/${encodedName}/live`,
|
|
1040
|
+
request,
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Delete an org-owned play definition, including its revisions, trigger
|
|
1046
|
+
* bindings, and local run records. Deepline prebuilt plays are read-only.
|
|
1047
|
+
*/
|
|
1048
|
+
async deletePlay(name: string): Promise<DeletePlayResult> {
|
|
1049
|
+
const encodedName = encodeURIComponent(name);
|
|
1050
|
+
return this.http.delete<DeletePlayResult>(`/api/v2/plays/${encodedName}`);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// ——————————————————————————————————————————————————————————
|
|
1054
|
+
// Plays — high-level orchestration
|
|
1055
|
+
// ——————————————————————————————————————————————————————————
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* Run a play end-to-end: submit, poll until terminal, return result.
|
|
1059
|
+
*
|
|
1060
|
+
* This is the highest-level play execution method. It submits the play,
|
|
1061
|
+
* polls for status updates, and returns a structured result with logs
|
|
1062
|
+
* and timing. Supports cancellation via `AbortSignal`.
|
|
1063
|
+
*
|
|
1064
|
+
* @param code - Source string fallback; pass the bundled artifact in `options.artifact`
|
|
1065
|
+
* @param csvPath - Input CSV path, or `null`
|
|
1066
|
+
* @param name - Play name
|
|
1067
|
+
* @param options - Execution options
|
|
1068
|
+
* @returns Final execution result with success/failure, output, logs, and duration
|
|
1069
|
+
*
|
|
1070
|
+
* @example
|
|
1071
|
+
* ```typescript
|
|
1072
|
+
* const result = await client.runPlay(bundledCode, null, 'my-play', {
|
|
1073
|
+
* input: { domain: 'stripe.com' },
|
|
1074
|
+
* onProgress: (status) => {
|
|
1075
|
+
* const logs = status.progress?.logs ?? [];
|
|
1076
|
+
* console.log(`[${status.status}] ${logs.length} log lines`);
|
|
1077
|
+
* },
|
|
1078
|
+
* pollIntervalMs: 1000,
|
|
1079
|
+
* });
|
|
1080
|
+
*
|
|
1081
|
+
* if (result.success) {
|
|
1082
|
+
* console.log('Output:', result.result);
|
|
1083
|
+
* } else {
|
|
1084
|
+
* console.error(`Failed after ${result.durationMs}ms:`, result.error);
|
|
1085
|
+
* }
|
|
1086
|
+
* ```
|
|
1087
|
+
*
|
|
1088
|
+
* @example Cancellation
|
|
1089
|
+
* ```typescript
|
|
1090
|
+
* const controller = new AbortController();
|
|
1091
|
+
* setTimeout(() => controller.abort(), 30_000); // 30s timeout
|
|
1092
|
+
*
|
|
1093
|
+
* const result = await client.runPlay(code, null, 'slow-play', {
|
|
1094
|
+
* signal: controller.signal,
|
|
1095
|
+
* });
|
|
1096
|
+
* // result.success === false, result.error === 'Cancelled by user'
|
|
1097
|
+
* ```
|
|
1098
|
+
*/
|
|
1099
|
+
async runPlay(
|
|
1100
|
+
code: string,
|
|
1101
|
+
csvPath: string | null,
|
|
1102
|
+
name?: string,
|
|
1103
|
+
options?: {
|
|
1104
|
+
/** Called on each poll iteration with the current status. */
|
|
1105
|
+
onProgress?: (status: PlayStatus) => void;
|
|
1106
|
+
/** Milliseconds between status polls. Default: `500`. */
|
|
1107
|
+
pollIntervalMs?: number;
|
|
1108
|
+
/** Abort signal — triggers cancellation and immediate return. */
|
|
1109
|
+
signal?: AbortSignal;
|
|
1110
|
+
/** Runtime input for the play function. */
|
|
1111
|
+
input?: Record<string, unknown>;
|
|
1112
|
+
sourceCode?: string;
|
|
1113
|
+
artifact?: Record<string, unknown>;
|
|
1114
|
+
compilerManifest?: PlayCompilerManifest;
|
|
1115
|
+
inputFile?: PlayStagedFileRef | null;
|
|
1116
|
+
packagedFiles?: PlayStagedFileRef[];
|
|
1117
|
+
/** Force-supersede any active runs for this play before starting. */
|
|
1118
|
+
force?: boolean;
|
|
1119
|
+
},
|
|
1120
|
+
): Promise<PlayRunResult> {
|
|
1121
|
+
const { workflowId } = await this.submitPlay(code, csvPath, name, {
|
|
1122
|
+
input: options?.input,
|
|
1123
|
+
sourceCode: options?.sourceCode,
|
|
1124
|
+
artifact: options?.artifact,
|
|
1125
|
+
compilerManifest: options?.compilerManifest,
|
|
1126
|
+
inputFile: options?.inputFile,
|
|
1127
|
+
packagedFiles: options?.packagedFiles,
|
|
1128
|
+
force: options?.force,
|
|
1129
|
+
});
|
|
1130
|
+
const pollInterval = options?.pollIntervalMs ?? 500;
|
|
1131
|
+
const start = Date.now();
|
|
1132
|
+
|
|
1133
|
+
while (true) {
|
|
1134
|
+
if (options?.signal?.aborted) {
|
|
1135
|
+
await this.cancelPlay(workflowId);
|
|
1136
|
+
return {
|
|
1137
|
+
success: false,
|
|
1138
|
+
runId: workflowId,
|
|
1139
|
+
logs: [],
|
|
1140
|
+
durationMs: Date.now() - start,
|
|
1141
|
+
error: 'Cancelled by user',
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
const status = await this.getPlayStatus(workflowId);
|
|
1146
|
+
options?.onProgress?.(status);
|
|
1147
|
+
|
|
1148
|
+
if (TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
1149
|
+
return {
|
|
1150
|
+
success: status.status === 'completed',
|
|
1151
|
+
runId: status.runId || workflowId,
|
|
1152
|
+
result: status.result,
|
|
1153
|
+
logs: status.progress?.logs ?? [],
|
|
1154
|
+
durationMs: Date.now() - start,
|
|
1155
|
+
error:
|
|
1156
|
+
status.progress?.error ??
|
|
1157
|
+
(status.status !== 'completed' ? status.status : undefined),
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// ——————————————————————————————————————————————————————————
|
|
1166
|
+
// Health
|
|
1167
|
+
// ——————————————————————————————————————————————————————————
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Check API connectivity and server health.
|
|
1171
|
+
*
|
|
1172
|
+
* @returns Health status with API version
|
|
1173
|
+
*
|
|
1174
|
+
* @example
|
|
1175
|
+
* ```typescript
|
|
1176
|
+
* const health = await client.health();
|
|
1177
|
+
* console.log(`API: ${health.status} (${health.version})`);
|
|
1178
|
+
* // { status: "ok", version: "v2" }
|
|
1179
|
+
* ```
|
|
1180
|
+
*/
|
|
1181
|
+
async health(): Promise<{ status: string; version?: string }> {
|
|
1182
|
+
return this.http.get<{ status: string; version?: string }>(
|
|
1183
|
+
'/api/v2/health',
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
export type { PlayRunListItem, PlayStatus } from './types.js';
|