deepline 0.2.71 → 0.2.73

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 CHANGED
@@ -1044,7 +1044,7 @@ var SDK_RELEASE = {
1044
1044
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
1045
1045
  // exposed storage-dependent synchronous access. This deliberate minor
1046
1046
  // release keeps lazy paging semantics independent of row residency.
1047
- version: "0.2.71",
1047
+ version: "0.2.73",
1048
1048
  contracts: {
1049
1049
  api: {
1050
1050
  name: "sdk-http-api",
@@ -5992,9 +5992,10 @@ var DeeplineClient = class {
5992
5992
  * console.log(`Total runs: ${detail.play.runCount}`);
5993
5993
  * ```
5994
5994
  */
5995
- async getPlay(name) {
5995
+ async getPlay(name, options) {
5996
5996
  const encodedName = encodeURIComponent(name);
5997
- return this.http.get(`/api/v2/plays/${encodedName}`);
5997
+ const query = options?.source ? `?include=source&revision=${encodeURIComponent(options.source)}` : "";
5998
+ return this.http.get(`/api/v2/plays/${encodedName}${query}`);
5998
5999
  }
5999
6000
  /**
6000
6001
  * Get a normalized play description suitable for agents and CLIs.
@@ -18591,7 +18592,10 @@ async function assertCanonicalNamedPlayReference(client2, target, options = {})
18591
18592
  const parsed = parseReferencedPlayTarget2(target);
18592
18593
  let detail;
18593
18594
  try {
18594
- detail = await client2.getPlay(parsed.playName);
18595
+ detail = await client2.getPlay(
18596
+ parsed.playName,
18597
+ options.source ? { source: options.source } : void 0
18598
+ );
18595
18599
  } catch (error) {
18596
18600
  if (isPlayNotFoundError(error)) {
18597
18601
  throw await buildPlayReferenceNotFoundError({
@@ -18644,19 +18648,54 @@ function materializeRemotePlaySource(input2) {
18644
18648
  if (isFileTarget(input2.target)) {
18645
18649
  return null;
18646
18650
  }
18647
- if (!input2.sourceCode.trim()) {
18651
+ const entrySource = input2.source.files[input2.source.entryFile];
18652
+ if (!entrySource?.trim()) {
18648
18653
  return null;
18649
18654
  }
18655
+ const files = Object.entries(input2.source.files);
18656
+ if (files.length > 1) {
18657
+ if (!input2.outPath) {
18658
+ throw new Error(
18659
+ `Play ${input2.playName} has ${files.length} source files. Use --out <directory> to write the complete bundle.`
18660
+ );
18661
+ }
18662
+ const outputDirectory = (0, import_node_path14.resolve)(input2.outPath);
18663
+ let created = 0;
18664
+ let updated = 0;
18665
+ for (const [logicalPath, sourceCode] of files) {
18666
+ const outputPath2 = (0, import_node_path14.resolve)(outputDirectory, logicalPath);
18667
+ const outputRelativePath = (0, import_node_path14.relative)(outputDirectory, outputPath2);
18668
+ if (!outputRelativePath || outputRelativePath.startsWith("..") || (0, import_node_path14.isAbsolute)(outputRelativePath)) {
18669
+ throw new Error(
18670
+ `Refusing to materialize unsafe Play source path ${JSON.stringify(logicalPath)}.`
18671
+ );
18672
+ }
18673
+ (0, import_node_fs12.mkdirSync)((0, import_node_path14.dirname)(outputPath2), { recursive: true });
18674
+ if (!(0, import_node_fs12.existsSync)(outputPath2)) {
18675
+ (0, import_node_fs12.writeFileSync)(outputPath2, sourceCode, "utf-8");
18676
+ created += 1;
18677
+ } else if ((0, import_node_fs12.readFileSync)(outputPath2, "utf-8") !== sourceCode) {
18678
+ (0, import_node_fs12.writeFileSync)(outputPath2, sourceCode, "utf-8");
18679
+ updated += 1;
18680
+ }
18681
+ }
18682
+ return {
18683
+ path: outputDirectory,
18684
+ status: created > 0 ? "created" : updated > 0 ? "updated" : "unchanged",
18685
+ created: created > 0,
18686
+ fileCount: files.length
18687
+ };
18688
+ }
18650
18689
  const outputPath = input2.outPath ?? defaultMaterializedPlayPath(input2.playName);
18651
18690
  if ((0, import_node_fs12.existsSync)(outputPath)) {
18652
18691
  const existingSource = (0, import_node_fs12.readFileSync)(outputPath, "utf-8");
18653
- if (existingSource === input2.sourceCode) {
18692
+ if (existingSource === entrySource) {
18654
18693
  return { path: outputPath, status: "unchanged", created: false };
18655
18694
  }
18656
- (0, import_node_fs12.writeFileSync)(outputPath, input2.sourceCode, "utf-8");
18695
+ (0, import_node_fs12.writeFileSync)(outputPath, entrySource, "utf-8");
18657
18696
  return { path: outputPath, status: "updated", created: false };
18658
18697
  }
18659
- (0, import_node_fs12.writeFileSync)(outputPath, input2.sourceCode, "utf-8");
18698
+ (0, import_node_fs12.writeFileSync)(outputPath, entrySource, "utf-8");
18660
18699
  return { path: outputPath, status: "created", created: true };
18661
18700
  }
18662
18701
  function formatLoadedPlayMessage(materializedFile) {
@@ -22529,6 +22568,47 @@ ${PLAY_PUBLISH_USAGE}`
22529
22568
  jsonOutput: argsWantJson(args)
22530
22569
  };
22531
22570
  }
22571
+ var PLAY_SAVE_USAGE = "Usage: deepline plays save <play-file.ts> [--expected-artifact <hash>] [--dry-run] [--json]";
22572
+ function parsePlaySaveOptions(args) {
22573
+ const target = args[0];
22574
+ if (!target) throw new Error(PLAY_SAVE_USAGE);
22575
+ if (target.startsWith("-")) {
22576
+ throw new Error(`${PLAY_SAVE_USAGE}
22577
+ The Play file is positional.`);
22578
+ }
22579
+ let expectedArtifactHash;
22580
+ let dryRun = false;
22581
+ for (let index = 1; index < args.length; index += 1) {
22582
+ const arg = args[index];
22583
+ if (arg === "--expected-artifact") {
22584
+ expectedArtifactHash = args[++index];
22585
+ if (!expectedArtifactHash) {
22586
+ throw new Error("--expected-artifact requires an artifact hash.");
22587
+ }
22588
+ continue;
22589
+ }
22590
+ if (arg.startsWith("--expected-artifact=")) {
22591
+ expectedArtifactHash = arg.slice("--expected-artifact=".length);
22592
+ if (!expectedArtifactHash) {
22593
+ throw new Error("--expected-artifact requires an artifact hash.");
22594
+ }
22595
+ continue;
22596
+ }
22597
+ if (arg === "--dry-run") {
22598
+ dryRun = true;
22599
+ continue;
22600
+ }
22601
+ if (arg === "--json") continue;
22602
+ throw new Error(`Unknown option: ${arg}
22603
+ ${PLAY_SAVE_USAGE}`);
22604
+ }
22605
+ return {
22606
+ target,
22607
+ expectedArtifactHash,
22608
+ dryRun,
22609
+ jsonOutput: argsWantJson(args)
22610
+ };
22611
+ }
22532
22612
  function formatByteBudget(usedBytes, limitBytes) {
22533
22613
  return `${usedBytes.toLocaleString()} B / ${limitBytes.toLocaleString()} B`;
22534
22614
  }
@@ -23992,24 +24072,63 @@ async function handlePlayGet(args) {
23992
24072
  const explicitJson = args.includes("--json");
23993
24073
  const sourceOutput = args.includes("--source");
23994
24074
  const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
24075
+ const requestedRevisionFlags = [
24076
+ args.includes("--working"),
24077
+ args.includes("--live"),
24078
+ args.includes("--revision")
24079
+ ].filter(Boolean).length;
24080
+ if (requestedRevisionFlags > 1) {
24081
+ console.error("Use only one of --working, --live, or --revision <number>.");
24082
+ return 2;
24083
+ }
24084
+ let sourceSelector = "working";
24085
+ if (args.includes("--live")) {
24086
+ sourceSelector = "live";
24087
+ }
23995
24088
  let outPath = null;
24089
+ let requestedVersion = null;
23996
24090
  for (let index = 1; index < args.length; index += 1) {
23997
24091
  const arg = args[index];
23998
24092
  if (arg === "--out" && args[index + 1]) {
23999
24093
  outPath = (0, import_node_path14.resolve)(args[++index]);
24000
24094
  }
24095
+ if (arg === "--revision") {
24096
+ const rawVersion = args[++index];
24097
+ const version = Number(rawVersion);
24098
+ if (!rawVersion || !Number.isInteger(version) || version < 1) {
24099
+ console.error("--revision must be a positive integer.");
24100
+ return 2;
24101
+ }
24102
+ requestedVersion = version;
24103
+ }
24104
+ }
24105
+ if (requestedVersion !== null) {
24106
+ sourceSelector = `version:${requestedVersion}`;
24107
+ }
24108
+ if (requestedRevisionFlags > 0 && !sourceOutput && !outPath) {
24109
+ console.error(
24110
+ "--working, --live, and --revision only apply when reading source with --source or --out."
24111
+ );
24112
+ return 2;
24001
24113
  }
24002
24114
  const playName = isFileTarget(target) ? extractPlayName((0, import_node_fs12.readFileSync)((0, import_node_path14.resolve)(target), "utf-8"), (0, import_node_path14.resolve)(target)) : parseReferencedPlayTarget2(target).playName;
24003
- const detail = isFileTarget(target) ? await client2.getPlay(playName) : await assertCanonicalNamedPlayReference(client2, target, {
24004
- command: "get"
24005
- });
24006
- const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
24007
- const materializedFile = outPath ? materializeRemotePlaySource({
24008
- target,
24115
+ const includeSource = sourceOutput || outPath !== null;
24116
+ const detail = isFileTarget(target) ? await client2.getPlay(
24009
24117
  playName,
24010
- sourceCode: resolvedSource,
24011
- outPath
24012
- }) : null;
24118
+ includeSource ? { source: sourceSelector } : void 0
24119
+ ) : await assertCanonicalNamedPlayReference(client2, target, {
24120
+ command: "get",
24121
+ ...includeSource ? { source: sourceSelector } : {}
24122
+ });
24123
+ const source = detail.source;
24124
+ const resolvedSource = source?.files[source.entryFile] ?? "";
24125
+ let materializedFile = null;
24126
+ try {
24127
+ materializedFile = outPath && source ? materializeRemotePlaySource({ target, playName, source, outPath }) : null;
24128
+ } catch (error) {
24129
+ console.error(error instanceof Error ? error.message : String(error));
24130
+ return 2;
24131
+ }
24013
24132
  const loadedMessage = materializedFile ? formatLoadedPlayMessage(materializedFile) : null;
24014
24133
  if (jsonOutput) {
24015
24134
  process.stdout.write(
@@ -24030,14 +24149,20 @@ async function handlePlayGet(args) {
24030
24149
  return 0;
24031
24150
  }
24032
24151
  if (sourceOutput) {
24033
- if (!resolvedSource.trim()) {
24152
+ if (!source || !resolvedSource.trim()) {
24034
24153
  console.error(`No source code available for ${playName}.`);
24035
- return 1;
24154
+ return 4;
24036
24155
  }
24037
24156
  if (materializedFile) {
24038
24157
  console.log(loadedMessage);
24039
24158
  return 0;
24040
24159
  }
24160
+ if (Object.keys(source.files).length > 1) {
24161
+ console.error(
24162
+ `Play ${playName} has ${Object.keys(source.files).length} source files. Use --out <directory> to write the complete bundle.`
24163
+ );
24164
+ return 2;
24165
+ }
24041
24166
  process.stdout.write(resolvedSource);
24042
24167
  if (!resolvedSource.endsWith("\n")) {
24043
24168
  process.stdout.write("\n");
@@ -24559,6 +24684,128 @@ async function handlePlayDescribe(args) {
24559
24684
  printPlayDescription(play);
24560
24685
  return 0;
24561
24686
  }
24687
+ async function handlePlaySave(args) {
24688
+ let options;
24689
+ try {
24690
+ options = parsePlaySaveOptions(args);
24691
+ } catch (error) {
24692
+ console.error(error instanceof Error ? error.message : String(error));
24693
+ return 2;
24694
+ }
24695
+ if (!isFileTarget(options.target)) {
24696
+ console.error(
24697
+ "plays save requires a local .play.ts file. Use plays publish <play-name> to promote an existing saved revision."
24698
+ );
24699
+ return 2;
24700
+ }
24701
+ let graph;
24702
+ try {
24703
+ graph = await traceCliSpan(
24704
+ "cli.play_save_bundle_graph",
24705
+ { targetKind: "file" },
24706
+ () => collectBundledPlayGraph((0, import_node_path14.resolve)(options.target))
24707
+ );
24708
+ assertBundledPlayGraphDescriptions(graph);
24709
+ } catch (error) {
24710
+ console.error(error instanceof Error ? error.message : String(error));
24711
+ return 7;
24712
+ }
24713
+ const playName = graph.root.playName ?? extractPlayName(graph.root.sourceCode, graph.root.filePath);
24714
+ const artifactHash = graph.root.artifact.artifactHash;
24715
+ if (options.expectedArtifactHash && options.expectedArtifactHash !== artifactHash) {
24716
+ const result = {
24717
+ ok: false,
24718
+ code: "PLAY_ARTIFACT_HASH_MISMATCH",
24719
+ message: "Refusing to save: the local file no longer produces the artifact checked earlier.",
24720
+ expectedArtifactHash: options.expectedArtifactHash,
24721
+ currentArtifactHash: artifactHash,
24722
+ next: `deepline plays check ${shellQuote2(options.target)}`
24723
+ };
24724
+ if (options.jsonOutput) {
24725
+ process.stdout.write(`${JSON.stringify(result)}
24726
+ `);
24727
+ } else {
24728
+ console.error(result.message);
24729
+ console.error(` checked artifact: ${result.expectedArtifactHash}`);
24730
+ console.error(` current artifact: ${result.currentArtifactHash}`);
24731
+ console.error(` run: ${result.next}`);
24732
+ }
24733
+ return 7;
24734
+ }
24735
+ const dryRun = {
24736
+ ok: true,
24737
+ dryRun: true,
24738
+ name: playName,
24739
+ sourcePath: graph.root.filePath,
24740
+ sourceHash: graph.root.artifact.sourceHash,
24741
+ artifactHash,
24742
+ graphHash: graph.root.artifact.graphHash,
24743
+ sourceFileCount: Object.keys(graph.root.sourceFiles).length,
24744
+ plannedMutation: "save or update the mutable working draft; does not change the live revision"
24745
+ };
24746
+ if (options.dryRun) {
24747
+ if (options.jsonOutput) {
24748
+ process.stdout.write(`${JSON.stringify(dryRun)}
24749
+ `);
24750
+ } else {
24751
+ console.log(`Dry run: ${playName}`);
24752
+ console.log(` source: ${dryRun.sourceHash}`);
24753
+ console.log(` artifact: ${dryRun.artifactHash}`);
24754
+ console.log(` would: ${dryRun.plannedMutation}`);
24755
+ }
24756
+ return 0;
24757
+ }
24758
+ const client2 = new DeeplineClient();
24759
+ try {
24760
+ await traceCliSpan(
24761
+ "cli.play_save_compile_manifests",
24762
+ { targetKind: "file", nodeCount: graph.nodes.size },
24763
+ () => compileBundledPlayGraphManifests(client2, graph)
24764
+ );
24765
+ const saved = await traceCliSpan(
24766
+ "cli.play_save_register_draft",
24767
+ { targetKind: "file", playName, artifactHash },
24768
+ () => client2.registerPlayArtifact({
24769
+ name: playName,
24770
+ sourceCode: graph.root.sourceCode,
24771
+ sourceFiles: graph.root.sourceFiles,
24772
+ description: graph.root.playDescription ?? void 0,
24773
+ artifact: graph.root.artifact,
24774
+ compilerManifest: requireCompilerManifest(graph.root),
24775
+ publish: false
24776
+ })
24777
+ );
24778
+ const result = {
24779
+ success: true,
24780
+ name: playName,
24781
+ draft: true,
24782
+ revisionId: saved.revisionId ?? null,
24783
+ version: saved.version ?? null,
24784
+ sourceHash: graph.root.artifact.sourceHash,
24785
+ artifactHash,
24786
+ graphHash: graph.root.artifact.graphHash,
24787
+ artifactStorageKey: saved.artifactStorageKey,
24788
+ next: {
24789
+ publish: `deepline plays publish ${shellQuote2(options.target)} --expected-artifact ${artifactHash}`,
24790
+ get: `deepline plays get ${playName} --source --out ./${playName}/ --working`
24791
+ }
24792
+ };
24793
+ if (options.jsonOutput) {
24794
+ process.stdout.write(`${JSON.stringify(result)}
24795
+ `);
24796
+ } else {
24797
+ console.log(
24798
+ `\u2713 Saved ${playName} working draft as v${result.version ?? "?"}`
24799
+ );
24800
+ console.log(" Live revision unchanged.");
24801
+ console.log(` publish: ${result.next.publish}`);
24802
+ }
24803
+ return 0;
24804
+ } catch (error) {
24805
+ console.error(error instanceof Error ? error.message : String(error));
24806
+ return 5;
24807
+ }
24808
+ }
24562
24809
  async function handlePlayPublish(args) {
24563
24810
  let options;
24564
24811
  try {
@@ -25089,7 +25336,9 @@ Pass-through input flags:
25089
25336
  ]);
25090
25337
  });
25091
25338
  registerPlayBootstrapCommand(play);
25092
- play.command("get <target>").description("Fetch full play details.").addHelpText(
25339
+ play.command("get <target>").description(
25340
+ "Fetch full play details or materialize a saved source bundle."
25341
+ ).addHelpText(
25093
25342
  "after",
25094
25343
  `
25095
25344
  Notes:
@@ -25101,17 +25350,24 @@ Examples:
25101
25350
  deepline plays get prebuilt/person-linkedin-to-email
25102
25351
  deepline plays get prebuilt/person-linkedin-to-email --json | jq '.play.liveRevision'
25103
25352
  deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source > email-waterfall.play.ts
25104
- deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source --out ./email-waterfall.play.ts
25353
+ deepline plays get my-play --source --out ./my-play/ --working
25354
+ deepline plays get my-play --source --out ./my-play-v3/ --revision 3
25105
25355
  `
25106
25356
  ).option("--json", "Emit JSON output. Also automatic when stdout is piped").option(
25107
25357
  "--source",
25108
- "Print raw source code; combine with --out to write a file"
25109
- ).option("--out <path>", "Write source to a specific path").action(async (target, options) => {
25358
+ "Read source; prints single-file Plays or combines with --out to write the full bundle"
25359
+ ).option(
25360
+ "--out <path>",
25361
+ "Write source to a file or, for multi-file Plays, a directory"
25362
+ ).option("--working", "Read the current working draft source (default)").option("--live", "Read the live revision source").option("--revision <number>", "Read a specific revision source").action(async (target, options) => {
25110
25363
  process.exitCode = await handlePlayGet([
25111
25364
  target,
25112
25365
  ...options.json ? ["--json"] : [],
25113
25366
  ...options.source ? ["--source"] : [],
25114
- ...options.out ? ["--out", options.out] : []
25367
+ ...options.out ? ["--out", options.out] : [],
25368
+ ...options.working ? ["--working"] : [],
25369
+ ...options.live ? ["--live"] : [],
25370
+ ...options.revision ? ["--revision", options.revision] : []
25115
25371
  ]);
25116
25372
  });
25117
25373
  play.command("list").description("List saved and prebuilt plays.").addHelpText(
@@ -25246,6 +25502,32 @@ Examples:
25246
25502
  ...options.json ? ["--json"] : []
25247
25503
  ]);
25248
25504
  });
25505
+ play.command("save <file>").description(
25506
+ "Save or update a local Play working draft without publishing it."
25507
+ ).addHelpText(
25508
+ "after",
25509
+ `
25510
+ Notes:
25511
+ Mutates cloud state, but does not change the live revision or active triggers.
25512
+ Repeated saves update one working draft. Publishing it, or running a named
25513
+ draft revision, freezes it; the next save starts a new revision.
25514
+
25515
+ Examples:
25516
+ deepline plays save ./company-research.play.ts
25517
+ deepline plays save ./company-research.play.ts --dry-run --json
25518
+ deepline plays publish ./company-research.play.ts
25519
+ `
25520
+ ).option("--expected-artifact <hash>", "Require the checked artifact hash").option(
25521
+ "--dry-run",
25522
+ "Bundle and show the planned draft save without mutation"
25523
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (file, options) => {
25524
+ process.exitCode = await handlePlaySave([
25525
+ file,
25526
+ ...options.expectedArtifact ? ["--expected-artifact", options.expectedArtifact] : [],
25527
+ ...options.dryRun ? ["--dry-run"] : [],
25528
+ ...options.json ? ["--json"] : []
25529
+ ]);
25530
+ });
25249
25531
  const addPublishHelp = (command) => command.addHelpText(
25250
25532
  "after",
25251
25533
  `
@@ -33757,16 +34039,16 @@ Examples:
33757
34039
  // src/cli/getting-started.ts
33758
34040
  var DEEPLINE_GTM_SKILL = "/deepline-gtm";
33759
34041
  var DEEPLINE_GTM_STARTER_PROMPTS = [
34042
+ {
34043
+ id: "competitor-tracking",
34044
+ title: "Competitor tracking",
34045
+ prompt: "/deepline-gtm Track competitors of my company. Use my business email domain when available; otherwise ask for my company name."
34046
+ },
33760
34047
  {
33761
34048
  id: "contact-email-enrichment",
33762
34049
  title: "Contact email enrichment",
33763
34050
  prompt: "/deepline-gtm Find 10 CTOs at B2B SaaS companies and enrich them with email addresses."
33764
34051
  },
33765
- {
33766
- id: "company-signal-enrichment",
33767
- title: "Company signal enrichment",
33768
- prompt: "/deepline-gtm Enrich these company domains with employee count, funding, and recent hiring signals."
33769
- },
33770
34052
  {
33771
34053
  id: "decision-maker-discovery",
33772
34054
  title: "Decision-maker discovery",