deepline 0.1.10 → 0.1.12
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 +4 -4
- package/dist/cli/index.js +509 -353
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +513 -358
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +250 -305
- package/dist/index.d.ts +250 -305
- package/dist/index.js +174 -286
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +174 -285
- package/dist/index.mjs.map +1 -1
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +23 -13
- package/dist/repo/apps/play-runner-workers/src/entry.ts +581 -1220
- package/dist/repo/sdk/src/cli/commands/play.ts +381 -247
- package/dist/repo/sdk/src/cli/commands/tools.ts +1 -1
- package/dist/repo/sdk/src/cli/dataset-stats.ts +86 -12
- package/dist/repo/sdk/src/client.ts +54 -51
- package/dist/repo/sdk/src/index.ts +7 -16
- package/dist/repo/sdk/src/play.ts +122 -135
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +6 -3
- package/dist/repo/sdk/src/tool-output.ts +0 -111
- package/dist/repo/sdk/src/types.ts +2 -0
- package/dist/repo/sdk/src/version.ts +1 -1
- package/dist/repo/sdk/src/worker-play-entry.ts +3 -0
- package/dist/repo/shared_libs/play-runtime/context.ts +510 -267
- package/dist/repo/shared_libs/play-runtime/csv-rename.ts +180 -0
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +13 -1
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +139 -114
- package/dist/repo/shared_libs/plays/bundling/index.ts +68 -5
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +1 -1
- package/dist/repo/shared_libs/plays/dataset.ts +1 -1
- package/dist/repo/shared_libs/plays/runtime-validation.ts +8 -28
- package/package.json +1 -1
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +0 -184
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import {
|
|
3
3
|
existsSync,
|
|
4
|
-
mkdirSync,
|
|
5
4
|
readFileSync,
|
|
6
5
|
readdirSync,
|
|
7
6
|
realpathSync,
|
|
@@ -37,12 +36,10 @@ import {
|
|
|
37
36
|
getActiveCliProgress,
|
|
38
37
|
type CliProgress,
|
|
39
38
|
} from '../progress.js';
|
|
40
|
-
import { recordCliTrace, traceCliSpan } from '../trace.js';
|
|
41
39
|
import { argsWantJson } from '../utils.js';
|
|
42
40
|
|
|
43
41
|
type PlayRunCommandOptions = {
|
|
44
42
|
target: { kind: 'file'; path: string } | { kind: 'name'; name: string };
|
|
45
|
-
csvPath: string | null;
|
|
46
43
|
input: Record<string, unknown> | null;
|
|
47
44
|
revisionId: string | null;
|
|
48
45
|
revisionSelector: 'live' | 'latest' | null;
|
|
@@ -55,6 +52,10 @@ type PlayRunCommandOptions = {
|
|
|
55
52
|
outPath: string | null;
|
|
56
53
|
};
|
|
57
54
|
|
|
55
|
+
type FileInputBinding = {
|
|
56
|
+
inputPath: string;
|
|
57
|
+
};
|
|
58
|
+
|
|
58
59
|
type PlayCheckCommandOptions = {
|
|
59
60
|
target: string;
|
|
60
61
|
jsonOutput: boolean;
|
|
@@ -141,81 +142,6 @@ function defaultMaterializedPlayPath(reference: string): string {
|
|
|
141
142
|
return resolve(`${safeName || 'play'}.play.ts`);
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
function sanitizeGeneratedPlayName(value: string): string {
|
|
145
|
-
return (
|
|
146
|
-
value
|
|
147
|
-
.trim()
|
|
148
|
-
.toLowerCase()
|
|
149
|
-
.replace(/^prebuilt\//, '')
|
|
150
|
-
.replace(/[^a-z0-9-]/g, '-')
|
|
151
|
-
.replace(/-+/g, '-')
|
|
152
|
-
.replace(/^-|-$/g, '') || 'play'
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function buildGeneratedCsvWrapperSource(input: {
|
|
157
|
-
wrapperName: string;
|
|
158
|
-
playRef: string;
|
|
159
|
-
}): string {
|
|
160
|
-
return `import { definePlay } from 'deepline';
|
|
161
|
-
|
|
162
|
-
export default definePlay(
|
|
163
|
-
${JSON.stringify(input.wrapperName)},
|
|
164
|
-
async (ctx, input: Record<string, unknown> & { file: string }) => {
|
|
165
|
-
const rows = await ctx.csv<Record<string, unknown>>(input.file);
|
|
166
|
-
const constants = Object.fromEntries(
|
|
167
|
-
Object.entries(input).filter(([key]) => key !== 'file'),
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
const mappedRows = await ctx
|
|
171
|
-
.map('csv_rows', rows, {
|
|
172
|
-
key: (row, index) =>
|
|
173
|
-
String(
|
|
174
|
-
row.id ??
|
|
175
|
-
row.lead_id ??
|
|
176
|
-
row.email ??
|
|
177
|
-
row.linkedin_url ??
|
|
178
|
-
row.domain ??
|
|
179
|
-
index,
|
|
180
|
-
),
|
|
181
|
-
})
|
|
182
|
-
.step('result', (row, rowCtx) =>
|
|
183
|
-
rowCtx.runPlay(
|
|
184
|
-
'row_play',
|
|
185
|
-
${JSON.stringify(input.playRef)},
|
|
186
|
-
{
|
|
187
|
-
...constants,
|
|
188
|
-
...row,
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
description: 'Run the source play for this CSV row.',
|
|
192
|
-
},
|
|
193
|
-
),
|
|
194
|
-
)
|
|
195
|
-
.run({ description: 'Run the source play once per CSV row.' });
|
|
196
|
-
|
|
197
|
-
return { rows: mappedRows };
|
|
198
|
-
},
|
|
199
|
-
);
|
|
200
|
-
`;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function writeGeneratedCsvWrapperPlay(playRef: string): string {
|
|
204
|
-
const baseName = sanitizeGeneratedPlayName(
|
|
205
|
-
parseReferencedPlayTarget(playRef).unqualifiedPlayName,
|
|
206
|
-
);
|
|
207
|
-
const wrapperName = `${baseName}-csv`;
|
|
208
|
-
const outputDir = resolve('.deepline', 'generated');
|
|
209
|
-
const outputPath = join(outputDir, `${wrapperName}.play.ts`);
|
|
210
|
-
mkdirSync(outputDir, { recursive: true });
|
|
211
|
-
writeFileSync(
|
|
212
|
-
outputPath,
|
|
213
|
-
buildGeneratedCsvWrapperSource({ wrapperName, playRef }),
|
|
214
|
-
'utf-8',
|
|
215
|
-
);
|
|
216
|
-
return outputPath;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
145
|
type MaterializedRemotePlaySource = {
|
|
220
146
|
path: string;
|
|
221
147
|
status: 'created' | 'updated' | 'unchanged';
|
|
@@ -308,8 +234,15 @@ function looksLikeFilePath(target: string): boolean {
|
|
|
308
234
|
if (target.trim().toLowerCase().startsWith('prebuilt/')) {
|
|
309
235
|
return false;
|
|
310
236
|
}
|
|
237
|
+
if (
|
|
238
|
+
target.startsWith('./') ||
|
|
239
|
+
target.startsWith('../') ||
|
|
240
|
+
target.startsWith('/') ||
|
|
241
|
+
target.startsWith('~/')
|
|
242
|
+
) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
311
245
|
return (
|
|
312
|
-
target.includes('/') ||
|
|
313
246
|
target.includes('\\') ||
|
|
314
247
|
/\.(ts|js|mjs|play\.ts)$/.test(target)
|
|
315
248
|
);
|
|
@@ -334,6 +267,187 @@ function parseJsonInput(raw: string): Record<string, unknown> {
|
|
|
334
267
|
return parsed as Record<string, unknown>;
|
|
335
268
|
}
|
|
336
269
|
|
|
270
|
+
function parseInputFieldFlag(
|
|
271
|
+
rawFlag: string,
|
|
272
|
+
nextArg: string | undefined,
|
|
273
|
+
): { path: string; value: string } {
|
|
274
|
+
const flag = rawFlag.slice(2);
|
|
275
|
+
const equalsIndex = flag.indexOf('=');
|
|
276
|
+
if (equalsIndex > 0) {
|
|
277
|
+
const path = flag.slice(0, equalsIndex).trim();
|
|
278
|
+
const value = flag.slice(equalsIndex + 1);
|
|
279
|
+
if (!path) {
|
|
280
|
+
throw new Error(`Invalid play input flag: ${rawFlag}`);
|
|
281
|
+
}
|
|
282
|
+
return { path, value };
|
|
283
|
+
}
|
|
284
|
+
if (!nextArg || nextArg.startsWith('--')) {
|
|
285
|
+
throw new Error(`Play input flag ${rawFlag} requires a value.`);
|
|
286
|
+
}
|
|
287
|
+
return { path: flag, value: nextArg };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function parseInputFlagValue(raw: string): unknown {
|
|
291
|
+
const trimmed = raw.trim();
|
|
292
|
+
if (!trimmed) return '';
|
|
293
|
+
if (
|
|
294
|
+
trimmed === 'true' ||
|
|
295
|
+
trimmed === 'false' ||
|
|
296
|
+
trimmed === 'null' ||
|
|
297
|
+
trimmed.startsWith('{') ||
|
|
298
|
+
trimmed.startsWith('[') ||
|
|
299
|
+
/^-?\d+(\.\d+)?$/.test(trimmed)
|
|
300
|
+
) {
|
|
301
|
+
try {
|
|
302
|
+
return JSON.parse(trimmed);
|
|
303
|
+
} catch {
|
|
304
|
+
return raw;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return raw;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function getDottedInputValue(
|
|
311
|
+
input: Record<string, unknown>,
|
|
312
|
+
path: string,
|
|
313
|
+
): unknown {
|
|
314
|
+
const parts = path.split('.').map((part) => part.trim()).filter(Boolean);
|
|
315
|
+
let cursor: unknown = input;
|
|
316
|
+
for (const part of parts) {
|
|
317
|
+
if (!cursor || typeof cursor !== 'object' || Array.isArray(cursor)) {
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
320
|
+
cursor = (cursor as Record<string, unknown>)[part];
|
|
321
|
+
}
|
|
322
|
+
return cursor;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function setDottedInputValue(
|
|
326
|
+
input: Record<string, unknown>,
|
|
327
|
+
path: string,
|
|
328
|
+
value: unknown,
|
|
329
|
+
): void {
|
|
330
|
+
const parts = path.split('.').map((part) => part.trim()).filter(Boolean);
|
|
331
|
+
if (parts.length === 0) {
|
|
332
|
+
throw new Error(`Invalid play input flag path: ${path}`);
|
|
333
|
+
}
|
|
334
|
+
let cursor: Record<string, unknown> = input;
|
|
335
|
+
for (const part of parts.slice(0, -1)) {
|
|
336
|
+
const existing = cursor[part];
|
|
337
|
+
if (
|
|
338
|
+
existing !== undefined &&
|
|
339
|
+
(!existing || typeof existing !== 'object' || Array.isArray(existing))
|
|
340
|
+
) {
|
|
341
|
+
throw new Error(
|
|
342
|
+
`Cannot set --${path}; input.${part} is already a non-object value.`,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
if (!existing) {
|
|
346
|
+
cursor[part] = {};
|
|
347
|
+
}
|
|
348
|
+
cursor = cursor[part] as Record<string, unknown>;
|
|
349
|
+
}
|
|
350
|
+
cursor[parts[parts.length - 1]!] = value;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function schemaMetadata(
|
|
354
|
+
schema: Record<string, unknown> | null | undefined,
|
|
355
|
+
key: string,
|
|
356
|
+
): Record<string, unknown> | null {
|
|
357
|
+
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) return null;
|
|
358
|
+
const value = schema[key];
|
|
359
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
360
|
+
? (value as Record<string, unknown>)
|
|
361
|
+
: null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function stringMetadata(
|
|
365
|
+
metadata: Record<string, unknown> | null | undefined,
|
|
366
|
+
key: string,
|
|
367
|
+
): string | null {
|
|
368
|
+
const value = metadata?.[key];
|
|
369
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function inputFieldFromCsvArg(csvArg: unknown): string | null {
|
|
373
|
+
if (typeof csvArg !== 'string') return null;
|
|
374
|
+
const match = /^input\.([A-Za-z_$][\w$]*)$/.exec(csvArg.trim());
|
|
375
|
+
return match?.[1] ?? null;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function fileInputBindingsFromPlaySchema(
|
|
379
|
+
inputSchema: Record<string, unknown> | null | undefined,
|
|
380
|
+
): FileInputBinding[] {
|
|
381
|
+
const csvInput = schemaMetadata(inputSchema, 'csvInput');
|
|
382
|
+
if (!csvInput) return [];
|
|
383
|
+
return [
|
|
384
|
+
{
|
|
385
|
+
inputPath: stringMetadata(csvInput, 'inputField') ?? 'csv',
|
|
386
|
+
},
|
|
387
|
+
];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function fileInputBindingsFromStaticPipeline(
|
|
391
|
+
staticPipeline: unknown,
|
|
392
|
+
): FileInputBinding[] {
|
|
393
|
+
if (!staticPipeline || typeof staticPipeline !== 'object' || Array.isArray(staticPipeline)) {
|
|
394
|
+
return [];
|
|
395
|
+
}
|
|
396
|
+
const inputField = inputFieldFromCsvArg(
|
|
397
|
+
(staticPipeline as Record<string, unknown>).csvArg,
|
|
398
|
+
);
|
|
399
|
+
return inputField ? [{ inputPath: inputField }] : [];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function isLocalFilePathValue(value: unknown): value is string {
|
|
403
|
+
if (typeof value !== 'string' || !value.trim()) return false;
|
|
404
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
405
|
+
return existsSync(resolve(value));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async function stageFileInputArgs(input: {
|
|
409
|
+
client: DeeplineClient;
|
|
410
|
+
runtimeInput: Record<string, unknown>;
|
|
411
|
+
bindings: FileInputBinding[];
|
|
412
|
+
progress: CliProgress;
|
|
413
|
+
}): Promise<{
|
|
414
|
+
inputFile: PlayStagedFileRef | null;
|
|
415
|
+
packagedFiles: PlayStagedFileRef[];
|
|
416
|
+
}> {
|
|
417
|
+
const uniqueBindings = [
|
|
418
|
+
...new Map(input.bindings.map((binding) => [binding.inputPath, binding])).values(),
|
|
419
|
+
];
|
|
420
|
+
const localFiles = uniqueBindings.flatMap((binding) => {
|
|
421
|
+
const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
|
|
422
|
+
if (!isLocalFilePathValue(value)) return [];
|
|
423
|
+
const absolutePath = resolve(value);
|
|
424
|
+
return [{ binding, absolutePath, logicalPath: basename(absolutePath) }];
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
if (localFiles.length === 0) {
|
|
428
|
+
return { inputFile: null, packagedFiles: [] };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
input.progress.phase(
|
|
432
|
+
localFiles.length === 1 ? 'staging input file' : 'staging input files',
|
|
433
|
+
);
|
|
434
|
+
const staged = await input.client.stagePlayFiles(
|
|
435
|
+
localFiles.map((file) => stageFile(file.logicalPath, file.absolutePath)),
|
|
436
|
+
);
|
|
437
|
+
for (const [index, file] of localFiles.entries()) {
|
|
438
|
+
setDottedInputValue(input.runtimeInput, file.binding.inputPath, file.logicalPath);
|
|
439
|
+
const stagedFile = staged[index];
|
|
440
|
+
if (stagedFile && stagedFile.logicalPath !== file.logicalPath) {
|
|
441
|
+
setDottedInputValue(input.runtimeInput, file.binding.inputPath, stagedFile.logicalPath);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return {
|
|
446
|
+
inputFile: staged[0] ?? null,
|
|
447
|
+
packagedFiles: staged.slice(1),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
337
451
|
function stageFile(logicalPath: string, absolutePath: string) {
|
|
338
452
|
const buffer = readFileSync(absolutePath);
|
|
339
453
|
return {
|
|
@@ -937,24 +1051,10 @@ async function startAndWaitForPlayCompletionByStream(input: {
|
|
|
937
1051
|
Math.max(1, input.waitTimeoutMs),
|
|
938
1052
|
);
|
|
939
1053
|
|
|
940
|
-
recordCliTrace({
|
|
941
|
-
phase: 'cli.start_stream_request',
|
|
942
|
-
playName: input.playName,
|
|
943
|
-
});
|
|
944
1054
|
try {
|
|
945
|
-
let eventCount = 0;
|
|
946
1055
|
for await (const event of input.client.startPlayRunStream(input.request, {
|
|
947
1056
|
signal: controller.signal,
|
|
948
1057
|
})) {
|
|
949
|
-
eventCount += 1;
|
|
950
|
-
if (eventCount === 1) {
|
|
951
|
-
recordCliTrace({
|
|
952
|
-
phase: 'cli.start_stream_first_event',
|
|
953
|
-
ms: Date.now() - startedAt,
|
|
954
|
-
playName: input.playName,
|
|
955
|
-
eventType: event.type,
|
|
956
|
-
});
|
|
957
|
-
}
|
|
958
1058
|
const eventRunId = getEventPayload(event).runId;
|
|
959
1059
|
if (
|
|
960
1060
|
typeof eventRunId === 'string' &&
|
|
@@ -1002,14 +1102,6 @@ async function startAndWaitForPlayCompletionByStream(input: {
|
|
|
1002
1102
|
|
|
1003
1103
|
const finalStatus = getFinalStatusFromLiveEvent(event);
|
|
1004
1104
|
if (finalStatus) {
|
|
1005
|
-
recordCliTrace({
|
|
1006
|
-
phase: 'cli.start_stream_final_event',
|
|
1007
|
-
ms: Date.now() - startedAt,
|
|
1008
|
-
playName: input.playName,
|
|
1009
|
-
runId: finalStatus.runId,
|
|
1010
|
-
status: finalStatus.status,
|
|
1011
|
-
eventCount,
|
|
1012
|
-
});
|
|
1013
1105
|
return finalStatus;
|
|
1014
1106
|
}
|
|
1015
1107
|
}
|
|
@@ -1488,7 +1580,6 @@ function buildRunNextCommands(runId: string): Record<string, string> {
|
|
|
1488
1580
|
return {
|
|
1489
1581
|
exportCsv: `deepline runs export ${runId} --out output.csv`,
|
|
1490
1582
|
status: `deepline runs status ${runId} --json`,
|
|
1491
|
-
fullStatus: `deepline runs status ${runId} --json --full`,
|
|
1492
1583
|
logs: `deepline runs logs ${runId}`,
|
|
1493
1584
|
};
|
|
1494
1585
|
}
|
|
@@ -1542,13 +1633,30 @@ function compactPlayStatus(
|
|
|
1542
1633
|
...(result !== undefined ? { result } : {}),
|
|
1543
1634
|
...(status.resultView ? { resultView: status.resultView } : {}),
|
|
1544
1635
|
...(datasetStats ? { dataset_stats: datasetStats } : {}),
|
|
1545
|
-
...(rowsInfo ? { previewRows: rowsInfo.rows.slice(0,
|
|
1636
|
+
...(rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {}),
|
|
1546
1637
|
...(billing ? { billing } : {}),
|
|
1547
1638
|
...(status.run ? { run: status.run } : {}),
|
|
1548
1639
|
next: buildRunNextCommands(status.runId),
|
|
1549
1640
|
};
|
|
1550
1641
|
}
|
|
1551
1642
|
|
|
1643
|
+
function enrichPlayStatusWithDatasetStats(
|
|
1644
|
+
status: PlayStatus,
|
|
1645
|
+
): PlayStatus & { dataset_stats?: DatasetStats } {
|
|
1646
|
+
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
1647
|
+
if (!rowsInfo?.complete) {
|
|
1648
|
+
return status;
|
|
1649
|
+
}
|
|
1650
|
+
return {
|
|
1651
|
+
...status,
|
|
1652
|
+
dataset_stats: buildDatasetStats(
|
|
1653
|
+
rowsInfo.rows,
|
|
1654
|
+
rowsInfo.totalRows,
|
|
1655
|
+
rowsInfo.columns,
|
|
1656
|
+
),
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1552
1660
|
function formatDatasetStatsLines(datasetStats: DatasetStats | null): string[] {
|
|
1553
1661
|
if (!datasetStats) {
|
|
1554
1662
|
return [];
|
|
@@ -1559,17 +1667,17 @@ function formatDatasetStatsLines(datasetStats: DatasetStats | null): string[] {
|
|
|
1559
1667
|
12,
|
|
1560
1668
|
)) {
|
|
1561
1669
|
const topValues = stat.top_values
|
|
1562
|
-
? `,
|
|
1670
|
+
? `, top_values=${Object.entries(stat.top_values)
|
|
1563
1671
|
.slice(0, 3)
|
|
1564
1672
|
.map(([value, count]) => `${value}=${count}`)
|
|
1565
1673
|
.join(', ')}`
|
|
1566
1674
|
: '';
|
|
1567
1675
|
const sample =
|
|
1568
1676
|
stat.sample_value !== undefined
|
|
1569
|
-
? `,
|
|
1677
|
+
? `, sample_value=${JSON.stringify(stat.sample_value)}`
|
|
1570
1678
|
: '';
|
|
1571
1679
|
lines.push(
|
|
1572
|
-
` ${column}:
|
|
1680
|
+
` ${column}: non_empty=${stat.non_empty}, unique=${stat.unique}${topValues}${sample}`,
|
|
1573
1681
|
);
|
|
1574
1682
|
}
|
|
1575
1683
|
return lines;
|
|
@@ -1583,7 +1691,9 @@ function writePlayResult(
|
|
|
1583
1691
|
if (jsonOutput) {
|
|
1584
1692
|
process.stdout.write(
|
|
1585
1693
|
`${JSON.stringify(
|
|
1586
|
-
options?.fullJson
|
|
1694
|
+
options?.fullJson
|
|
1695
|
+
? enrichPlayStatusWithDatasetStats(status)
|
|
1696
|
+
: compactPlayStatus(status, options),
|
|
1587
1697
|
)}\n`,
|
|
1588
1698
|
);
|
|
1589
1699
|
return;
|
|
@@ -1833,13 +1943,12 @@ function writeStartedPlayRun(input: {
|
|
|
1833
1943
|
|
|
1834
1944
|
function parsePlayRunOptions(args: string[]): PlayRunCommandOptions {
|
|
1835
1945
|
const usage =
|
|
1836
|
-
"Usage: deepline plays run <play-name> [--input '{...}'] [--
|
|
1837
|
-
" deepline plays run <play-file.ts> [--input '{...}'] [--
|
|
1838
|
-
" deepline plays run --file <play-file.ts> [--input '{...}'] [--
|
|
1839
|
-
" deepline plays run --name <name> [--input '{...}'] [--
|
|
1946
|
+
"Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n" +
|
|
1947
|
+
" deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n" +
|
|
1948
|
+
" deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n" +
|
|
1949
|
+
" deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--json] [--<input> value]";
|
|
1840
1950
|
let filePath: string | null = null;
|
|
1841
1951
|
let playName: string | null = null;
|
|
1842
|
-
let csvPath: string | null = null;
|
|
1843
1952
|
let input: Record<string, unknown> | null = null;
|
|
1844
1953
|
let revisionId: string | null = null;
|
|
1845
1954
|
let revisionSelector: 'live' | 'latest' | null = null;
|
|
@@ -1861,10 +1970,6 @@ function parsePlayRunOptions(args: string[]): PlayRunCommandOptions {
|
|
|
1861
1970
|
playName = parseReferencedPlayTarget(args[++index]!).playName;
|
|
1862
1971
|
continue;
|
|
1863
1972
|
}
|
|
1864
|
-
if (arg === '--csv' && args[index + 1]) {
|
|
1865
|
-
csvPath = resolve(args[++index]!);
|
|
1866
|
-
continue;
|
|
1867
|
-
}
|
|
1868
1973
|
if ((arg === '--input' || arg === '-i') && args[index + 1]) {
|
|
1869
1974
|
input = parseJsonInput(args[++index]!);
|
|
1870
1975
|
continue;
|
|
@@ -1927,7 +2032,13 @@ function parsePlayRunOptions(args: string[]): PlayRunCommandOptions {
|
|
|
1927
2032
|
continue;
|
|
1928
2033
|
}
|
|
1929
2034
|
if (arg.startsWith('--')) {
|
|
1930
|
-
|
|
2035
|
+
const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
|
|
2036
|
+
input ??= {};
|
|
2037
|
+
setDottedInputValue(input, path, parseInputFlagValue(value));
|
|
2038
|
+
if (!arg.includes('=')) {
|
|
2039
|
+
index += 1;
|
|
2040
|
+
}
|
|
2041
|
+
continue;
|
|
1931
2042
|
}
|
|
1932
2043
|
if (!arg.startsWith('--') && !filePath && !playName) {
|
|
1933
2044
|
if (isFileTarget(arg) || looksLikeFilePath(arg)) {
|
|
@@ -1967,7 +2078,6 @@ function parsePlayRunOptions(args: string[]): PlayRunCommandOptions {
|
|
|
1967
2078
|
target: filePath
|
|
1968
2079
|
? { kind: 'file', path: filePath }
|
|
1969
2080
|
: { kind: 'name', name: playName! },
|
|
1970
|
-
csvPath,
|
|
1971
2081
|
input,
|
|
1972
2082
|
revisionId,
|
|
1973
2083
|
revisionSelector,
|
|
@@ -2055,37 +2165,15 @@ async function handleFileBackedRun(
|
|
|
2055
2165
|
const progress =
|
|
2056
2166
|
getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
2057
2167
|
const absolutePlayPath = resolve(options.target.path);
|
|
2058
|
-
recordCliTrace({
|
|
2059
|
-
phase: 'cli.play_run_file_start',
|
|
2060
|
-
playPath: absolutePlayPath,
|
|
2061
|
-
watch: options.watch,
|
|
2062
|
-
hasCsv: Boolean(options.csvPath),
|
|
2063
|
-
force: options.force,
|
|
2064
|
-
});
|
|
2065
2168
|
progress.phase('compiling play');
|
|
2066
|
-
const readSourceStartedAt = Date.now();
|
|
2067
2169
|
const sourceCode = readFileSync(absolutePlayPath, 'utf-8');
|
|
2068
|
-
recordCliTrace({
|
|
2069
|
-
phase: 'cli.read_play_source',
|
|
2070
|
-
ms: Date.now() - readSourceStartedAt,
|
|
2071
|
-
bytes: sourceCode.length,
|
|
2072
|
-
playPath: absolutePlayPath,
|
|
2073
|
-
});
|
|
2074
2170
|
let graph: {
|
|
2075
2171
|
root: BundledPlayFileSuccess;
|
|
2076
2172
|
nodes: Map<string, BundledPlayFileSuccess>;
|
|
2077
2173
|
};
|
|
2078
2174
|
try {
|
|
2079
|
-
graph = await
|
|
2080
|
-
|
|
2081
|
-
{ playPath: absolutePlayPath },
|
|
2082
|
-
() => collectBundledPlayGraph(absolutePlayPath),
|
|
2083
|
-
);
|
|
2084
|
-
await traceCliSpan(
|
|
2085
|
-
'cli.compile_play_manifest',
|
|
2086
|
-
{ playPath: absolutePlayPath, nodeCount: graph.nodes.size },
|
|
2087
|
-
() => compileBundledPlayGraphManifests(client, graph),
|
|
2088
|
-
);
|
|
2175
|
+
graph = await collectBundledPlayGraph(absolutePlayPath);
|
|
2176
|
+
await compileBundledPlayGraphManifests(client, graph);
|
|
2089
2177
|
progress.phase('compiled play');
|
|
2090
2178
|
} catch (error) {
|
|
2091
2179
|
progress.fail();
|
|
@@ -2099,11 +2187,7 @@ async function handleFileBackedRun(
|
|
|
2099
2187
|
|
|
2100
2188
|
try {
|
|
2101
2189
|
progress.phase('publishing imported plays');
|
|
2102
|
-
await
|
|
2103
|
-
'cli.publish_imported_plays',
|
|
2104
|
-
{ playName, nodeCount: graph.nodes.size },
|
|
2105
|
-
() => publishImportedPlayDependencies(client, graph),
|
|
2106
|
-
);
|
|
2190
|
+
await publishImportedPlayDependencies(client, graph);
|
|
2107
2191
|
} catch (error) {
|
|
2108
2192
|
progress.fail();
|
|
2109
2193
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -2111,26 +2195,16 @@ async function handleFileBackedRun(
|
|
|
2111
2195
|
}
|
|
2112
2196
|
|
|
2113
2197
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
2114
|
-
const prepareFilesStartedAt = Date.now();
|
|
2115
2198
|
const packagedFileUploads = bundleResult.packagedFiles.map((file) =>
|
|
2116
2199
|
stageFile(file.logicalPath, file.absolutePath),
|
|
2117
2200
|
);
|
|
2118
|
-
const
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
) {
|
|
2126
|
-
runtimeInput.file = basename(options.csvPath);
|
|
2127
|
-
}
|
|
2128
|
-
recordCliTrace({
|
|
2129
|
-
phase: 'cli.prepare_input_files',
|
|
2130
|
-
ms: Date.now() - prepareFilesStartedAt,
|
|
2131
|
-
playName,
|
|
2132
|
-
packagedFileCount: packagedFileUploads.length,
|
|
2133
|
-
hasInputFile: Boolean(inputFileUpload),
|
|
2201
|
+
const stagedFileInputs = await stageFileInputArgs({
|
|
2202
|
+
client,
|
|
2203
|
+
runtimeInput,
|
|
2204
|
+
bindings: fileInputBindingsFromStaticPipeline(
|
|
2205
|
+
requireCompilerManifest(bundleResult).staticPipeline,
|
|
2206
|
+
),
|
|
2207
|
+
progress,
|
|
2134
2208
|
});
|
|
2135
2209
|
|
|
2136
2210
|
const startRequest = {
|
|
@@ -2138,59 +2212,41 @@ async function handleFileBackedRun(
|
|
|
2138
2212
|
sourceCode: bundleResult.sourceCode,
|
|
2139
2213
|
runtimeArtifact: bundleResult.artifact,
|
|
2140
2214
|
compilerManifest: requireCompilerManifest(bundleResult),
|
|
2141
|
-
inputFileUpload,
|
|
2142
2215
|
packagedFileUploads,
|
|
2143
2216
|
...(Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {}),
|
|
2217
|
+
...(stagedFileInputs.inputFile
|
|
2218
|
+
? { inputFile: stagedFileInputs.inputFile }
|
|
2219
|
+
: {}),
|
|
2220
|
+
...(stagedFileInputs.packagedFiles.length
|
|
2221
|
+
? { packagedFiles: stagedFileInputs.packagedFiles }
|
|
2222
|
+
: {}),
|
|
2144
2223
|
...(options.force ? { force: true } : {}),
|
|
2145
2224
|
};
|
|
2146
2225
|
|
|
2147
2226
|
if (options.watch) {
|
|
2148
2227
|
progress.phase('starting run');
|
|
2149
|
-
const finalStatus = await
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
() =>
|
|
2153
|
-
startAndWaitForPlayCompletionByStream({
|
|
2154
|
-
client,
|
|
2155
|
-
request: startRequest,
|
|
2156
|
-
playName,
|
|
2157
|
-
jsonOutput: options.jsonOutput,
|
|
2158
|
-
emitLogs: options.emitLogs,
|
|
2159
|
-
waitTimeoutMs: options.waitTimeoutMs,
|
|
2160
|
-
progress,
|
|
2161
|
-
}),
|
|
2162
|
-
);
|
|
2163
|
-
const exportStartedAt = Date.now();
|
|
2164
|
-
const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
|
|
2165
|
-
recordCliTrace({
|
|
2166
|
-
phase: 'cli.export_rows',
|
|
2167
|
-
ms: Date.now() - exportStartedAt,
|
|
2228
|
+
const finalStatus = await startAndWaitForPlayCompletionByStream({
|
|
2229
|
+
client,
|
|
2230
|
+
request: startRequest,
|
|
2168
2231
|
playName,
|
|
2169
|
-
|
|
2232
|
+
jsonOutput: options.jsonOutput,
|
|
2233
|
+
emitLogs: options.emitLogs,
|
|
2234
|
+
waitTimeoutMs: options.waitTimeoutMs,
|
|
2235
|
+
progress,
|
|
2170
2236
|
});
|
|
2237
|
+
const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
|
|
2171
2238
|
if (finalStatus.status === 'completed') {
|
|
2172
2239
|
progress.complete();
|
|
2173
2240
|
} else {
|
|
2174
2241
|
progress.fail();
|
|
2175
2242
|
}
|
|
2176
|
-
recordCliTrace({
|
|
2177
|
-
phase: 'cli.write_play_result',
|
|
2178
|
-
playName,
|
|
2179
|
-
status: finalStatus.status,
|
|
2180
|
-
runId: finalStatus.runId,
|
|
2181
|
-
});
|
|
2182
2243
|
writePlayResult(finalStatus, options.jsonOutput, { exportedPath });
|
|
2183
2244
|
return finalStatus.status === 'completed' ? 0 : 1;
|
|
2184
2245
|
}
|
|
2185
2246
|
|
|
2186
2247
|
progress.phase('starting run');
|
|
2187
|
-
const started = await
|
|
2188
|
-
|
|
2189
|
-
{ playName },
|
|
2190
|
-
() => client.startPlayRun(startRequest),
|
|
2191
|
-
);
|
|
2192
|
-
const fallbackDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
2193
|
-
const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
|
|
2248
|
+
const started = await client.startPlayRun(startRequest);
|
|
2249
|
+
const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
2194
2250
|
progress.phase(`loading play on ${dashboardUrl}`);
|
|
2195
2251
|
progress.complete();
|
|
2196
2252
|
|
|
@@ -2199,7 +2255,7 @@ async function handleFileBackedRun(
|
|
|
2199
2255
|
playName,
|
|
2200
2256
|
status: started.status,
|
|
2201
2257
|
statusUrl: started.statusUrl,
|
|
2202
|
-
dashboardUrl,
|
|
2258
|
+
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
2203
2259
|
jsonOutput: options.jsonOutput,
|
|
2204
2260
|
progress,
|
|
2205
2261
|
});
|
|
@@ -2233,10 +2289,9 @@ async function handleNamedRun(options: PlayRunCommandOptions): Promise<number> {
|
|
|
2233
2289
|
const client = new DeeplineClient();
|
|
2234
2290
|
const progress =
|
|
2235
2291
|
getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
2236
|
-
let stagedInputFile: PlayStagedFileRef | null = null;
|
|
2237
2292
|
|
|
2238
2293
|
progress.phase('loading play definition');
|
|
2239
|
-
await assertCanonicalNamedPlayReference(client, options.target.name);
|
|
2294
|
+
const playDetail = await assertCanonicalNamedPlayReference(client, options.target.name);
|
|
2240
2295
|
progress.phase('selecting revision');
|
|
2241
2296
|
const selectedRevisionId = await resolveNamedRunRevisionId({
|
|
2242
2297
|
client,
|
|
@@ -2245,21 +2300,27 @@ async function handleNamedRun(options: PlayRunCommandOptions): Promise<number> {
|
|
|
2245
2300
|
selector: options.revisionSelector,
|
|
2246
2301
|
});
|
|
2247
2302
|
|
|
2248
|
-
if (options.csvPath) {
|
|
2249
|
-
progress.phase('staging input file');
|
|
2250
|
-
const [staged] = await client.stagePlayFiles([
|
|
2251
|
-
stageFile(basename(options.csvPath), options.csvPath),
|
|
2252
|
-
]);
|
|
2253
|
-
stagedInputFile = staged ?? null;
|
|
2254
|
-
}
|
|
2255
|
-
|
|
2256
2303
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
2304
|
+
const stagedFileInputs = await stageFileInputArgs({
|
|
2305
|
+
client,
|
|
2306
|
+
runtimeInput,
|
|
2307
|
+
bindings: [
|
|
2308
|
+
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
2309
|
+
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline),
|
|
2310
|
+
],
|
|
2311
|
+
progress,
|
|
2312
|
+
});
|
|
2257
2313
|
|
|
2258
2314
|
const startRequest = {
|
|
2259
2315
|
name: options.target.name,
|
|
2260
2316
|
...(selectedRevisionId ? { revisionId: selectedRevisionId } : {}),
|
|
2261
2317
|
...(Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {}),
|
|
2262
|
-
...(
|
|
2318
|
+
...(stagedFileInputs.inputFile
|
|
2319
|
+
? { inputFile: stagedFileInputs.inputFile }
|
|
2320
|
+
: {}),
|
|
2321
|
+
...(stagedFileInputs.packagedFiles.length
|
|
2322
|
+
? { packagedFiles: stagedFileInputs.packagedFiles }
|
|
2323
|
+
: {}),
|
|
2263
2324
|
...(options.force ? { force: true } : {}),
|
|
2264
2325
|
};
|
|
2265
2326
|
|
|
@@ -2274,22 +2335,6 @@ async function handleNamedRun(options: PlayRunCommandOptions): Promise<number> {
|
|
|
2274
2335
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
2275
2336
|
progress,
|
|
2276
2337
|
});
|
|
2277
|
-
if (finalStatus.status !== 'completed' && options.csvPath) {
|
|
2278
|
-
progress.phase('generating csv wrapper play');
|
|
2279
|
-
const generatedPlayPath = writeGeneratedCsvWrapperPlay(
|
|
2280
|
-
options.target.name,
|
|
2281
|
-
);
|
|
2282
|
-
progress.writeLogLine(
|
|
2283
|
-
`Generated CSV wrapper play: ${generatedPlayPath}`,
|
|
2284
|
-
);
|
|
2285
|
-
progress.phase('running generated csv wrapper play');
|
|
2286
|
-
return handleFileBackedRun({
|
|
2287
|
-
...options,
|
|
2288
|
-
target: { kind: 'file', path: generatedPlayPath },
|
|
2289
|
-
revisionId: null,
|
|
2290
|
-
revisionSelector: null,
|
|
2291
|
-
});
|
|
2292
|
-
}
|
|
2293
2338
|
const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
|
|
2294
2339
|
if (finalStatus.status === 'completed') {
|
|
2295
2340
|
progress.complete();
|
|
@@ -2302,11 +2347,10 @@ async function handleNamedRun(options: PlayRunCommandOptions): Promise<number> {
|
|
|
2302
2347
|
|
|
2303
2348
|
progress.phase('starting run');
|
|
2304
2349
|
const started = await client.startPlayRun(startRequest);
|
|
2305
|
-
const
|
|
2350
|
+
const dashboardUrl = buildPlayDashboardUrl(
|
|
2306
2351
|
client.baseUrl,
|
|
2307
2352
|
options.target.name,
|
|
2308
2353
|
);
|
|
2309
|
-
const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
|
|
2310
2354
|
progress.phase(`loading play on ${dashboardUrl}`);
|
|
2311
2355
|
progress.complete();
|
|
2312
2356
|
|
|
@@ -2315,7 +2359,7 @@ async function handleNamedRun(options: PlayRunCommandOptions): Promise<number> {
|
|
|
2315
2359
|
playName: started.name ?? options.target.name,
|
|
2316
2360
|
status: started.status,
|
|
2317
2361
|
statusUrl: started.statusUrl,
|
|
2318
|
-
dashboardUrl,
|
|
2362
|
+
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
2319
2363
|
jsonOutput: options.jsonOutput,
|
|
2320
2364
|
progress,
|
|
2321
2365
|
});
|
|
@@ -2558,8 +2602,9 @@ export async function handlePlayGet(args: string[]): Promise<number> {
|
|
|
2558
2602
|
}
|
|
2559
2603
|
|
|
2560
2604
|
const client = new DeeplineClient();
|
|
2561
|
-
const
|
|
2605
|
+
const explicitJson = args.includes('--json');
|
|
2562
2606
|
const sourceOutput = args.includes('--source');
|
|
2607
|
+
const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
|
|
2563
2608
|
let outPath: string | null = null;
|
|
2564
2609
|
for (let index = 1; index < args.length; index += 1) {
|
|
2565
2610
|
const arg = args[index]!;
|
|
@@ -2580,7 +2625,7 @@ export async function handlePlayGet(args: string[]): Promise<number> {
|
|
|
2580
2625
|
detail.play.sourceCode ??
|
|
2581
2626
|
'';
|
|
2582
2627
|
const materializedFile =
|
|
2583
|
-
|
|
2628
|
+
outPath
|
|
2584
2629
|
? materializeRemotePlaySource({
|
|
2585
2630
|
target,
|
|
2586
2631
|
playName,
|
|
@@ -2628,6 +2673,11 @@ export async function handlePlayGet(args: string[]): Promise<number> {
|
|
|
2628
2673
|
return 0;
|
|
2629
2674
|
}
|
|
2630
2675
|
|
|
2676
|
+
if (outPath && loadedMessage) {
|
|
2677
|
+
console.log(loadedMessage);
|
|
2678
|
+
return 0;
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2631
2681
|
console.log(`Play: ${formatPlayReference(detail.play)}`);
|
|
2632
2682
|
console.log(
|
|
2633
2683
|
`Working version: ${detail.play.workingRevision?.version ?? '—'}`,
|
|
@@ -2808,6 +2858,20 @@ function printPlayDescription(play: PlayDescription): void {
|
|
|
2808
2858
|
console.log(` ${line}`);
|
|
2809
2859
|
}
|
|
2810
2860
|
}
|
|
2861
|
+
if (play.csvInput) {
|
|
2862
|
+
console.log(' CSV input:');
|
|
2863
|
+
const rendered = JSON.stringify(play.csvInput, null, 2);
|
|
2864
|
+
for (const line of rendered.split('\n')) {
|
|
2865
|
+
console.log(` ${line}`);
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
if (play.rowOutputSchema) {
|
|
2869
|
+
console.log(' Row output schema:');
|
|
2870
|
+
const rendered = JSON.stringify(play.rowOutputSchema, null, 2);
|
|
2871
|
+
for (const line of rendered.split('\n')) {
|
|
2872
|
+
console.log(` ${line}`);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2811
2875
|
console.log(` Run: ${play.runCommand}`);
|
|
2812
2876
|
}
|
|
2813
2877
|
|
|
@@ -2961,6 +3025,47 @@ export async function handlePlayPublish(args: string[]): Promise<number> {
|
|
|
2961
3025
|
return result.success ? 0 : 1;
|
|
2962
3026
|
}
|
|
2963
3027
|
|
|
3028
|
+
export async function handlePlayDelete(args: string[]): Promise<number> {
|
|
3029
|
+
const playName = args[0];
|
|
3030
|
+
if (!playName) {
|
|
3031
|
+
console.error('Usage: deepline plays delete <play-name> --yes [--json]');
|
|
3032
|
+
return 1;
|
|
3033
|
+
}
|
|
3034
|
+
const confirmed =
|
|
3035
|
+
args.includes('--yes') || args.includes('-y') || args.includes('--force');
|
|
3036
|
+
if (!confirmed) {
|
|
3037
|
+
console.error(
|
|
3038
|
+
'Refusing to delete without --yes. This deletes the org-owned play, its revisions, trigger bindings, and local run records.',
|
|
3039
|
+
);
|
|
3040
|
+
return 1;
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
const client = new DeeplineClient();
|
|
3044
|
+
let detail: PlayDetail;
|
|
3045
|
+
try {
|
|
3046
|
+
detail = await client.getPlay(parseReferencedPlayTarget(playName).playName);
|
|
3047
|
+
} catch (error) {
|
|
3048
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
3049
|
+
return 1;
|
|
3050
|
+
}
|
|
3051
|
+
if (detail.play.ownerType === 'deepline' || detail.play.origin === 'prebuilt') {
|
|
3052
|
+
console.error(`Cannot delete prebuilt play: ${formatPlayReference(detail.play)}`);
|
|
3053
|
+
return 1;
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
const result = await client.deletePlay(
|
|
3057
|
+
parseReferencedPlayTarget(formatPlayReference(detail.play)).playName,
|
|
3058
|
+
);
|
|
3059
|
+
if (argsWantJson(args)) {
|
|
3060
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
3061
|
+
return result.deleted ? 0 : 1;
|
|
3062
|
+
}
|
|
3063
|
+
process.stdout.write(
|
|
3064
|
+
`Deleted ${result.name}: revisions=${result.deletedRevisionCount}, bindings=${result.deletedBindingCount}, runs=${result.deletedRunCount}\n`,
|
|
3065
|
+
);
|
|
3066
|
+
return result.deleted ? 0 : 1;
|
|
3067
|
+
}
|
|
3068
|
+
|
|
2964
3069
|
export function registerPlayCommands(program: Command): void {
|
|
2965
3070
|
const play = program
|
|
2966
3071
|
.command('plays')
|
|
@@ -3008,14 +3113,18 @@ Examples:
|
|
|
3008
3113
|
play
|
|
3009
3114
|
.command('run [target]')
|
|
3010
3115
|
.description('Run a play file or named play.')
|
|
3116
|
+
.allowUnknownOption(true)
|
|
3117
|
+
.allowExcessArguments(true)
|
|
3011
3118
|
.addHelpText(
|
|
3012
3119
|
'after',
|
|
3013
3120
|
`
|
|
3014
3121
|
Notes:
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3122
|
+
Local play files are bundled locally, then validated and executed in Deepline cloud.
|
|
3123
|
+
Named plays run the stored live cloud revision.
|
|
3124
|
+
Unknown --foo and --foo.bar flags are treated as play input args.
|
|
3125
|
+
File-like input args accept local paths; the CLI stages those files before submit.
|
|
3126
|
+
Run performs server preflight automatically. Use \`deepline plays check <file>\`
|
|
3127
|
+
to validate without starting a run.
|
|
3019
3128
|
|
|
3020
3129
|
Examples:
|
|
3021
3130
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
|
|
@@ -3025,7 +3134,6 @@ Examples:
|
|
|
3025
3134
|
)
|
|
3026
3135
|
.option('--file <path>', 'Local play file to run')
|
|
3027
3136
|
.option('--name <name>', 'Saved play name to run')
|
|
3028
|
-
.option('--csv <path>', 'Attach a CSV file')
|
|
3029
3137
|
.option('-i, --input <json>', 'Input JSON object or @file path')
|
|
3030
3138
|
.option('--live', 'Run the current live revision explicitly')
|
|
3031
3139
|
.option('--latest', 'Run the newest saved revision, even if it is not live')
|
|
@@ -3046,12 +3154,26 @@ Examples:
|
|
|
3046
3154
|
.option('--tail-timeout-ms <ms>', 'Timeout while tailing')
|
|
3047
3155
|
.option('--force', 'Supersede any active runs for this play')
|
|
3048
3156
|
.option('--json', 'Emit JSON output')
|
|
3049
|
-
.action(async (target, options) => {
|
|
3157
|
+
.action(async (target, options, command) => {
|
|
3158
|
+
const passthroughArgs = [...command.args];
|
|
3159
|
+
const explicitTarget = options.file || options.name;
|
|
3160
|
+
const targetIsInputFlag = typeof target === 'string' && target.startsWith('--');
|
|
3161
|
+
const effectiveTarget = explicitTarget || targetIsInputFlag ? null : target;
|
|
3162
|
+
if (
|
|
3163
|
+
explicitTarget &&
|
|
3164
|
+
typeof target === 'string' &&
|
|
3165
|
+
!targetIsInputFlag &&
|
|
3166
|
+
!passthroughArgs.includes(target)
|
|
3167
|
+
) {
|
|
3168
|
+
passthroughArgs.push(target);
|
|
3169
|
+
}
|
|
3170
|
+
if (effectiveTarget && passthroughArgs[0] === effectiveTarget) {
|
|
3171
|
+
passthroughArgs.shift();
|
|
3172
|
+
}
|
|
3050
3173
|
process.exitCode = await handlePlayRun([
|
|
3051
|
-
...(
|
|
3174
|
+
...(effectiveTarget ? [effectiveTarget] : []),
|
|
3052
3175
|
...(options.file ? ['--file', options.file] : []),
|
|
3053
3176
|
...(options.name ? ['--name', options.name] : []),
|
|
3054
|
-
...(options.csv ? ['--csv', options.csv] : []),
|
|
3055
3177
|
...(options.input ? ['--input', options.input] : []),
|
|
3056
3178
|
...(options.live ? ['--live'] : []),
|
|
3057
3179
|
...(options.latest ? ['--latest'] : []),
|
|
@@ -3067,6 +3189,7 @@ Examples:
|
|
|
3067
3189
|
: []),
|
|
3068
3190
|
...(options.force ? ['--force'] : []),
|
|
3069
3191
|
...(options.json ? ['--json'] : []),
|
|
3192
|
+
...passthroughArgs,
|
|
3070
3193
|
]);
|
|
3071
3194
|
});
|
|
3072
3195
|
|
|
@@ -3084,15 +3207,13 @@ Notes:
|
|
|
3084
3207
|
Examples:
|
|
3085
3208
|
deepline plays get person-linkedin-to-email
|
|
3086
3209
|
deepline plays get person-linkedin-to-email --json | jq '.play.liveRevision'
|
|
3210
|
+
deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source > email-waterfall.play.ts
|
|
3211
|
+
deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source --out ./email-waterfall.play.ts
|
|
3087
3212
|
`,
|
|
3088
3213
|
)
|
|
3089
3214
|
.option('--json', 'Emit JSON output. Also automatic when stdout is piped')
|
|
3090
|
-
.
|
|
3091
|
-
|
|
3092
|
-
)
|
|
3093
|
-
.addOption(
|
|
3094
|
-
new Option('--out <path>', 'Write source to a specific path').hideHelp(),
|
|
3095
|
-
)
|
|
3215
|
+
.option('--source', 'Print raw source code; combine with --out to write a file')
|
|
3216
|
+
.option('--out <path>', 'Write source to a specific path')
|
|
3096
3217
|
.action(async (target, options) => {
|
|
3097
3218
|
process.exitCode = await handlePlayGet([
|
|
3098
3219
|
target,
|
|
@@ -3200,7 +3321,7 @@ Examples:
|
|
|
3200
3321
|
.option('--run-id <runId>', 'Run id to inspect')
|
|
3201
3322
|
.option('--name <name>', 'Inspect the latest run for a named play')
|
|
3202
3323
|
.option('--json', 'Emit JSON output')
|
|
3203
|
-
.option('--full', '
|
|
3324
|
+
.option('--full', 'Debug only: with --json, emit the raw status payload')
|
|
3204
3325
|
.action(async (options) => {
|
|
3205
3326
|
process.exitCode = await handlePlayStatus([
|
|
3206
3327
|
...(options.runId ? ['--run-id', options.runId] : []),
|
|
@@ -3254,6 +3375,19 @@ Examples:
|
|
|
3254
3375
|
]);
|
|
3255
3376
|
});
|
|
3256
3377
|
|
|
3378
|
+
play
|
|
3379
|
+
.command('delete <target>')
|
|
3380
|
+
.description('Delete an org-owned play and its saved revisions/runs.')
|
|
3381
|
+
.option('-y, --yes', 'Confirm deletion')
|
|
3382
|
+
.option('--json', 'Emit JSON output. Also automatic when stdout is piped')
|
|
3383
|
+
.action(async (target, options) => {
|
|
3384
|
+
process.exitCode = await handlePlayDelete([
|
|
3385
|
+
target,
|
|
3386
|
+
...(options.yes ? ['--yes'] : []),
|
|
3387
|
+
...(options.json ? ['--json'] : []),
|
|
3388
|
+
]);
|
|
3389
|
+
});
|
|
3390
|
+
|
|
3257
3391
|
const runs = program
|
|
3258
3392
|
.command('runs')
|
|
3259
3393
|
.description('Inspect and export play runs.')
|
|
@@ -3271,7 +3405,7 @@ Examples:
|
|
|
3271
3405
|
.command('status <runId>')
|
|
3272
3406
|
.description('Show compact status for a play run.')
|
|
3273
3407
|
.option('--json', 'Emit JSON output. Also automatic when stdout is piped')
|
|
3274
|
-
.option('--full', '
|
|
3408
|
+
.option('--full', 'Debug only: with --json, emit the raw status payload')
|
|
3275
3409
|
.action(async (runId, options) => {
|
|
3276
3410
|
process.exitCode = await handleRunStatus([
|
|
3277
3411
|
runId,
|