deepline 0.1.20 → 0.1.22
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/dist/cli/index.js +845 -337
- package/dist/cli/index.mjs +902 -389
- package/dist/index.d.mts +23 -58
- package/dist/index.d.ts +23 -58
- package/dist/index.js +195 -92
- package/dist/index.mjs +195 -92
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +888 -227
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +540 -36
- package/dist/repo/apps/play-runner-workers/src/entry.ts +477 -374
- package/dist/repo/sdk/src/client.ts +245 -118
- package/dist/repo/sdk/src/http.ts +19 -1
- package/dist/repo/sdk/src/plays/harness-stub.ts +12 -0
- package/dist/repo/sdk/src/types.ts +8 -14
- package/dist/repo/sdk/src/version.ts +1 -1
- package/dist/repo/shared_libs/play-runtime/profiles.ts +4 -14
- package/dist/repo/shared_libs/play-runtime/runtime-actions.ts +1 -1
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +3 -6
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -266,7 +266,7 @@ function saveProjectDeeplineEnvValues(baseUrl, values, startDir = projectEnvStar
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
// src/version.ts
|
|
269
|
-
var SDK_VERSION = "0.1.
|
|
269
|
+
var SDK_VERSION = "0.1.22";
|
|
270
270
|
var SDK_API_CONTRACT = "2026-05-runs-v2";
|
|
271
271
|
|
|
272
272
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
@@ -349,7 +349,7 @@ var HttpClient = class {
|
|
|
349
349
|
const response = await fetch(candidateUrl, {
|
|
350
350
|
method,
|
|
351
351
|
headers,
|
|
352
|
-
body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
352
|
+
body: options?.formData !== void 0 ? options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
353
353
|
signal: controller.signal
|
|
354
354
|
});
|
|
355
355
|
clearTimeout(timeoutId);
|
|
@@ -468,6 +468,13 @@ var HttpClient = class {
|
|
|
468
468
|
headers
|
|
469
469
|
});
|
|
470
470
|
}
|
|
471
|
+
async postFormData(path, formData, headers) {
|
|
472
|
+
return this.request(path, {
|
|
473
|
+
method: "POST",
|
|
474
|
+
formData,
|
|
475
|
+
headers
|
|
476
|
+
});
|
|
477
|
+
}
|
|
471
478
|
/**
|
|
472
479
|
* Send a DELETE request.
|
|
473
480
|
*
|
|
@@ -551,7 +558,7 @@ function isRecord(value) {
|
|
|
551
558
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
552
559
|
}
|
|
553
560
|
function normalizePlayStatus(raw) {
|
|
554
|
-
const status = typeof raw.status === "string" ? raw.status :
|
|
561
|
+
const status = typeof raw.status === "string" ? raw.status : "running";
|
|
555
562
|
const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
|
|
556
563
|
return {
|
|
557
564
|
...raw,
|
|
@@ -559,22 +566,86 @@ function normalizePlayStatus(raw) {
|
|
|
559
566
|
status
|
|
560
567
|
};
|
|
561
568
|
}
|
|
562
|
-
function
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
569
|
+
function decodeBase64Bytes(value) {
|
|
570
|
+
const binary = atob(value);
|
|
571
|
+
const bytes = new Uint8Array(binary.length);
|
|
572
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
573
|
+
bytes[index] = binary.charCodeAt(index);
|
|
574
|
+
}
|
|
575
|
+
return bytes;
|
|
576
|
+
}
|
|
577
|
+
function readStringArray(value) {
|
|
578
|
+
return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
|
|
579
|
+
}
|
|
580
|
+
function getPlayLiveEventPayload(event) {
|
|
581
|
+
return event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
582
|
+
}
|
|
583
|
+
function normalizeLiveStatus(value) {
|
|
584
|
+
if (value === "queued" || value === "running" || value === "waiting" || value === "completed" || value === "failed" || value === "cancelled") {
|
|
585
|
+
return value;
|
|
586
|
+
}
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
function updatePlayLiveStatusState(state, event) {
|
|
590
|
+
const payload = getPlayLiveEventPayload(event);
|
|
591
|
+
if (event.type === "play.run.log") {
|
|
592
|
+
state.logs.push(...readStringArray(payload.lines));
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : state.runId;
|
|
599
|
+
const status = normalizeLiveStatus(payload.status) ?? state.status;
|
|
600
|
+
const logs = readStringArray(payload.logs);
|
|
601
|
+
if (logs.length > 0 || event.type === "play.run.snapshot") {
|
|
602
|
+
state.logs = logs;
|
|
603
|
+
}
|
|
604
|
+
if ("result" in payload) {
|
|
605
|
+
state.result = payload.result;
|
|
606
|
+
}
|
|
607
|
+
if (typeof payload.error === "string" && payload.error.trim()) {
|
|
608
|
+
state.error = payload.error;
|
|
577
609
|
}
|
|
610
|
+
state.runId = runId;
|
|
611
|
+
state.status = status;
|
|
612
|
+
const progressRecord = payload.progress && typeof payload.progress === "object" && !Array.isArray(payload.progress) ? payload.progress : {};
|
|
613
|
+
const next = {
|
|
614
|
+
...payload,
|
|
615
|
+
runId,
|
|
616
|
+
status,
|
|
617
|
+
progress: {
|
|
618
|
+
...progressRecord,
|
|
619
|
+
status: typeof progressRecord.status === "string" ? progressRecord.status : status,
|
|
620
|
+
logs: state.logs,
|
|
621
|
+
...state.error ? { error: state.error } : {}
|
|
622
|
+
},
|
|
623
|
+
..."result" in state ? { result: state.result } : {}
|
|
624
|
+
};
|
|
625
|
+
state.latest = next;
|
|
626
|
+
return next;
|
|
627
|
+
}
|
|
628
|
+
function playRunResultFromStatus(status, startedAt, fallbackRunId) {
|
|
629
|
+
return {
|
|
630
|
+
success: status.status === "completed",
|
|
631
|
+
runId: status.runId || fallbackRunId,
|
|
632
|
+
result: status.result,
|
|
633
|
+
logs: status.progress?.logs ?? [],
|
|
634
|
+
durationMs: Date.now() - startedAt,
|
|
635
|
+
error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function playRunStatusFromState(state) {
|
|
639
|
+
return {
|
|
640
|
+
runId: state.runId,
|
|
641
|
+
status: state.status,
|
|
642
|
+
progress: {
|
|
643
|
+
status: state.status,
|
|
644
|
+
logs: state.logs,
|
|
645
|
+
...state.error ? { error: state.error } : {}
|
|
646
|
+
},
|
|
647
|
+
..."result" in state ? { result: state.result } : {}
|
|
648
|
+
};
|
|
578
649
|
}
|
|
579
650
|
var DeeplineClient = class {
|
|
580
651
|
http;
|
|
@@ -685,7 +756,7 @@ var DeeplineClient = class {
|
|
|
685
756
|
/**
|
|
686
757
|
* Search available tools using Deepline's ranked backend search.
|
|
687
758
|
*
|
|
688
|
-
* This is the same discovery surface used by the
|
|
759
|
+
* This is the same discovery surface used by the CLI: it ranks across
|
|
689
760
|
* tool metadata, categories, agent guidance, and input schema fields.
|
|
690
761
|
*/
|
|
691
762
|
async searchTools(options = {}) {
|
|
@@ -778,7 +849,7 @@ var DeeplineClient = class {
|
|
|
778
849
|
* `progress.logs`; they are not part of the user output object.
|
|
779
850
|
*
|
|
780
851
|
* @param request - Play run configuration (name, code, input, etc.)
|
|
781
|
-
* @returns
|
|
852
|
+
* @returns Run metadata including the public `workflowId`
|
|
782
853
|
*
|
|
783
854
|
* @example
|
|
784
855
|
* ```typescript
|
|
@@ -1027,9 +1098,34 @@ var DeeplineClient = class {
|
|
|
1027
1098
|
* ```
|
|
1028
1099
|
*/
|
|
1029
1100
|
async stagePlayFiles(files) {
|
|
1030
|
-
const
|
|
1101
|
+
const formData = new FormData();
|
|
1102
|
+
formData.set(
|
|
1103
|
+
"metadata",
|
|
1104
|
+
JSON.stringify({
|
|
1105
|
+
files: files.map((file, index) => ({
|
|
1106
|
+
index,
|
|
1107
|
+
logicalPath: file.logicalPath,
|
|
1108
|
+
contentHash: file.contentHash,
|
|
1109
|
+
contentType: file.contentType,
|
|
1110
|
+
bytes: file.bytes
|
|
1111
|
+
}))
|
|
1112
|
+
})
|
|
1113
|
+
);
|
|
1114
|
+
for (const [index, file] of files.entries()) {
|
|
1115
|
+
const bytes = decodeBase64Bytes(file.contentBase64);
|
|
1116
|
+
const body = bytes.buffer.slice(
|
|
1117
|
+
bytes.byteOffset,
|
|
1118
|
+
bytes.byteOffset + bytes.byteLength
|
|
1119
|
+
);
|
|
1120
|
+
formData.set(
|
|
1121
|
+
`file:${index}`,
|
|
1122
|
+
new Blob([body], { type: file.contentType }),
|
|
1123
|
+
file.logicalPath
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
const response = await this.http.postFormData(
|
|
1031
1127
|
"/api/v2/plays/files/stage",
|
|
1032
|
-
|
|
1128
|
+
formData
|
|
1033
1129
|
);
|
|
1034
1130
|
return response.files;
|
|
1035
1131
|
}
|
|
@@ -1045,9 +1141,6 @@ var DeeplineClient = class {
|
|
|
1045
1141
|
* Internal/advanced primitive. Public callers should usually prefer
|
|
1046
1142
|
* {@link runPlay}, {@link PlayJob.get}, or `deepline play run --watch`.
|
|
1047
1143
|
*
|
|
1048
|
-
* Poll this method until `status` reaches a terminal state:
|
|
1049
|
-
* `'completed'`, `'failed'`, or `'cancelled'`.
|
|
1050
|
-
*
|
|
1051
1144
|
* @param workflowId - Play-run id from {@link startPlayRun}
|
|
1052
1145
|
* @returns Current status with progress logs and partial results
|
|
1053
1146
|
*
|
|
@@ -1058,41 +1151,22 @@ var DeeplineClient = class {
|
|
|
1058
1151
|
* console.log(`Logs: ${status.progress?.logs.length ?? 0} lines`);
|
|
1059
1152
|
* ```
|
|
1060
1153
|
*/
|
|
1061
|
-
async getPlayStatus(workflowId) {
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
return normalizePlayStatus(response);
|
|
1066
|
-
}
|
|
1067
|
-
/**
|
|
1068
|
-
* Get the lightweight tail-polling status for a play execution.
|
|
1069
|
-
*
|
|
1070
|
-
* This is intentionally smaller than {@link getPlayStatus}: it returns the
|
|
1071
|
-
* fields needed for CLI log tailing while the run is in flight, without
|
|
1072
|
-
* forcing the API to rebuild final result views on every poll. Call
|
|
1073
|
-
* {@link getPlayStatus} once after a terminal state for the full result.
|
|
1074
|
-
*/
|
|
1075
|
-
async getPlayTailStatus(workflowId, options) {
|
|
1076
|
-
const params = new URLSearchParams({ mode: "tail" });
|
|
1077
|
-
if (typeof options?.afterLogIndex === "number") {
|
|
1078
|
-
params.set("afterLogIndex", String(options.afterLogIndex));
|
|
1079
|
-
}
|
|
1080
|
-
if (typeof options?.waitMs === "number") {
|
|
1081
|
-
params.set("waitMs", String(options.waitMs));
|
|
1082
|
-
}
|
|
1083
|
-
if (options?.terminalOnly) {
|
|
1084
|
-
params.set("terminalOnly", "true");
|
|
1154
|
+
async getPlayStatus(workflowId, options) {
|
|
1155
|
+
const params = new URLSearchParams();
|
|
1156
|
+
if (options?.billing === false) {
|
|
1157
|
+
params.set("billing", "false");
|
|
1085
1158
|
}
|
|
1159
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
1086
1160
|
const response = await this.http.get(
|
|
1087
|
-
`/api/v2/plays/run/${encodeURIComponent(workflowId)}
|
|
1161
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}${query}`
|
|
1088
1162
|
);
|
|
1089
1163
|
return normalizePlayStatus(response);
|
|
1090
1164
|
}
|
|
1091
1165
|
/**
|
|
1092
1166
|
* Stream semantic play-run events using the same SSE feed as the dashboard.
|
|
1093
1167
|
*
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
1168
|
+
* The server emits a canonical `play.run.snapshot` event first for every
|
|
1169
|
+
* connection, then incremental live events until terminal state or reconnect.
|
|
1096
1170
|
*/
|
|
1097
1171
|
async *streamPlayRunEvents(workflowId, options) {
|
|
1098
1172
|
const headers = options?.lastEventId && options.lastEventId.trim() ? { "Last-Event-ID": options.lastEventId.trim() } : void 0;
|
|
@@ -1112,7 +1186,7 @@ var DeeplineClient = class {
|
|
|
1112
1186
|
*
|
|
1113
1187
|
* Sends a stop request for the run.
|
|
1114
1188
|
*
|
|
1115
|
-
* @param workflowId -
|
|
1189
|
+
* @param workflowId - Public Deepline play-run id to cancel
|
|
1116
1190
|
*
|
|
1117
1191
|
* @example
|
|
1118
1192
|
* ```typescript
|
|
@@ -1128,7 +1202,7 @@ var DeeplineClient = class {
|
|
|
1128
1202
|
/**
|
|
1129
1203
|
* Stop a running play execution, including open HITL waits.
|
|
1130
1204
|
*
|
|
1131
|
-
* @param workflowId -
|
|
1205
|
+
* @param workflowId - Public Deepline play-run id to stop
|
|
1132
1206
|
* @param options.reason - Optional audit/debug reason
|
|
1133
1207
|
*/
|
|
1134
1208
|
async stopPlay(workflowId, options) {
|
|
@@ -1200,32 +1274,42 @@ var DeeplineClient = class {
|
|
|
1200
1274
|
);
|
|
1201
1275
|
return response.runs ?? [];
|
|
1202
1276
|
}
|
|
1203
|
-
/**
|
|
1204
|
-
* Fetch the lightweight tail status for a run using the public runs resource model.
|
|
1205
|
-
*
|
|
1206
|
-
* This is the SDK equivalent of:
|
|
1207
|
-
*
|
|
1208
|
-
* ```bash
|
|
1209
|
-
* deepline runs tail <run-id> --json
|
|
1210
|
-
* ```
|
|
1211
|
-
*/
|
|
1277
|
+
/** Read the canonical run stream and return the latest run snapshot. */
|
|
1212
1278
|
async tailRun(runId, options) {
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1279
|
+
const state = {
|
|
1280
|
+
runId,
|
|
1281
|
+
status: "running",
|
|
1282
|
+
logs: [],
|
|
1283
|
+
latest: null
|
|
1284
|
+
};
|
|
1285
|
+
let terminal = false;
|
|
1286
|
+
for await (const event of this.streamPlayRunEvents(runId, {
|
|
1287
|
+
mode: "cli",
|
|
1288
|
+
signal: options?.signal
|
|
1289
|
+
})) {
|
|
1290
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
1291
|
+
if (!status) {
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
terminal = TERMINAL_PLAY_STATUSES.has(status.status);
|
|
1295
|
+
if (terminal) {
|
|
1296
|
+
break;
|
|
1297
|
+
}
|
|
1217
1298
|
}
|
|
1218
|
-
if (
|
|
1219
|
-
|
|
1299
|
+
if (terminal && state.latest) {
|
|
1300
|
+
return await this.getRunStatus(state.latest.runId || runId).catch(
|
|
1301
|
+
() => state.latest ?? playRunStatusFromState(state)
|
|
1302
|
+
);
|
|
1220
1303
|
}
|
|
1221
|
-
if (
|
|
1222
|
-
|
|
1304
|
+
if (state.latest) {
|
|
1305
|
+
return state.latest;
|
|
1223
1306
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1307
|
+
throw new DeeplineError(
|
|
1308
|
+
`Run stream for ${runId} ended before the initial snapshot.`,
|
|
1309
|
+
void 0,
|
|
1310
|
+
"PLAY_RUN_STREAM_EMPTY",
|
|
1311
|
+
{ runId }
|
|
1227
1312
|
);
|
|
1228
|
-
return normalizePlayStatus(response);
|
|
1229
1313
|
}
|
|
1230
1314
|
/**
|
|
1231
1315
|
* Fetch persisted logs for a run using the public runs resource model.
|
|
@@ -1382,11 +1466,11 @@ var DeeplineClient = class {
|
|
|
1382
1466
|
// Plays — high-level orchestration
|
|
1383
1467
|
// ——————————————————————————————————————————————————————————
|
|
1384
1468
|
/**
|
|
1385
|
-
* Run a play end-to-end: submit,
|
|
1469
|
+
* Run a play end-to-end: submit, stream until terminal, return result.
|
|
1386
1470
|
*
|
|
1387
1471
|
* This is the highest-level play execution method. It submits the play,
|
|
1388
|
-
*
|
|
1389
|
-
* and timing. Supports cancellation via `AbortSignal`.
|
|
1472
|
+
* reads the canonical run stream for status updates, and returns a structured
|
|
1473
|
+
* result with logs and timing. Supports cancellation via `AbortSignal`.
|
|
1390
1474
|
*
|
|
1391
1475
|
* @param code - Source string fallback; pass the bundled artifact in `options.artifact`
|
|
1392
1476
|
* @param csvPath - Input CSV path, or `null`
|
|
@@ -1402,7 +1486,6 @@ var DeeplineClient = class {
|
|
|
1402
1486
|
* const logs = status.progress?.logs ?? [];
|
|
1403
1487
|
* console.log(`[${status.status}] ${logs.length} log lines`);
|
|
1404
1488
|
* },
|
|
1405
|
-
* pollIntervalMs: 1000,
|
|
1406
1489
|
* });
|
|
1407
1490
|
*
|
|
1408
1491
|
* if (result.success) {
|
|
@@ -1433,33 +1516,53 @@ var DeeplineClient = class {
|
|
|
1433
1516
|
packagedFiles: options?.packagedFiles,
|
|
1434
1517
|
force: options?.force
|
|
1435
1518
|
});
|
|
1436
|
-
const pollInterval = options?.pollIntervalMs ?? 500;
|
|
1437
1519
|
const start = Date.now();
|
|
1438
|
-
|
|
1520
|
+
const state = {
|
|
1521
|
+
runId: workflowId,
|
|
1522
|
+
status: "running",
|
|
1523
|
+
logs: [],
|
|
1524
|
+
latest: null
|
|
1525
|
+
};
|
|
1526
|
+
if (options?.signal?.aborted) {
|
|
1527
|
+
await this.cancelPlay(workflowId);
|
|
1528
|
+
return {
|
|
1529
|
+
success: false,
|
|
1530
|
+
runId: workflowId,
|
|
1531
|
+
logs: [],
|
|
1532
|
+
durationMs: Date.now() - start,
|
|
1533
|
+
error: "Cancelled by user"
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
for await (const event of this.streamPlayRunEvents(workflowId, {
|
|
1537
|
+
mode: "cli",
|
|
1538
|
+
signal: options?.signal
|
|
1539
|
+
})) {
|
|
1439
1540
|
if (options?.signal?.aborted) {
|
|
1440
1541
|
await this.cancelPlay(workflowId);
|
|
1441
1542
|
return {
|
|
1442
1543
|
success: false,
|
|
1443
1544
|
runId: workflowId,
|
|
1444
|
-
logs:
|
|
1545
|
+
logs: state.logs,
|
|
1445
1546
|
durationMs: Date.now() - start,
|
|
1446
1547
|
error: "Cancelled by user"
|
|
1447
1548
|
};
|
|
1448
1549
|
}
|
|
1449
|
-
const status =
|
|
1550
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
1551
|
+
if (!status) {
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1450
1554
|
options?.onProgress?.(status);
|
|
1451
1555
|
if (TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
runId: status.runId || workflowId,
|
|
1455
|
-
result: status.result,
|
|
1456
|
-
logs: status.progress?.logs ?? [],
|
|
1457
|
-
durationMs: Date.now() - start,
|
|
1458
|
-
error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
|
|
1459
|
-
};
|
|
1556
|
+
const finalStatus = await this.getPlayStatus(status.runId || workflowId).catch(() => status);
|
|
1557
|
+
return playRunResultFromStatus(finalStatus, start, workflowId);
|
|
1460
1558
|
}
|
|
1461
|
-
await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
|
|
1462
1559
|
}
|
|
1560
|
+
throw new DeeplineError(
|
|
1561
|
+
`Run stream for ${workflowId} ended before the run reached a terminal state.`,
|
|
1562
|
+
void 0,
|
|
1563
|
+
"PLAY_RUN_STREAM_ENDED",
|
|
1564
|
+
{ runId: workflowId, workflowId }
|
|
1565
|
+
);
|
|
1463
1566
|
}
|
|
1464
1567
|
// ——————————————————————————————————————————————————————————
|
|
1465
1568
|
// Health
|
|
@@ -1545,6 +1648,7 @@ var import_node_path2 = require("path");
|
|
|
1545
1648
|
var import_node_child_process = require("child_process");
|
|
1546
1649
|
var import_sync = require("csv-parse/sync");
|
|
1547
1650
|
var import_sync2 = require("csv-stringify/sync");
|
|
1651
|
+
var BROWSER_FOCUS_COOLDOWN_MS = 3e4;
|
|
1548
1652
|
function getAuthedHttpClient() {
|
|
1549
1653
|
const config = resolveConfig();
|
|
1550
1654
|
return { config, http: new HttpClient(config) };
|
|
@@ -1556,12 +1660,215 @@ async function writeOutputFile(filename, content) {
|
|
|
1556
1660
|
await (0, import_promises.writeFile)(fullPath, content, "utf-8");
|
|
1557
1661
|
return fullPath;
|
|
1558
1662
|
}
|
|
1663
|
+
function browserFocusStateFile() {
|
|
1664
|
+
const homeDir = process.env.HOME || (0, import_node_os2.homedir)();
|
|
1665
|
+
return (0, import_node_path2.join)(
|
|
1666
|
+
homeDir,
|
|
1667
|
+
".local",
|
|
1668
|
+
"deepline",
|
|
1669
|
+
"runtime",
|
|
1670
|
+
"state",
|
|
1671
|
+
"browser-focus.json"
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
function claimBrowserFocus(now = Date.now()) {
|
|
1675
|
+
const statePath = browserFocusStateFile();
|
|
1676
|
+
try {
|
|
1677
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(statePath), { recursive: true });
|
|
1678
|
+
let lastFocusedAt = 0;
|
|
1679
|
+
if ((0, import_node_fs2.existsSync)(statePath)) {
|
|
1680
|
+
const payload = JSON.parse((0, import_node_fs2.readFileSync)(statePath, "utf-8"));
|
|
1681
|
+
const value = payload.lastFocusedAt ?? payload.last_focused_at;
|
|
1682
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1683
|
+
lastFocusedAt = value;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
if (lastFocusedAt > now) {
|
|
1687
|
+
lastFocusedAt = 0;
|
|
1688
|
+
}
|
|
1689
|
+
if (now - lastFocusedAt < BROWSER_FOCUS_COOLDOWN_MS) {
|
|
1690
|
+
return false;
|
|
1691
|
+
}
|
|
1692
|
+
(0, import_node_fs2.writeFileSync)(statePath, JSON.stringify({ lastFocusedAt: now }), "utf-8");
|
|
1693
|
+
return true;
|
|
1694
|
+
} catch {
|
|
1695
|
+
return true;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
function extractUrlHost(raw) {
|
|
1699
|
+
try {
|
|
1700
|
+
const parsed = new URL(raw.includes("://") ? raw : `https://${raw}`);
|
|
1701
|
+
return parsed.port ? `${parsed.hostname.toLowerCase()}:${parsed.port}` : parsed.hostname.toLowerCase();
|
|
1702
|
+
} catch {
|
|
1703
|
+
return "";
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
function browserAppNameFromBundleId(bundleId) {
|
|
1707
|
+
const names = {
|
|
1708
|
+
"com.google.chrome": "Google Chrome",
|
|
1709
|
+
"com.google.chrome.canary": "Google Chrome Canary",
|
|
1710
|
+
"com.microsoft.edgemac": "Microsoft Edge",
|
|
1711
|
+
"com.brave.browser": "Brave Browser",
|
|
1712
|
+
"com.operasoftware.opera": "Opera",
|
|
1713
|
+
"com.operasoftware.operagx": "Opera GX",
|
|
1714
|
+
"com.vivaldi.vivaldi": "Vivaldi",
|
|
1715
|
+
"company.thebrowser.browser": "Arc",
|
|
1716
|
+
"com.apple.safari": "Safari"
|
|
1717
|
+
};
|
|
1718
|
+
return names[bundleId.toLowerCase()] ?? "";
|
|
1719
|
+
}
|
|
1720
|
+
function readDefaultMacBrowserBundleId() {
|
|
1721
|
+
try {
|
|
1722
|
+
const output = (0, import_node_child_process.execFileSync)(
|
|
1723
|
+
"/usr/bin/defaults",
|
|
1724
|
+
[
|
|
1725
|
+
"read",
|
|
1726
|
+
`${(0, import_node_os2.homedir)()}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`,
|
|
1727
|
+
"LSHandlers"
|
|
1728
|
+
],
|
|
1729
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
|
|
1730
|
+
);
|
|
1731
|
+
const httpsMatch = output.match(
|
|
1732
|
+
/LSHandlerURLScheme\s*=\s*https;[\s\S]*?LSHandlerRole(?:All|Viewer|Editor)\s*=\s*"([^"]+)"/
|
|
1733
|
+
);
|
|
1734
|
+
const httpMatch = output.match(
|
|
1735
|
+
/LSHandlerURLScheme\s*=\s*http;[\s\S]*?LSHandlerRole(?:All|Viewer|Editor)\s*=\s*"([^"]+)"/
|
|
1736
|
+
);
|
|
1737
|
+
return (httpsMatch?.[1] ?? httpMatch?.[1] ?? "").trim();
|
|
1738
|
+
} catch {
|
|
1739
|
+
return "";
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
function browserStrategyForBundleId(bundleId) {
|
|
1743
|
+
const normalized = bundleId.toLowerCase();
|
|
1744
|
+
if ((/* @__PURE__ */ new Set([
|
|
1745
|
+
"com.google.chrome",
|
|
1746
|
+
"com.google.chrome.canary",
|
|
1747
|
+
"com.microsoft.edgemac",
|
|
1748
|
+
"com.brave.browser",
|
|
1749
|
+
"com.operasoftware.opera",
|
|
1750
|
+
"com.operasoftware.operagx",
|
|
1751
|
+
"com.vivaldi.vivaldi",
|
|
1752
|
+
"company.thebrowser.browser"
|
|
1753
|
+
])).has(normalized)) {
|
|
1754
|
+
return "chromium";
|
|
1755
|
+
}
|
|
1756
|
+
return normalized === "com.apple.safari" ? "safari" : "fallback";
|
|
1757
|
+
}
|
|
1758
|
+
function runAppleScript(script, args) {
|
|
1759
|
+
const result = (0, import_node_child_process.spawnSync)("osascript", ["-", ...args], {
|
|
1760
|
+
input: script,
|
|
1761
|
+
encoding: "utf-8",
|
|
1762
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
1763
|
+
timeout: 5e3
|
|
1764
|
+
});
|
|
1765
|
+
return result.status === 0;
|
|
1766
|
+
}
|
|
1767
|
+
function retargetChromiumMacos(appName, targetUrl, allowFocus) {
|
|
1768
|
+
const host = extractUrlHost(targetUrl);
|
|
1769
|
+
if (!host) return false;
|
|
1770
|
+
const escapedAppName = appName.replace(/"/g, '\\"');
|
|
1771
|
+
const activateBlock = allowFocus ? " activate\n" : "";
|
|
1772
|
+
const foundTabBlock = allowFocus ? " set active tab index of w to i\n set index of w to 1\n set URL of t to targetUrl\n" : " set URL of t to targetUrl\n";
|
|
1773
|
+
const newTabBlock = allowFocus ? " tell window 1\n make new tab with properties {URL:targetUrl}\n set active tab index to (count of tabs)\n set index to 1\n end tell\n" : " tell window 1\n make new tab with properties {URL:targetUrl}\n end tell\n";
|
|
1774
|
+
const script = `
|
|
1775
|
+
on run argv
|
|
1776
|
+
set targetUrl to item 1 of argv
|
|
1777
|
+
set targetHost to item 2 of argv
|
|
1778
|
+
tell application "${escapedAppName}"
|
|
1779
|
+
${activateBlock} if (count of windows) is 0 then
|
|
1780
|
+
make new window
|
|
1781
|
+
end if
|
|
1782
|
+
set foundTab to false
|
|
1783
|
+
repeat with w in windows
|
|
1784
|
+
set tabCount to count of tabs of w
|
|
1785
|
+
repeat with i from 1 to tabCount
|
|
1786
|
+
set t to tab i of w
|
|
1787
|
+
if (URL of t) contains targetHost then
|
|
1788
|
+
${foundTabBlock} set foundTab to true
|
|
1789
|
+
exit repeat
|
|
1790
|
+
end if
|
|
1791
|
+
end repeat
|
|
1792
|
+
if foundTab then exit repeat
|
|
1793
|
+
end repeat
|
|
1794
|
+
if not foundTab then
|
|
1795
|
+
${newTabBlock} end if
|
|
1796
|
+
end tell
|
|
1797
|
+
end run
|
|
1798
|
+
`;
|
|
1799
|
+
return runAppleScript(script, [targetUrl, host]);
|
|
1800
|
+
}
|
|
1801
|
+
function retargetSafariMacos(appName, targetUrl, allowFocus) {
|
|
1802
|
+
const host = extractUrlHost(targetUrl);
|
|
1803
|
+
if (!host) return false;
|
|
1804
|
+
const escapedAppName = appName.replace(/"/g, '\\"');
|
|
1805
|
+
const activateBlock = allowFocus ? " activate\n" : "";
|
|
1806
|
+
const foundTabBlock = allowFocus ? " set current tab of w to t\n set index of w to 1\n set URL of t to targetUrl\n" : " set URL of t to targetUrl\n";
|
|
1807
|
+
const newTabBlock = allowFocus ? " tell window 1\n set current tab to (make new tab with properties {URL:targetUrl})\n set index to 1\n end tell\n" : " tell window 1\n make new tab with properties {URL:targetUrl}\n end tell\n";
|
|
1808
|
+
const script = `
|
|
1809
|
+
on run argv
|
|
1810
|
+
set targetUrl to item 1 of argv
|
|
1811
|
+
set targetHost to item 2 of argv
|
|
1812
|
+
tell application "${escapedAppName}"
|
|
1813
|
+
${activateBlock} if (count of windows) is 0 then
|
|
1814
|
+
make new document
|
|
1815
|
+
end if
|
|
1816
|
+
set foundTab to false
|
|
1817
|
+
repeat with w in windows
|
|
1818
|
+
set tabCount to count of tabs of w
|
|
1819
|
+
repeat with i from 1 to tabCount
|
|
1820
|
+
set t to tab i of w
|
|
1821
|
+
if (URL of t) contains targetHost then
|
|
1822
|
+
${foundTabBlock} set foundTab to true
|
|
1823
|
+
exit repeat
|
|
1824
|
+
end if
|
|
1825
|
+
end repeat
|
|
1826
|
+
if foundTab then exit repeat
|
|
1827
|
+
end repeat
|
|
1828
|
+
if not foundTab then
|
|
1829
|
+
${newTabBlock} end if
|
|
1830
|
+
end tell
|
|
1831
|
+
end run
|
|
1832
|
+
`;
|
|
1833
|
+
return runAppleScript(script, [targetUrl, host]);
|
|
1834
|
+
}
|
|
1835
|
+
function openUrlMacos(targetUrl, allowFocus) {
|
|
1836
|
+
const defaultBundleId = readDefaultMacBrowserBundleId();
|
|
1837
|
+
const appName = defaultBundleId ? browserAppNameFromBundleId(defaultBundleId) : "";
|
|
1838
|
+
const strategy = browserStrategyForBundleId(defaultBundleId);
|
|
1839
|
+
if (appName && strategy === "chromium" && retargetChromiumMacos(appName, targetUrl, allowFocus)) {
|
|
1840
|
+
return true;
|
|
1841
|
+
}
|
|
1842
|
+
if (appName && strategy === "safari" && retargetSafariMacos(appName, targetUrl, allowFocus)) {
|
|
1843
|
+
return true;
|
|
1844
|
+
}
|
|
1845
|
+
if (!allowFocus) {
|
|
1846
|
+
return false;
|
|
1847
|
+
}
|
|
1848
|
+
try {
|
|
1849
|
+
(0, import_node_child_process.execFileSync)("open", [targetUrl], { stdio: "ignore" });
|
|
1850
|
+
return true;
|
|
1851
|
+
} catch {
|
|
1852
|
+
return false;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1559
1855
|
function openInBrowser(url) {
|
|
1560
1856
|
try {
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1857
|
+
const targetUrl = String(url || "").trim();
|
|
1858
|
+
if (!targetUrl) return;
|
|
1859
|
+
const allowFocus = claimBrowserFocus();
|
|
1860
|
+
if (process.platform === "darwin") {
|
|
1861
|
+
openUrlMacos(targetUrl, allowFocus);
|
|
1862
|
+
return;
|
|
1863
|
+
}
|
|
1864
|
+
if (!allowFocus) return;
|
|
1865
|
+
if (process.platform === "win32") {
|
|
1866
|
+
(0, import_node_child_process.execFileSync)("cmd.exe", ["/c", "start", "", targetUrl], {
|
|
1867
|
+
stdio: "ignore"
|
|
1868
|
+
});
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
(0, import_node_child_process.execFileSync)("xdg-open", [targetUrl], { stdio: "ignore" });
|
|
1565
1872
|
} catch {
|
|
1566
1873
|
}
|
|
1567
1874
|
}
|
|
@@ -3817,13 +4124,6 @@ var PLAY_DEDUP_BACKENDS = {
|
|
|
3817
4124
|
|
|
3818
4125
|
// ../shared_libs/play-runtime/profiles.ts
|
|
3819
4126
|
var PLAY_EXECUTION_PROFILES = {
|
|
3820
|
-
legacy: {
|
|
3821
|
-
id: "legacy",
|
|
3822
|
-
scheduler: PLAY_SCHEDULER_BACKENDS.temporal,
|
|
3823
|
-
runner: PLAY_RUNTIME_BACKENDS.daytona,
|
|
3824
|
-
dedup: PLAY_DEDUP_BACKENDS.inMemory,
|
|
3825
|
-
label: "Daytona + Temporal (production today)"
|
|
3826
|
-
},
|
|
3827
4127
|
workers_edge: {
|
|
3828
4128
|
id: "workers_edge",
|
|
3829
4129
|
scheduler: PLAY_SCHEDULER_BACKENDS.cfWorkflows,
|
|
@@ -4362,7 +4662,77 @@ function createCliProgress(enabled) {
|
|
|
4362
4662
|
return progress;
|
|
4363
4663
|
}
|
|
4364
4664
|
|
|
4665
|
+
// src/cli/trace.ts
|
|
4666
|
+
var cliTraceStartedAt = Date.now();
|
|
4667
|
+
function isTruthyEnv(value) {
|
|
4668
|
+
return value === "1" || value === "true" || value === "yes";
|
|
4669
|
+
}
|
|
4670
|
+
function isCliTraceEnabled() {
|
|
4671
|
+
return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
|
|
4672
|
+
}
|
|
4673
|
+
function recordCliTrace(event) {
|
|
4674
|
+
if (!isCliTraceEnabled()) {
|
|
4675
|
+
return;
|
|
4676
|
+
}
|
|
4677
|
+
const now = Date.now();
|
|
4678
|
+
const payload = {
|
|
4679
|
+
ts: now,
|
|
4680
|
+
source: "cli",
|
|
4681
|
+
sinceStartMs: now - cliTraceStartedAt,
|
|
4682
|
+
...event
|
|
4683
|
+
};
|
|
4684
|
+
process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
|
|
4685
|
+
`);
|
|
4686
|
+
}
|
|
4687
|
+
async function traceCliSpan(phase, fields, run) {
|
|
4688
|
+
if (!isCliTraceEnabled()) {
|
|
4689
|
+
return run();
|
|
4690
|
+
}
|
|
4691
|
+
const startedAt = Date.now();
|
|
4692
|
+
try {
|
|
4693
|
+
const result = await run();
|
|
4694
|
+
recordCliTrace({
|
|
4695
|
+
phase,
|
|
4696
|
+
ms: Date.now() - startedAt,
|
|
4697
|
+
ok: true,
|
|
4698
|
+
...fields
|
|
4699
|
+
});
|
|
4700
|
+
return result;
|
|
4701
|
+
} catch (error) {
|
|
4702
|
+
recordCliTrace({
|
|
4703
|
+
phase,
|
|
4704
|
+
ms: Date.now() - startedAt,
|
|
4705
|
+
ok: false,
|
|
4706
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4707
|
+
...fields
|
|
4708
|
+
});
|
|
4709
|
+
throw error;
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
|
|
4365
4713
|
// src/cli/commands/play.ts
|
|
4714
|
+
function traceCliSync(phase, fields, run) {
|
|
4715
|
+
const startedAt = Date.now();
|
|
4716
|
+
try {
|
|
4717
|
+
const result = run();
|
|
4718
|
+
recordCliTrace({
|
|
4719
|
+
phase,
|
|
4720
|
+
ms: Date.now() - startedAt,
|
|
4721
|
+
ok: true,
|
|
4722
|
+
...fields
|
|
4723
|
+
});
|
|
4724
|
+
return result;
|
|
4725
|
+
} catch (error) {
|
|
4726
|
+
recordCliTrace({
|
|
4727
|
+
phase,
|
|
4728
|
+
ms: Date.now() - startedAt,
|
|
4729
|
+
ok: false,
|
|
4730
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4731
|
+
...fields
|
|
4732
|
+
});
|
|
4733
|
+
throw error;
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4366
4736
|
function parseReferencedPlayTarget(target) {
|
|
4367
4737
|
const trimmed = target.trim();
|
|
4368
4738
|
const slashIndex = trimmed.indexOf("/");
|
|
@@ -4601,6 +4971,23 @@ function isLocalFilePathValue(value) {
|
|
|
4601
4971
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
4602
4972
|
return (0, import_node_fs6.existsSync)((0, import_node_path8.resolve)(value));
|
|
4603
4973
|
}
|
|
4974
|
+
function inputContainsLocalFilePath(value) {
|
|
4975
|
+
if (isLocalFilePathValue(value)) {
|
|
4976
|
+
return true;
|
|
4977
|
+
}
|
|
4978
|
+
if (Array.isArray(value)) {
|
|
4979
|
+
return value.some((entry) => inputContainsLocalFilePath(entry));
|
|
4980
|
+
}
|
|
4981
|
+
if (value && typeof value === "object") {
|
|
4982
|
+
return Object.values(value).some(
|
|
4983
|
+
(entry) => inputContainsLocalFilePath(entry)
|
|
4984
|
+
);
|
|
4985
|
+
}
|
|
4986
|
+
return false;
|
|
4987
|
+
}
|
|
4988
|
+
function namedRunNeedsPlayDefinition(input) {
|
|
4989
|
+
return input.revisionSelector === "latest" || getDottedInputValue(input.runtimeInput, "csv") != null || inputContainsLocalFilePath(input.runtimeInput);
|
|
4990
|
+
}
|
|
4604
4991
|
async function stageFileInputArgs(input) {
|
|
4605
4992
|
const uniqueBindings = [
|
|
4606
4993
|
...new Map(input.bindings.map((binding) => [binding.inputPath, binding])).values()
|
|
@@ -4787,7 +5174,7 @@ function formatTimestamp(value) {
|
|
|
4787
5174
|
function formatRunLine(run) {
|
|
4788
5175
|
return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)}`;
|
|
4789
5176
|
}
|
|
4790
|
-
function
|
|
5177
|
+
function isTransientPlayStreamError(error) {
|
|
4791
5178
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
4792
5179
|
return error.statusCode >= 500 && error.statusCode < 600;
|
|
4793
5180
|
}
|
|
@@ -4796,12 +5183,6 @@ function isTransientPlayStatusPollError(error) {
|
|
|
4796
5183
|
text
|
|
4797
5184
|
);
|
|
4798
5185
|
}
|
|
4799
|
-
function isTerminalPlayStatusPollError(input) {
|
|
4800
|
-
if (input.error instanceof DeeplineError && input.error.statusCode === 404 && input.hasSeenRun) {
|
|
4801
|
-
return true;
|
|
4802
|
-
}
|
|
4803
|
-
return false;
|
|
4804
|
-
}
|
|
4805
5186
|
var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
|
|
4806
5187
|
"completed",
|
|
4807
5188
|
"failed",
|
|
@@ -4867,6 +5248,12 @@ function buildPlayDashboardUrl(baseUrl, playName) {
|
|
|
4867
5248
|
const encodedPlayName = encodeURIComponent(playName);
|
|
4868
5249
|
return `${trimmedBase}/dashboard/plays/${encodedPlayName}`;
|
|
4869
5250
|
}
|
|
5251
|
+
function openPlayDashboard(input) {
|
|
5252
|
+
if (input.jsonOutput || input.noOpen || !input.dashboardUrl) {
|
|
5253
|
+
return;
|
|
5254
|
+
}
|
|
5255
|
+
openInBrowser(input.dashboardUrl);
|
|
5256
|
+
}
|
|
4870
5257
|
function getDashboardUrlFromLiveEvent(event) {
|
|
4871
5258
|
const dashboardUrl = getEventPayload(event).dashboardUrl;
|
|
4872
5259
|
return typeof dashboardUrl === "string" && dashboardUrl.trim() ? dashboardUrl.trim() : null;
|
|
@@ -4903,6 +5290,68 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
4903
5290
|
);
|
|
4904
5291
|
}
|
|
4905
5292
|
}
|
|
5293
|
+
async function waitForPlayCompletionByStream(input) {
|
|
5294
|
+
const controller = new AbortController();
|
|
5295
|
+
let timedOut = false;
|
|
5296
|
+
let lastPhase = null;
|
|
5297
|
+
const timeout = input.waitTimeoutMs === null ? null : setTimeout(
|
|
5298
|
+
() => {
|
|
5299
|
+
timedOut = true;
|
|
5300
|
+
controller.abort();
|
|
5301
|
+
},
|
|
5302
|
+
Math.max(1, input.waitTimeoutMs - (Date.now() - input.startedAt))
|
|
5303
|
+
);
|
|
5304
|
+
try {
|
|
5305
|
+
for await (const event of input.client.streamPlayRunEvents(
|
|
5306
|
+
input.workflowId,
|
|
5307
|
+
{ signal: controller.signal }
|
|
5308
|
+
)) {
|
|
5309
|
+
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
5310
|
+
const phase = describeLiveEventPhase(event);
|
|
5311
|
+
if (phase) {
|
|
5312
|
+
lastPhase = phase;
|
|
5313
|
+
input.progress.phase(phase);
|
|
5314
|
+
}
|
|
5315
|
+
printPlayLogLines({
|
|
5316
|
+
lines: getLogLinesFromLiveEvent(event),
|
|
5317
|
+
status: null,
|
|
5318
|
+
jsonOutput: input.jsonOutput,
|
|
5319
|
+
emitLogs: input.emitLogs,
|
|
5320
|
+
state: input.state,
|
|
5321
|
+
progress: input.progress
|
|
5322
|
+
});
|
|
5323
|
+
const status = getStatusFromLiveEvent(event);
|
|
5324
|
+
if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
|
|
5325
|
+
const finalStatus = await input.client.getPlayStatus(input.workflowId, {
|
|
5326
|
+
billing: false
|
|
5327
|
+
});
|
|
5328
|
+
if (TERMINAL_PLAY_STATUSES2.has(finalStatus.status)) {
|
|
5329
|
+
return finalStatus;
|
|
5330
|
+
}
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
} catch (error) {
|
|
5334
|
+
if (timedOut) {
|
|
5335
|
+
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
5336
|
+
}
|
|
5337
|
+
throw error;
|
|
5338
|
+
} finally {
|
|
5339
|
+
if (timeout) {
|
|
5340
|
+
clearTimeout(timeout);
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
5344
|
+
throw new DeeplineError(
|
|
5345
|
+
`Play live stream ended before the run reached a terminal state runId=${input.workflowId}${phaseSuffix}.`,
|
|
5346
|
+
void 0,
|
|
5347
|
+
"PLAY_LIVE_STREAM_ENDED",
|
|
5348
|
+
{
|
|
5349
|
+
runId: input.workflowId,
|
|
5350
|
+
workflowId: input.workflowId,
|
|
5351
|
+
...lastPhase ? { phase: lastPhase } : {}
|
|
5352
|
+
}
|
|
5353
|
+
);
|
|
5354
|
+
}
|
|
4906
5355
|
async function startAndWaitForPlayCompletionByStream(input) {
|
|
4907
5356
|
const startedAt = Date.now();
|
|
4908
5357
|
const state = {
|
|
@@ -4913,6 +5362,8 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4913
5362
|
let timedOut = false;
|
|
4914
5363
|
let emittedDashboardUrl = false;
|
|
4915
5364
|
let lastKnownWorkflowId = "";
|
|
5365
|
+
let eventCount = 0;
|
|
5366
|
+
let firstRunIdMs = null;
|
|
4916
5367
|
let lastPhase = null;
|
|
4917
5368
|
const timeout = input.waitTimeoutMs === null ? null : setTimeout(
|
|
4918
5369
|
() => {
|
|
@@ -4925,13 +5376,20 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4925
5376
|
for await (const event of input.client.startPlayRunStream(input.request, {
|
|
4926
5377
|
signal: controller.signal
|
|
4927
5378
|
})) {
|
|
5379
|
+
eventCount += 1;
|
|
4928
5380
|
const eventRunId = getEventPayload(event).runId;
|
|
4929
5381
|
if (typeof eventRunId === "string" && eventRunId && eventRunId !== "pending") {
|
|
4930
5382
|
lastKnownWorkflowId = eventRunId;
|
|
5383
|
+
firstRunIdMs ??= Date.now() - startedAt;
|
|
4931
5384
|
}
|
|
4932
5385
|
const workflowId = lastKnownWorkflowId || "pending";
|
|
4933
5386
|
if (workflowId !== "pending" && !emittedDashboardUrl) {
|
|
4934
5387
|
const dashboardUrl = getDashboardUrlFromLiveEvent(event) ?? buildPlayDashboardUrl(input.client.baseUrl, input.playName);
|
|
5388
|
+
openPlayDashboard({
|
|
5389
|
+
dashboardUrl,
|
|
5390
|
+
jsonOutput: input.jsonOutput,
|
|
5391
|
+
noOpen: input.noOpen
|
|
5392
|
+
});
|
|
4935
5393
|
if (!input.jsonOutput) {
|
|
4936
5394
|
writeStartedPlayRun({
|
|
4937
5395
|
runId: workflowId,
|
|
@@ -4965,6 +5423,16 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4965
5423
|
});
|
|
4966
5424
|
const finalStatus = getFinalStatusFromLiveEvent(event);
|
|
4967
5425
|
if (finalStatus) {
|
|
5426
|
+
recordCliTrace({
|
|
5427
|
+
phase: "cli.play_start_stream_terminal",
|
|
5428
|
+
ms: Date.now() - startedAt,
|
|
5429
|
+
ok: true,
|
|
5430
|
+
playName: input.playName,
|
|
5431
|
+
workflowId: finalStatus.runId || lastKnownWorkflowId || null,
|
|
5432
|
+
eventCount,
|
|
5433
|
+
firstRunIdMs,
|
|
5434
|
+
lastPhase
|
|
5435
|
+
});
|
|
4968
5436
|
return finalStatus;
|
|
4969
5437
|
}
|
|
4970
5438
|
}
|
|
@@ -4977,21 +5445,31 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4977
5445
|
lastPhase
|
|
4978
5446
|
});
|
|
4979
5447
|
}
|
|
4980
|
-
if (lastKnownWorkflowId &&
|
|
5448
|
+
if (lastKnownWorkflowId && isTransientPlayStreamError(error)) {
|
|
4981
5449
|
if (timeout) {
|
|
4982
5450
|
clearTimeout(timeout);
|
|
4983
5451
|
}
|
|
4984
5452
|
const reason = error instanceof Error ? error.message : String(error);
|
|
4985
5453
|
if (!input.jsonOutput) {
|
|
4986
5454
|
process.stderr.write(
|
|
4987
|
-
`[play watch] start stream failed after run ${lastKnownWorkflowId};
|
|
5455
|
+
`[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to run stream (${reason})
|
|
4988
5456
|
`
|
|
4989
5457
|
);
|
|
4990
5458
|
}
|
|
4991
|
-
|
|
5459
|
+
recordCliTrace({
|
|
5460
|
+
phase: "cli.play_start_stream_reconnect",
|
|
5461
|
+
ms: Date.now() - startedAt,
|
|
5462
|
+
ok: true,
|
|
5463
|
+
playName: input.playName,
|
|
5464
|
+
workflowId: lastKnownWorkflowId,
|
|
5465
|
+
eventCount,
|
|
5466
|
+
firstRunIdMs,
|
|
5467
|
+
lastPhase,
|
|
5468
|
+
reason
|
|
5469
|
+
});
|
|
5470
|
+
return waitForPlayCompletionByStream({
|
|
4992
5471
|
client: input.client,
|
|
4993
5472
|
workflowId: lastKnownWorkflowId,
|
|
4994
|
-
pollIntervalMs: 500,
|
|
4995
5473
|
jsonOutput: input.jsonOutput,
|
|
4996
5474
|
emitLogs: input.emitLogs,
|
|
4997
5475
|
waitTimeoutMs: input.waitTimeoutMs,
|
|
@@ -5009,13 +5487,23 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
5009
5487
|
if (lastKnownWorkflowId) {
|
|
5010
5488
|
if (!input.jsonOutput) {
|
|
5011
5489
|
input.progress.writeLine(
|
|
5012
|
-
`[play watch] start stream ended after run ${lastKnownWorkflowId};
|
|
5490
|
+
`[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to run stream`
|
|
5013
5491
|
);
|
|
5014
5492
|
}
|
|
5015
|
-
|
|
5493
|
+
recordCliTrace({
|
|
5494
|
+
phase: "cli.play_start_stream_reconnect",
|
|
5495
|
+
ms: Date.now() - startedAt,
|
|
5496
|
+
ok: true,
|
|
5497
|
+
playName: input.playName,
|
|
5498
|
+
workflowId: lastKnownWorkflowId,
|
|
5499
|
+
eventCount,
|
|
5500
|
+
firstRunIdMs,
|
|
5501
|
+
lastPhase,
|
|
5502
|
+
reason: "stream ended before terminal event"
|
|
5503
|
+
});
|
|
5504
|
+
return waitForPlayCompletionByStream({
|
|
5016
5505
|
client: input.client,
|
|
5017
5506
|
workflowId: lastKnownWorkflowId,
|
|
5018
|
-
pollIntervalMs: 500,
|
|
5019
5507
|
jsonOutput: input.jsonOutput,
|
|
5020
5508
|
emitLogs: input.emitLogs,
|
|
5021
5509
|
waitTimeoutMs: input.waitTimeoutMs,
|
|
@@ -5036,71 +5524,6 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
5036
5524
|
}
|
|
5037
5525
|
);
|
|
5038
5526
|
}
|
|
5039
|
-
async function waitForPlayCompletionByPolling(input) {
|
|
5040
|
-
let lastTransientPollWarningAt = 0;
|
|
5041
|
-
let hasSeenRun = false;
|
|
5042
|
-
while (true) {
|
|
5043
|
-
assertPlayWaitNotTimedOut(input);
|
|
5044
|
-
let status;
|
|
5045
|
-
try {
|
|
5046
|
-
status = await input.client.getPlayTailStatus(input.workflowId, {
|
|
5047
|
-
afterLogIndex: input.state.lastLogIndex,
|
|
5048
|
-
// Keep the server-side tail wait close to the caller's requested poll
|
|
5049
|
-
// cadence. A long wait makes tiny remote runs look slow whenever the
|
|
5050
|
-
// terminal update lands just after the held request starts.
|
|
5051
|
-
waitMs: Math.max(50, Math.min(input.pollIntervalMs, 1e3))
|
|
5052
|
-
});
|
|
5053
|
-
} catch (error) {
|
|
5054
|
-
if (isTerminalPlayStatusPollError({ error, hasSeenRun })) {
|
|
5055
|
-
throw new DeeplineError(
|
|
5056
|
-
`Play run ${input.workflowId} no longer exists on the server (404). The run was deleted or the backend lost it.`,
|
|
5057
|
-
404,
|
|
5058
|
-
"PLAY_RUN_NOT_FOUND"
|
|
5059
|
-
);
|
|
5060
|
-
}
|
|
5061
|
-
if (!isTransientPlayStatusPollError(error)) {
|
|
5062
|
-
throw error;
|
|
5063
|
-
}
|
|
5064
|
-
const now = Date.now();
|
|
5065
|
-
if (now - lastTransientPollWarningAt >= 3e4) {
|
|
5066
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5067
|
-
input.progress.writeLine(
|
|
5068
|
-
`[play tail] transient status poll failed; retrying: ${message}`
|
|
5069
|
-
);
|
|
5070
|
-
lastTransientPollWarningAt = now;
|
|
5071
|
-
}
|
|
5072
|
-
await new Promise(
|
|
5073
|
-
(resolvePromise) => setTimeout(resolvePromise, input.pollIntervalMs)
|
|
5074
|
-
);
|
|
5075
|
-
continue;
|
|
5076
|
-
}
|
|
5077
|
-
hasSeenRun = true;
|
|
5078
|
-
const logs = status.progress?.logs ?? [];
|
|
5079
|
-
input.progress.phase(status.status);
|
|
5080
|
-
printPlayLogLines({
|
|
5081
|
-
lines: logs.slice(input.state.lastLogIndex),
|
|
5082
|
-
status,
|
|
5083
|
-
jsonOutput: input.jsonOutput,
|
|
5084
|
-
emitLogs: input.emitLogs,
|
|
5085
|
-
state: input.state,
|
|
5086
|
-
progress: input.progress
|
|
5087
|
-
});
|
|
5088
|
-
if (TERMINAL_PLAY_STATUSES2.has(status.status)) {
|
|
5089
|
-
return status.result !== void 0 ? status : await input.client.getPlayStatus(input.workflowId);
|
|
5090
|
-
}
|
|
5091
|
-
const authoritativeStatus = await input.client.getPlayStatus(
|
|
5092
|
-
input.workflowId
|
|
5093
|
-
);
|
|
5094
|
-
if (TERMINAL_PLAY_STATUSES2.has(authoritativeStatus.status)) {
|
|
5095
|
-
return authoritativeStatus;
|
|
5096
|
-
}
|
|
5097
|
-
if ((status.progress?.logs ?? []).length === input.state.lastLogIndex) {
|
|
5098
|
-
await new Promise(
|
|
5099
|
-
(resolvePromise) => setTimeout(resolvePromise, input.pollIntervalMs)
|
|
5100
|
-
);
|
|
5101
|
-
}
|
|
5102
|
-
}
|
|
5103
|
-
}
|
|
5104
5527
|
function formatInteger(value) {
|
|
5105
5528
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
|
|
5106
5529
|
}
|
|
@@ -5241,7 +5664,6 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
5241
5664
|
function buildRunNextCommands(runId, rowsInfo) {
|
|
5242
5665
|
const commands = {
|
|
5243
5666
|
get: `deepline runs get ${runId} --json`,
|
|
5244
|
-
tail: `deepline runs tail ${runId} --json`,
|
|
5245
5667
|
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
5246
5668
|
logs: `deepline runs logs ${runId} --out run.log --json`
|
|
5247
5669
|
};
|
|
@@ -5357,10 +5779,9 @@ function normalizeErrorsForEnvelope(status, error) {
|
|
|
5357
5779
|
}
|
|
5358
5780
|
function normalizeLogsForEnvelope(status) {
|
|
5359
5781
|
const logs = Array.isArray(status.progress?.logs) ? status.progress.logs : [];
|
|
5360
|
-
const
|
|
5361
|
-
const totalCount = offset + logs.length;
|
|
5782
|
+
const totalCount = logs.length;
|
|
5362
5783
|
const entries = logs.slice(Math.max(0, logs.length - RUN_LOG_PREVIEW_LIMIT));
|
|
5363
|
-
const firstSequence = entries.length === 0 ? null :
|
|
5784
|
+
const firstSequence = entries.length === 0 ? null : logs.length - entries.length + 1;
|
|
5364
5785
|
const lastSequence = totalCount === 0 ? null : totalCount;
|
|
5365
5786
|
return {
|
|
5366
5787
|
totalCount,
|
|
@@ -5606,7 +6027,6 @@ function writeStartedPlayRun(input) {
|
|
|
5606
6027
|
workflowId: input.runId,
|
|
5607
6028
|
name: input.playName,
|
|
5608
6029
|
status: input.status ?? "started",
|
|
5609
|
-
statusUrl: input.statusUrl,
|
|
5610
6030
|
dashboardUrl: input.dashboardUrl
|
|
5611
6031
|
};
|
|
5612
6032
|
if (input.jsonOutput) {
|
|
@@ -5618,7 +6038,7 @@ function writeStartedPlayRun(input) {
|
|
|
5618
6038
|
`Started ${input.playName}`,
|
|
5619
6039
|
` run id: ${input.runId}`,
|
|
5620
6040
|
` get status: deepline runs get ${input.runId} --json`,
|
|
5621
|
-
`
|
|
6041
|
+
` logs: deepline runs logs ${input.runId} --json`,
|
|
5622
6042
|
` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`,
|
|
5623
6043
|
` result JSON: deepline runs get ${input.runId} --json`
|
|
5624
6044
|
];
|
|
@@ -5633,7 +6053,7 @@ function writeStartedPlayRun(input) {
|
|
|
5633
6053
|
console.log(output);
|
|
5634
6054
|
}
|
|
5635
6055
|
function parsePlayRunOptions(args) {
|
|
5636
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--json] [--<input> value]";
|
|
6056
|
+
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--<input> value]\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.map guidance.";
|
|
5637
6057
|
let filePath = null;
|
|
5638
6058
|
let playName = null;
|
|
5639
6059
|
let input = null;
|
|
@@ -5643,8 +6063,8 @@ function parsePlayRunOptions(args) {
|
|
|
5643
6063
|
let jsonOutput = watch ? args.includes("--json") : argsWantJson(args);
|
|
5644
6064
|
const emitLogs = !jsonOutput || args.includes("--logs");
|
|
5645
6065
|
const force = args.includes("--force");
|
|
6066
|
+
const noOpen = args.includes("--no-open");
|
|
5646
6067
|
let outPath = null;
|
|
5647
|
-
let pollIntervalMs = 500;
|
|
5648
6068
|
let waitTimeoutMs = null;
|
|
5649
6069
|
for (let index = 0; index < args.length; index += 1) {
|
|
5650
6070
|
const arg = args[index];
|
|
@@ -5676,15 +6096,16 @@ function parsePlayRunOptions(args) {
|
|
|
5676
6096
|
outPath = (0, import_node_path8.resolve)(args[++index]);
|
|
5677
6097
|
continue;
|
|
5678
6098
|
}
|
|
5679
|
-
if (
|
|
5680
|
-
|
|
5681
|
-
|
|
6099
|
+
if (arg === "--poll-interval-ms" || arg === "--interval-ms") {
|
|
6100
|
+
throw new Error(
|
|
6101
|
+
`${arg} was removed. --watch uses the canonical run stream directly.`
|
|
6102
|
+
);
|
|
5682
6103
|
}
|
|
5683
6104
|
if ((arg === "--tail-timeout-ms" || arg === "--timeout-ms") && args[index + 1]) {
|
|
5684
6105
|
waitTimeoutMs = parsePositiveInteger2(args[++index], arg);
|
|
5685
6106
|
continue;
|
|
5686
6107
|
}
|
|
5687
|
-
if (arg === "--json" || arg === "--wait" || arg === "--tail" || arg === "--watch" || arg === "--logs" || arg === "--force") {
|
|
6108
|
+
if (arg === "--json" || arg === "--wait" || arg === "--tail" || arg === "--watch" || arg === "--logs" || arg === "--force" || arg === "--no-open") {
|
|
5688
6109
|
if (arg === "--watch") {
|
|
5689
6110
|
continue;
|
|
5690
6111
|
}
|
|
@@ -5753,10 +6174,10 @@ function parsePlayRunOptions(args) {
|
|
|
5753
6174
|
watch,
|
|
5754
6175
|
emitLogs,
|
|
5755
6176
|
jsonOutput,
|
|
5756
|
-
pollIntervalMs,
|
|
5757
6177
|
waitTimeoutMs,
|
|
5758
6178
|
force,
|
|
5759
|
-
outPath
|
|
6179
|
+
outPath,
|
|
6180
|
+
noOpen
|
|
5760
6181
|
};
|
|
5761
6182
|
}
|
|
5762
6183
|
function parsePlayCheckOptions(args) {
|
|
@@ -5844,11 +6265,24 @@ async function handleFileBackedRun(options) {
|
|
|
5844
6265
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
5845
6266
|
const absolutePlayPath = (0, import_node_path8.resolve)(options.target.path);
|
|
5846
6267
|
progress.phase("compiling play");
|
|
5847
|
-
const sourceCode = (
|
|
6268
|
+
const sourceCode = traceCliSync(
|
|
6269
|
+
"cli.play_file_read_source",
|
|
6270
|
+
{ targetKind: "file" },
|
|
6271
|
+
() => (0, import_node_fs6.readFileSync)(absolutePlayPath, "utf-8")
|
|
6272
|
+
);
|
|
6273
|
+
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5848
6274
|
let graph;
|
|
5849
6275
|
try {
|
|
5850
|
-
graph = await
|
|
5851
|
-
|
|
6276
|
+
graph = await traceCliSpan(
|
|
6277
|
+
"cli.play_file_bundle_graph",
|
|
6278
|
+
{ targetKind: "file" },
|
|
6279
|
+
() => collectBundledPlayGraph(absolutePlayPath)
|
|
6280
|
+
);
|
|
6281
|
+
await traceCliSpan(
|
|
6282
|
+
"cli.play_file_compile_manifests",
|
|
6283
|
+
{ targetKind: "file" },
|
|
6284
|
+
() => compileBundledPlayGraphManifests(client, graph)
|
|
6285
|
+
);
|
|
5852
6286
|
progress.phase("compiled play");
|
|
5853
6287
|
} catch (error) {
|
|
5854
6288
|
progress.fail();
|
|
@@ -5859,36 +6293,47 @@ async function handleFileBackedRun(options) {
|
|
|
5859
6293
|
const playName = bundleResult.playName ?? extractPlayName(sourceCode, absolutePlayPath);
|
|
5860
6294
|
try {
|
|
5861
6295
|
progress.phase("publishing imported plays");
|
|
5862
|
-
await
|
|
6296
|
+
await traceCliSpan(
|
|
6297
|
+
"cli.play_file_publish_imports",
|
|
6298
|
+
{ targetKind: "file" },
|
|
6299
|
+
() => publishImportedPlayDependencies(client, graph)
|
|
6300
|
+
);
|
|
5863
6301
|
} catch (error) {
|
|
5864
6302
|
progress.fail();
|
|
5865
6303
|
console.error(error instanceof Error ? error.message : String(error));
|
|
5866
6304
|
return 1;
|
|
5867
6305
|
}
|
|
5868
|
-
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5869
6306
|
const packagedFileUploads = bundleResult.packagedFiles.map(
|
|
5870
6307
|
(file) => stageFile(file.logicalPath, file.absolutePath)
|
|
5871
6308
|
);
|
|
6309
|
+
const compilerManifest = requireCompilerManifest(bundleResult);
|
|
5872
6310
|
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
5873
|
-
|
|
6311
|
+
compilerManifest.staticPipeline
|
|
5874
6312
|
);
|
|
5875
6313
|
applyCsvShortcutInput({
|
|
5876
6314
|
runtimeInput,
|
|
5877
6315
|
bindings: fileInputBindings,
|
|
5878
6316
|
fallbackInputPath: "file"
|
|
5879
6317
|
});
|
|
5880
|
-
const stagedFileInputs = await
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
6318
|
+
const stagedFileInputs = await traceCliSpan(
|
|
6319
|
+
"cli.play_stage_inputs",
|
|
6320
|
+
{
|
|
6321
|
+
targetKind: "file",
|
|
6322
|
+
bindingCount: fileInputBindings.length
|
|
6323
|
+
},
|
|
6324
|
+
() => stageFileInputArgs({
|
|
6325
|
+
client,
|
|
6326
|
+
runtimeInput,
|
|
6327
|
+
bindings: fileInputBindings,
|
|
6328
|
+
progress
|
|
6329
|
+
})
|
|
6330
|
+
);
|
|
5886
6331
|
const startRequest = {
|
|
5887
6332
|
name: playName,
|
|
5888
6333
|
sourceCode: bundleResult.sourceCode,
|
|
5889
6334
|
sourceFiles: bundleResult.sourceFiles,
|
|
5890
6335
|
runtimeArtifact: bundleResult.artifact,
|
|
5891
|
-
compilerManifest
|
|
6336
|
+
compilerManifest,
|
|
5892
6337
|
packagedFileUploads,
|
|
5893
6338
|
...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
|
|
5894
6339
|
...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
|
|
@@ -5897,35 +6342,57 @@ async function handleFileBackedRun(options) {
|
|
|
5897
6342
|
};
|
|
5898
6343
|
if (options.watch) {
|
|
5899
6344
|
progress.phase("starting run");
|
|
5900
|
-
const finalStatus = await
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
6345
|
+
const finalStatus = await traceCliSpan(
|
|
6346
|
+
"cli.play_start_watch",
|
|
6347
|
+
{ targetKind: "file", playName },
|
|
6348
|
+
() => startAndWaitForPlayCompletionByStream({
|
|
6349
|
+
client,
|
|
6350
|
+
request: startRequest,
|
|
6351
|
+
playName,
|
|
6352
|
+
jsonOutput: options.jsonOutput,
|
|
6353
|
+
emitLogs: options.emitLogs,
|
|
6354
|
+
waitTimeoutMs: options.waitTimeoutMs,
|
|
6355
|
+
noOpen: options.noOpen,
|
|
6356
|
+
progress
|
|
6357
|
+
})
|
|
6358
|
+
);
|
|
6359
|
+
const exportedPath = traceCliSync(
|
|
6360
|
+
"cli.play_export_rows",
|
|
6361
|
+
{ targetKind: "file", playName },
|
|
6362
|
+
() => exportPlayStatusRows(finalStatus, options.outPath)
|
|
6363
|
+
);
|
|
5910
6364
|
if (finalStatus.status === "completed") {
|
|
5911
6365
|
progress.complete();
|
|
5912
6366
|
} else {
|
|
5913
6367
|
progress.fail();
|
|
5914
6368
|
}
|
|
5915
|
-
|
|
6369
|
+
traceCliSync(
|
|
6370
|
+
"cli.play_write_result",
|
|
6371
|
+
{ targetKind: "file", playName },
|
|
6372
|
+
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath })
|
|
6373
|
+
);
|
|
5916
6374
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
5917
6375
|
}
|
|
5918
6376
|
progress.phase("starting run");
|
|
5919
|
-
const started = await
|
|
6377
|
+
const started = await traceCliSpan(
|
|
6378
|
+
"cli.play_start_unwatched",
|
|
6379
|
+
{ targetKind: "file", playName },
|
|
6380
|
+
() => client.startPlayRun(startRequest)
|
|
6381
|
+
);
|
|
5920
6382
|
const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
5921
|
-
|
|
6383
|
+
const resolvedDashboardUrl = started.dashboardUrl ?? dashboardUrl;
|
|
6384
|
+
openPlayDashboard({
|
|
6385
|
+
dashboardUrl: resolvedDashboardUrl,
|
|
6386
|
+
jsonOutput: options.jsonOutput,
|
|
6387
|
+
noOpen: options.noOpen
|
|
6388
|
+
});
|
|
6389
|
+
progress.phase(`loading play on ${resolvedDashboardUrl}`);
|
|
5922
6390
|
progress.complete();
|
|
5923
6391
|
writeStartedPlayRun({
|
|
5924
6392
|
runId: started.workflowId,
|
|
5925
6393
|
playName,
|
|
5926
6394
|
status: started.status,
|
|
5927
|
-
|
|
5928
|
-
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
6395
|
+
dashboardUrl: resolvedDashboardUrl,
|
|
5929
6396
|
jsonOutput: options.jsonOutput,
|
|
5930
6397
|
progress
|
|
5931
6398
|
});
|
|
@@ -5951,32 +6418,67 @@ async function handleNamedRun(options) {
|
|
|
5951
6418
|
}
|
|
5952
6419
|
const client = new DeeplineClient();
|
|
5953
6420
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
5954
|
-
|
|
5955
|
-
const playDetail = await assertCanonicalNamedPlayReference(client, options.target.name);
|
|
5956
|
-
progress.phase("selecting revision");
|
|
5957
|
-
const selectedRevisionId = await resolveNamedRunRevisionId({
|
|
5958
|
-
client,
|
|
5959
|
-
playName: options.target.name,
|
|
5960
|
-
revisionId: options.revisionId,
|
|
5961
|
-
selector: options.revisionSelector
|
|
5962
|
-
});
|
|
6421
|
+
const playName = options.target.name;
|
|
5963
6422
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5964
|
-
const
|
|
6423
|
+
const needsPlayDefinition = namedRunNeedsPlayDefinition({
|
|
6424
|
+
runtimeInput,
|
|
6425
|
+
revisionSelector: options.revisionSelector
|
|
6426
|
+
});
|
|
6427
|
+
const playDetail = needsPlayDefinition ? await (async () => {
|
|
6428
|
+
progress.phase("loading play definition");
|
|
6429
|
+
return traceCliSpan(
|
|
6430
|
+
"cli.play_load_definition",
|
|
6431
|
+
{ targetKind: "name", playName, skipped: false },
|
|
6432
|
+
() => assertCanonicalNamedPlayReference(client, playName)
|
|
6433
|
+
);
|
|
6434
|
+
})() : (recordCliTrace({
|
|
6435
|
+
phase: "cli.play_load_definition",
|
|
6436
|
+
ms: 0,
|
|
6437
|
+
ok: true,
|
|
6438
|
+
targetKind: "name",
|
|
6439
|
+
playName,
|
|
6440
|
+
skipped: true
|
|
6441
|
+
}), null);
|
|
6442
|
+
progress.phase("selecting revision");
|
|
6443
|
+
const selectedRevisionId = await traceCliSpan(
|
|
6444
|
+
"cli.play_select_revision",
|
|
6445
|
+
{
|
|
6446
|
+
targetKind: "name",
|
|
6447
|
+
playName,
|
|
6448
|
+
selector: options.revisionSelector,
|
|
6449
|
+
hasExplicitRevisionId: Boolean(options.revisionId)
|
|
6450
|
+
},
|
|
6451
|
+
() => resolveNamedRunRevisionId({
|
|
6452
|
+
client,
|
|
6453
|
+
playName,
|
|
6454
|
+
revisionId: options.revisionId,
|
|
6455
|
+
selector: options.revisionSelector
|
|
6456
|
+
})
|
|
6457
|
+
);
|
|
6458
|
+
const fileInputBindings = playDetail ? [
|
|
5965
6459
|
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
5966
6460
|
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
5967
|
-
];
|
|
6461
|
+
] : [];
|
|
5968
6462
|
applyCsvShortcutInput({
|
|
5969
6463
|
runtimeInput,
|
|
5970
6464
|
bindings: fileInputBindings
|
|
5971
6465
|
});
|
|
5972
|
-
const stagedFileInputs = await
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
6466
|
+
const stagedFileInputs = await traceCliSpan(
|
|
6467
|
+
"cli.play_stage_inputs",
|
|
6468
|
+
{
|
|
6469
|
+
targetKind: "name",
|
|
6470
|
+
playName,
|
|
6471
|
+
bindingCount: fileInputBindings.length
|
|
6472
|
+
},
|
|
6473
|
+
() => stageFileInputArgs({
|
|
6474
|
+
client,
|
|
6475
|
+
runtimeInput,
|
|
6476
|
+
bindings: fileInputBindings,
|
|
6477
|
+
progress
|
|
6478
|
+
})
|
|
6479
|
+
);
|
|
5978
6480
|
const startRequest = {
|
|
5979
|
-
name:
|
|
6481
|
+
name: playName,
|
|
5980
6482
|
...selectedRevisionId ? { revisionId: selectedRevisionId } : {},
|
|
5981
6483
|
...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
|
|
5982
6484
|
...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
|
|
@@ -5985,38 +6487,60 @@ async function handleNamedRun(options) {
|
|
|
5985
6487
|
};
|
|
5986
6488
|
if (options.watch) {
|
|
5987
6489
|
progress.phase("starting run");
|
|
5988
|
-
const finalStatus = await
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
6490
|
+
const finalStatus = await traceCliSpan(
|
|
6491
|
+
"cli.play_start_watch",
|
|
6492
|
+
{ targetKind: "name", playName },
|
|
6493
|
+
() => startAndWaitForPlayCompletionByStream({
|
|
6494
|
+
client,
|
|
6495
|
+
request: startRequest,
|
|
6496
|
+
playName,
|
|
6497
|
+
jsonOutput: options.jsonOutput,
|
|
6498
|
+
emitLogs: options.emitLogs,
|
|
6499
|
+
waitTimeoutMs: options.waitTimeoutMs,
|
|
6500
|
+
noOpen: options.noOpen,
|
|
6501
|
+
progress
|
|
6502
|
+
})
|
|
6503
|
+
);
|
|
6504
|
+
const exportedPath = traceCliSync(
|
|
6505
|
+
"cli.play_export_rows",
|
|
6506
|
+
{ targetKind: "name", playName },
|
|
6507
|
+
() => exportPlayStatusRows(finalStatus, options.outPath)
|
|
6508
|
+
);
|
|
5998
6509
|
if (finalStatus.status === "completed") {
|
|
5999
6510
|
progress.complete();
|
|
6000
6511
|
} else {
|
|
6001
6512
|
progress.fail();
|
|
6002
6513
|
}
|
|
6003
|
-
|
|
6514
|
+
traceCliSync(
|
|
6515
|
+
"cli.play_write_result",
|
|
6516
|
+
{ targetKind: "name", playName },
|
|
6517
|
+
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath })
|
|
6518
|
+
);
|
|
6004
6519
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
6005
6520
|
}
|
|
6006
6521
|
progress.phase("starting run");
|
|
6007
|
-
const started = await
|
|
6522
|
+
const started = await traceCliSpan(
|
|
6523
|
+
"cli.play_start_unwatched",
|
|
6524
|
+
{ targetKind: "name", playName },
|
|
6525
|
+
() => client.startPlayRun(startRequest)
|
|
6526
|
+
);
|
|
6008
6527
|
const dashboardUrl = buildPlayDashboardUrl(
|
|
6009
6528
|
client.baseUrl,
|
|
6010
|
-
|
|
6529
|
+
playName
|
|
6011
6530
|
);
|
|
6012
|
-
|
|
6531
|
+
const resolvedDashboardUrl = started.dashboardUrl ?? dashboardUrl;
|
|
6532
|
+
openPlayDashboard({
|
|
6533
|
+
dashboardUrl: resolvedDashboardUrl,
|
|
6534
|
+
jsonOutput: options.jsonOutput,
|
|
6535
|
+
noOpen: options.noOpen
|
|
6536
|
+
});
|
|
6537
|
+
progress.phase(`loading play on ${resolvedDashboardUrl}`);
|
|
6013
6538
|
progress.complete();
|
|
6014
6539
|
writeStartedPlayRun({
|
|
6015
6540
|
runId: started.workflowId,
|
|
6016
|
-
playName: started.name ??
|
|
6541
|
+
playName: started.name ?? playName,
|
|
6017
6542
|
status: started.status,
|
|
6018
|
-
|
|
6019
|
-
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
6543
|
+
dashboardUrl: resolvedDashboardUrl,
|
|
6020
6544
|
jsonOutput: options.jsonOutput,
|
|
6021
6545
|
progress
|
|
6022
6546
|
});
|
|
@@ -6059,7 +6583,7 @@ function parseRunIdPositional(args, usage) {
|
|
|
6059
6583
|
}
|
|
6060
6584
|
continue;
|
|
6061
6585
|
}
|
|
6062
|
-
if ((arg === "--out" || arg === "--
|
|
6586
|
+
if ((arg === "--out" || arg === "--reason") && args[index + 1]) {
|
|
6063
6587
|
index += 1;
|
|
6064
6588
|
continue;
|
|
6065
6589
|
}
|
|
@@ -6144,7 +6668,7 @@ async function handleRunsList(args) {
|
|
|
6144
6668
|
return 0;
|
|
6145
6669
|
}
|
|
6146
6670
|
async function handleRunTail(args) {
|
|
6147
|
-
const usage = "Usage: deepline runs tail <run-id> [--json] [--compact]
|
|
6671
|
+
const usage = "Usage: deepline runs tail <run-id> [--json] [--compact]";
|
|
6148
6672
|
let runId;
|
|
6149
6673
|
try {
|
|
6150
6674
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -6152,20 +6676,19 @@ async function handleRunTail(args) {
|
|
|
6152
6676
|
console.error(error instanceof Error ? error.message : usage);
|
|
6153
6677
|
return 1;
|
|
6154
6678
|
}
|
|
6155
|
-
const client = new DeeplineClient();
|
|
6156
|
-
let afterLogIndex;
|
|
6157
6679
|
for (let index = 0; index < args.length; index += 1) {
|
|
6158
6680
|
const arg = args[index];
|
|
6159
|
-
if (arg === "--cursor"
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6681
|
+
if (arg === "--cursor") {
|
|
6682
|
+
console.error("--cursor was removed. deepline runs tail reads the canonical run stream.");
|
|
6683
|
+
return 1;
|
|
6684
|
+
}
|
|
6685
|
+
if (arg.startsWith("--") && arg !== "--json" && arg !== "--compact") {
|
|
6686
|
+
console.error(`${arg} is not supported by deepline runs tail.`);
|
|
6687
|
+
return 1;
|
|
6164
6688
|
}
|
|
6165
6689
|
}
|
|
6166
|
-
const
|
|
6167
|
-
|
|
6168
|
-
});
|
|
6690
|
+
const client = new DeeplineClient();
|
|
6691
|
+
const status = await client.runs.tail(runId);
|
|
6169
6692
|
writePlayResult(status, argsWantJson(args));
|
|
6170
6693
|
return status.status === "failed" ? 1 : 0;
|
|
6171
6694
|
}
|
|
@@ -6706,8 +7229,9 @@ function registerPlayCommands(program) {
|
|
|
6706
7229
|
"after",
|
|
6707
7230
|
`
|
|
6708
7231
|
Concepts:
|
|
6709
|
-
Plays are durable
|
|
6710
|
-
|
|
7232
|
+
Plays are durable cloud workflows.
|
|
7233
|
+
Stable ctx.tools.execute({ id, tool, input }) calls are replay-safe.
|
|
7234
|
+
ctx.map adds row keys and row progress.
|
|
6711
7235
|
|
|
6712
7236
|
Common commands:
|
|
6713
7237
|
deepline plays search email --json
|
|
@@ -6738,17 +7262,42 @@ Examples:
|
|
|
6738
7262
|
"after",
|
|
6739
7263
|
`
|
|
6740
7264
|
Notes:
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
7265
|
+
Local files are bundled, preflighted, then run in Deepline cloud.
|
|
7266
|
+
Named plays run the live saved revision.
|
|
7267
|
+
Unknown --foo and --foo.bar flags are treated as play input args.
|
|
7268
|
+
File args accept local paths; the CLI stages files before submit.
|
|
7269
|
+
--watch prints logs, previews, stats, and next commands.
|
|
7270
|
+
The play page opens in your browser as soon as the run starts; use --no-open
|
|
7271
|
+
to only print the URL.
|
|
7272
|
+
--force supersedes active runs; it does not bypass completed reuse.
|
|
7273
|
+
|
|
7274
|
+
Idempotent execution:
|
|
7275
|
+
Stable tool call ids are the reuse key:
|
|
7276
|
+
|
|
7277
|
+
await ctx.tools.execute({ id: 'company_lookup', tool, input });
|
|
7278
|
+
|
|
7279
|
+
For rows, use ctx.map plus a stable row key:
|
|
7280
|
+
|
|
7281
|
+
const rows = await ctx
|
|
7282
|
+
.map('companies_v1', companies)
|
|
7283
|
+
.step('cto', (row, ctx) => ctx.tools.execute({
|
|
7284
|
+
id: 'find_cto',
|
|
7285
|
+
tool: 'apollo_search_people_with_match',
|
|
7286
|
+
input: { q_organization_domains_list: [row.domain], per_page: 1 },
|
|
7287
|
+
}))
|
|
7288
|
+
.run({ key: 'domain' });
|
|
7289
|
+
|
|
7290
|
+
Reuse needs the same play, tool id, map name, row key, and compatible logic.
|
|
7291
|
+
To refresh, change the id/map key or set staleAfterSeconds:
|
|
7292
|
+
|
|
7293
|
+
.run({ key: 'domain', staleAfterSeconds: 86400 })
|
|
6747
7294
|
|
|
6748
7295
|
Examples:
|
|
6749
7296
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
|
|
6750
7297
|
deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
|
|
6751
7298
|
deepline plays run enrich.play.ts --csv leads.csv --watch --out leads-enriched.csv
|
|
7299
|
+
deepline plays run cto-search.play.ts --limit 5 --watch
|
|
7300
|
+
deepline runs get <run-id>
|
|
6752
7301
|
`
|
|
6753
7302
|
).option("--file <path>", "Local play file to run").option("--name <name>", "Saved play name to run").option("-i, --input <json>", "Input JSON object or @file path").option("--live", "Run the current live revision explicitly").option("--latest", "Run the newest saved revision, even if it is not live").option(
|
|
6754
7303
|
"--revision-id <id>",
|
|
@@ -6759,7 +7308,7 @@ Examples:
|
|
|
6759
7308
|
).option("--watch", "Stream logs until completion").option(
|
|
6760
7309
|
"--logs",
|
|
6761
7310
|
"When output is non-interactive, stream play logs to stderr while waiting"
|
|
6762
|
-
).option("--
|
|
7311
|
+
).option("--tail-timeout-ms <ms>", "Timeout while watching the run stream").option("--force", "Supersede any active runs for this play").option("--no-open", "Print the play page URL without opening a browser").option("--json", "Emit JSON output").action(async (target, options, command) => {
|
|
6763
7312
|
const passthroughArgs = [...command.args];
|
|
6764
7313
|
const explicitTarget = options.file || options.name;
|
|
6765
7314
|
const targetIsInputFlag = typeof target === "string" && target.startsWith("--");
|
|
@@ -6781,9 +7330,9 @@ Examples:
|
|
|
6781
7330
|
...options.out ? ["--out", options.out] : [],
|
|
6782
7331
|
...options.watch ? ["--watch"] : [],
|
|
6783
7332
|
...options.logs ? ["--logs"] : [],
|
|
6784
|
-
...options.pollIntervalMs ? ["--poll-interval-ms", options.pollIntervalMs] : [],
|
|
6785
7333
|
...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
|
|
6786
7334
|
...options.force ? ["--force"] : [],
|
|
7335
|
+
...options.open === false ? ["--no-open"] : [],
|
|
6787
7336
|
...options.json ? ["--json"] : [],
|
|
6788
7337
|
...passthroughArgs
|
|
6789
7338
|
]);
|
|
@@ -6890,12 +7439,11 @@ Examples:
|
|
|
6890
7439
|
...options.json ? ["--json"] : []
|
|
6891
7440
|
]);
|
|
6892
7441
|
});
|
|
6893
|
-
runs.command("tail <runId>").description("
|
|
7442
|
+
runs.command("tail <runId>").description("Read the canonical live stream for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--compact", "Drop verbose fields from JSON output").action(async (runId, options) => {
|
|
6894
7443
|
process.exitCode = await handleRunTail([
|
|
6895
7444
|
runId,
|
|
6896
7445
|
...options.json ? ["--json"] : [],
|
|
6897
|
-
...options.compact ? ["--compact"] : []
|
|
6898
|
-
...options.cursor ? ["--cursor", options.cursor] : []
|
|
7446
|
+
...options.compact ? ["--compact"] : []
|
|
6899
7447
|
]);
|
|
6900
7448
|
});
|
|
6901
7449
|
runs.command("logs <runId>").description("Fetch persisted logs for a play run.").option("--limit <count>", "Maximum recent log lines to print without --out", "200").option("--out <path>", "Write the full persisted log stream to a file").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
@@ -7876,54 +8424,6 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
|
7876
8424
|
writeSdkSkillsStatusLine("SDK skills are up to date.");
|
|
7877
8425
|
}
|
|
7878
8426
|
|
|
7879
|
-
// src/cli/trace.ts
|
|
7880
|
-
var cliTraceStartedAt = Date.now();
|
|
7881
|
-
function isTruthyEnv(value) {
|
|
7882
|
-
return value === "1" || value === "true" || value === "yes";
|
|
7883
|
-
}
|
|
7884
|
-
function isCliTraceEnabled() {
|
|
7885
|
-
return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
|
|
7886
|
-
}
|
|
7887
|
-
function recordCliTrace(event) {
|
|
7888
|
-
if (!isCliTraceEnabled()) {
|
|
7889
|
-
return;
|
|
7890
|
-
}
|
|
7891
|
-
const now = Date.now();
|
|
7892
|
-
const payload = {
|
|
7893
|
-
ts: now,
|
|
7894
|
-
source: "cli",
|
|
7895
|
-
sinceStartMs: now - cliTraceStartedAt,
|
|
7896
|
-
...event
|
|
7897
|
-
};
|
|
7898
|
-
process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
|
|
7899
|
-
`);
|
|
7900
|
-
}
|
|
7901
|
-
async function traceCliSpan(phase, fields, run) {
|
|
7902
|
-
if (!isCliTraceEnabled()) {
|
|
7903
|
-
return run();
|
|
7904
|
-
}
|
|
7905
|
-
const startedAt = Date.now();
|
|
7906
|
-
try {
|
|
7907
|
-
const result = await run();
|
|
7908
|
-
recordCliTrace({
|
|
7909
|
-
phase,
|
|
7910
|
-
ms: Date.now() - startedAt,
|
|
7911
|
-
ok: true,
|
|
7912
|
-
...fields
|
|
7913
|
-
});
|
|
7914
|
-
return result;
|
|
7915
|
-
} catch (error) {
|
|
7916
|
-
recordCliTrace({
|
|
7917
|
-
phase,
|
|
7918
|
-
ms: Date.now() - startedAt,
|
|
7919
|
-
ok: false,
|
|
7920
|
-
error: error instanceof Error ? error.message : String(error),
|
|
7921
|
-
...fields
|
|
7922
|
-
});
|
|
7923
|
-
throw error;
|
|
7924
|
-
}
|
|
7925
|
-
}
|
|
7926
|
-
|
|
7927
8427
|
// src/cli/index.ts
|
|
7928
8428
|
function shouldPrintStartupPhase() {
|
|
7929
8429
|
if (process.argv.includes("--json")) {
|
|
@@ -7934,6 +8434,12 @@ function shouldPrintStartupPhase() {
|
|
|
7934
8434
|
const subcommand = args[1];
|
|
7935
8435
|
return (command === "play" || command === "plays") && subcommand === "run";
|
|
7936
8436
|
}
|
|
8437
|
+
function shouldDeferSkillsSyncForCommand() {
|
|
8438
|
+
const args = process.argv.slice(2);
|
|
8439
|
+
const command = args[0];
|
|
8440
|
+
const subcommand = args[1];
|
|
8441
|
+
return (command === "play" || command === "plays") && subcommand === "run" && args.includes("--json");
|
|
8442
|
+
}
|
|
7937
8443
|
async function main() {
|
|
7938
8444
|
const mainStartedAt = Date.now();
|
|
7939
8445
|
recordCliTrace({
|
|
@@ -7978,11 +8484,13 @@ Output:
|
|
|
7978
8484
|
if (printStartupPhase) {
|
|
7979
8485
|
progress?.phase("checking sdk skills");
|
|
7980
8486
|
}
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
8487
|
+
if (!shouldDeferSkillsSyncForCommand()) {
|
|
8488
|
+
await traceCliSpan(
|
|
8489
|
+
"cli.sdk_skills_sync",
|
|
8490
|
+
{ baseUrl },
|
|
8491
|
+
() => syncSdkSkillsIfNeeded(baseUrl)
|
|
8492
|
+
);
|
|
8493
|
+
}
|
|
7986
8494
|
});
|
|
7987
8495
|
registerAuthCommands(program);
|
|
7988
8496
|
registerToolsCommands(program);
|