deepline 0.1.21 → 0.1.23
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 +552 -237
- package/dist/cli/index.mjs +609 -289
- package/dist/index.d.mts +21 -58
- package/dist/index.d.ts +21 -58
- package/dist/index.js +177 -92
- package/dist/index.mjs +177 -92
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +3 -1
- package/dist/repo/apps/play-runner-workers/src/entry.ts +153 -0
- package/dist/repo/sdk/src/client.ts +243 -124
- 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/execution-plan.ts +27 -2
- package/dist/repo/shared_libs/play-runtime/profiles.ts +4 -14
- package/dist/repo/shared_libs/play-runtime/runtime-actions.ts +1 -1
- 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.23";
|
|
270
270
|
var SDK_API_CONTRACT = "2026-05-runs-v2";
|
|
271
271
|
|
|
272
272
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
@@ -554,11 +554,24 @@ function sleep(ms) {
|
|
|
554
554
|
// src/client.ts
|
|
555
555
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
556
556
|
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
557
|
+
var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
|
|
558
|
+
function sleep2(ms) {
|
|
559
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
560
|
+
}
|
|
561
|
+
function isTransientCompileManifestError(error) {
|
|
562
|
+
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
563
|
+
return error.statusCode === 408 || error.statusCode === 425 || error.statusCode === 499 || error.statusCode >= 500 && error.statusCode < 600;
|
|
564
|
+
}
|
|
565
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
566
|
+
return /fetch failed|connection (?:closed|reset|terminated)|socket hang up|econnreset|etimedout|eai_again|abort/i.test(
|
|
567
|
+
message
|
|
568
|
+
);
|
|
569
|
+
}
|
|
557
570
|
function isRecord(value) {
|
|
558
571
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
559
572
|
}
|
|
560
573
|
function normalizePlayStatus(raw) {
|
|
561
|
-
const status = typeof raw.status === "string" ? raw.status :
|
|
574
|
+
const status = typeof raw.status === "string" ? raw.status : "running";
|
|
562
575
|
const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
|
|
563
576
|
return {
|
|
564
577
|
...raw,
|
|
@@ -566,23 +579,6 @@ function normalizePlayStatus(raw) {
|
|
|
566
579
|
status
|
|
567
580
|
};
|
|
568
581
|
}
|
|
569
|
-
function mapLegacyTemporalStatus(status) {
|
|
570
|
-
switch (status.trim().toUpperCase()) {
|
|
571
|
-
case "PENDING":
|
|
572
|
-
return "queued";
|
|
573
|
-
case "COMPLETED":
|
|
574
|
-
return "completed";
|
|
575
|
-
case "FAILED":
|
|
576
|
-
return "failed";
|
|
577
|
-
case "CANCELLED":
|
|
578
|
-
case "TERMINATED":
|
|
579
|
-
case "TIMED_OUT":
|
|
580
|
-
return "cancelled";
|
|
581
|
-
case "RUNNING":
|
|
582
|
-
default:
|
|
583
|
-
return "running";
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
582
|
function decodeBase64Bytes(value) {
|
|
587
583
|
const binary = atob(value);
|
|
588
584
|
const bytes = new Uint8Array(binary.length);
|
|
@@ -591,6 +587,79 @@ function decodeBase64Bytes(value) {
|
|
|
591
587
|
}
|
|
592
588
|
return bytes;
|
|
593
589
|
}
|
|
590
|
+
function readStringArray(value) {
|
|
591
|
+
return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
|
|
592
|
+
}
|
|
593
|
+
function getPlayLiveEventPayload(event) {
|
|
594
|
+
return event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
595
|
+
}
|
|
596
|
+
function normalizeLiveStatus(value) {
|
|
597
|
+
if (value === "queued" || value === "running" || value === "waiting" || value === "completed" || value === "failed" || value === "cancelled") {
|
|
598
|
+
return value;
|
|
599
|
+
}
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
function updatePlayLiveStatusState(state, event) {
|
|
603
|
+
const payload = getPlayLiveEventPayload(event);
|
|
604
|
+
if (event.type === "play.run.log") {
|
|
605
|
+
state.logs.push(...readStringArray(payload.lines));
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : state.runId;
|
|
612
|
+
const status = normalizeLiveStatus(payload.status) ?? state.status;
|
|
613
|
+
const logs = readStringArray(payload.logs);
|
|
614
|
+
if (logs.length > 0 || event.type === "play.run.snapshot") {
|
|
615
|
+
state.logs = logs;
|
|
616
|
+
}
|
|
617
|
+
if ("result" in payload) {
|
|
618
|
+
state.result = payload.result;
|
|
619
|
+
}
|
|
620
|
+
if (typeof payload.error === "string" && payload.error.trim()) {
|
|
621
|
+
state.error = payload.error;
|
|
622
|
+
}
|
|
623
|
+
state.runId = runId;
|
|
624
|
+
state.status = status;
|
|
625
|
+
const progressRecord = payload.progress && typeof payload.progress === "object" && !Array.isArray(payload.progress) ? payload.progress : {};
|
|
626
|
+
const next = {
|
|
627
|
+
...payload,
|
|
628
|
+
runId,
|
|
629
|
+
status,
|
|
630
|
+
progress: {
|
|
631
|
+
...progressRecord,
|
|
632
|
+
status: typeof progressRecord.status === "string" ? progressRecord.status : status,
|
|
633
|
+
logs: state.logs,
|
|
634
|
+
...state.error ? { error: state.error } : {}
|
|
635
|
+
},
|
|
636
|
+
..."result" in state ? { result: state.result } : {}
|
|
637
|
+
};
|
|
638
|
+
state.latest = next;
|
|
639
|
+
return next;
|
|
640
|
+
}
|
|
641
|
+
function playRunResultFromStatus(status, startedAt, fallbackRunId) {
|
|
642
|
+
return {
|
|
643
|
+
success: status.status === "completed",
|
|
644
|
+
runId: status.runId || fallbackRunId,
|
|
645
|
+
result: status.result,
|
|
646
|
+
logs: status.progress?.logs ?? [],
|
|
647
|
+
durationMs: Date.now() - startedAt,
|
|
648
|
+
error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function playRunStatusFromState(state) {
|
|
652
|
+
return {
|
|
653
|
+
runId: state.runId,
|
|
654
|
+
status: state.status,
|
|
655
|
+
progress: {
|
|
656
|
+
status: state.status,
|
|
657
|
+
logs: state.logs,
|
|
658
|
+
...state.error ? { error: state.error } : {}
|
|
659
|
+
},
|
|
660
|
+
..."result" in state ? { result: state.result } : {}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
594
663
|
var DeeplineClient = class {
|
|
595
664
|
http;
|
|
596
665
|
config;
|
|
@@ -700,7 +769,7 @@ var DeeplineClient = class {
|
|
|
700
769
|
/**
|
|
701
770
|
* Search available tools using Deepline's ranked backend search.
|
|
702
771
|
*
|
|
703
|
-
* This is the same discovery surface used by the
|
|
772
|
+
* This is the same discovery surface used by the CLI: it ranks across
|
|
704
773
|
* tool metadata, categories, agent guidance, and input schema fields.
|
|
705
774
|
*/
|
|
706
775
|
async searchTools(options = {}) {
|
|
@@ -793,7 +862,7 @@ var DeeplineClient = class {
|
|
|
793
862
|
* `progress.logs`; they are not part of the user output object.
|
|
794
863
|
*
|
|
795
864
|
* @param request - Play run configuration (name, code, input, etc.)
|
|
796
|
-
* @returns
|
|
865
|
+
* @returns Run metadata including the public `workflowId`
|
|
797
866
|
*
|
|
798
867
|
* @example
|
|
799
868
|
* ```typescript
|
|
@@ -903,8 +972,22 @@ var DeeplineClient = class {
|
|
|
903
972
|
});
|
|
904
973
|
}
|
|
905
974
|
async compilePlayManifest(input) {
|
|
906
|
-
const
|
|
907
|
-
|
|
975
|
+
const retryDelays = COMPILE_MANIFEST_RETRY_DELAYS_MS.slice(
|
|
976
|
+
0,
|
|
977
|
+
Math.max(0, this.config.maxRetries)
|
|
978
|
+
);
|
|
979
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
980
|
+
try {
|
|
981
|
+
const response = await this.http.post("/api/v2/plays/compile-manifest", input);
|
|
982
|
+
return response.compilerManifest;
|
|
983
|
+
} catch (error) {
|
|
984
|
+
const delayMs = retryDelays[attempt];
|
|
985
|
+
if (delayMs === void 0 || !isTransientCompileManifestError(error)) {
|
|
986
|
+
throw error;
|
|
987
|
+
}
|
|
988
|
+
await sleep2(delayMs);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
908
991
|
}
|
|
909
992
|
/**
|
|
910
993
|
* Check a bundled play artifact against the server's current play compiler.
|
|
@@ -1085,9 +1168,6 @@ var DeeplineClient = class {
|
|
|
1085
1168
|
* Internal/advanced primitive. Public callers should usually prefer
|
|
1086
1169
|
* {@link runPlay}, {@link PlayJob.get}, or `deepline play run --watch`.
|
|
1087
1170
|
*
|
|
1088
|
-
* Poll this method until `status` reaches a terminal state:
|
|
1089
|
-
* `'completed'`, `'failed'`, or `'cancelled'`.
|
|
1090
|
-
*
|
|
1091
1171
|
* @param workflowId - Play-run id from {@link startPlayRun}
|
|
1092
1172
|
* @returns Current status with progress logs and partial results
|
|
1093
1173
|
*
|
|
@@ -1109,35 +1189,11 @@ var DeeplineClient = class {
|
|
|
1109
1189
|
);
|
|
1110
1190
|
return normalizePlayStatus(response);
|
|
1111
1191
|
}
|
|
1112
|
-
/**
|
|
1113
|
-
* Get the lightweight tail-polling status for a play execution.
|
|
1114
|
-
*
|
|
1115
|
-
* This is intentionally smaller than {@link getPlayStatus}: it returns the
|
|
1116
|
-
* fields needed for CLI log tailing while the run is in flight, without
|
|
1117
|
-
* forcing the API to rebuild final result views on every poll. Call
|
|
1118
|
-
* {@link getPlayStatus} once after a terminal state for the full result.
|
|
1119
|
-
*/
|
|
1120
|
-
async getPlayTailStatus(workflowId, options) {
|
|
1121
|
-
const params = new URLSearchParams({ mode: "tail" });
|
|
1122
|
-
if (typeof options?.afterLogIndex === "number") {
|
|
1123
|
-
params.set("afterLogIndex", String(options.afterLogIndex));
|
|
1124
|
-
}
|
|
1125
|
-
if (typeof options?.waitMs === "number") {
|
|
1126
|
-
params.set("waitMs", String(options.waitMs));
|
|
1127
|
-
}
|
|
1128
|
-
if (options?.terminalOnly) {
|
|
1129
|
-
params.set("terminalOnly", "true");
|
|
1130
|
-
}
|
|
1131
|
-
const response = await this.http.get(
|
|
1132
|
-
`/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`
|
|
1133
|
-
);
|
|
1134
|
-
return normalizePlayStatus(response);
|
|
1135
|
-
}
|
|
1136
1192
|
/**
|
|
1137
1193
|
* Stream semantic play-run events using the same SSE feed as the dashboard.
|
|
1138
1194
|
*
|
|
1139
|
-
*
|
|
1140
|
-
*
|
|
1195
|
+
* The server emits a canonical `play.run.snapshot` event first for every
|
|
1196
|
+
* connection, then incremental live events until terminal state or reconnect.
|
|
1141
1197
|
*/
|
|
1142
1198
|
async *streamPlayRunEvents(workflowId, options) {
|
|
1143
1199
|
const headers = options?.lastEventId && options.lastEventId.trim() ? { "Last-Event-ID": options.lastEventId.trim() } : void 0;
|
|
@@ -1157,7 +1213,7 @@ var DeeplineClient = class {
|
|
|
1157
1213
|
*
|
|
1158
1214
|
* Sends a stop request for the run.
|
|
1159
1215
|
*
|
|
1160
|
-
* @param workflowId -
|
|
1216
|
+
* @param workflowId - Public Deepline play-run id to cancel
|
|
1161
1217
|
*
|
|
1162
1218
|
* @example
|
|
1163
1219
|
* ```typescript
|
|
@@ -1173,7 +1229,7 @@ var DeeplineClient = class {
|
|
|
1173
1229
|
/**
|
|
1174
1230
|
* Stop a running play execution, including open HITL waits.
|
|
1175
1231
|
*
|
|
1176
|
-
* @param workflowId -
|
|
1232
|
+
* @param workflowId - Public Deepline play-run id to stop
|
|
1177
1233
|
* @param options.reason - Optional audit/debug reason
|
|
1178
1234
|
*/
|
|
1179
1235
|
async stopPlay(workflowId, options) {
|
|
@@ -1245,32 +1301,42 @@ var DeeplineClient = class {
|
|
|
1245
1301
|
);
|
|
1246
1302
|
return response.runs ?? [];
|
|
1247
1303
|
}
|
|
1248
|
-
/**
|
|
1249
|
-
* Fetch the lightweight tail status for a run using the public runs resource model.
|
|
1250
|
-
*
|
|
1251
|
-
* This is the SDK equivalent of:
|
|
1252
|
-
*
|
|
1253
|
-
* ```bash
|
|
1254
|
-
* deepline runs tail <run-id> --json
|
|
1255
|
-
* ```
|
|
1256
|
-
*/
|
|
1304
|
+
/** Read the canonical run stream and return the latest run snapshot. */
|
|
1257
1305
|
async tailRun(runId, options) {
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1306
|
+
const state = {
|
|
1307
|
+
runId,
|
|
1308
|
+
status: "running",
|
|
1309
|
+
logs: [],
|
|
1310
|
+
latest: null
|
|
1311
|
+
};
|
|
1312
|
+
let terminal = false;
|
|
1313
|
+
for await (const event of this.streamPlayRunEvents(runId, {
|
|
1314
|
+
mode: "cli",
|
|
1315
|
+
signal: options?.signal
|
|
1316
|
+
})) {
|
|
1317
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
1318
|
+
if (!status) {
|
|
1319
|
+
continue;
|
|
1320
|
+
}
|
|
1321
|
+
terminal = TERMINAL_PLAY_STATUSES.has(status.status);
|
|
1322
|
+
if (terminal) {
|
|
1323
|
+
break;
|
|
1324
|
+
}
|
|
1262
1325
|
}
|
|
1263
|
-
if (
|
|
1264
|
-
|
|
1326
|
+
if (terminal && state.latest) {
|
|
1327
|
+
return await this.getRunStatus(state.latest.runId || runId).catch(
|
|
1328
|
+
() => state.latest ?? playRunStatusFromState(state)
|
|
1329
|
+
);
|
|
1265
1330
|
}
|
|
1266
|
-
if (
|
|
1267
|
-
|
|
1331
|
+
if (state.latest) {
|
|
1332
|
+
return state.latest;
|
|
1268
1333
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1334
|
+
throw new DeeplineError(
|
|
1335
|
+
`Run stream for ${runId} ended before the initial snapshot.`,
|
|
1336
|
+
void 0,
|
|
1337
|
+
"PLAY_RUN_STREAM_EMPTY",
|
|
1338
|
+
{ runId }
|
|
1272
1339
|
);
|
|
1273
|
-
return normalizePlayStatus(response);
|
|
1274
1340
|
}
|
|
1275
1341
|
/**
|
|
1276
1342
|
* Fetch persisted logs for a run using the public runs resource model.
|
|
@@ -1427,11 +1493,11 @@ var DeeplineClient = class {
|
|
|
1427
1493
|
// Plays — high-level orchestration
|
|
1428
1494
|
// ——————————————————————————————————————————————————————————
|
|
1429
1495
|
/**
|
|
1430
|
-
* Run a play end-to-end: submit,
|
|
1496
|
+
* Run a play end-to-end: submit, stream until terminal, return result.
|
|
1431
1497
|
*
|
|
1432
1498
|
* This is the highest-level play execution method. It submits the play,
|
|
1433
|
-
*
|
|
1434
|
-
* and timing. Supports cancellation via `AbortSignal`.
|
|
1499
|
+
* reads the canonical run stream for status updates, and returns a structured
|
|
1500
|
+
* result with logs and timing. Supports cancellation via `AbortSignal`.
|
|
1435
1501
|
*
|
|
1436
1502
|
* @param code - Source string fallback; pass the bundled artifact in `options.artifact`
|
|
1437
1503
|
* @param csvPath - Input CSV path, or `null`
|
|
@@ -1447,7 +1513,6 @@ var DeeplineClient = class {
|
|
|
1447
1513
|
* const logs = status.progress?.logs ?? [];
|
|
1448
1514
|
* console.log(`[${status.status}] ${logs.length} log lines`);
|
|
1449
1515
|
* },
|
|
1450
|
-
* pollIntervalMs: 1000,
|
|
1451
1516
|
* });
|
|
1452
1517
|
*
|
|
1453
1518
|
* if (result.success) {
|
|
@@ -1478,33 +1543,53 @@ var DeeplineClient = class {
|
|
|
1478
1543
|
packagedFiles: options?.packagedFiles,
|
|
1479
1544
|
force: options?.force
|
|
1480
1545
|
});
|
|
1481
|
-
const pollInterval = options?.pollIntervalMs ?? 500;
|
|
1482
1546
|
const start = Date.now();
|
|
1483
|
-
|
|
1547
|
+
const state = {
|
|
1548
|
+
runId: workflowId,
|
|
1549
|
+
status: "running",
|
|
1550
|
+
logs: [],
|
|
1551
|
+
latest: null
|
|
1552
|
+
};
|
|
1553
|
+
if (options?.signal?.aborted) {
|
|
1554
|
+
await this.cancelPlay(workflowId);
|
|
1555
|
+
return {
|
|
1556
|
+
success: false,
|
|
1557
|
+
runId: workflowId,
|
|
1558
|
+
logs: [],
|
|
1559
|
+
durationMs: Date.now() - start,
|
|
1560
|
+
error: "Cancelled by user"
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
for await (const event of this.streamPlayRunEvents(workflowId, {
|
|
1564
|
+
mode: "cli",
|
|
1565
|
+
signal: options?.signal
|
|
1566
|
+
})) {
|
|
1484
1567
|
if (options?.signal?.aborted) {
|
|
1485
1568
|
await this.cancelPlay(workflowId);
|
|
1486
1569
|
return {
|
|
1487
1570
|
success: false,
|
|
1488
1571
|
runId: workflowId,
|
|
1489
|
-
logs:
|
|
1572
|
+
logs: state.logs,
|
|
1490
1573
|
durationMs: Date.now() - start,
|
|
1491
1574
|
error: "Cancelled by user"
|
|
1492
1575
|
};
|
|
1493
1576
|
}
|
|
1494
|
-
const status =
|
|
1577
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
1578
|
+
if (!status) {
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1495
1581
|
options?.onProgress?.(status);
|
|
1496
1582
|
if (TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
runId: status.runId || workflowId,
|
|
1500
|
-
result: status.result,
|
|
1501
|
-
logs: status.progress?.logs ?? [],
|
|
1502
|
-
durationMs: Date.now() - start,
|
|
1503
|
-
error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
|
|
1504
|
-
};
|
|
1583
|
+
const finalStatus = await this.getPlayStatus(status.runId || workflowId).catch(() => status);
|
|
1584
|
+
return playRunResultFromStatus(finalStatus, start, workflowId);
|
|
1505
1585
|
}
|
|
1506
|
-
await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
|
|
1507
1586
|
}
|
|
1587
|
+
throw new DeeplineError(
|
|
1588
|
+
`Run stream for ${workflowId} ended before the run reached a terminal state.`,
|
|
1589
|
+
void 0,
|
|
1590
|
+
"PLAY_RUN_STREAM_ENDED",
|
|
1591
|
+
{ runId: workflowId, workflowId }
|
|
1592
|
+
);
|
|
1508
1593
|
}
|
|
1509
1594
|
// ——————————————————————————————————————————————————————————
|
|
1510
1595
|
// Health
|
|
@@ -1590,6 +1675,7 @@ var import_node_path2 = require("path");
|
|
|
1590
1675
|
var import_node_child_process = require("child_process");
|
|
1591
1676
|
var import_sync = require("csv-parse/sync");
|
|
1592
1677
|
var import_sync2 = require("csv-stringify/sync");
|
|
1678
|
+
var BROWSER_FOCUS_COOLDOWN_MS = 3e4;
|
|
1593
1679
|
function getAuthedHttpClient() {
|
|
1594
1680
|
const config = resolveConfig();
|
|
1595
1681
|
return { config, http: new HttpClient(config) };
|
|
@@ -1601,12 +1687,215 @@ async function writeOutputFile(filename, content) {
|
|
|
1601
1687
|
await (0, import_promises.writeFile)(fullPath, content, "utf-8");
|
|
1602
1688
|
return fullPath;
|
|
1603
1689
|
}
|
|
1690
|
+
function browserFocusStateFile() {
|
|
1691
|
+
const homeDir = process.env.HOME || (0, import_node_os2.homedir)();
|
|
1692
|
+
return (0, import_node_path2.join)(
|
|
1693
|
+
homeDir,
|
|
1694
|
+
".local",
|
|
1695
|
+
"deepline",
|
|
1696
|
+
"runtime",
|
|
1697
|
+
"state",
|
|
1698
|
+
"browser-focus.json"
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
function claimBrowserFocus(now = Date.now()) {
|
|
1702
|
+
const statePath = browserFocusStateFile();
|
|
1703
|
+
try {
|
|
1704
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(statePath), { recursive: true });
|
|
1705
|
+
let lastFocusedAt = 0;
|
|
1706
|
+
if ((0, import_node_fs2.existsSync)(statePath)) {
|
|
1707
|
+
const payload = JSON.parse((0, import_node_fs2.readFileSync)(statePath, "utf-8"));
|
|
1708
|
+
const value = payload.lastFocusedAt ?? payload.last_focused_at;
|
|
1709
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1710
|
+
lastFocusedAt = value;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
if (lastFocusedAt > now) {
|
|
1714
|
+
lastFocusedAt = 0;
|
|
1715
|
+
}
|
|
1716
|
+
if (now - lastFocusedAt < BROWSER_FOCUS_COOLDOWN_MS) {
|
|
1717
|
+
return false;
|
|
1718
|
+
}
|
|
1719
|
+
(0, import_node_fs2.writeFileSync)(statePath, JSON.stringify({ lastFocusedAt: now }), "utf-8");
|
|
1720
|
+
return true;
|
|
1721
|
+
} catch {
|
|
1722
|
+
return true;
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
function extractUrlHost(raw) {
|
|
1726
|
+
try {
|
|
1727
|
+
const parsed = new URL(raw.includes("://") ? raw : `https://${raw}`);
|
|
1728
|
+
return parsed.port ? `${parsed.hostname.toLowerCase()}:${parsed.port}` : parsed.hostname.toLowerCase();
|
|
1729
|
+
} catch {
|
|
1730
|
+
return "";
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
function browserAppNameFromBundleId(bundleId) {
|
|
1734
|
+
const names = {
|
|
1735
|
+
"com.google.chrome": "Google Chrome",
|
|
1736
|
+
"com.google.chrome.canary": "Google Chrome Canary",
|
|
1737
|
+
"com.microsoft.edgemac": "Microsoft Edge",
|
|
1738
|
+
"com.brave.browser": "Brave Browser",
|
|
1739
|
+
"com.operasoftware.opera": "Opera",
|
|
1740
|
+
"com.operasoftware.operagx": "Opera GX",
|
|
1741
|
+
"com.vivaldi.vivaldi": "Vivaldi",
|
|
1742
|
+
"company.thebrowser.browser": "Arc",
|
|
1743
|
+
"com.apple.safari": "Safari"
|
|
1744
|
+
};
|
|
1745
|
+
return names[bundleId.toLowerCase()] ?? "";
|
|
1746
|
+
}
|
|
1747
|
+
function readDefaultMacBrowserBundleId() {
|
|
1748
|
+
try {
|
|
1749
|
+
const output = (0, import_node_child_process.execFileSync)(
|
|
1750
|
+
"/usr/bin/defaults",
|
|
1751
|
+
[
|
|
1752
|
+
"read",
|
|
1753
|
+
`${(0, import_node_os2.homedir)()}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`,
|
|
1754
|
+
"LSHandlers"
|
|
1755
|
+
],
|
|
1756
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
|
|
1757
|
+
);
|
|
1758
|
+
const httpsMatch = output.match(
|
|
1759
|
+
/LSHandlerURLScheme\s*=\s*https;[\s\S]*?LSHandlerRole(?:All|Viewer|Editor)\s*=\s*"([^"]+)"/
|
|
1760
|
+
);
|
|
1761
|
+
const httpMatch = output.match(
|
|
1762
|
+
/LSHandlerURLScheme\s*=\s*http;[\s\S]*?LSHandlerRole(?:All|Viewer|Editor)\s*=\s*"([^"]+)"/
|
|
1763
|
+
);
|
|
1764
|
+
return (httpsMatch?.[1] ?? httpMatch?.[1] ?? "").trim();
|
|
1765
|
+
} catch {
|
|
1766
|
+
return "";
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
function browserStrategyForBundleId(bundleId) {
|
|
1770
|
+
const normalized = bundleId.toLowerCase();
|
|
1771
|
+
if ((/* @__PURE__ */ new Set([
|
|
1772
|
+
"com.google.chrome",
|
|
1773
|
+
"com.google.chrome.canary",
|
|
1774
|
+
"com.microsoft.edgemac",
|
|
1775
|
+
"com.brave.browser",
|
|
1776
|
+
"com.operasoftware.opera",
|
|
1777
|
+
"com.operasoftware.operagx",
|
|
1778
|
+
"com.vivaldi.vivaldi",
|
|
1779
|
+
"company.thebrowser.browser"
|
|
1780
|
+
])).has(normalized)) {
|
|
1781
|
+
return "chromium";
|
|
1782
|
+
}
|
|
1783
|
+
return normalized === "com.apple.safari" ? "safari" : "fallback";
|
|
1784
|
+
}
|
|
1785
|
+
function runAppleScript(script, args) {
|
|
1786
|
+
const result = (0, import_node_child_process.spawnSync)("osascript", ["-", ...args], {
|
|
1787
|
+
input: script,
|
|
1788
|
+
encoding: "utf-8",
|
|
1789
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
1790
|
+
timeout: 5e3
|
|
1791
|
+
});
|
|
1792
|
+
return result.status === 0;
|
|
1793
|
+
}
|
|
1794
|
+
function retargetChromiumMacos(appName, targetUrl, allowFocus) {
|
|
1795
|
+
const host = extractUrlHost(targetUrl);
|
|
1796
|
+
if (!host) return false;
|
|
1797
|
+
const escapedAppName = appName.replace(/"/g, '\\"');
|
|
1798
|
+
const activateBlock = allowFocus ? " activate\n" : "";
|
|
1799
|
+
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";
|
|
1800
|
+
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";
|
|
1801
|
+
const script = `
|
|
1802
|
+
on run argv
|
|
1803
|
+
set targetUrl to item 1 of argv
|
|
1804
|
+
set targetHost to item 2 of argv
|
|
1805
|
+
tell application "${escapedAppName}"
|
|
1806
|
+
${activateBlock} if (count of windows) is 0 then
|
|
1807
|
+
make new window
|
|
1808
|
+
end if
|
|
1809
|
+
set foundTab to false
|
|
1810
|
+
repeat with w in windows
|
|
1811
|
+
set tabCount to count of tabs of w
|
|
1812
|
+
repeat with i from 1 to tabCount
|
|
1813
|
+
set t to tab i of w
|
|
1814
|
+
if (URL of t) contains targetHost then
|
|
1815
|
+
${foundTabBlock} set foundTab to true
|
|
1816
|
+
exit repeat
|
|
1817
|
+
end if
|
|
1818
|
+
end repeat
|
|
1819
|
+
if foundTab then exit repeat
|
|
1820
|
+
end repeat
|
|
1821
|
+
if not foundTab then
|
|
1822
|
+
${newTabBlock} end if
|
|
1823
|
+
end tell
|
|
1824
|
+
end run
|
|
1825
|
+
`;
|
|
1826
|
+
return runAppleScript(script, [targetUrl, host]);
|
|
1827
|
+
}
|
|
1828
|
+
function retargetSafariMacos(appName, targetUrl, allowFocus) {
|
|
1829
|
+
const host = extractUrlHost(targetUrl);
|
|
1830
|
+
if (!host) return false;
|
|
1831
|
+
const escapedAppName = appName.replace(/"/g, '\\"');
|
|
1832
|
+
const activateBlock = allowFocus ? " activate\n" : "";
|
|
1833
|
+
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";
|
|
1834
|
+
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";
|
|
1835
|
+
const script = `
|
|
1836
|
+
on run argv
|
|
1837
|
+
set targetUrl to item 1 of argv
|
|
1838
|
+
set targetHost to item 2 of argv
|
|
1839
|
+
tell application "${escapedAppName}"
|
|
1840
|
+
${activateBlock} if (count of windows) is 0 then
|
|
1841
|
+
make new document
|
|
1842
|
+
end if
|
|
1843
|
+
set foundTab to false
|
|
1844
|
+
repeat with w in windows
|
|
1845
|
+
set tabCount to count of tabs of w
|
|
1846
|
+
repeat with i from 1 to tabCount
|
|
1847
|
+
set t to tab i of w
|
|
1848
|
+
if (URL of t) contains targetHost then
|
|
1849
|
+
${foundTabBlock} set foundTab to true
|
|
1850
|
+
exit repeat
|
|
1851
|
+
end if
|
|
1852
|
+
end repeat
|
|
1853
|
+
if foundTab then exit repeat
|
|
1854
|
+
end repeat
|
|
1855
|
+
if not foundTab then
|
|
1856
|
+
${newTabBlock} end if
|
|
1857
|
+
end tell
|
|
1858
|
+
end run
|
|
1859
|
+
`;
|
|
1860
|
+
return runAppleScript(script, [targetUrl, host]);
|
|
1861
|
+
}
|
|
1862
|
+
function openUrlMacos(targetUrl, allowFocus) {
|
|
1863
|
+
const defaultBundleId = readDefaultMacBrowserBundleId();
|
|
1864
|
+
const appName = defaultBundleId ? browserAppNameFromBundleId(defaultBundleId) : "";
|
|
1865
|
+
const strategy = browserStrategyForBundleId(defaultBundleId);
|
|
1866
|
+
if (appName && strategy === "chromium" && retargetChromiumMacos(appName, targetUrl, allowFocus)) {
|
|
1867
|
+
return true;
|
|
1868
|
+
}
|
|
1869
|
+
if (appName && strategy === "safari" && retargetSafariMacos(appName, targetUrl, allowFocus)) {
|
|
1870
|
+
return true;
|
|
1871
|
+
}
|
|
1872
|
+
if (!allowFocus) {
|
|
1873
|
+
return false;
|
|
1874
|
+
}
|
|
1875
|
+
try {
|
|
1876
|
+
(0, import_node_child_process.execFileSync)("open", [targetUrl], { stdio: "ignore" });
|
|
1877
|
+
return true;
|
|
1878
|
+
} catch {
|
|
1879
|
+
return false;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1604
1882
|
function openInBrowser(url) {
|
|
1605
1883
|
try {
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1884
|
+
const targetUrl = String(url || "").trim();
|
|
1885
|
+
if (!targetUrl) return;
|
|
1886
|
+
const allowFocus = claimBrowserFocus();
|
|
1887
|
+
if (process.platform === "darwin") {
|
|
1888
|
+
openUrlMacos(targetUrl, allowFocus);
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
if (!allowFocus) return;
|
|
1892
|
+
if (process.platform === "win32") {
|
|
1893
|
+
(0, import_node_child_process.execFileSync)("cmd.exe", ["/c", "start", "", targetUrl], {
|
|
1894
|
+
stdio: "ignore"
|
|
1895
|
+
});
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
(0, import_node_child_process.execFileSync)("xdg-open", [targetUrl], { stdio: "ignore" });
|
|
1610
1899
|
} catch {
|
|
1611
1900
|
}
|
|
1612
1901
|
}
|
|
@@ -1751,7 +2040,7 @@ function buildCandidateUrls2(url) {
|
|
|
1751
2040
|
return [url];
|
|
1752
2041
|
}
|
|
1753
2042
|
}
|
|
1754
|
-
function
|
|
2043
|
+
function sleep3(ms) {
|
|
1755
2044
|
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
1756
2045
|
}
|
|
1757
2046
|
function printDeeplineLogo() {
|
|
@@ -1834,7 +2123,7 @@ async function handleRegister(args) {
|
|
|
1834
2123
|
return EXIT_AUTH;
|
|
1835
2124
|
}
|
|
1836
2125
|
if (s >= 500 || s === 0 || s === 400) {
|
|
1837
|
-
await
|
|
2126
|
+
await sleep3(2e3);
|
|
1838
2127
|
continue;
|
|
1839
2128
|
}
|
|
1840
2129
|
if (s >= 400) {
|
|
@@ -1858,7 +2147,7 @@ async function handleRegister(args) {
|
|
|
1858
2147
|
console.log("That approval link expired. Please run: deepline auth register");
|
|
1859
2148
|
return EXIT_AUTH;
|
|
1860
2149
|
}
|
|
1861
|
-
await
|
|
2150
|
+
await sleep3(2e3);
|
|
1862
2151
|
}
|
|
1863
2152
|
}
|
|
1864
2153
|
async function handleWait(args) {
|
|
@@ -1895,7 +2184,7 @@ async function handleWait(args) {
|
|
|
1895
2184
|
return EXIT_AUTH;
|
|
1896
2185
|
}
|
|
1897
2186
|
if (status >= 500 || status === 0 || status === 400) {
|
|
1898
|
-
await
|
|
2187
|
+
await sleep3(2e3);
|
|
1899
2188
|
continue;
|
|
1900
2189
|
}
|
|
1901
2190
|
if (status >= 400) {
|
|
@@ -1919,7 +2208,7 @@ async function handleWait(args) {
|
|
|
1919
2208
|
console.error("That approval link expired. Run: deepline auth register");
|
|
1920
2209
|
return EXIT_AUTH;
|
|
1921
2210
|
}
|
|
1922
|
-
await
|
|
2211
|
+
await sleep3(2e3);
|
|
1923
2212
|
}
|
|
1924
2213
|
console.error("Still pending. Approve the browser link, then run: deepline auth wait");
|
|
1925
2214
|
return EXIT_AUTH;
|
|
@@ -3862,13 +4151,6 @@ var PLAY_DEDUP_BACKENDS = {
|
|
|
3862
4151
|
|
|
3863
4152
|
// ../shared_libs/play-runtime/profiles.ts
|
|
3864
4153
|
var PLAY_EXECUTION_PROFILES = {
|
|
3865
|
-
legacy: {
|
|
3866
|
-
id: "legacy",
|
|
3867
|
-
scheduler: PLAY_SCHEDULER_BACKENDS.temporal,
|
|
3868
|
-
runner: PLAY_RUNTIME_BACKENDS.daytona,
|
|
3869
|
-
dedup: PLAY_DEDUP_BACKENDS.inMemory,
|
|
3870
|
-
label: "Daytona + Temporal (production today)"
|
|
3871
|
-
},
|
|
3872
4154
|
workers_edge: {
|
|
3873
4155
|
id: "workers_edge",
|
|
3874
4156
|
scheduler: PLAY_SCHEDULER_BACKENDS.cfWorkflows,
|
|
@@ -4919,7 +5201,7 @@ function formatTimestamp(value) {
|
|
|
4919
5201
|
function formatRunLine(run) {
|
|
4920
5202
|
return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)}`;
|
|
4921
5203
|
}
|
|
4922
|
-
function
|
|
5204
|
+
function isTransientPlayStreamError(error) {
|
|
4923
5205
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
4924
5206
|
return error.statusCode >= 500 && error.statusCode < 600;
|
|
4925
5207
|
}
|
|
@@ -4928,12 +5210,6 @@ function isTransientPlayStatusPollError(error) {
|
|
|
4928
5210
|
text
|
|
4929
5211
|
);
|
|
4930
5212
|
}
|
|
4931
|
-
function isTerminalPlayStatusPollError(input) {
|
|
4932
|
-
if (input.error instanceof DeeplineError && input.error.statusCode === 404 && input.hasSeenRun) {
|
|
4933
|
-
return true;
|
|
4934
|
-
}
|
|
4935
|
-
return false;
|
|
4936
|
-
}
|
|
4937
5213
|
var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
|
|
4938
5214
|
"completed",
|
|
4939
5215
|
"failed",
|
|
@@ -4999,6 +5275,12 @@ function buildPlayDashboardUrl(baseUrl, playName) {
|
|
|
4999
5275
|
const encodedPlayName = encodeURIComponent(playName);
|
|
5000
5276
|
return `${trimmedBase}/dashboard/plays/${encodedPlayName}`;
|
|
5001
5277
|
}
|
|
5278
|
+
function openPlayDashboard(input) {
|
|
5279
|
+
if (input.jsonOutput || input.noOpen || !input.dashboardUrl) {
|
|
5280
|
+
return;
|
|
5281
|
+
}
|
|
5282
|
+
openInBrowser(input.dashboardUrl);
|
|
5283
|
+
}
|
|
5002
5284
|
function getDashboardUrlFromLiveEvent(event) {
|
|
5003
5285
|
const dashboardUrl = getEventPayload(event).dashboardUrl;
|
|
5004
5286
|
return typeof dashboardUrl === "string" && dashboardUrl.trim() ? dashboardUrl.trim() : null;
|
|
@@ -5035,6 +5317,68 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
5035
5317
|
);
|
|
5036
5318
|
}
|
|
5037
5319
|
}
|
|
5320
|
+
async function waitForPlayCompletionByStream(input) {
|
|
5321
|
+
const controller = new AbortController();
|
|
5322
|
+
let timedOut = false;
|
|
5323
|
+
let lastPhase = null;
|
|
5324
|
+
const timeout = input.waitTimeoutMs === null ? null : setTimeout(
|
|
5325
|
+
() => {
|
|
5326
|
+
timedOut = true;
|
|
5327
|
+
controller.abort();
|
|
5328
|
+
},
|
|
5329
|
+
Math.max(1, input.waitTimeoutMs - (Date.now() - input.startedAt))
|
|
5330
|
+
);
|
|
5331
|
+
try {
|
|
5332
|
+
for await (const event of input.client.streamPlayRunEvents(
|
|
5333
|
+
input.workflowId,
|
|
5334
|
+
{ signal: controller.signal }
|
|
5335
|
+
)) {
|
|
5336
|
+
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
5337
|
+
const phase = describeLiveEventPhase(event);
|
|
5338
|
+
if (phase) {
|
|
5339
|
+
lastPhase = phase;
|
|
5340
|
+
input.progress.phase(phase);
|
|
5341
|
+
}
|
|
5342
|
+
printPlayLogLines({
|
|
5343
|
+
lines: getLogLinesFromLiveEvent(event),
|
|
5344
|
+
status: null,
|
|
5345
|
+
jsonOutput: input.jsonOutput,
|
|
5346
|
+
emitLogs: input.emitLogs,
|
|
5347
|
+
state: input.state,
|
|
5348
|
+
progress: input.progress
|
|
5349
|
+
});
|
|
5350
|
+
const status = getStatusFromLiveEvent(event);
|
|
5351
|
+
if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
|
|
5352
|
+
const finalStatus = await input.client.getPlayStatus(input.workflowId, {
|
|
5353
|
+
billing: false
|
|
5354
|
+
});
|
|
5355
|
+
if (TERMINAL_PLAY_STATUSES2.has(finalStatus.status)) {
|
|
5356
|
+
return finalStatus;
|
|
5357
|
+
}
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
} catch (error) {
|
|
5361
|
+
if (timedOut) {
|
|
5362
|
+
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
5363
|
+
}
|
|
5364
|
+
throw error;
|
|
5365
|
+
} finally {
|
|
5366
|
+
if (timeout) {
|
|
5367
|
+
clearTimeout(timeout);
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
5370
|
+
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
5371
|
+
throw new DeeplineError(
|
|
5372
|
+
`Play live stream ended before the run reached a terminal state runId=${input.workflowId}${phaseSuffix}.`,
|
|
5373
|
+
void 0,
|
|
5374
|
+
"PLAY_LIVE_STREAM_ENDED",
|
|
5375
|
+
{
|
|
5376
|
+
runId: input.workflowId,
|
|
5377
|
+
workflowId: input.workflowId,
|
|
5378
|
+
...lastPhase ? { phase: lastPhase } : {}
|
|
5379
|
+
}
|
|
5380
|
+
);
|
|
5381
|
+
}
|
|
5038
5382
|
async function startAndWaitForPlayCompletionByStream(input) {
|
|
5039
5383
|
const startedAt = Date.now();
|
|
5040
5384
|
const state = {
|
|
@@ -5068,6 +5412,11 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
5068
5412
|
const workflowId = lastKnownWorkflowId || "pending";
|
|
5069
5413
|
if (workflowId !== "pending" && !emittedDashboardUrl) {
|
|
5070
5414
|
const dashboardUrl = getDashboardUrlFromLiveEvent(event) ?? buildPlayDashboardUrl(input.client.baseUrl, input.playName);
|
|
5415
|
+
openPlayDashboard({
|
|
5416
|
+
dashboardUrl,
|
|
5417
|
+
jsonOutput: input.jsonOutput,
|
|
5418
|
+
noOpen: input.noOpen
|
|
5419
|
+
});
|
|
5071
5420
|
if (!input.jsonOutput) {
|
|
5072
5421
|
writeStartedPlayRun({
|
|
5073
5422
|
runId: workflowId,
|
|
@@ -5123,21 +5472,21 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
5123
5472
|
lastPhase
|
|
5124
5473
|
});
|
|
5125
5474
|
}
|
|
5126
|
-
if (lastKnownWorkflowId &&
|
|
5475
|
+
if (lastKnownWorkflowId && isTransientPlayStreamError(error)) {
|
|
5127
5476
|
if (timeout) {
|
|
5128
5477
|
clearTimeout(timeout);
|
|
5129
5478
|
}
|
|
5130
5479
|
const reason = error instanceof Error ? error.message : String(error);
|
|
5131
5480
|
if (!input.jsonOutput) {
|
|
5132
5481
|
process.stderr.write(
|
|
5133
|
-
`[play watch] start stream failed after run ${lastKnownWorkflowId};
|
|
5482
|
+
`[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to run stream (${reason})
|
|
5134
5483
|
`
|
|
5135
5484
|
);
|
|
5136
5485
|
}
|
|
5137
5486
|
recordCliTrace({
|
|
5138
|
-
phase: "cli.
|
|
5487
|
+
phase: "cli.play_start_stream_reconnect",
|
|
5139
5488
|
ms: Date.now() - startedAt,
|
|
5140
|
-
ok:
|
|
5489
|
+
ok: true,
|
|
5141
5490
|
playName: input.playName,
|
|
5142
5491
|
workflowId: lastKnownWorkflowId,
|
|
5143
5492
|
eventCount,
|
|
@@ -5145,10 +5494,9 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
5145
5494
|
lastPhase,
|
|
5146
5495
|
reason
|
|
5147
5496
|
});
|
|
5148
|
-
return
|
|
5497
|
+
return waitForPlayCompletionByStream({
|
|
5149
5498
|
client: input.client,
|
|
5150
5499
|
workflowId: lastKnownWorkflowId,
|
|
5151
|
-
pollIntervalMs: 500,
|
|
5152
5500
|
jsonOutput: input.jsonOutput,
|
|
5153
5501
|
emitLogs: input.emitLogs,
|
|
5154
5502
|
waitTimeoutMs: input.waitTimeoutMs,
|
|
@@ -5166,13 +5514,13 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
5166
5514
|
if (lastKnownWorkflowId) {
|
|
5167
5515
|
if (!input.jsonOutput) {
|
|
5168
5516
|
input.progress.writeLine(
|
|
5169
|
-
`[play watch] start stream ended after run ${lastKnownWorkflowId};
|
|
5517
|
+
`[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to run stream`
|
|
5170
5518
|
);
|
|
5171
5519
|
}
|
|
5172
5520
|
recordCliTrace({
|
|
5173
|
-
phase: "cli.
|
|
5521
|
+
phase: "cli.play_start_stream_reconnect",
|
|
5174
5522
|
ms: Date.now() - startedAt,
|
|
5175
|
-
ok:
|
|
5523
|
+
ok: true,
|
|
5176
5524
|
playName: input.playName,
|
|
5177
5525
|
workflowId: lastKnownWorkflowId,
|
|
5178
5526
|
eventCount,
|
|
@@ -5180,10 +5528,9 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
5180
5528
|
lastPhase,
|
|
5181
5529
|
reason: "stream ended before terminal event"
|
|
5182
5530
|
});
|
|
5183
|
-
return
|
|
5531
|
+
return waitForPlayCompletionByStream({
|
|
5184
5532
|
client: input.client,
|
|
5185
5533
|
workflowId: lastKnownWorkflowId,
|
|
5186
|
-
pollIntervalMs: 500,
|
|
5187
5534
|
jsonOutput: input.jsonOutput,
|
|
5188
5535
|
emitLogs: input.emitLogs,
|
|
5189
5536
|
waitTimeoutMs: input.waitTimeoutMs,
|
|
@@ -5204,72 +5551,6 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
5204
5551
|
}
|
|
5205
5552
|
);
|
|
5206
5553
|
}
|
|
5207
|
-
async function waitForPlayCompletionByPolling(input) {
|
|
5208
|
-
let lastTransientPollWarningAt = 0;
|
|
5209
|
-
let hasSeenRun = false;
|
|
5210
|
-
while (true) {
|
|
5211
|
-
assertPlayWaitNotTimedOut(input);
|
|
5212
|
-
let status;
|
|
5213
|
-
try {
|
|
5214
|
-
status = await input.client.getPlayTailStatus(input.workflowId, {
|
|
5215
|
-
afterLogIndex: input.state.lastLogIndex,
|
|
5216
|
-
// Keep the server-side tail wait close to the caller's requested poll
|
|
5217
|
-
// cadence. A long wait makes tiny remote runs look slow whenever the
|
|
5218
|
-
// terminal update lands just after the held request starts.
|
|
5219
|
-
waitMs: Math.max(50, Math.min(input.pollIntervalMs, 1e3))
|
|
5220
|
-
});
|
|
5221
|
-
} catch (error) {
|
|
5222
|
-
if (isTerminalPlayStatusPollError({ error, hasSeenRun })) {
|
|
5223
|
-
throw new DeeplineError(
|
|
5224
|
-
`Play run ${input.workflowId} no longer exists on the server (404). The run was deleted or the backend lost it.`,
|
|
5225
|
-
404,
|
|
5226
|
-
"PLAY_RUN_NOT_FOUND"
|
|
5227
|
-
);
|
|
5228
|
-
}
|
|
5229
|
-
if (!isTransientPlayStatusPollError(error)) {
|
|
5230
|
-
throw error;
|
|
5231
|
-
}
|
|
5232
|
-
const now = Date.now();
|
|
5233
|
-
if (now - lastTransientPollWarningAt >= 3e4) {
|
|
5234
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5235
|
-
input.progress.writeLine(
|
|
5236
|
-
`[play tail] transient status poll failed; retrying: ${message}`
|
|
5237
|
-
);
|
|
5238
|
-
lastTransientPollWarningAt = now;
|
|
5239
|
-
}
|
|
5240
|
-
await new Promise(
|
|
5241
|
-
(resolvePromise) => setTimeout(resolvePromise, input.pollIntervalMs)
|
|
5242
|
-
);
|
|
5243
|
-
continue;
|
|
5244
|
-
}
|
|
5245
|
-
hasSeenRun = true;
|
|
5246
|
-
const logs = status.progress?.logs ?? [];
|
|
5247
|
-
input.progress.phase(status.status);
|
|
5248
|
-
printPlayLogLines({
|
|
5249
|
-
lines: logs.slice(input.state.lastLogIndex),
|
|
5250
|
-
status,
|
|
5251
|
-
jsonOutput: input.jsonOutput,
|
|
5252
|
-
emitLogs: input.emitLogs,
|
|
5253
|
-
state: input.state,
|
|
5254
|
-
progress: input.progress
|
|
5255
|
-
});
|
|
5256
|
-
if (TERMINAL_PLAY_STATUSES2.has(status.status)) {
|
|
5257
|
-
return status.result !== void 0 ? status : await input.client.getPlayStatus(input.workflowId, { billing: false });
|
|
5258
|
-
}
|
|
5259
|
-
const authoritativeStatus = await input.client.getPlayStatus(
|
|
5260
|
-
input.workflowId,
|
|
5261
|
-
{ billing: false }
|
|
5262
|
-
);
|
|
5263
|
-
if (TERMINAL_PLAY_STATUSES2.has(authoritativeStatus.status)) {
|
|
5264
|
-
return authoritativeStatus;
|
|
5265
|
-
}
|
|
5266
|
-
if ((status.progress?.logs ?? []).length === input.state.lastLogIndex) {
|
|
5267
|
-
await new Promise(
|
|
5268
|
-
(resolvePromise) => setTimeout(resolvePromise, input.pollIntervalMs)
|
|
5269
|
-
);
|
|
5270
|
-
}
|
|
5271
|
-
}
|
|
5272
|
-
}
|
|
5273
5554
|
function formatInteger(value) {
|
|
5274
5555
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
|
|
5275
5556
|
}
|
|
@@ -5410,7 +5691,6 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
5410
5691
|
function buildRunNextCommands(runId, rowsInfo) {
|
|
5411
5692
|
const commands = {
|
|
5412
5693
|
get: `deepline runs get ${runId} --json`,
|
|
5413
|
-
tail: `deepline runs tail ${runId} --json`,
|
|
5414
5694
|
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
5415
5695
|
logs: `deepline runs logs ${runId} --out run.log --json`
|
|
5416
5696
|
};
|
|
@@ -5526,10 +5806,9 @@ function normalizeErrorsForEnvelope(status, error) {
|
|
|
5526
5806
|
}
|
|
5527
5807
|
function normalizeLogsForEnvelope(status) {
|
|
5528
5808
|
const logs = Array.isArray(status.progress?.logs) ? status.progress.logs : [];
|
|
5529
|
-
const
|
|
5530
|
-
const totalCount = offset + logs.length;
|
|
5809
|
+
const totalCount = logs.length;
|
|
5531
5810
|
const entries = logs.slice(Math.max(0, logs.length - RUN_LOG_PREVIEW_LIMIT));
|
|
5532
|
-
const firstSequence = entries.length === 0 ? null :
|
|
5811
|
+
const firstSequence = entries.length === 0 ? null : logs.length - entries.length + 1;
|
|
5533
5812
|
const lastSequence = totalCount === 0 ? null : totalCount;
|
|
5534
5813
|
return {
|
|
5535
5814
|
totalCount,
|
|
@@ -5775,7 +6054,6 @@ function writeStartedPlayRun(input) {
|
|
|
5775
6054
|
workflowId: input.runId,
|
|
5776
6055
|
name: input.playName,
|
|
5777
6056
|
status: input.status ?? "started",
|
|
5778
|
-
statusUrl: input.statusUrl,
|
|
5779
6057
|
dashboardUrl: input.dashboardUrl
|
|
5780
6058
|
};
|
|
5781
6059
|
if (input.jsonOutput) {
|
|
@@ -5787,7 +6065,7 @@ function writeStartedPlayRun(input) {
|
|
|
5787
6065
|
`Started ${input.playName}`,
|
|
5788
6066
|
` run id: ${input.runId}`,
|
|
5789
6067
|
` get status: deepline runs get ${input.runId} --json`,
|
|
5790
|
-
`
|
|
6068
|
+
` logs: deepline runs logs ${input.runId} --json`,
|
|
5791
6069
|
` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`,
|
|
5792
6070
|
` result JSON: deepline runs get ${input.runId} --json`
|
|
5793
6071
|
];
|
|
@@ -5802,7 +6080,7 @@ function writeStartedPlayRun(input) {
|
|
|
5802
6080
|
console.log(output);
|
|
5803
6081
|
}
|
|
5804
6082
|
function parsePlayRunOptions(args) {
|
|
5805
|
-
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]";
|
|
6083
|
+
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.";
|
|
5806
6084
|
let filePath = null;
|
|
5807
6085
|
let playName = null;
|
|
5808
6086
|
let input = null;
|
|
@@ -5812,8 +6090,8 @@ function parsePlayRunOptions(args) {
|
|
|
5812
6090
|
let jsonOutput = watch ? args.includes("--json") : argsWantJson(args);
|
|
5813
6091
|
const emitLogs = !jsonOutput || args.includes("--logs");
|
|
5814
6092
|
const force = args.includes("--force");
|
|
6093
|
+
const noOpen = args.includes("--no-open");
|
|
5815
6094
|
let outPath = null;
|
|
5816
|
-
let pollIntervalMs = 500;
|
|
5817
6095
|
let waitTimeoutMs = null;
|
|
5818
6096
|
for (let index = 0; index < args.length; index += 1) {
|
|
5819
6097
|
const arg = args[index];
|
|
@@ -5845,15 +6123,16 @@ function parsePlayRunOptions(args) {
|
|
|
5845
6123
|
outPath = (0, import_node_path8.resolve)(args[++index]);
|
|
5846
6124
|
continue;
|
|
5847
6125
|
}
|
|
5848
|
-
if (
|
|
5849
|
-
|
|
5850
|
-
|
|
6126
|
+
if (arg === "--poll-interval-ms" || arg === "--interval-ms") {
|
|
6127
|
+
throw new Error(
|
|
6128
|
+
`${arg} was removed. --watch uses the canonical run stream directly.`
|
|
6129
|
+
);
|
|
5851
6130
|
}
|
|
5852
6131
|
if ((arg === "--tail-timeout-ms" || arg === "--timeout-ms") && args[index + 1]) {
|
|
5853
6132
|
waitTimeoutMs = parsePositiveInteger2(args[++index], arg);
|
|
5854
6133
|
continue;
|
|
5855
6134
|
}
|
|
5856
|
-
if (arg === "--json" || arg === "--wait" || arg === "--tail" || arg === "--watch" || arg === "--logs" || arg === "--force") {
|
|
6135
|
+
if (arg === "--json" || arg === "--wait" || arg === "--tail" || arg === "--watch" || arg === "--logs" || arg === "--force" || arg === "--no-open") {
|
|
5857
6136
|
if (arg === "--watch") {
|
|
5858
6137
|
continue;
|
|
5859
6138
|
}
|
|
@@ -5922,10 +6201,10 @@ function parsePlayRunOptions(args) {
|
|
|
5922
6201
|
watch,
|
|
5923
6202
|
emitLogs,
|
|
5924
6203
|
jsonOutput,
|
|
5925
|
-
pollIntervalMs,
|
|
5926
6204
|
waitTimeoutMs,
|
|
5927
6205
|
force,
|
|
5928
|
-
outPath
|
|
6206
|
+
outPath,
|
|
6207
|
+
noOpen
|
|
5929
6208
|
};
|
|
5930
6209
|
}
|
|
5931
6210
|
function parsePlayCheckOptions(args) {
|
|
@@ -6100,6 +6379,7 @@ async function handleFileBackedRun(options) {
|
|
|
6100
6379
|
jsonOutput: options.jsonOutput,
|
|
6101
6380
|
emitLogs: options.emitLogs,
|
|
6102
6381
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
6382
|
+
noOpen: options.noOpen,
|
|
6103
6383
|
progress
|
|
6104
6384
|
})
|
|
6105
6385
|
);
|
|
@@ -6127,14 +6407,19 @@ async function handleFileBackedRun(options) {
|
|
|
6127
6407
|
() => client.startPlayRun(startRequest)
|
|
6128
6408
|
);
|
|
6129
6409
|
const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
6130
|
-
|
|
6410
|
+
const resolvedDashboardUrl = started.dashboardUrl ?? dashboardUrl;
|
|
6411
|
+
openPlayDashboard({
|
|
6412
|
+
dashboardUrl: resolvedDashboardUrl,
|
|
6413
|
+
jsonOutput: options.jsonOutput,
|
|
6414
|
+
noOpen: options.noOpen
|
|
6415
|
+
});
|
|
6416
|
+
progress.phase(`loading play on ${resolvedDashboardUrl}`);
|
|
6131
6417
|
progress.complete();
|
|
6132
6418
|
writeStartedPlayRun({
|
|
6133
6419
|
runId: started.workflowId,
|
|
6134
6420
|
playName,
|
|
6135
6421
|
status: started.status,
|
|
6136
|
-
|
|
6137
|
-
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
6422
|
+
dashboardUrl: resolvedDashboardUrl,
|
|
6138
6423
|
jsonOutput: options.jsonOutput,
|
|
6139
6424
|
progress
|
|
6140
6425
|
});
|
|
@@ -6239,6 +6524,7 @@ async function handleNamedRun(options) {
|
|
|
6239
6524
|
jsonOutput: options.jsonOutput,
|
|
6240
6525
|
emitLogs: options.emitLogs,
|
|
6241
6526
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
6527
|
+
noOpen: options.noOpen,
|
|
6242
6528
|
progress
|
|
6243
6529
|
})
|
|
6244
6530
|
);
|
|
@@ -6269,14 +6555,19 @@ async function handleNamedRun(options) {
|
|
|
6269
6555
|
client.baseUrl,
|
|
6270
6556
|
playName
|
|
6271
6557
|
);
|
|
6272
|
-
|
|
6558
|
+
const resolvedDashboardUrl = started.dashboardUrl ?? dashboardUrl;
|
|
6559
|
+
openPlayDashboard({
|
|
6560
|
+
dashboardUrl: resolvedDashboardUrl,
|
|
6561
|
+
jsonOutput: options.jsonOutput,
|
|
6562
|
+
noOpen: options.noOpen
|
|
6563
|
+
});
|
|
6564
|
+
progress.phase(`loading play on ${resolvedDashboardUrl}`);
|
|
6273
6565
|
progress.complete();
|
|
6274
6566
|
writeStartedPlayRun({
|
|
6275
6567
|
runId: started.workflowId,
|
|
6276
6568
|
playName: started.name ?? playName,
|
|
6277
6569
|
status: started.status,
|
|
6278
|
-
|
|
6279
|
-
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
6570
|
+
dashboardUrl: resolvedDashboardUrl,
|
|
6280
6571
|
jsonOutput: options.jsonOutput,
|
|
6281
6572
|
progress
|
|
6282
6573
|
});
|
|
@@ -6319,7 +6610,7 @@ function parseRunIdPositional(args, usage) {
|
|
|
6319
6610
|
}
|
|
6320
6611
|
continue;
|
|
6321
6612
|
}
|
|
6322
|
-
if ((arg === "--out" || arg === "--
|
|
6613
|
+
if ((arg === "--out" || arg === "--reason") && args[index + 1]) {
|
|
6323
6614
|
index += 1;
|
|
6324
6615
|
continue;
|
|
6325
6616
|
}
|
|
@@ -6404,7 +6695,7 @@ async function handleRunsList(args) {
|
|
|
6404
6695
|
return 0;
|
|
6405
6696
|
}
|
|
6406
6697
|
async function handleRunTail(args) {
|
|
6407
|
-
const usage = "Usage: deepline runs tail <run-id> [--json] [--compact]
|
|
6698
|
+
const usage = "Usage: deepline runs tail <run-id> [--json] [--compact]";
|
|
6408
6699
|
let runId;
|
|
6409
6700
|
try {
|
|
6410
6701
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -6412,20 +6703,19 @@ async function handleRunTail(args) {
|
|
|
6412
6703
|
console.error(error instanceof Error ? error.message : usage);
|
|
6413
6704
|
return 1;
|
|
6414
6705
|
}
|
|
6415
|
-
const client = new DeeplineClient();
|
|
6416
|
-
let afterLogIndex;
|
|
6417
6706
|
for (let index = 0; index < args.length; index += 1) {
|
|
6418
6707
|
const arg = args[index];
|
|
6419
|
-
if (arg === "--cursor"
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6708
|
+
if (arg === "--cursor") {
|
|
6709
|
+
console.error("--cursor was removed. deepline runs tail reads the canonical run stream.");
|
|
6710
|
+
return 1;
|
|
6711
|
+
}
|
|
6712
|
+
if (arg.startsWith("--") && arg !== "--json" && arg !== "--compact") {
|
|
6713
|
+
console.error(`${arg} is not supported by deepline runs tail.`);
|
|
6714
|
+
return 1;
|
|
6424
6715
|
}
|
|
6425
6716
|
}
|
|
6426
|
-
const
|
|
6427
|
-
|
|
6428
|
-
});
|
|
6717
|
+
const client = new DeeplineClient();
|
|
6718
|
+
const status = await client.runs.tail(runId);
|
|
6429
6719
|
writePlayResult(status, argsWantJson(args));
|
|
6430
6720
|
return status.status === "failed" ? 1 : 0;
|
|
6431
6721
|
}
|
|
@@ -6966,8 +7256,9 @@ function registerPlayCommands(program) {
|
|
|
6966
7256
|
"after",
|
|
6967
7257
|
`
|
|
6968
7258
|
Concepts:
|
|
6969
|
-
Plays are durable
|
|
6970
|
-
|
|
7259
|
+
Plays are durable cloud workflows.
|
|
7260
|
+
Stable ctx.tools.execute({ id, tool, input }) calls are replay-safe.
|
|
7261
|
+
ctx.map adds row keys and row progress.
|
|
6971
7262
|
|
|
6972
7263
|
Common commands:
|
|
6973
7264
|
deepline plays search email --json
|
|
@@ -6998,17 +7289,42 @@ Examples:
|
|
|
6998
7289
|
"after",
|
|
6999
7290
|
`
|
|
7000
7291
|
Notes:
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7292
|
+
Local files are bundled, preflighted, then run in Deepline cloud.
|
|
7293
|
+
Named plays run the live saved revision.
|
|
7294
|
+
Unknown --foo and --foo.bar flags are treated as play input args.
|
|
7295
|
+
File args accept local paths; the CLI stages files before submit.
|
|
7296
|
+
--watch prints logs, previews, stats, and next commands.
|
|
7297
|
+
The play page opens in your browser as soon as the run starts; use --no-open
|
|
7298
|
+
to only print the URL.
|
|
7299
|
+
--force supersedes active runs; it does not bypass completed reuse.
|
|
7300
|
+
|
|
7301
|
+
Idempotent execution:
|
|
7302
|
+
Stable tool call ids are the reuse key:
|
|
7303
|
+
|
|
7304
|
+
await ctx.tools.execute({ id: 'company_lookup', tool, input });
|
|
7305
|
+
|
|
7306
|
+
For rows, use ctx.map plus a stable row key:
|
|
7307
|
+
|
|
7308
|
+
const rows = await ctx
|
|
7309
|
+
.map('companies_v1', companies)
|
|
7310
|
+
.step('cto', (row, ctx) => ctx.tools.execute({
|
|
7311
|
+
id: 'find_cto',
|
|
7312
|
+
tool: 'apollo_search_people_with_match',
|
|
7313
|
+
input: { q_organization_domains_list: [row.domain], per_page: 1 },
|
|
7314
|
+
}))
|
|
7315
|
+
.run({ key: 'domain' });
|
|
7316
|
+
|
|
7317
|
+
Reuse needs the same play, tool id, map name, row key, and compatible logic.
|
|
7318
|
+
To refresh, change the id/map key or set staleAfterSeconds:
|
|
7319
|
+
|
|
7320
|
+
.run({ key: 'domain', staleAfterSeconds: 86400 })
|
|
7007
7321
|
|
|
7008
7322
|
Examples:
|
|
7009
7323
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
|
|
7010
7324
|
deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
|
|
7011
7325
|
deepline plays run enrich.play.ts --csv leads.csv --watch --out leads-enriched.csv
|
|
7326
|
+
deepline plays run cto-search.play.ts --limit 5 --watch
|
|
7327
|
+
deepline runs get <run-id>
|
|
7012
7328
|
`
|
|
7013
7329
|
).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(
|
|
7014
7330
|
"--revision-id <id>",
|
|
@@ -7019,7 +7335,7 @@ Examples:
|
|
|
7019
7335
|
).option("--watch", "Stream logs until completion").option(
|
|
7020
7336
|
"--logs",
|
|
7021
7337
|
"When output is non-interactive, stream play logs to stderr while waiting"
|
|
7022
|
-
).option("--
|
|
7338
|
+
).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) => {
|
|
7023
7339
|
const passthroughArgs = [...command.args];
|
|
7024
7340
|
const explicitTarget = options.file || options.name;
|
|
7025
7341
|
const targetIsInputFlag = typeof target === "string" && target.startsWith("--");
|
|
@@ -7041,9 +7357,9 @@ Examples:
|
|
|
7041
7357
|
...options.out ? ["--out", options.out] : [],
|
|
7042
7358
|
...options.watch ? ["--watch"] : [],
|
|
7043
7359
|
...options.logs ? ["--logs"] : [],
|
|
7044
|
-
...options.pollIntervalMs ? ["--poll-interval-ms", options.pollIntervalMs] : [],
|
|
7045
7360
|
...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
|
|
7046
7361
|
...options.force ? ["--force"] : [],
|
|
7362
|
+
...options.open === false ? ["--no-open"] : [],
|
|
7047
7363
|
...options.json ? ["--json"] : [],
|
|
7048
7364
|
...passthroughArgs
|
|
7049
7365
|
]);
|
|
@@ -7150,12 +7466,11 @@ Examples:
|
|
|
7150
7466
|
...options.json ? ["--json"] : []
|
|
7151
7467
|
]);
|
|
7152
7468
|
});
|
|
7153
|
-
runs.command("tail <runId>").description("
|
|
7469
|
+
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) => {
|
|
7154
7470
|
process.exitCode = await handleRunTail([
|
|
7155
7471
|
runId,
|
|
7156
7472
|
...options.json ? ["--json"] : [],
|
|
7157
|
-
...options.compact ? ["--compact"] : []
|
|
7158
|
-
...options.cursor ? ["--cursor", options.cursor] : []
|
|
7473
|
+
...options.compact ? ["--compact"] : []
|
|
7159
7474
|
]);
|
|
7160
7475
|
});
|
|
7161
7476
|
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) => {
|