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.
@@ -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.19";
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 msg = typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : `HTTP ${response.status}`;
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 response = await this.http.post(
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
- { files }
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
- return {
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(rowsInfo, options?.exportedPath),
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(rowsInfo, options?.exportedPath);
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 = readFileSync3(absolutePlayPath, "utf-8");
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 collectBundledPlayGraph(absolutePlayPath);
5790
- await compileBundledPlayGraphManifests(client, graph);
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 publishImportedPlayDependencies(client, graph);
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
- requireCompilerManifest(bundleResult).staticPipeline
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 stageFileInputArgs({
5820
- client,
5821
- runtimeInput,
5822
- bindings: fileInputBindings,
5823
- progress
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: requireCompilerManifest(bundleResult),
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 startAndWaitForPlayCompletionByStream({
5840
- client,
5841
- request: startRequest,
5842
- playName,
5843
- jsonOutput: options.jsonOutput,
5844
- emitLogs: options.emitLogs,
5845
- waitTimeoutMs: options.waitTimeoutMs,
5846
- progress
5847
- });
5848
- const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
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
- writePlayResult(finalStatus, options.jsonOutput, { exportedPath });
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 client.startPlayRun(startRequest);
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
- progress.phase("loading play definition");
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 fileInputBindings = [
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 stageFileInputArgs({
5912
- client,
5913
- runtimeInput,
5914
- bindings: fileInputBindings,
5915
- progress
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: options.target.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 startAndWaitForPlayCompletionByStream({
5928
- client,
5929
- request: startRequest,
5930
- playName: options.target.name,
5931
- jsonOutput: options.jsonOutput,
5932
- emitLogs: options.emitLogs,
5933
- waitTimeoutMs: options.waitTimeoutMs,
5934
- progress
5935
- });
5936
- const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
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
- writePlayResult(finalStatus, options.jsonOutput, { exportedPath });
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 client.startPlayRun(startRequest);
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
- options.target.name
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 ?? options.target.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
- await traceCliSpan(
7921
- "cli.sdk_skills_sync",
7922
- { baseUrl },
7923
- () => syncSdkSkillsIfNeeded(baseUrl)
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);