deepline 0.1.220 → 0.1.222

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.
@@ -610,10 +610,10 @@ var SDK_RELEASE = {
610
610
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
611
611
  // 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
612
612
  // automatically without blocking their current command.
613
- version: "0.1.220",
613
+ version: "0.1.222",
614
614
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
615
615
  supportPolicy: {
616
- latest: "0.1.220",
616
+ latest: "0.1.222",
617
617
  minimumSupported: "0.1.53",
618
618
  deprecatedBelow: "0.1.219",
619
619
  commandMinimumSupported: [
@@ -5341,6 +5341,12 @@ function printCommandEnvelope(envelope, options = {}) {
5341
5341
  }
5342
5342
 
5343
5343
  // src/cli/commands/admin.ts
5344
+ function laneStatusLine(input2) {
5345
+ if (input2.lane) {
5346
+ return `status: ${input2.lane.status}${input2.lane.active ? " (active)" : ""}`;
5347
+ }
5348
+ return input2.registration ? `status: ${input2.registration.status}` : "status: (not in registry)";
5349
+ }
5344
5350
  function normalizeEnvironment(value) {
5345
5351
  const trimmed = value?.trim().toLowerCase();
5346
5352
  if (!trimmed || trimmed === "production" || trimmed === "prod") {
@@ -5393,7 +5399,7 @@ async function handleLanesShow(releaseId, options) {
5393
5399
  title: `Lane ${releaseId} (${environment}):`,
5394
5400
  lines: [
5395
5401
  `registered: ${payload.registered}`,
5396
- payload.lane ? `status: ${payload.lane.status}${payload.lane.active ? " (active)" : ""}` : "status: (not in registry)",
5402
+ laneStatusLine(payload),
5397
5403
  `queue: ${payload.retireCheck.queue}`,
5398
5404
  `queued tasks: ${payload.retireCheck.queuedTasks}`,
5399
5405
  `non-terminal runs: ${payload.retireCheck.nonTerminalRuns}`,
@@ -5435,7 +5441,7 @@ async function handleReleasesActivate(releaseId, options) {
5435
5441
  {
5436
5442
  releaseId,
5437
5443
  environment,
5438
- ...options.schedulerBackend ? { schedulerBackend: options.schedulerBackend } : {}
5444
+ schedulerBackend: options.schedulerBackend ?? "absurd"
5439
5445
  }
5440
5446
  );
5441
5447
  printCommandEnvelope(
@@ -5460,7 +5466,10 @@ function registerAdminCommands(program) {
5460
5466
  lanes.command("show <releaseId>").description("Show one lane and its retire-gate depth.").option("--environment <env>", "production (default) or preview").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleLanesShow);
5461
5467
  lanes.command("retire-check <releaseId>").description("Check whether a lane is safe to retire (queue empty).").option("--environment <env>", "production (default) or preview").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleLanesRetireCheck);
5462
5468
  const releases = admin.command("releases").description("Manage the active runtime release pointer.");
5463
- releases.command("activate <releaseId>").description("Flip the active runtime release to a prior registered lane.").option("--environment <env>", "production (default) or preview").option("--scheduler-backend <backend>", "absurd or workers_edge").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleReleasesActivate);
5469
+ releases.command("activate <releaseId>").description("Activate a prior registered Absurd release lane.").option("--environment <env>", "production (default) or preview").option(
5470
+ "--scheduler-backend <backend>",
5471
+ "compatibility flag: absurd only; workers_edge is disabled"
5472
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleReleasesActivate);
5464
5473
  }
5465
5474
 
5466
5475
  // src/cli/commands/auth.ts
@@ -14677,6 +14686,73 @@ function parsePlayCheckOptions(args) {
14677
14686
  const jsonOutput = argsWantJson(args);
14678
14687
  return { target, jsonOutput };
14679
14688
  }
14689
+ function parsePlayPublishOptions(args) {
14690
+ const target = args[0];
14691
+ if (!target) {
14692
+ throw new Error(
14693
+ "Usage: deepline plays publish <play-file.ts|play-name> [--expected-artifact <hash>] [--dry-run] [--latest|--revision-id <id>] [--json]"
14694
+ );
14695
+ }
14696
+ let revisionId;
14697
+ let useLatest = false;
14698
+ let expectedArtifactHash;
14699
+ let dryRun = false;
14700
+ for (let index = 1; index < args.length; index += 1) {
14701
+ const arg = args[index];
14702
+ if (arg === "--revision-id") {
14703
+ revisionId = args[++index];
14704
+ if (!revisionId) throw new Error("--revision-id requires a revision id.");
14705
+ continue;
14706
+ }
14707
+ if (arg === "--latest") {
14708
+ useLatest = true;
14709
+ continue;
14710
+ }
14711
+ if (arg === "--expected-artifact") {
14712
+ expectedArtifactHash = args[++index];
14713
+ if (!expectedArtifactHash) {
14714
+ throw new Error("--expected-artifact requires an artifact hash.");
14715
+ }
14716
+ continue;
14717
+ }
14718
+ if (arg.startsWith("--expected-artifact=")) {
14719
+ expectedArtifactHash = arg.slice("--expected-artifact=".length);
14720
+ if (!expectedArtifactHash) {
14721
+ throw new Error("--expected-artifact requires an artifact hash.");
14722
+ }
14723
+ continue;
14724
+ }
14725
+ if (arg === "--dry-run") {
14726
+ dryRun = true;
14727
+ continue;
14728
+ }
14729
+ if (arg === "--json") continue;
14730
+ throw new Error(`Unknown plays publish option: ${arg}`);
14731
+ }
14732
+ if (revisionId && useLatest) {
14733
+ throw new Error("Choose only one live target: --latest or --revision-id.");
14734
+ }
14735
+ return {
14736
+ target,
14737
+ revisionId,
14738
+ useLatest,
14739
+ expectedArtifactHash,
14740
+ dryRun,
14741
+ jsonOutput: argsWantJson(args)
14742
+ };
14743
+ }
14744
+ function formatByteBudget(usedBytes, limitBytes) {
14745
+ return `${usedBytes.toLocaleString()} B / ${limitBytes.toLocaleString()} B`;
14746
+ }
14747
+ function printPlayCheckLimits(limits) {
14748
+ if (!limits) return;
14749
+ console.log(
14750
+ ` revision storage: ${formatByteBudget(limits.revisionStorage.usedBytes, limits.revisionStorage.limitBytes)}`
14751
+ );
14752
+ console.log(
14753
+ ` bundle: ${formatByteBudget(limits.bundle.usedBytes, limits.bundle.limitBytes)}`
14754
+ );
14755
+ }
14680
14756
  function shouldUseLocalOnlyPlayCheck() {
14681
14757
  const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
14682
14758
  return value === "1" || value === "true" || value === "yes" || value === "on";
@@ -14989,7 +15065,11 @@ async function handlePlayCheck(args) {
14989
15065
  );
14990
15066
  } else {
14991
15067
  console.log(`\u2713 ${playName} passed local play check`);
15068
+ console.log(` source: ${graph.root.artifact.sourceHash.slice(0, 12)}`);
14992
15069
  console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
15070
+ console.log(
15071
+ ` publish: deepline plays publish ${shellQuote2(options.target)} --expected-artifact ${result2.artifactHash}`
15072
+ );
14993
15073
  printToolGetterHints(result2.toolGetterHints);
14994
15074
  }
14995
15075
  return 0;
@@ -15031,6 +15111,15 @@ async function handlePlayCheck(args) {
15031
15111
  if (enrichedResult.artifactHash) {
15032
15112
  console.log(` artifact: ${enrichedResult.artifactHash.slice(0, 12)}`);
15033
15113
  }
15114
+ if (enrichedResult.sourceHash) {
15115
+ console.log(` source: ${enrichedResult.sourceHash.slice(0, 12)}`);
15116
+ }
15117
+ printPlayCheckLimits(enrichedResult.limits);
15118
+ if (enrichedResult.artifactHash) {
15119
+ console.log(
15120
+ ` publish: deepline plays publish ${shellQuote2(options.target)} --expected-artifact ${enrichedResult.artifactHash}`
15121
+ );
15122
+ }
15034
15123
  printPlayTriggers(enrichedResult.triggers);
15035
15124
  printRecognizedSummary(enrichedResult.recognized);
15036
15125
  printPlayCheckIssues(enrichedResult.issues, (line) => console.log(line));
@@ -16387,28 +16476,15 @@ async function handlePlayDescribe(args) {
16387
16476
  return 0;
16388
16477
  }
16389
16478
  async function handlePlayPublish(args) {
16390
- const playName = args[0];
16391
- if (!playName) {
16392
- console.error(
16393
- "Usage: deepline plays publish <play-file.ts|play-name> [--latest|--revision-id <id>] [--json]"
16394
- );
16395
- return 1;
16396
- }
16397
- let revisionId;
16398
- let useLatest = false;
16399
- for (let index = 1; index < args.length; index += 1) {
16400
- const arg = args[index];
16401
- if (arg === "--revision-id" && args[index + 1]) {
16402
- revisionId = args[++index];
16403
- }
16404
- if (arg === "--latest") {
16405
- useLatest = true;
16406
- }
16407
- }
16408
- if (revisionId && useLatest) {
16409
- console.error("Choose only one live target: --latest or --revision-id.");
16410
- return 1;
16479
+ let options;
16480
+ try {
16481
+ options = parsePlayPublishOptions(args);
16482
+ } catch (error) {
16483
+ console.error(error instanceof Error ? error.message : String(error));
16484
+ return 2;
16411
16485
  }
16486
+ const { target: playName, useLatest } = options;
16487
+ let revisionId = options.revisionId;
16412
16488
  const client2 = new DeeplineClient();
16413
16489
  if (isFileTarget(playName)) {
16414
16490
  if (revisionId || useLatest) {
@@ -16425,6 +16501,66 @@ async function handlePlayPublish(args) {
16425
16501
  () => collectBundledPlayGraph(resolve8(playName))
16426
16502
  );
16427
16503
  assertBundledPlayGraphDescriptions(graph);
16504
+ } catch (error) {
16505
+ console.error(error instanceof Error ? error.message : String(error));
16506
+ return 1;
16507
+ }
16508
+ const rootPlayName = graph.root.playName ?? extractPlayName(graph.root.sourceCode, graph.root.filePath);
16509
+ const artifactHash = graph.root.artifact.artifactHash;
16510
+ if (options.expectedArtifactHash && options.expectedArtifactHash !== artifactHash) {
16511
+ const result3 = {
16512
+ ok: false,
16513
+ code: "PLAY_ARTIFACT_HASH_MISMATCH",
16514
+ message: "Refusing to publish: the local file no longer produces the artifact checked earlier.",
16515
+ expectedArtifactHash: options.expectedArtifactHash,
16516
+ currentArtifactHash: artifactHash,
16517
+ next: `deepline plays check ${shellQuote2(playName)}`
16518
+ };
16519
+ if (options.jsonOutput) {
16520
+ process.stdout.write(`${JSON.stringify(result3)}
16521
+ `);
16522
+ } else {
16523
+ console.error(result3.message);
16524
+ console.error(` checked artifact: ${result3.expectedArtifactHash}`);
16525
+ console.error(` current artifact: ${result3.currentArtifactHash}`);
16526
+ console.error(` run: ${result3.next}`);
16527
+ }
16528
+ return 7;
16529
+ }
16530
+ const dryRun = {
16531
+ ok: true,
16532
+ dryRun: true,
16533
+ name: rootPlayName,
16534
+ sourcePath: graph.root.filePath,
16535
+ sourceHash: graph.root.artifact.sourceHash,
16536
+ artifactHash,
16537
+ graphHash: graph.root.artifact.graphHash,
16538
+ importedPlayCount: Math.max(0, graph.nodes.size - 1),
16539
+ plannedMutation: "register revision and promote it live",
16540
+ expectedArtifactHash: options.expectedArtifactHash ?? null
16541
+ };
16542
+ if (options.dryRun) {
16543
+ if (options.jsonOutput) {
16544
+ process.stdout.write(`${JSON.stringify(dryRun)}
16545
+ `);
16546
+ } else {
16547
+ console.log(`Dry run: ${rootPlayName}`);
16548
+ console.log(` source: ${dryRun.sourceHash}`);
16549
+ console.log(` artifact: ${dryRun.artifactHash}`);
16550
+ console.log(` would: ${dryRun.plannedMutation}`);
16551
+ }
16552
+ return 0;
16553
+ }
16554
+ let previousLive = null;
16555
+ try {
16556
+ previousLive = (await client2.getPlay(rootPlayName)).play.liveRevision ?? null;
16557
+ } catch (error) {
16558
+ if (!(error instanceof DeeplineError) || error.statusCode !== 404) {
16559
+ console.error(error instanceof Error ? error.message : String(error));
16560
+ return 5;
16561
+ }
16562
+ }
16563
+ try {
16428
16564
  await traceCliSpan(
16429
16565
  "cli.play_publish_compile_manifests",
16430
16566
  { targetKind: "file", nodeCount: graph.nodes.size },
@@ -16439,13 +16575,12 @@ async function handlePlayPublish(args) {
16439
16575
  console.error(error instanceof Error ? error.message : String(error));
16440
16576
  return 1;
16441
16577
  }
16442
- const rootPlayName = graph.root.playName ?? extractPlayName(graph.root.sourceCode, graph.root.filePath);
16443
16578
  const published = await traceCliSpan(
16444
16579
  "cli.play_publish_register_root",
16445
16580
  {
16446
16581
  targetKind: "file",
16447
16582
  playName: rootPlayName,
16448
- artifactHash: graph.root.artifact.artifactHash
16583
+ artifactHash
16449
16584
  },
16450
16585
  () => client2.registerPlayArtifact({
16451
16586
  name: rootPlayName,
@@ -16457,17 +16592,63 @@ async function handlePlayPublish(args) {
16457
16592
  publish: true
16458
16593
  })
16459
16594
  );
16460
- process.stdout.write(
16461
- `${JSON.stringify({
16462
- success: true,
16463
- name: rootPlayName,
16464
- liveVersion: published.version ?? null,
16465
- revisionId: published.revisionId ?? null,
16466
- triggerMetadata: published.triggerMetadata ?? null,
16467
- triggerBindings: published.triggerBindings ?? []
16468
- })}
16469
- `
16470
- );
16595
+ const result2 = {
16596
+ success: true,
16597
+ name: rootPlayName,
16598
+ sourcePath: graph.root.filePath,
16599
+ sourceHash: graph.root.artifact.sourceHash,
16600
+ artifactHash,
16601
+ liveVersion: published.version ?? null,
16602
+ revisionId: published.revisionId ?? null,
16603
+ previousLiveVersion: previousLive?.version ?? null,
16604
+ previousArtifactHash: previousLive?.artifactHash ?? null,
16605
+ sourceBytes: {
16606
+ before: previousLive?.sourceCode ? Buffer.byteLength(previousLive.sourceCode, "utf8") : null,
16607
+ after: Buffer.byteLength(graph.root.sourceCode, "utf8")
16608
+ },
16609
+ triggerMetadata: published.triggerMetadata ?? null,
16610
+ triggerBindings: published.triggerBindings ?? []
16611
+ };
16612
+ if (options.jsonOutput) {
16613
+ process.stdout.write(`${JSON.stringify(result2)}
16614
+ `);
16615
+ } else {
16616
+ console.log(
16617
+ `\u2713 Published ${rootPlayName} as v${result2.liveVersion ?? "?"}`
16618
+ );
16619
+ console.log(` source: ${result2.sourceHash.slice(0, 12)}`);
16620
+ console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
16621
+ if (result2.previousLiveVersion !== null) {
16622
+ console.log(
16623
+ ` previous live: v${result2.previousLiveVersion} (${result2.previousArtifactHash?.slice(0, 12) ?? "unknown artifact"})`
16624
+ );
16625
+ }
16626
+ if (result2.sourceBytes.before !== null) {
16627
+ console.log(
16628
+ ` source bytes: ${result2.sourceBytes.before.toLocaleString()} \u2192 ${result2.sourceBytes.after.toLocaleString()}`
16629
+ );
16630
+ }
16631
+ console.log(
16632
+ ` revisions: deepline plays versions --name ${rootPlayName}`
16633
+ );
16634
+ }
16635
+ return 0;
16636
+ }
16637
+ if (options.expectedArtifactHash) {
16638
+ console.error("--expected-artifact only applies to a local play file.");
16639
+ return 2;
16640
+ }
16641
+ if (options.dryRun) {
16642
+ const result2 = {
16643
+ ok: true,
16644
+ dryRun: true,
16645
+ name: playName,
16646
+ plannedMutation: "promote a saved revision live",
16647
+ revisionId: revisionId ?? null,
16648
+ latest: useLatest
16649
+ };
16650
+ process.stdout.write(`${JSON.stringify(result2)}
16651
+ `);
16471
16652
  return 0;
16472
16653
  }
16473
16654
  const resolvedName = parseReferencedPlayTarget2(playName).playName;
@@ -16831,7 +17012,9 @@ Notes:
16831
17012
  Mutates cloud state. For a saved play, --latest or --revision-id promotes an
16832
17013
  existing saved revision live.
16833
17014
  Local .play.ts publishing bundles the file, validates it, saves a revision,
16834
- and promotes that revision live.
17015
+ and promotes that revision live. Use --expected-artifact with the hash from
17016
+ plays check to refuse publishing bytes that changed after validation.
17017
+ --dry-run bundles only and prints the exact artifact that would be published.
16835
17018
  Running a local file with plays run does not publish it.
16836
17019
 
16837
17020
  Examples:
@@ -16839,27 +17022,40 @@ Examples:
16839
17022
  deepline plays set-live my-play --revision-id <revision-id> --json
16840
17023
  deepline plays set-live my.play.ts --json
16841
17024
  deepline plays publish my.play.ts --json
17025
+ deepline plays check my.play.ts
17026
+ deepline plays publish my.play.ts --expected-artifact <artifact-hash>
17027
+ deepline plays publish my.play.ts --dry-run --json
16842
17028
  `
16843
17029
  );
16844
17030
  addPublishHelp(
16845
17031
  play.command("publish <target>").description(
16846
17032
  "Promote a saved play revision or publish a local play file."
16847
17033
  )
16848
- ).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
17034
+ ).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
17035
+ "--expected-artifact <hash>",
17036
+ "Require the local file to produce this checked artifact hash"
17037
+ ).option("--dry-run", "Bundle and show the planned publish without mutation").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
16849
17038
  process.exitCode = await handlePlayPublish([
16850
17039
  target,
16851
17040
  ...options.latest ? ["--latest"] : [],
16852
17041
  ...options.revisionId ? ["--revision-id", options.revisionId] : [],
17042
+ ...options.expectedArtifact ? ["--expected-artifact", options.expectedArtifact] : [],
17043
+ ...options.dryRun ? ["--dry-run"] : [],
16853
17044
  ...options.json ? ["--json"] : []
16854
17045
  ]);
16855
17046
  });
16856
17047
  addPublishHelp(
16857
17048
  play.command("set-live <target>").description("Promote a saved revision or publish a local play file.")
16858
- ).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
17049
+ ).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
17050
+ "--expected-artifact <hash>",
17051
+ "Require the local file to produce this checked artifact hash"
17052
+ ).option("--dry-run", "Bundle and show the planned publish without mutation").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
16859
17053
  process.exitCode = await handlePlayPublish([
16860
17054
  target,
16861
17055
  ...options.latest ? ["--latest"] : [],
16862
17056
  ...options.revisionId ? ["--revision-id", options.revisionId] : [],
17057
+ ...options.expectedArtifact ? ["--expected-artifact", options.expectedArtifact] : [],
17058
+ ...options.dryRun ? ["--dry-run"] : [],
16863
17059
  ...options.json ? ["--json"] : []
16864
17060
  ]);
16865
17061
  });
package/dist/index.d.mts CHANGED
@@ -945,6 +945,27 @@ interface PlayCheckResult {
945
945
  summary?: string;
946
946
  artifactHash?: string | null;
947
947
  graphHash?: string | null;
948
+ /** SHA-256 of the exact source bytes checked by Deepline. */
949
+ sourceHash?: string | null;
950
+ /** Enforceable byte budgets measured during cloud preflight. */
951
+ limits?: {
952
+ revisionStorage: {
953
+ usedBytes: number;
954
+ limitBytes: number;
955
+ withinLimit: boolean;
956
+ breakdown: {
957
+ sourceCodeBytes: number;
958
+ staticPipelineBytes: number;
959
+ bindingsBytes: number;
960
+ descriptionBytes: number;
961
+ };
962
+ };
963
+ bundle: {
964
+ usedBytes: number;
965
+ limitBytes: number;
966
+ withinLimit: boolean;
967
+ };
968
+ };
948
969
  }
949
970
  /** Severity of a {@link PlayCheckIssue}. `error` fails the check; `warning` does not. */
950
971
  type PlayCheckIssueSeverity = 'error' | 'warning';
package/dist/index.d.ts CHANGED
@@ -945,6 +945,27 @@ interface PlayCheckResult {
945
945
  summary?: string;
946
946
  artifactHash?: string | null;
947
947
  graphHash?: string | null;
948
+ /** SHA-256 of the exact source bytes checked by Deepline. */
949
+ sourceHash?: string | null;
950
+ /** Enforceable byte budgets measured during cloud preflight. */
951
+ limits?: {
952
+ revisionStorage: {
953
+ usedBytes: number;
954
+ limitBytes: number;
955
+ withinLimit: boolean;
956
+ breakdown: {
957
+ sourceCodeBytes: number;
958
+ staticPipelineBytes: number;
959
+ bindingsBytes: number;
960
+ descriptionBytes: number;
961
+ };
962
+ };
963
+ bundle: {
964
+ usedBytes: number;
965
+ limitBytes: number;
966
+ withinLimit: boolean;
967
+ };
968
+ };
948
969
  }
949
970
  /** Severity of a {@link PlayCheckIssue}. `error` fails the check; `warning` does not. */
950
971
  type PlayCheckIssueSeverity = 'error' | 'warning';
package/dist/index.js CHANGED
@@ -424,10 +424,10 @@ var SDK_RELEASE = {
424
424
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
425
425
  // 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
426
426
  // automatically without blocking their current command.
427
- version: "0.1.220",
427
+ version: "0.1.222",
428
428
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
429
429
  supportPolicy: {
430
- latest: "0.1.220",
430
+ latest: "0.1.222",
431
431
  minimumSupported: "0.1.53",
432
432
  deprecatedBelow: "0.1.219",
433
433
  commandMinimumSupported: [
package/dist/index.mjs CHANGED
@@ -354,10 +354,10 @@ var SDK_RELEASE = {
354
354
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
355
355
  // 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
356
356
  // automatically without blocking their current command.
357
- version: "0.1.220",
357
+ version: "0.1.222",
358
358
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
359
359
  supportPolicy: {
360
- latest: "0.1.220",
360
+ latest: "0.1.222",
361
361
  minimumSupported: "0.1.53",
362
362
  deprecatedBelow: "0.1.219",
363
363
  commandMinimumSupported: [
@@ -353,6 +353,17 @@ body {
353
353
  border: 1px solid var(--red);
354
354
  background: rgba(248,81,73,0.06);
355
355
  }
356
+ .tool-detail-toggle {
357
+ margin-top: 8px;
358
+ background: var(--bg-tertiary);
359
+ border: 1px solid var(--border);
360
+ border-radius: 4px;
361
+ color: var(--blue);
362
+ cursor: pointer;
363
+ font-size: 12px;
364
+ padding: 4px 8px;
365
+ }
366
+ .tool-detail-toggle:hover { border-color: var(--blue); }
356
367
 
357
368
  /* Result card */
358
369
  .result-card {
@@ -1334,7 +1334,7 @@ function renderTimeline(timeline) {
1334
1334
  <div class="tool-detail" id="detail-${i}">
1335
1335
  <div class="tool-detail-section">
1336
1336
  <div class="label">Input</div>
1337
- <pre>${esc(formatInput(e.tool, e.input))}</pre>
1337
+ ${formatToolInputHtml(e.tool, e.input, i)}
1338
1338
  </div>
1339
1339
  ${e.result_content != null ? `<div class="tool-detail-section">
1340
1340
  <div class="label">Result</div>
@@ -1347,8 +1347,17 @@ function renderTimeline(timeline) {
1347
1347
  return html;
1348
1348
  }
1349
1349
 
1350
+ function isShellCommandTool(tool) {
1351
+ const normalized = String(tool || '').toLowerCase().replace(/[^a-z]/g, '');
1352
+ return ['bash', 'exec', 'execcommand', 'runcommand', 'shell'].includes(normalized);
1353
+ }
1354
+
1355
+ function shellCommand(input) {
1356
+ return input.command || input.cmd || input.CommandLine || null;
1357
+ }
1358
+
1350
1359
  function formatInput(tool, input) {
1351
- if (tool === 'Bash') return input.command || JSON.stringify(input, null, 2);
1360
+ if (isShellCommandTool(tool)) return shellCommand(input) || JSON.stringify(input, null, 2);
1352
1361
  if (tool === 'Write') {
1353
1362
  let s = 'file: ' + (input.file_path || input.path || '') + '\n\n';
1354
1363
  if (input.content != null) s += '--- content ---\n' + String(input.content);
@@ -1364,6 +1373,39 @@ function formatInput(tool, input) {
1364
1373
  return JSON.stringify(input, null, 2);
1365
1374
  }
1366
1375
 
1376
+ function isLongShellCommand(command) {
1377
+ return command.length > 600 || command.split('\n').length > 10;
1378
+ }
1379
+
1380
+ function shellCommandPreview(command) {
1381
+ const lines = command.split('\n');
1382
+ let preview = lines.slice(0, 10).join('\n');
1383
+ if (preview.length > 600) preview = preview.slice(0, 600);
1384
+ return preview + '\n...[full command hidden]';
1385
+ }
1386
+
1387
+ function formatToolInputHtml(tool, input, entryIndex) {
1388
+ const formatted = formatInput(tool, input);
1389
+ if (!isShellCommandTool(tool) || !isLongShellCommand(formatted)) {
1390
+ return `<pre>${esc(formatted)}</pre>`;
1391
+ }
1392
+ const previewId = `command-preview-${entryIndex}`;
1393
+ const fullId = `command-full-${entryIndex}`;
1394
+ return `<pre id="${previewId}">${esc(shellCommandPreview(formatted))}</pre>
1395
+ <button class="tool-detail-toggle" onclick="toggleCommandInput('${previewId}', '${fullId}', this)">Show full command</button>
1396
+ <pre id="${fullId}" hidden>${esc(formatted)}</pre>`;
1397
+ }
1398
+
1399
+ function toggleCommandInput(previewId, fullId, toggle) {
1400
+ const preview = document.getElementById(previewId);
1401
+ const full = document.getElementById(fullId);
1402
+ if (!preview || !full) return;
1403
+ const showFull = full.hidden;
1404
+ full.hidden = !showFull;
1405
+ preview.hidden = showFull;
1406
+ toggle.textContent = showFull ? 'Show less' : 'Show full command';
1407
+ }
1408
+
1367
1409
  function toggleReasoning(id, toggle) {
1368
1410
  const el = document.getElementById(id);
1369
1411
  const collapsed = el.classList.toggle('collapsed');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.220",
3
+ "version": "0.1.222",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {