deepline 0.1.19 → 0.1.21
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 +391 -129
- package/dist/cli/index.mjs +391 -129
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +53 -7
- package/dist/index.mjs +53 -7
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +999 -257
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +604 -75
- package/dist/repo/apps/play-runner-workers/src/entry.ts +442 -357
- package/dist/repo/sdk/src/client.ts +46 -4
- package/dist/repo/sdk/src/http.ts +38 -4
- package/dist/repo/sdk/src/plays/harness-stub.ts +12 -0
- package/dist/repo/sdk/src/version.ts +1 -1
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +3 -6
- package/dist/repo/shared_libs/plays/row-identity.ts +59 -4
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -243,7 +243,7 @@ function saveProjectDeeplineEnvValues(baseUrl, values, startDir = projectEnvStar
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
// src/version.ts
|
|
246
|
-
var SDK_VERSION = "0.1.
|
|
246
|
+
var SDK_VERSION = "0.1.21";
|
|
247
247
|
var SDK_API_CONTRACT = "2026-05-runs-v2";
|
|
248
248
|
|
|
249
249
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
@@ -326,7 +326,7 @@ var HttpClient = class {
|
|
|
326
326
|
const response = await fetch(candidateUrl, {
|
|
327
327
|
method,
|
|
328
328
|
headers,
|
|
329
|
-
body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
329
|
+
body: options?.formData !== void 0 ? options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
330
330
|
signal: controller.signal
|
|
331
331
|
});
|
|
332
332
|
clearTimeout(timeoutId);
|
|
@@ -350,7 +350,8 @@ var HttpClient = class {
|
|
|
350
350
|
parsed = body;
|
|
351
351
|
}
|
|
352
352
|
if (!response.ok) {
|
|
353
|
-
const
|
|
353
|
+
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
354
|
+
const msg = typeof errorValue === "string" ? errorValue : errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string" ? errorValue.message : typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string" ? parsed.message : `HTTP ${response.status}`;
|
|
354
355
|
throw new DeeplineError(msg, response.status, "API_ERROR", {
|
|
355
356
|
response: parsed
|
|
356
357
|
});
|
|
@@ -444,6 +445,13 @@ var HttpClient = class {
|
|
|
444
445
|
headers
|
|
445
446
|
});
|
|
446
447
|
}
|
|
448
|
+
async postFormData(path, formData, headers) {
|
|
449
|
+
return this.request(path, {
|
|
450
|
+
method: "POST",
|
|
451
|
+
formData,
|
|
452
|
+
headers
|
|
453
|
+
});
|
|
454
|
+
}
|
|
447
455
|
/**
|
|
448
456
|
* Send a DELETE request.
|
|
449
457
|
*
|
|
@@ -552,6 +560,14 @@ function mapLegacyTemporalStatus(status) {
|
|
|
552
560
|
return "running";
|
|
553
561
|
}
|
|
554
562
|
}
|
|
563
|
+
function decodeBase64Bytes(value) {
|
|
564
|
+
const binary = atob(value);
|
|
565
|
+
const bytes = new Uint8Array(binary.length);
|
|
566
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
567
|
+
bytes[index] = binary.charCodeAt(index);
|
|
568
|
+
}
|
|
569
|
+
return bytes;
|
|
570
|
+
}
|
|
555
571
|
var DeeplineClient = class {
|
|
556
572
|
http;
|
|
557
573
|
config;
|
|
@@ -1003,9 +1019,34 @@ var DeeplineClient = class {
|
|
|
1003
1019
|
* ```
|
|
1004
1020
|
*/
|
|
1005
1021
|
async stagePlayFiles(files) {
|
|
1006
|
-
const
|
|
1022
|
+
const formData = new FormData();
|
|
1023
|
+
formData.set(
|
|
1024
|
+
"metadata",
|
|
1025
|
+
JSON.stringify({
|
|
1026
|
+
files: files.map((file, index) => ({
|
|
1027
|
+
index,
|
|
1028
|
+
logicalPath: file.logicalPath,
|
|
1029
|
+
contentHash: file.contentHash,
|
|
1030
|
+
contentType: file.contentType,
|
|
1031
|
+
bytes: file.bytes
|
|
1032
|
+
}))
|
|
1033
|
+
})
|
|
1034
|
+
);
|
|
1035
|
+
for (const [index, file] of files.entries()) {
|
|
1036
|
+
const bytes = decodeBase64Bytes(file.contentBase64);
|
|
1037
|
+
const body = bytes.buffer.slice(
|
|
1038
|
+
bytes.byteOffset,
|
|
1039
|
+
bytes.byteOffset + bytes.byteLength
|
|
1040
|
+
);
|
|
1041
|
+
formData.set(
|
|
1042
|
+
`file:${index}`,
|
|
1043
|
+
new Blob([body], { type: file.contentType }),
|
|
1044
|
+
file.logicalPath
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
const response = await this.http.postFormData(
|
|
1007
1048
|
"/api/v2/plays/files/stage",
|
|
1008
|
-
|
|
1049
|
+
formData
|
|
1009
1050
|
);
|
|
1010
1051
|
return response.files;
|
|
1011
1052
|
}
|
|
@@ -1034,9 +1075,14 @@ var DeeplineClient = class {
|
|
|
1034
1075
|
* console.log(`Logs: ${status.progress?.logs.length ?? 0} lines`);
|
|
1035
1076
|
* ```
|
|
1036
1077
|
*/
|
|
1037
|
-
async getPlayStatus(workflowId) {
|
|
1078
|
+
async getPlayStatus(workflowId, options) {
|
|
1079
|
+
const params = new URLSearchParams();
|
|
1080
|
+
if (options?.billing === false) {
|
|
1081
|
+
params.set("billing", "false");
|
|
1082
|
+
}
|
|
1083
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
1038
1084
|
const response = await this.http.get(
|
|
1039
|
-
`/api/v2/plays/run/${encodeURIComponent(workflowId)}`
|
|
1085
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}${query}`
|
|
1040
1086
|
);
|
|
1041
1087
|
return normalizePlayStatus(response);
|
|
1042
1088
|
}
|
|
@@ -4342,7 +4388,77 @@ function createCliProgress(enabled) {
|
|
|
4342
4388
|
return progress;
|
|
4343
4389
|
}
|
|
4344
4390
|
|
|
4391
|
+
// src/cli/trace.ts
|
|
4392
|
+
var cliTraceStartedAt = Date.now();
|
|
4393
|
+
function isTruthyEnv(value) {
|
|
4394
|
+
return value === "1" || value === "true" || value === "yes";
|
|
4395
|
+
}
|
|
4396
|
+
function isCliTraceEnabled() {
|
|
4397
|
+
return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
|
|
4398
|
+
}
|
|
4399
|
+
function recordCliTrace(event) {
|
|
4400
|
+
if (!isCliTraceEnabled()) {
|
|
4401
|
+
return;
|
|
4402
|
+
}
|
|
4403
|
+
const now = Date.now();
|
|
4404
|
+
const payload = {
|
|
4405
|
+
ts: now,
|
|
4406
|
+
source: "cli",
|
|
4407
|
+
sinceStartMs: now - cliTraceStartedAt,
|
|
4408
|
+
...event
|
|
4409
|
+
};
|
|
4410
|
+
process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
|
|
4411
|
+
`);
|
|
4412
|
+
}
|
|
4413
|
+
async function traceCliSpan(phase, fields, run) {
|
|
4414
|
+
if (!isCliTraceEnabled()) {
|
|
4415
|
+
return run();
|
|
4416
|
+
}
|
|
4417
|
+
const startedAt = Date.now();
|
|
4418
|
+
try {
|
|
4419
|
+
const result = await run();
|
|
4420
|
+
recordCliTrace({
|
|
4421
|
+
phase,
|
|
4422
|
+
ms: Date.now() - startedAt,
|
|
4423
|
+
ok: true,
|
|
4424
|
+
...fields
|
|
4425
|
+
});
|
|
4426
|
+
return result;
|
|
4427
|
+
} catch (error) {
|
|
4428
|
+
recordCliTrace({
|
|
4429
|
+
phase,
|
|
4430
|
+
ms: Date.now() - startedAt,
|
|
4431
|
+
ok: false,
|
|
4432
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4433
|
+
...fields
|
|
4434
|
+
});
|
|
4435
|
+
throw error;
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4438
|
+
|
|
4345
4439
|
// src/cli/commands/play.ts
|
|
4440
|
+
function traceCliSync(phase, fields, run) {
|
|
4441
|
+
const startedAt = Date.now();
|
|
4442
|
+
try {
|
|
4443
|
+
const result = run();
|
|
4444
|
+
recordCliTrace({
|
|
4445
|
+
phase,
|
|
4446
|
+
ms: Date.now() - startedAt,
|
|
4447
|
+
ok: true,
|
|
4448
|
+
...fields
|
|
4449
|
+
});
|
|
4450
|
+
return result;
|
|
4451
|
+
} catch (error) {
|
|
4452
|
+
recordCliTrace({
|
|
4453
|
+
phase,
|
|
4454
|
+
ms: Date.now() - startedAt,
|
|
4455
|
+
ok: false,
|
|
4456
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4457
|
+
...fields
|
|
4458
|
+
});
|
|
4459
|
+
throw error;
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4346
4462
|
function parseReferencedPlayTarget(target) {
|
|
4347
4463
|
const trimmed = target.trim();
|
|
4348
4464
|
const slashIndex = trimmed.indexOf("/");
|
|
@@ -4581,6 +4697,23 @@ function isLocalFilePathValue(value) {
|
|
|
4581
4697
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
4582
4698
|
return existsSync4(resolve7(value));
|
|
4583
4699
|
}
|
|
4700
|
+
function inputContainsLocalFilePath(value) {
|
|
4701
|
+
if (isLocalFilePathValue(value)) {
|
|
4702
|
+
return true;
|
|
4703
|
+
}
|
|
4704
|
+
if (Array.isArray(value)) {
|
|
4705
|
+
return value.some((entry) => inputContainsLocalFilePath(entry));
|
|
4706
|
+
}
|
|
4707
|
+
if (value && typeof value === "object") {
|
|
4708
|
+
return Object.values(value).some(
|
|
4709
|
+
(entry) => inputContainsLocalFilePath(entry)
|
|
4710
|
+
);
|
|
4711
|
+
}
|
|
4712
|
+
return false;
|
|
4713
|
+
}
|
|
4714
|
+
function namedRunNeedsPlayDefinition(input) {
|
|
4715
|
+
return input.revisionSelector === "latest" || getDottedInputValue(input.runtimeInput, "csv") != null || inputContainsLocalFilePath(input.runtimeInput);
|
|
4716
|
+
}
|
|
4584
4717
|
async function stageFileInputArgs(input) {
|
|
4585
4718
|
const uniqueBindings = [
|
|
4586
4719
|
...new Map(input.bindings.map((binding) => [binding.inputPath, binding])).values()
|
|
@@ -4893,6 +5026,8 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4893
5026
|
let timedOut = false;
|
|
4894
5027
|
let emittedDashboardUrl = false;
|
|
4895
5028
|
let lastKnownWorkflowId = "";
|
|
5029
|
+
let eventCount = 0;
|
|
5030
|
+
let firstRunIdMs = null;
|
|
4896
5031
|
let lastPhase = null;
|
|
4897
5032
|
const timeout = input.waitTimeoutMs === null ? null : setTimeout(
|
|
4898
5033
|
() => {
|
|
@@ -4905,9 +5040,11 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4905
5040
|
for await (const event of input.client.startPlayRunStream(input.request, {
|
|
4906
5041
|
signal: controller.signal
|
|
4907
5042
|
})) {
|
|
5043
|
+
eventCount += 1;
|
|
4908
5044
|
const eventRunId = getEventPayload(event).runId;
|
|
4909
5045
|
if (typeof eventRunId === "string" && eventRunId && eventRunId !== "pending") {
|
|
4910
5046
|
lastKnownWorkflowId = eventRunId;
|
|
5047
|
+
firstRunIdMs ??= Date.now() - startedAt;
|
|
4911
5048
|
}
|
|
4912
5049
|
const workflowId = lastKnownWorkflowId || "pending";
|
|
4913
5050
|
if (workflowId !== "pending" && !emittedDashboardUrl) {
|
|
@@ -4945,6 +5082,16 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4945
5082
|
});
|
|
4946
5083
|
const finalStatus = getFinalStatusFromLiveEvent(event);
|
|
4947
5084
|
if (finalStatus) {
|
|
5085
|
+
recordCliTrace({
|
|
5086
|
+
phase: "cli.play_start_stream_terminal",
|
|
5087
|
+
ms: Date.now() - startedAt,
|
|
5088
|
+
ok: true,
|
|
5089
|
+
playName: input.playName,
|
|
5090
|
+
workflowId: finalStatus.runId || lastKnownWorkflowId || null,
|
|
5091
|
+
eventCount,
|
|
5092
|
+
firstRunIdMs,
|
|
5093
|
+
lastPhase
|
|
5094
|
+
});
|
|
4948
5095
|
return finalStatus;
|
|
4949
5096
|
}
|
|
4950
5097
|
}
|
|
@@ -4968,6 +5115,17 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4968
5115
|
`
|
|
4969
5116
|
);
|
|
4970
5117
|
}
|
|
5118
|
+
recordCliTrace({
|
|
5119
|
+
phase: "cli.play_start_stream_fallback",
|
|
5120
|
+
ms: Date.now() - startedAt,
|
|
5121
|
+
ok: false,
|
|
5122
|
+
playName: input.playName,
|
|
5123
|
+
workflowId: lastKnownWorkflowId,
|
|
5124
|
+
eventCount,
|
|
5125
|
+
firstRunIdMs,
|
|
5126
|
+
lastPhase,
|
|
5127
|
+
reason
|
|
5128
|
+
});
|
|
4971
5129
|
return waitForPlayCompletionByPolling({
|
|
4972
5130
|
client: input.client,
|
|
4973
5131
|
workflowId: lastKnownWorkflowId,
|
|
@@ -4992,6 +5150,17 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4992
5150
|
`[play watch] start stream ended after run ${lastKnownWorkflowId}; falling back to polling`
|
|
4993
5151
|
);
|
|
4994
5152
|
}
|
|
5153
|
+
recordCliTrace({
|
|
5154
|
+
phase: "cli.play_start_stream_fallback",
|
|
5155
|
+
ms: Date.now() - startedAt,
|
|
5156
|
+
ok: false,
|
|
5157
|
+
playName: input.playName,
|
|
5158
|
+
workflowId: lastKnownWorkflowId,
|
|
5159
|
+
eventCount,
|
|
5160
|
+
firstRunIdMs,
|
|
5161
|
+
lastPhase,
|
|
5162
|
+
reason: "stream ended before terminal event"
|
|
5163
|
+
});
|
|
4995
5164
|
return waitForPlayCompletionByPolling({
|
|
4996
5165
|
client: input.client,
|
|
4997
5166
|
workflowId: lastKnownWorkflowId,
|
|
@@ -5066,10 +5235,11 @@ async function waitForPlayCompletionByPolling(input) {
|
|
|
5066
5235
|
progress: input.progress
|
|
5067
5236
|
});
|
|
5068
5237
|
if (TERMINAL_PLAY_STATUSES2.has(status.status)) {
|
|
5069
|
-
return status.result !== void 0 ? status : await input.client.getPlayStatus(input.workflowId);
|
|
5238
|
+
return status.result !== void 0 ? status : await input.client.getPlayStatus(input.workflowId, { billing: false });
|
|
5070
5239
|
}
|
|
5071
5240
|
const authoritativeStatus = await input.client.getPlayStatus(
|
|
5072
|
-
input.workflowId
|
|
5241
|
+
input.workflowId,
|
|
5242
|
+
{ billing: false }
|
|
5073
5243
|
);
|
|
5074
5244
|
if (TERMINAL_PLAY_STATUSES2.has(authoritativeStatus.status)) {
|
|
5075
5245
|
return authoritativeStatus;
|
|
@@ -5187,14 +5357,20 @@ function formatReturnValue(result) {
|
|
|
5187
5357
|
}
|
|
5188
5358
|
return lines;
|
|
5189
5359
|
}
|
|
5190
|
-
function buildOutputSummary(rowsInfo, exportedPath) {
|
|
5360
|
+
function buildOutputSummary(rowsInfo, runId, exportedPath) {
|
|
5191
5361
|
if (!rowsInfo) {
|
|
5192
5362
|
return exportedPath ? { csv_path: exportedPath } : null;
|
|
5193
5363
|
}
|
|
5364
|
+
const isPartial = !rowsInfo.complete;
|
|
5194
5365
|
return {
|
|
5195
5366
|
kind: "rows",
|
|
5196
5367
|
rowCount: rowsInfo.totalRows,
|
|
5197
5368
|
previewRowCount: rowsInfo.rows.length,
|
|
5369
|
+
...isPartial ? {
|
|
5370
|
+
isPartial: true,
|
|
5371
|
+
previewCount: rowsInfo.rows.length,
|
|
5372
|
+
totalCount: rowsInfo.totalRows
|
|
5373
|
+
} : { isPartial: false },
|
|
5198
5374
|
complete: rowsInfo.complete,
|
|
5199
5375
|
columns: rowsInfo.columns,
|
|
5200
5376
|
source: rowsInfo.source,
|
|
@@ -5205,16 +5381,27 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
5205
5381
|
if (status.status === "completed" && rowsInfo?.totalRows === 0) {
|
|
5206
5382
|
return ["Run completed with 0 output rows."];
|
|
5207
5383
|
}
|
|
5384
|
+
if (rowsInfo && !rowsInfo.complete) {
|
|
5385
|
+
return [
|
|
5386
|
+
`Run output is partial: showing ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}.`
|
|
5387
|
+
];
|
|
5388
|
+
}
|
|
5208
5389
|
return [];
|
|
5209
5390
|
}
|
|
5210
|
-
function buildRunNextCommands(runId) {
|
|
5211
|
-
|
|
5391
|
+
function buildRunNextCommands(runId, rowsInfo) {
|
|
5392
|
+
const commands = {
|
|
5212
5393
|
get: `deepline runs get ${runId} --json`,
|
|
5213
5394
|
tail: `deepline runs tail ${runId} --json`,
|
|
5214
5395
|
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
5215
|
-
logs: `deepline runs logs ${runId} --out run.log --json
|
|
5216
|
-
exportCsv: `deepline runs export ${runId} --out output.csv`
|
|
5396
|
+
logs: `deepline runs logs ${runId} --out run.log --json`
|
|
5217
5397
|
};
|
|
5398
|
+
if (!rowsInfo || rowsInfo.complete) {
|
|
5399
|
+
commands.exportCsv = buildRunExportCommand(runId);
|
|
5400
|
+
}
|
|
5401
|
+
return commands;
|
|
5402
|
+
}
|
|
5403
|
+
function buildRunExportCommand(runId) {
|
|
5404
|
+
return `deepline runs export ${runId} --out output.csv`;
|
|
5218
5405
|
}
|
|
5219
5406
|
var RUN_LOG_PREVIEW_LIMIT = 20;
|
|
5220
5407
|
function getRecordField(value, key) {
|
|
@@ -5262,10 +5449,11 @@ function normalizeProgressForEnvelope(status, rowsInfo) {
|
|
|
5262
5449
|
wait: status.wait ?? null
|
|
5263
5450
|
};
|
|
5264
5451
|
}
|
|
5265
|
-
function normalizeOutputsForEnvelope(rowsInfo, exportedPath) {
|
|
5452
|
+
function normalizeOutputsForEnvelope(rowsInfo, runId, exportedPath) {
|
|
5266
5453
|
if (!rowsInfo) {
|
|
5267
5454
|
return exportedPath ? [{ name: "output", kind: "file", path: exportedPath }] : [];
|
|
5268
5455
|
}
|
|
5456
|
+
const isPartial = !rowsInfo.complete;
|
|
5269
5457
|
return [
|
|
5270
5458
|
{
|
|
5271
5459
|
name: "rows",
|
|
@@ -5275,6 +5463,11 @@ function normalizeOutputsForEnvelope(rowsInfo, exportedPath) {
|
|
|
5275
5463
|
preview: rowsInfo.rows.slice(0, 5),
|
|
5276
5464
|
previewRowCount: Math.min(rowsInfo.rows.length, 5),
|
|
5277
5465
|
previewLimit: 5,
|
|
5466
|
+
...isPartial ? {
|
|
5467
|
+
isPartial: true,
|
|
5468
|
+
previewCount: rowsInfo.rows.length,
|
|
5469
|
+
totalCount: rowsInfo.totalRows
|
|
5470
|
+
} : { isPartial: false },
|
|
5278
5471
|
complete: rowsInfo.complete,
|
|
5279
5472
|
source: rowsInfo.source,
|
|
5280
5473
|
...exportedPath ? { csv_path: exportedPath } : {}
|
|
@@ -5366,19 +5559,23 @@ function compactPlayStatus(status, options) {
|
|
|
5366
5559
|
status: status.status,
|
|
5367
5560
|
run: normalizeRunStatusForEnvelope(status),
|
|
5368
5561
|
progress: normalizeProgressForEnvelope(status, rowsInfo),
|
|
5369
|
-
outputs: normalizeOutputsForEnvelope(
|
|
5562
|
+
outputs: normalizeOutputsForEnvelope(
|
|
5563
|
+
rowsInfo,
|
|
5564
|
+
status.runId,
|
|
5565
|
+
options?.exportedPath
|
|
5566
|
+
),
|
|
5370
5567
|
steps: normalizeStepsForEnvelope(status),
|
|
5371
5568
|
errors: normalizeErrorsForEnvelope(status, error),
|
|
5372
5569
|
logs: normalizeLogsForEnvelope(status),
|
|
5373
5570
|
...error ? { error } : {},
|
|
5374
5571
|
...warnings.length > 0 ? { warnings } : {},
|
|
5375
|
-
output: buildOutputSummary(rowsInfo, options?.exportedPath) ?? result ?? null,
|
|
5572
|
+
output: buildOutputSummary(rowsInfo, status.runId, options?.exportedPath) ?? result ?? null,
|
|
5376
5573
|
...result !== void 0 ? { result } : {},
|
|
5377
5574
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
5378
5575
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
5379
5576
|
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
|
|
5380
5577
|
...billing ? { billing } : {},
|
|
5381
|
-
next: buildRunNextCommands(status.runId)
|
|
5578
|
+
next: buildRunNextCommands(status.runId, rowsInfo)
|
|
5382
5579
|
};
|
|
5383
5580
|
}
|
|
5384
5581
|
function enrichPlayStatusWithDatasetStats(status) {
|
|
@@ -5438,13 +5635,22 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
5438
5635
|
rowsInfo.columns,
|
|
5439
5636
|
extractDatasetExecutionStats(status)
|
|
5440
5637
|
) : null;
|
|
5441
|
-
const outputSummary = buildOutputSummary(
|
|
5638
|
+
const outputSummary = buildOutputSummary(
|
|
5639
|
+
rowsInfo,
|
|
5640
|
+
runId,
|
|
5641
|
+
options?.exportedPath
|
|
5642
|
+
);
|
|
5442
5643
|
if (outputSummary) {
|
|
5443
5644
|
const columns = Array.isArray(outputSummary.columns) ? outputSummary.columns.length : 0;
|
|
5444
5645
|
const path = typeof outputSummary.csv_path === "string" ? ` file=${outputSummary.csv_path}` : "";
|
|
5445
5646
|
lines.push(
|
|
5446
5647
|
` output: rows=${formatInteger(outputSummary.rowCount)} columns=${formatInteger(columns)}${path}`
|
|
5447
5648
|
);
|
|
5649
|
+
if (outputSummary.isPartial === true) {
|
|
5650
|
+
lines.push(
|
|
5651
|
+
` partial output: showing ${formatInteger(outputSummary.previewCount)} preview row(s) of ${formatInteger(outputSummary.totalCount)}`
|
|
5652
|
+
);
|
|
5653
|
+
}
|
|
5448
5654
|
}
|
|
5449
5655
|
for (const warning of warnings) {
|
|
5450
5656
|
lines.push(` warning: ${warning}`);
|
|
@@ -5474,6 +5680,11 @@ function exportPlayStatusRows(status, outPath) {
|
|
|
5474
5680
|
`Run ${status.runId} did not expose a row-shaped final output to export.`
|
|
5475
5681
|
);
|
|
5476
5682
|
}
|
|
5683
|
+
if (!rowsInfo.complete) {
|
|
5684
|
+
throw new DeeplineError(
|
|
5685
|
+
`Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; full dataset export is not available from this status response yet.`
|
|
5686
|
+
);
|
|
5687
|
+
}
|
|
5477
5688
|
return writeCanonicalRowsCsv(rowsInfo, outPath);
|
|
5478
5689
|
}
|
|
5479
5690
|
function renderServerResultView(value) {
|
|
@@ -5783,11 +5994,24 @@ async function handleFileBackedRun(options) {
|
|
|
5783
5994
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
5784
5995
|
const absolutePlayPath = resolve7(options.target.path);
|
|
5785
5996
|
progress.phase("compiling play");
|
|
5786
|
-
const sourceCode =
|
|
5997
|
+
const sourceCode = traceCliSync(
|
|
5998
|
+
"cli.play_file_read_source",
|
|
5999
|
+
{ targetKind: "file" },
|
|
6000
|
+
() => readFileSync3(absolutePlayPath, "utf-8")
|
|
6001
|
+
);
|
|
6002
|
+
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5787
6003
|
let graph;
|
|
5788
6004
|
try {
|
|
5789
|
-
graph = await
|
|
5790
|
-
|
|
6005
|
+
graph = await traceCliSpan(
|
|
6006
|
+
"cli.play_file_bundle_graph",
|
|
6007
|
+
{ targetKind: "file" },
|
|
6008
|
+
() => collectBundledPlayGraph(absolutePlayPath)
|
|
6009
|
+
);
|
|
6010
|
+
await traceCliSpan(
|
|
6011
|
+
"cli.play_file_compile_manifests",
|
|
6012
|
+
{ targetKind: "file" },
|
|
6013
|
+
() => compileBundledPlayGraphManifests(client, graph)
|
|
6014
|
+
);
|
|
5791
6015
|
progress.phase("compiled play");
|
|
5792
6016
|
} catch (error) {
|
|
5793
6017
|
progress.fail();
|
|
@@ -5798,36 +6022,47 @@ async function handleFileBackedRun(options) {
|
|
|
5798
6022
|
const playName = bundleResult.playName ?? extractPlayName(sourceCode, absolutePlayPath);
|
|
5799
6023
|
try {
|
|
5800
6024
|
progress.phase("publishing imported plays");
|
|
5801
|
-
await
|
|
6025
|
+
await traceCliSpan(
|
|
6026
|
+
"cli.play_file_publish_imports",
|
|
6027
|
+
{ targetKind: "file" },
|
|
6028
|
+
() => publishImportedPlayDependencies(client, graph)
|
|
6029
|
+
);
|
|
5802
6030
|
} catch (error) {
|
|
5803
6031
|
progress.fail();
|
|
5804
6032
|
console.error(error instanceof Error ? error.message : String(error));
|
|
5805
6033
|
return 1;
|
|
5806
6034
|
}
|
|
5807
|
-
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5808
6035
|
const packagedFileUploads = bundleResult.packagedFiles.map(
|
|
5809
6036
|
(file) => stageFile(file.logicalPath, file.absolutePath)
|
|
5810
6037
|
);
|
|
6038
|
+
const compilerManifest = requireCompilerManifest(bundleResult);
|
|
5811
6039
|
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
5812
|
-
|
|
6040
|
+
compilerManifest.staticPipeline
|
|
5813
6041
|
);
|
|
5814
6042
|
applyCsvShortcutInput({
|
|
5815
6043
|
runtimeInput,
|
|
5816
6044
|
bindings: fileInputBindings,
|
|
5817
6045
|
fallbackInputPath: "file"
|
|
5818
6046
|
});
|
|
5819
|
-
const stagedFileInputs = await
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
6047
|
+
const stagedFileInputs = await traceCliSpan(
|
|
6048
|
+
"cli.play_stage_inputs",
|
|
6049
|
+
{
|
|
6050
|
+
targetKind: "file",
|
|
6051
|
+
bindingCount: fileInputBindings.length
|
|
6052
|
+
},
|
|
6053
|
+
() => stageFileInputArgs({
|
|
6054
|
+
client,
|
|
6055
|
+
runtimeInput,
|
|
6056
|
+
bindings: fileInputBindings,
|
|
6057
|
+
progress
|
|
6058
|
+
})
|
|
6059
|
+
);
|
|
5825
6060
|
const startRequest = {
|
|
5826
6061
|
name: playName,
|
|
5827
6062
|
sourceCode: bundleResult.sourceCode,
|
|
5828
6063
|
sourceFiles: bundleResult.sourceFiles,
|
|
5829
6064
|
runtimeArtifact: bundleResult.artifact,
|
|
5830
|
-
compilerManifest
|
|
6065
|
+
compilerManifest,
|
|
5831
6066
|
packagedFileUploads,
|
|
5832
6067
|
...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
|
|
5833
6068
|
...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
|
|
@@ -5836,26 +6071,42 @@ async function handleFileBackedRun(options) {
|
|
|
5836
6071
|
};
|
|
5837
6072
|
if (options.watch) {
|
|
5838
6073
|
progress.phase("starting run");
|
|
5839
|
-
const finalStatus = await
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
6074
|
+
const finalStatus = await traceCliSpan(
|
|
6075
|
+
"cli.play_start_watch",
|
|
6076
|
+
{ targetKind: "file", playName },
|
|
6077
|
+
() => startAndWaitForPlayCompletionByStream({
|
|
6078
|
+
client,
|
|
6079
|
+
request: startRequest,
|
|
6080
|
+
playName,
|
|
6081
|
+
jsonOutput: options.jsonOutput,
|
|
6082
|
+
emitLogs: options.emitLogs,
|
|
6083
|
+
waitTimeoutMs: options.waitTimeoutMs,
|
|
6084
|
+
progress
|
|
6085
|
+
})
|
|
6086
|
+
);
|
|
6087
|
+
const exportedPath = traceCliSync(
|
|
6088
|
+
"cli.play_export_rows",
|
|
6089
|
+
{ targetKind: "file", playName },
|
|
6090
|
+
() => exportPlayStatusRows(finalStatus, options.outPath)
|
|
6091
|
+
);
|
|
5849
6092
|
if (finalStatus.status === "completed") {
|
|
5850
6093
|
progress.complete();
|
|
5851
6094
|
} else {
|
|
5852
6095
|
progress.fail();
|
|
5853
6096
|
}
|
|
5854
|
-
|
|
6097
|
+
traceCliSync(
|
|
6098
|
+
"cli.play_write_result",
|
|
6099
|
+
{ targetKind: "file", playName },
|
|
6100
|
+
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath })
|
|
6101
|
+
);
|
|
5855
6102
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
5856
6103
|
}
|
|
5857
6104
|
progress.phase("starting run");
|
|
5858
|
-
const started = await
|
|
6105
|
+
const started = await traceCliSpan(
|
|
6106
|
+
"cli.play_start_unwatched",
|
|
6107
|
+
{ targetKind: "file", playName },
|
|
6108
|
+
() => client.startPlayRun(startRequest)
|
|
6109
|
+
);
|
|
5859
6110
|
const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
5860
6111
|
progress.phase(`loading play on ${dashboardUrl}`);
|
|
5861
6112
|
progress.complete();
|
|
@@ -5890,32 +6141,67 @@ async function handleNamedRun(options) {
|
|
|
5890
6141
|
}
|
|
5891
6142
|
const client = new DeeplineClient();
|
|
5892
6143
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
5893
|
-
|
|
5894
|
-
const playDetail = await assertCanonicalNamedPlayReference(client, options.target.name);
|
|
5895
|
-
progress.phase("selecting revision");
|
|
5896
|
-
const selectedRevisionId = await resolveNamedRunRevisionId({
|
|
5897
|
-
client,
|
|
5898
|
-
playName: options.target.name,
|
|
5899
|
-
revisionId: options.revisionId,
|
|
5900
|
-
selector: options.revisionSelector
|
|
5901
|
-
});
|
|
6144
|
+
const playName = options.target.name;
|
|
5902
6145
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5903
|
-
const
|
|
6146
|
+
const needsPlayDefinition = namedRunNeedsPlayDefinition({
|
|
6147
|
+
runtimeInput,
|
|
6148
|
+
revisionSelector: options.revisionSelector
|
|
6149
|
+
});
|
|
6150
|
+
const playDetail = needsPlayDefinition ? await (async () => {
|
|
6151
|
+
progress.phase("loading play definition");
|
|
6152
|
+
return traceCliSpan(
|
|
6153
|
+
"cli.play_load_definition",
|
|
6154
|
+
{ targetKind: "name", playName, skipped: false },
|
|
6155
|
+
() => assertCanonicalNamedPlayReference(client, playName)
|
|
6156
|
+
);
|
|
6157
|
+
})() : (recordCliTrace({
|
|
6158
|
+
phase: "cli.play_load_definition",
|
|
6159
|
+
ms: 0,
|
|
6160
|
+
ok: true,
|
|
6161
|
+
targetKind: "name",
|
|
6162
|
+
playName,
|
|
6163
|
+
skipped: true
|
|
6164
|
+
}), null);
|
|
6165
|
+
progress.phase("selecting revision");
|
|
6166
|
+
const selectedRevisionId = await traceCliSpan(
|
|
6167
|
+
"cli.play_select_revision",
|
|
6168
|
+
{
|
|
6169
|
+
targetKind: "name",
|
|
6170
|
+
playName,
|
|
6171
|
+
selector: options.revisionSelector,
|
|
6172
|
+
hasExplicitRevisionId: Boolean(options.revisionId)
|
|
6173
|
+
},
|
|
6174
|
+
() => resolveNamedRunRevisionId({
|
|
6175
|
+
client,
|
|
6176
|
+
playName,
|
|
6177
|
+
revisionId: options.revisionId,
|
|
6178
|
+
selector: options.revisionSelector
|
|
6179
|
+
})
|
|
6180
|
+
);
|
|
6181
|
+
const fileInputBindings = playDetail ? [
|
|
5904
6182
|
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
5905
6183
|
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
5906
|
-
];
|
|
6184
|
+
] : [];
|
|
5907
6185
|
applyCsvShortcutInput({
|
|
5908
6186
|
runtimeInput,
|
|
5909
6187
|
bindings: fileInputBindings
|
|
5910
6188
|
});
|
|
5911
|
-
const stagedFileInputs = await
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
6189
|
+
const stagedFileInputs = await traceCliSpan(
|
|
6190
|
+
"cli.play_stage_inputs",
|
|
6191
|
+
{
|
|
6192
|
+
targetKind: "name",
|
|
6193
|
+
playName,
|
|
6194
|
+
bindingCount: fileInputBindings.length
|
|
6195
|
+
},
|
|
6196
|
+
() => stageFileInputArgs({
|
|
6197
|
+
client,
|
|
6198
|
+
runtimeInput,
|
|
6199
|
+
bindings: fileInputBindings,
|
|
6200
|
+
progress
|
|
6201
|
+
})
|
|
6202
|
+
);
|
|
5917
6203
|
const startRequest = {
|
|
5918
|
-
name:
|
|
6204
|
+
name: playName,
|
|
5919
6205
|
...selectedRevisionId ? { revisionId: selectedRevisionId } : {},
|
|
5920
6206
|
...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
|
|
5921
6207
|
...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
|
|
@@ -5924,35 +6210,51 @@ async function handleNamedRun(options) {
|
|
|
5924
6210
|
};
|
|
5925
6211
|
if (options.watch) {
|
|
5926
6212
|
progress.phase("starting run");
|
|
5927
|
-
const finalStatus = await
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
6213
|
+
const finalStatus = await traceCliSpan(
|
|
6214
|
+
"cli.play_start_watch",
|
|
6215
|
+
{ targetKind: "name", playName },
|
|
6216
|
+
() => startAndWaitForPlayCompletionByStream({
|
|
6217
|
+
client,
|
|
6218
|
+
request: startRequest,
|
|
6219
|
+
playName,
|
|
6220
|
+
jsonOutput: options.jsonOutput,
|
|
6221
|
+
emitLogs: options.emitLogs,
|
|
6222
|
+
waitTimeoutMs: options.waitTimeoutMs,
|
|
6223
|
+
progress
|
|
6224
|
+
})
|
|
6225
|
+
);
|
|
6226
|
+
const exportedPath = traceCliSync(
|
|
6227
|
+
"cli.play_export_rows",
|
|
6228
|
+
{ targetKind: "name", playName },
|
|
6229
|
+
() => exportPlayStatusRows(finalStatus, options.outPath)
|
|
6230
|
+
);
|
|
5937
6231
|
if (finalStatus.status === "completed") {
|
|
5938
6232
|
progress.complete();
|
|
5939
6233
|
} else {
|
|
5940
6234
|
progress.fail();
|
|
5941
6235
|
}
|
|
5942
|
-
|
|
6236
|
+
traceCliSync(
|
|
6237
|
+
"cli.play_write_result",
|
|
6238
|
+
{ targetKind: "name", playName },
|
|
6239
|
+
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath })
|
|
6240
|
+
);
|
|
5943
6241
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
5944
6242
|
}
|
|
5945
6243
|
progress.phase("starting run");
|
|
5946
|
-
const started = await
|
|
6244
|
+
const started = await traceCliSpan(
|
|
6245
|
+
"cli.play_start_unwatched",
|
|
6246
|
+
{ targetKind: "name", playName },
|
|
6247
|
+
() => client.startPlayRun(startRequest)
|
|
6248
|
+
);
|
|
5947
6249
|
const dashboardUrl = buildPlayDashboardUrl(
|
|
5948
6250
|
client.baseUrl,
|
|
5949
|
-
|
|
6251
|
+
playName
|
|
5950
6252
|
);
|
|
5951
6253
|
progress.phase(`loading play on ${dashboardUrl}`);
|
|
5952
6254
|
progress.complete();
|
|
5953
6255
|
writeStartedPlayRun({
|
|
5954
6256
|
runId: started.workflowId,
|
|
5955
|
-
playName: started.name ??
|
|
6257
|
+
playName: started.name ?? playName,
|
|
5956
6258
|
status: started.status,
|
|
5957
6259
|
statusUrl: started.statusUrl,
|
|
5958
6260
|
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
@@ -7815,54 +8117,6 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
|
7815
8117
|
writeSdkSkillsStatusLine("SDK skills are up to date.");
|
|
7816
8118
|
}
|
|
7817
8119
|
|
|
7818
|
-
// src/cli/trace.ts
|
|
7819
|
-
var cliTraceStartedAt = Date.now();
|
|
7820
|
-
function isTruthyEnv(value) {
|
|
7821
|
-
return value === "1" || value === "true" || value === "yes";
|
|
7822
|
-
}
|
|
7823
|
-
function isCliTraceEnabled() {
|
|
7824
|
-
return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
|
|
7825
|
-
}
|
|
7826
|
-
function recordCliTrace(event) {
|
|
7827
|
-
if (!isCliTraceEnabled()) {
|
|
7828
|
-
return;
|
|
7829
|
-
}
|
|
7830
|
-
const now = Date.now();
|
|
7831
|
-
const payload = {
|
|
7832
|
-
ts: now,
|
|
7833
|
-
source: "cli",
|
|
7834
|
-
sinceStartMs: now - cliTraceStartedAt,
|
|
7835
|
-
...event
|
|
7836
|
-
};
|
|
7837
|
-
process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
|
|
7838
|
-
`);
|
|
7839
|
-
}
|
|
7840
|
-
async function traceCliSpan(phase, fields, run) {
|
|
7841
|
-
if (!isCliTraceEnabled()) {
|
|
7842
|
-
return run();
|
|
7843
|
-
}
|
|
7844
|
-
const startedAt = Date.now();
|
|
7845
|
-
try {
|
|
7846
|
-
const result = await run();
|
|
7847
|
-
recordCliTrace({
|
|
7848
|
-
phase,
|
|
7849
|
-
ms: Date.now() - startedAt,
|
|
7850
|
-
ok: true,
|
|
7851
|
-
...fields
|
|
7852
|
-
});
|
|
7853
|
-
return result;
|
|
7854
|
-
} catch (error) {
|
|
7855
|
-
recordCliTrace({
|
|
7856
|
-
phase,
|
|
7857
|
-
ms: Date.now() - startedAt,
|
|
7858
|
-
ok: false,
|
|
7859
|
-
error: error instanceof Error ? error.message : String(error),
|
|
7860
|
-
...fields
|
|
7861
|
-
});
|
|
7862
|
-
throw error;
|
|
7863
|
-
}
|
|
7864
|
-
}
|
|
7865
|
-
|
|
7866
8120
|
// src/cli/index.ts
|
|
7867
8121
|
function shouldPrintStartupPhase() {
|
|
7868
8122
|
if (process.argv.includes("--json")) {
|
|
@@ -7873,6 +8127,12 @@ function shouldPrintStartupPhase() {
|
|
|
7873
8127
|
const subcommand = args[1];
|
|
7874
8128
|
return (command === "play" || command === "plays") && subcommand === "run";
|
|
7875
8129
|
}
|
|
8130
|
+
function shouldDeferSkillsSyncForCommand() {
|
|
8131
|
+
const args = process.argv.slice(2);
|
|
8132
|
+
const command = args[0];
|
|
8133
|
+
const subcommand = args[1];
|
|
8134
|
+
return (command === "play" || command === "plays") && subcommand === "run" && args.includes("--json");
|
|
8135
|
+
}
|
|
7876
8136
|
async function main() {
|
|
7877
8137
|
const mainStartedAt = Date.now();
|
|
7878
8138
|
recordCliTrace({
|
|
@@ -7917,11 +8177,13 @@ Output:
|
|
|
7917
8177
|
if (printStartupPhase) {
|
|
7918
8178
|
progress?.phase("checking sdk skills");
|
|
7919
8179
|
}
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
8180
|
+
if (!shouldDeferSkillsSyncForCommand()) {
|
|
8181
|
+
await traceCliSpan(
|
|
8182
|
+
"cli.sdk_skills_sync",
|
|
8183
|
+
{ baseUrl },
|
|
8184
|
+
() => syncSdkSkillsIfNeeded(baseUrl)
|
|
8185
|
+
);
|
|
8186
|
+
}
|
|
7925
8187
|
});
|
|
7926
8188
|
registerAuthCommands(program);
|
|
7927
8189
|
registerToolsCommands(program);
|