deepline 0.1.11 → 0.1.19

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.
Files changed (85) hide show
  1. package/README.md +18 -10
  2. package/dist/cli/index.js +1795 -1052
  3. package/dist/cli/index.mjs +1795 -1053
  4. package/dist/index.d.mts +427 -308
  5. package/dist/index.d.ts +427 -308
  6. package/dist/index.js +391 -326
  7. package/dist/index.mjs +391 -325
  8. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +88 -22
  9. package/dist/repo/apps/play-runner-workers/src/entry.ts +804 -1253
  10. package/dist/repo/sdk/src/client.ts +287 -47
  11. package/dist/repo/sdk/src/config.ts +125 -8
  12. package/dist/repo/sdk/src/http.ts +10 -2
  13. package/dist/repo/sdk/src/index.ts +7 -16
  14. package/dist/repo/sdk/src/play.ts +105 -140
  15. package/dist/repo/sdk/src/plays/bundle-play-file.ts +23 -6
  16. package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
  17. package/dist/repo/sdk/src/tool-output.ts +0 -146
  18. package/dist/repo/sdk/src/types.ts +27 -0
  19. package/dist/repo/sdk/src/version.ts +2 -2
  20. package/dist/repo/sdk/src/worker-play-entry.ts +3 -0
  21. package/dist/repo/shared_libs/play-runtime/csv-rename.ts +180 -0
  22. package/dist/repo/shared_libs/play-runtime/tool-result.ts +250 -133
  23. package/dist/repo/shared_libs/plays/bundling/index.ts +274 -234
  24. package/dist/repo/shared_libs/plays/dataset.ts +29 -1
  25. package/package.json +5 -4
  26. package/dist/cli/index.js.map +0 -1
  27. package/dist/cli/index.mjs.map +0 -1
  28. package/dist/index.js.map +0 -1
  29. package/dist/index.mjs.map +0 -1
  30. package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
  31. package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
  32. package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
  33. package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
  34. package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +0 -184
  35. package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
  36. package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
  37. package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
  38. package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
  39. package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
  40. package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
  41. package/dist/repo/sdk/src/cli/commands/play.ts +0 -3307
  42. package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
  43. package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -341
  44. package/dist/repo/sdk/src/cli/index.ts +0 -148
  45. package/dist/repo/sdk/src/cli/progress.ts +0 -149
  46. package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
  47. package/dist/repo/sdk/src/cli/trace.ts +0 -61
  48. package/dist/repo/sdk/src/cli/utils.ts +0 -145
  49. package/dist/repo/sdk/src/compat.ts +0 -77
  50. package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
  51. package/dist/repo/shared_libs/observability/tracing.ts +0 -98
  52. package/dist/repo/shared_libs/play-runtime/context.ts +0 -3999
  53. package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
  54. package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -713
  55. package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
  56. package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
  57. package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
  58. package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
  59. package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
  60. package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
  61. package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
  62. package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
  63. package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
  64. package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
  65. package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
  66. package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
  67. package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
  68. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
  69. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
  70. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
  71. package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
  72. package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
  73. package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
  74. package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
  75. package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
  76. package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
  77. package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
  78. package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
  79. package/dist/repo/shared_libs/plays/definition.ts +0 -264
  80. package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
  81. package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
  82. package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
  83. package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -415
  84. package/dist/repo/shared_libs/temporal/constants.ts +0 -39
  85. package/dist/repo/shared_libs/temporal/preview-config.ts +0 -153
package/dist/cli/index.js CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli/index.ts
27
- var import_commander2 = require("commander");
27
+ var import_commander = require("commander");
28
28
 
29
29
  // src/config.ts
30
30
  var import_node_fs = require("fs");
@@ -70,6 +70,16 @@ var ConfigError = class extends DeeplineError {
70
70
  var PROD_URL = "https://code.deepline.com";
71
71
  var DEFAULT_TIMEOUT = 6e4;
72
72
  var DEFAULT_MAX_RETRIES = 3;
73
+ var ACTIVE_DEEPLINE_ENV_FILE = ".env.deepline";
74
+ function isProdBaseUrl(baseUrl) {
75
+ return baseUrl.trim().replace(/\/$/, "") === PROD_URL;
76
+ }
77
+ function profileNameForBaseUrl(baseUrl) {
78
+ return isProdBaseUrl(baseUrl) ? "prod" : "dev";
79
+ }
80
+ function projectEnvStartDir() {
81
+ return process.env.DEEPLINE_PROJECT_ENV_DIR?.trim() || process.cwd();
82
+ }
73
83
  function baseUrlSlug(baseUrl) {
74
84
  let url;
75
85
  try {
@@ -105,16 +115,52 @@ function parseEnvFile(filePath) {
105
115
  }
106
116
  return env;
107
117
  }
108
- function findNearestWorktreeEnv(startDir = process.cwd()) {
118
+ function findNearestEnvFile(names, startDir = process.cwd()) {
109
119
  let current = (0, import_node_path.resolve)(startDir);
110
120
  while (true) {
111
- const values = parseEnvFile((0, import_node_path.join)(current, ".env.worktree"));
112
- if (Object.keys(values).length > 0) return values;
121
+ for (const name of names) {
122
+ const filePath = (0, import_node_path.join)(current, name);
123
+ if ((0, import_node_fs.existsSync)(filePath)) return filePath;
124
+ }
113
125
  const parent = (0, import_node_path.dirname)(current);
114
- if (parent === current) return {};
126
+ if (parent === current) return null;
115
127
  current = parent;
116
128
  }
117
129
  }
130
+ function findNearestEnv(names, startDir = process.cwd()) {
131
+ const filePath = findNearestEnvFile(names, startDir);
132
+ return filePath ? parseEnvFile(filePath) : {};
133
+ }
134
+ function findNearestWorktreeEnv(startDir = process.cwd()) {
135
+ return findNearestEnv([".env.worktree"], startDir);
136
+ }
137
+ function resolveProfileEnvFileNames() {
138
+ const explicitProfile = process.env.DEEPLINE_ENV_PROFILE?.trim() || process.env.DEEPLINE_PROFILE?.trim() || "";
139
+ const names = [];
140
+ if (explicitProfile) names.push(`.env.deepline.${explicitProfile}`);
141
+ const nodeEnv = process.env.NODE_ENV?.trim();
142
+ if (nodeEnv === "production") names.push(".env.deepline.prod");
143
+ else if (nodeEnv === "staging") names.push(".env.deepline.staging");
144
+ names.push(ACTIVE_DEEPLINE_ENV_FILE);
145
+ return names;
146
+ }
147
+ function resolveProjectAppEnvFileNames() {
148
+ const nodeEnv = process.env.NODE_ENV?.trim();
149
+ const names = [];
150
+ if (nodeEnv === "production") names.push(".env.prod");
151
+ if (nodeEnv === "staging") names.push(".env.staging");
152
+ names.push(".env.local", ".env");
153
+ return names;
154
+ }
155
+ function resolveBaseUrlFromEnvValues(env) {
156
+ return env.DEEPLINE_ORIGIN_URL?.trim() || env.DEEPLINE_API_BASE_URL?.trim() || "";
157
+ }
158
+ function loadProjectDeeplineEnv() {
159
+ return findNearestEnv(resolveProfileEnvFileNames(), projectEnvStartDir());
160
+ }
161
+ function loadProjectAppEnv() {
162
+ return findNearestEnv(resolveProjectAppEnvFileNames(), projectEnvStartDir());
163
+ }
118
164
  function normalizeWorktreeBaseUrl(baseUrl, worktreeEnv = findNearestWorktreeEnv()) {
119
165
  const trimmed = baseUrl.trim().replace(/\/$/, "");
120
166
  if (!trimmed) return trimmed;
@@ -166,6 +212,10 @@ function autoDetectBaseUrl() {
166
212
  if (envOrigin) return normalizeWorktreeBaseUrl(envOrigin);
167
213
  const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
168
214
  if (envBase) return normalizeWorktreeBaseUrl(envBase);
215
+ const projectDeeplineBaseUrl = resolveBaseUrlFromEnvValues(loadProjectDeeplineEnv());
216
+ if (projectDeeplineBaseUrl) return normalizeWorktreeBaseUrl(projectDeeplineBaseUrl);
217
+ const projectAppBaseUrl = resolveBaseUrlFromEnvValues(loadProjectAppEnv());
218
+ if (projectAppBaseUrl) return normalizeWorktreeBaseUrl(projectAppBaseUrl);
169
219
  const worktreeBaseUrl = resolveWorktreeBaseUrl();
170
220
  if (worktreeBaseUrl) return worktreeBaseUrl;
171
221
  const globalEnv = loadGlobalCliEnv();
@@ -177,7 +227,9 @@ function resolveConfig(options) {
177
227
  const requestedBaseUrl = options?.baseUrl?.trim() || autoDetectBaseUrl();
178
228
  const baseUrl = normalizeWorktreeBaseUrl(requestedBaseUrl);
179
229
  const cliEnv = loadCliEnv(baseUrl);
180
- const apiKey = options?.apiKey?.trim() || process.env.DEEPLINE_API_KEY?.trim() || cliEnv.DEEPLINE_API_KEY || "";
230
+ const projectDeeplineEnv = loadProjectDeeplineEnv();
231
+ const projectAppEnv = loadProjectAppEnv();
232
+ const apiKey = options?.apiKey?.trim() || process.env.DEEPLINE_API_KEY?.trim() || projectDeeplineEnv.DEEPLINE_API_KEY || projectAppEnv.DEEPLINE_API_KEY || cliEnv.DEEPLINE_API_KEY || "";
181
233
  if (!apiKey) {
182
234
  throw new ConfigError(
183
235
  `No API key found. Set DEEPLINE_API_KEY env var, pass apiKey option, or run: deepline auth register`
@@ -190,10 +242,32 @@ function resolveConfig(options) {
190
242
  maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
191
243
  };
192
244
  }
245
+ function mergeEnvFile(filePath, values) {
246
+ const existing = (0, import_node_fs.existsSync)(filePath) ? parseEnvFile(filePath) : {};
247
+ const merged = { ...existing, ...values };
248
+ const dir = (0, import_node_path.dirname)(filePath);
249
+ if (!(0, import_node_fs.existsSync)(dir)) (0, import_node_fs.mkdirSync)(dir, { recursive: true });
250
+ const lines = Object.entries(merged).filter(([, value]) => value !== "").map(([key, value]) => `${key}=${value}`);
251
+ (0, import_node_fs.writeFileSync)(filePath, `${lines.join("\n")}
252
+ `, "utf-8");
253
+ }
254
+ function saveProjectDeeplineEnvValues(baseUrl, values, startDir = projectEnvStartDir()) {
255
+ const root = (0, import_node_path.resolve)(startDir);
256
+ const profile = profileNameForBaseUrl(baseUrl);
257
+ const files = [
258
+ (0, import_node_path.join)(root, ACTIVE_DEEPLINE_ENV_FILE),
259
+ (0, import_node_path.join)(root, `.env.deepline.${profile}`)
260
+ ];
261
+ if (profile === "dev") files.push((0, import_node_path.join)(root, ".env"));
262
+ for (const filePath of files) {
263
+ mergeEnvFile(filePath, values);
264
+ }
265
+ return files;
266
+ }
193
267
 
194
268
  // src/version.ts
195
- var SDK_VERSION = "0.1.11";
196
- var SDK_API_CONTRACT = "2026-04-plays-v1";
269
+ var SDK_VERSION = "0.1.19";
270
+ var SDK_API_CONTRACT = "2026-05-runs-v2";
197
271
 
198
272
  // ../shared_libs/play-runtime/coordinator-headers.ts
199
273
  var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
@@ -386,8 +460,12 @@ var HttpClient = class {
386
460
  * @param path - API path
387
461
  * @param body - Request body (will be JSON-serialized)
388
462
  */
389
- async post(path, body) {
390
- return this.request(path, { method: "POST", body });
463
+ async post(path, body, headers) {
464
+ return this.request(path, {
465
+ method: "POST",
466
+ body,
467
+ headers
468
+ });
391
469
  }
392
470
  /**
393
471
  * Send a DELETE request.
@@ -467,6 +545,10 @@ function sleep(ms) {
467
545
 
468
546
  // src/client.ts
469
547
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
548
+ var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
549
+ function isRecord(value) {
550
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
551
+ }
470
552
  function normalizePlayStatus(raw) {
471
553
  const status = typeof raw.status === "string" ? raw.status : typeof raw.temporalStatus === "string" ? mapLegacyTemporalStatus(raw.temporalStatus) : "running";
472
554
  const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
@@ -496,6 +578,7 @@ function mapLegacyTemporalStatus(status) {
496
578
  var DeeplineClient = class {
497
579
  http;
498
580
  config;
581
+ runs;
499
582
  /**
500
583
  * @param options - Optional overrides for API key, base URL, timeout, and retries.
501
584
  * @throws {@link ConfigError} if no API key can be resolved from any source.
@@ -503,6 +586,13 @@ var DeeplineClient = class {
503
586
  constructor(options) {
504
587
  this.config = resolveConfig(options);
505
588
  this.http = new HttpClient(this.config);
589
+ this.runs = {
590
+ get: (runId) => this.getRunStatus(runId),
591
+ list: (options2) => this.listRuns(options2),
592
+ tail: (runId, options2) => this.tailRun(runId, options2),
593
+ logs: (runId, options2) => this.getRunLogs(runId, options2),
594
+ stop: (runId, options2) => this.stopRun(runId, options2)
595
+ };
506
596
  }
507
597
  /** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
508
598
  get baseUrl() {
@@ -519,12 +609,27 @@ var DeeplineClient = class {
519
609
  ).filter((field) => Boolean(field?.name)) : [];
520
610
  return fields.length > 0 ? { fields } : schema;
521
611
  }
522
- playRunCommand(name) {
523
- return `deepline plays run ${name} --input '{...}' --watch`;
612
+ schemaMetadata(schema, key) {
613
+ if (!isRecord(schema)) return null;
614
+ const value = schema[key];
615
+ return isRecord(value) ? value : null;
616
+ }
617
+ playRunCommand(play, options) {
618
+ const target = play.reference || play.name;
619
+ if (options?.csvInput) {
620
+ const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
621
+ return `deepline plays run ${target} --${inputField} leads.csv --watch`;
622
+ }
623
+ return `deepline plays run ${target} --input '{...}' --watch`;
524
624
  }
525
625
  summarizePlayListItem(play, options) {
526
626
  const aliases = play.aliases?.length ? play.aliases : [play.name];
527
- const runCommand = this.playRunCommand(play.name);
627
+ const csvInput = this.schemaMetadata(play.inputSchema, "csvInput");
628
+ const rowOutputSchema = this.schemaMetadata(
629
+ play.outputSchema,
630
+ "rowOutputSchema"
631
+ );
632
+ const runCommand = this.playRunCommand(play, { csvInput });
528
633
  return {
529
634
  name: play.name,
530
635
  ...play.reference ? { reference: play.reference } : {},
@@ -536,6 +641,8 @@ var DeeplineClient = class {
536
641
  aliases,
537
642
  inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
538
643
  outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
644
+ ...csvInput ? { csvInput } : {},
645
+ ...rowOutputSchema ? { rowOutputSchema } : {},
539
646
  runCommand,
540
647
  examples: [runCommand],
541
648
  currentPublishedVersion: play.currentPublishedVersion ?? null,
@@ -574,6 +681,31 @@ var DeeplineClient = class {
574
681
  );
575
682
  return res.tools;
576
683
  }
684
+ /**
685
+ * Search available tools using Deepline's ranked backend search.
686
+ *
687
+ * This is the same discovery surface used by the legacy CLI: it ranks across
688
+ * tool metadata, categories, agent guidance, and input schema fields.
689
+ */
690
+ async searchTools(options = {}) {
691
+ const params = new URLSearchParams();
692
+ const query = options.query?.trim() ?? "";
693
+ params.set("q", query);
694
+ params.set(
695
+ "include_search_debug",
696
+ options.includeSearchDebug ? "true" : "false"
697
+ );
698
+ params.set("search_mode", options.searchMode ?? "v2");
699
+ if (options.categories?.trim()) {
700
+ params.set("categories", options.categories.trim());
701
+ }
702
+ if (options.searchTerms?.trim()) {
703
+ params.set("search_terms", options.searchTerms.trim());
704
+ }
705
+ return this.http.get(
706
+ `/api/v2/integrations/list?${params.toString()}`
707
+ );
708
+ }
577
709
  /**
578
710
  * Get detailed metadata for a single tool.
579
711
  *
@@ -602,55 +734,24 @@ var DeeplineClient = class {
602
734
  );
603
735
  }
604
736
  /**
605
- * Execute a tool and return the extracted result.
606
- *
607
- * Sends the input payload to the tool and returns the `.result` field from the
608
- * response. For the full response envelope (including job_id, credits, etc.),
609
- * use {@link executeToolRaw}.
610
- *
611
- * @param toolId - Tool identifier (e.g. `"test_company_search"`)
612
- * @param input - Tool-specific input parameters
613
- * @returns The tool's output (shape varies by tool)
614
- * @throws {@link DeeplineError} if the tool execution fails
615
- *
616
- * @example
617
- * ```typescript
618
- * const company = await client.executeTool('test_company_search', {
619
- * domain: 'stripe.com',
620
- * });
621
- * console.log(company); // { name: "Stripe", industry: "Financial Services", ... }
622
- * ```
623
- */
624
- async executeTool(toolId, input) {
625
- const res = await this.http.post(
626
- `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
627
- { payload: input }
628
- );
629
- return res.result ?? res;
630
- }
631
- /**
632
- * Execute a tool and return the full response envelope.
633
- *
634
- * Unlike {@link executeTool}, this returns the complete API response including
635
- * `job_id`, `status`, `credits`, and the raw `result` object.
737
+ * Execute a tool and return the standard execution envelope.
636
738
  *
637
- * @param toolId - Tool identifier
638
- * @param input - Tool-specific input parameters
639
- * @returns Full response with job metadata and result
640
- *
641
- * @example
642
- * ```typescript
643
- * const raw = await client.executeToolRaw('test_company_search', { domain: 'stripe.com' });
644
- * console.log(`Job: ${raw.job_id}, Credits: ${raw.credits}`);
645
- * console.log(`Result:`, raw.result);
646
- * ```
739
+ * The `result.data` field contains the provider payload. `result.meta`
740
+ * contains provider/upstream metadata such as HTTP status or paging details.
741
+ * Top-level fields such as `status`, `job_id`, and `billing` describe the
742
+ * Deepline execution.
647
743
  */
648
- async executeToolRaw(toolId, input) {
744
+ async executeTool(toolId, input, options) {
745
+ const headers = options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : void 0;
649
746
  return this.http.post(
650
747
  `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
651
- { payload: input }
748
+ { payload: input },
749
+ headers
652
750
  );
653
751
  }
752
+ async executeToolRaw(toolId, input, options) {
753
+ return this.executeTool(toolId, input, options);
754
+ }
654
755
  async queryCustomerDb(input) {
655
756
  return this.http.post("/api/v2/db/query", {
656
757
  sql: input.sql,
@@ -699,6 +800,7 @@ var DeeplineClient = class {
699
800
  ...request.revisionId ? { revisionId: request.revisionId } : {},
700
801
  ...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
701
802
  ...request.sourceCode ? { sourceCode: request.sourceCode } : {},
803
+ ...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
702
804
  ..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
703
805
  ...request.artifactHash ? { artifactHash: request.artifactHash } : {},
704
806
  ...request.graphHash ? { graphHash: request.graphHash } : {},
@@ -723,6 +825,7 @@ var DeeplineClient = class {
723
825
  ...request.revisionId ? { revisionId: request.revisionId } : {},
724
826
  ...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
725
827
  ...request.sourceCode ? { sourceCode: request.sourceCode } : {},
828
+ ...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
726
829
  ..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
727
830
  ...request.artifactHash ? { artifactHash: request.artifactHash } : {},
728
831
  ...request.graphHash ? { graphHash: request.graphHash } : {},
@@ -759,6 +862,7 @@ var DeeplineClient = class {
759
862
  const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
760
863
  name: input.name,
761
864
  sourceCode: input.sourceCode,
865
+ sourceFiles: input.sourceFiles,
762
866
  artifact: input.artifact
763
867
  });
764
868
  return this.http.post("/api/v2/plays/artifacts", {
@@ -773,6 +877,7 @@ var DeeplineClient = class {
773
877
  compilerManifest: artifact.compilerManifest ?? await this.compilePlayManifest({
774
878
  name: artifact.name,
775
879
  sourceCode: artifact.sourceCode,
880
+ sourceFiles: artifact.sourceFiles,
776
881
  artifact: artifact.artifact
777
882
  })
778
883
  }))
@@ -799,11 +904,13 @@ var DeeplineClient = class {
799
904
  const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
800
905
  name: input.name,
801
906
  sourceCode: input.sourceCode,
907
+ sourceFiles: input.sourceFiles,
802
908
  artifact: input.artifact
803
909
  });
804
910
  const registeredArtifact = await this.registerPlayArtifact({
805
911
  name: input.name,
806
912
  sourceCode: input.sourceCode,
913
+ sourceFiles: input.sourceFiles,
807
914
  artifact: input.artifact,
808
915
  compilerManifest,
809
916
  publish: false
@@ -863,11 +970,13 @@ var DeeplineClient = class {
863
970
  const compilerManifest = options?.compilerManifest ?? await this.compilePlayManifest({
864
971
  name,
865
972
  sourceCode,
973
+ sourceFiles: options?.sourceFiles,
866
974
  artifact
867
975
  });
868
976
  const registeredArtifact = await this.registerPlayArtifact({
869
977
  name,
870
978
  sourceCode,
979
+ sourceFiles: options?.sourceFiles,
871
980
  artifact,
872
981
  compilerManifest,
873
982
  publish: false
@@ -1051,6 +1160,112 @@ var DeeplineClient = class {
1051
1160
  );
1052
1161
  return response.runs ?? [];
1053
1162
  }
1163
+ /**
1164
+ * Get a run by id using the public runs resource model.
1165
+ *
1166
+ * This is the SDK equivalent of:
1167
+ *
1168
+ * ```bash
1169
+ * deepline runs get <run-id> --json
1170
+ * ```
1171
+ */
1172
+ async getRunStatus(runId) {
1173
+ const response = await this.http.get(
1174
+ `/api/v2/runs/${encodeURIComponent(runId)}`
1175
+ );
1176
+ return normalizePlayStatus(response);
1177
+ }
1178
+ /**
1179
+ * List play runs using the public runs resource model.
1180
+ *
1181
+ * This is the SDK equivalent of:
1182
+ *
1183
+ * ```bash
1184
+ * deepline runs list --play <play-name> --status failed --json
1185
+ * ```
1186
+ */
1187
+ async listRuns(options) {
1188
+ const playName = options.play.trim();
1189
+ if (!playName) {
1190
+ throw new Error("runs.list requires options.play.");
1191
+ }
1192
+ const params = new URLSearchParams({ play: playName });
1193
+ const status = options.status?.trim();
1194
+ if (status) {
1195
+ params.set("status", status);
1196
+ }
1197
+ const response = await this.http.get(
1198
+ `/api/v2/runs?${params.toString()}`
1199
+ );
1200
+ return response.runs ?? [];
1201
+ }
1202
+ /**
1203
+ * Fetch the lightweight tail status for a run using the public runs resource model.
1204
+ *
1205
+ * This is the SDK equivalent of:
1206
+ *
1207
+ * ```bash
1208
+ * deepline runs tail <run-id> --json
1209
+ * ```
1210
+ */
1211
+ async tailRun(runId, options) {
1212
+ const afterLogIndex = typeof options?.afterLogIndex === "number" ? options.afterLogIndex : typeof options?.cursor === "number" ? options.cursor : typeof options?.cursor === "string" && options.cursor.trim() ? Number(options.cursor) : void 0;
1213
+ const params = new URLSearchParams();
1214
+ if (Number.isFinite(afterLogIndex)) {
1215
+ params.set("afterLogIndex", String(Number(afterLogIndex)));
1216
+ }
1217
+ if (typeof options?.waitMs === "number") {
1218
+ params.set("waitMs", String(options.waitMs));
1219
+ }
1220
+ if (options?.terminalOnly) {
1221
+ params.set("terminalOnly", "true");
1222
+ }
1223
+ const suffix = params.toString() ? `?${params.toString()}` : "";
1224
+ const response = await this.http.get(
1225
+ `/api/v2/runs/${encodeURIComponent(runId)}/tail${suffix}`
1226
+ );
1227
+ return normalizePlayStatus(response);
1228
+ }
1229
+ /**
1230
+ * Fetch persisted logs for a run using the public runs resource model.
1231
+ *
1232
+ * This is the SDK equivalent of:
1233
+ *
1234
+ * ```bash
1235
+ * deepline runs logs <run-id> --limit 200 --json
1236
+ * ```
1237
+ */
1238
+ async getRunLogs(runId, options) {
1239
+ const status = await this.getRunStatus(runId);
1240
+ const logs = status.progress?.logs ?? [];
1241
+ const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
1242
+ const entries = logs.slice(Math.max(0, logs.length - limit));
1243
+ return {
1244
+ runId: status.runId,
1245
+ totalCount: logs.length,
1246
+ returnedCount: entries.length,
1247
+ firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
1248
+ lastSequence: logs.length === 0 ? null : logs.length,
1249
+ truncated: logs.length > entries.length,
1250
+ hasMore: logs.length > entries.length,
1251
+ entries
1252
+ };
1253
+ }
1254
+ /**
1255
+ * Stop a run by id using the public runs resource model.
1256
+ *
1257
+ * This is the SDK equivalent of:
1258
+ *
1259
+ * ```bash
1260
+ * deepline runs stop <run-id> --reason "stale lock" --json
1261
+ * ```
1262
+ */
1263
+ async stopRun(runId, options) {
1264
+ return this.http.post(
1265
+ `/api/v2/runs/${encodeURIComponent(runId)}/stop`,
1266
+ options?.reason ? { reason: options.reason } : {}
1267
+ );
1268
+ }
1054
1269
  async listPlays() {
1055
1270
  const response = await this.http.get(
1056
1271
  "/api/v2/plays"
@@ -1437,6 +1652,7 @@ function saveEnvValues(values, baseUrl) {
1437
1652
  const merged = { ...existing, ...values };
1438
1653
  const lines = Object.entries(merged).filter(([, v]) => v !== "").map(([k, v]) => `${k}=${v}`);
1439
1654
  (0, import_node_fs3.writeFileSync)(filePath, lines.join("\n") + "\n", "utf-8");
1655
+ saveProjectDeeplineEnvValues(baseUrl, values);
1440
1656
  }
1441
1657
  async function httpJson(method, url, apiKey, body) {
1442
1658
  const headers = { "Content-Type": "application/json" };
@@ -1976,24 +2192,70 @@ function registerBillingCommands(program) {
1976
2192
  // src/cli/dataset-stats.ts
1977
2193
  var import_node_fs4 = require("fs");
1978
2194
  var import_node_path4 = require("path");
1979
- function isRecord(value) {
2195
+ var CSV_PROJECTED_FIELDS_KEY = "__deeplineCsvProjectedFields";
2196
+ function csvProjectedFields(row) {
2197
+ const serialized = row[CSV_PROJECTED_FIELDS_KEY];
2198
+ if (!Array.isArray(serialized)) {
2199
+ return /* @__PURE__ */ new Set();
2200
+ }
2201
+ return new Set(
2202
+ serialized.filter((field) => typeof field === "string")
2203
+ );
2204
+ }
2205
+ function stripCsvProjectionFields(row) {
2206
+ const projectedFields = csvProjectedFields(row);
2207
+ if (projectedFields.size === 0 && !(CSV_PROJECTED_FIELDS_KEY in row)) {
2208
+ return row;
2209
+ }
2210
+ const stripped = { ...row };
2211
+ for (const field of projectedFields) {
2212
+ delete stripped[field];
2213
+ }
2214
+ delete stripped[CSV_PROJECTED_FIELDS_KEY];
2215
+ return stripped;
2216
+ }
2217
+ function stripCsvProjectionColumns(columns, rows) {
2218
+ const projectedFields = /* @__PURE__ */ new Set();
2219
+ let hasProjectionMetadata = false;
2220
+ for (const row of rows) {
2221
+ for (const field of csvProjectedFields(row)) {
2222
+ projectedFields.add(field);
2223
+ }
2224
+ hasProjectionMetadata ||= CSV_PROJECTED_FIELDS_KEY in row;
2225
+ }
2226
+ if (!hasProjectionMetadata && projectedFields.size === 0) {
2227
+ return columns;
2228
+ }
2229
+ return columns.filter(
2230
+ (column) => column !== CSV_PROJECTED_FIELDS_KEY && !projectedFields.has(column)
2231
+ );
2232
+ }
2233
+ function sanitizeCsvProjectionInfo(input) {
2234
+ const columns = stripCsvProjectionColumns(input.columns, input.rows);
2235
+ const rows = input.rows.map(stripCsvProjectionFields);
2236
+ return { rows, columns };
2237
+ }
2238
+ function isRecord2(value) {
1980
2239
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1981
2240
  }
1982
2241
  function isSerializedDataset(value) {
1983
- return isRecord(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
2242
+ return isRecord2(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
1984
2243
  }
1985
2244
  function rowArray(value) {
1986
2245
  if (!Array.isArray(value)) {
1987
2246
  return null;
1988
2247
  }
1989
2248
  const rows = value.filter(
1990
- (row) => isRecord(row)
2249
+ (row) => isRecord2(row)
1991
2250
  );
1992
2251
  return rows.length === value.length ? rows : null;
1993
2252
  }
1994
2253
  function readNumber(value) {
1995
2254
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
1996
2255
  }
2256
+ function numericStat(value) {
2257
+ return readNumber(value) ?? 0;
2258
+ }
1997
2259
  function inferColumns(rows) {
1998
2260
  const columns = [];
1999
2261
  const seen = /* @__PURE__ */ new Set();
@@ -2011,12 +2273,12 @@ function inferColumns(rows) {
2011
2273
  return columns;
2012
2274
  }
2013
2275
  function extractCanonicalRowsInfo(statusOrResult) {
2014
- const root = isRecord(statusOrResult) ? statusOrResult : null;
2015
- const result = isRecord(root?.result) ? root.result : root;
2276
+ const root = isRecord2(statusOrResult) ? statusOrResult : null;
2277
+ const result = isRecord2(root?.result) ? root.result : root;
2016
2278
  if (!result) {
2017
2279
  return null;
2018
2280
  }
2019
- const metadata = isRecord(result._metadata) ? result._metadata : null;
2281
+ const metadata = isRecord2(result._metadata) ? result._metadata : null;
2020
2282
  const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
2021
2283
  const candidates = [
2022
2284
  { source: "result.contacts", value: result.contacts, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
@@ -2024,8 +2286,8 @@ function extractCanonicalRowsInfo(statusOrResult) {
2024
2286
  { source: "result.rows", value: result.rows, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
2025
2287
  { source: "result.results", value: result.results, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count }
2026
2288
  ];
2027
- if (isRecord(result.output)) {
2028
- const outputMetadata = isRecord(result.output._metadata) ? result.output._metadata : null;
2289
+ if (isRecord2(result.output)) {
2290
+ const outputMetadata = isRecord2(result.output._metadata) ? result.output._metadata : null;
2029
2291
  const outputTotalFromMetadata = outputMetadata?.totalRows ?? outputMetadata?.rowCount ?? outputMetadata?.count;
2030
2292
  candidates.push(
2031
2293
  { source: "result.output.contacts", value: result.output.contacts, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count },
@@ -2036,12 +2298,17 @@ function extractCanonicalRowsInfo(statusOrResult) {
2036
2298
  }
2037
2299
  for (const candidate of candidates) {
2038
2300
  if (isSerializedDataset(candidate.value)) {
2039
- const rows2 = rowArray(candidate.value.preview) ?? [];
2040
- const totalRows2 = readNumber(candidate.value.count) ?? rows2.length;
2301
+ const rawRows = rowArray(candidate.value.preview) ?? [];
2302
+ const totalRows2 = readNumber(candidate.value.count) ?? rawRows.length;
2303
+ const rawColumns = Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string") ? candidate.value.columns : inferColumns(rawRows);
2304
+ const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
2305
+ rows: rawRows,
2306
+ columns: rawColumns
2307
+ });
2041
2308
  return {
2042
2309
  rows: rows2,
2043
2310
  totalRows: totalRows2,
2044
- columns: Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string") ? candidate.value.columns : inferColumns(rows2),
2311
+ columns,
2045
2312
  complete: rows2.length === totalRows2,
2046
2313
  source: candidate.source
2047
2314
  };
@@ -2051,10 +2318,14 @@ function extractCanonicalRowsInfo(statusOrResult) {
2051
2318
  continue;
2052
2319
  }
2053
2320
  const totalRows = readNumber(candidate.total) ?? rows.length;
2054
- return {
2321
+ const sanitized = sanitizeCsvProjectionInfo({
2055
2322
  rows,
2323
+ columns: inferColumns(rows)
2324
+ });
2325
+ return {
2326
+ rows: sanitized.rows,
2056
2327
  totalRows,
2057
- columns: inferColumns(rows),
2328
+ columns: sanitized.columns,
2058
2329
  complete: rows.length === totalRows,
2059
2330
  source: candidate.source
2060
2331
  };
@@ -2064,6 +2335,31 @@ function extractCanonicalRowsInfo(statusOrResult) {
2064
2335
  function percentText(numerator, denominator) {
2065
2336
  return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
2066
2337
  }
2338
+ function isDatasetExecutionStatsInput(value) {
2339
+ return isRecord2(value) && isRecord2(value.columnStats) && Object.values(value.columnStats).every(isRecord2);
2340
+ }
2341
+ function extractDatasetExecutionStats(statusOrResult) {
2342
+ if (!isRecord2(statusOrResult)) {
2343
+ return null;
2344
+ }
2345
+ const direct = statusOrResult.dataset_execution_stats;
2346
+ if (isDatasetExecutionStatsInput(direct)) {
2347
+ return direct;
2348
+ }
2349
+ const nested = isRecord2(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
2350
+ return isDatasetExecutionStatsInput(nested) ? nested : null;
2351
+ }
2352
+ function formatExecutionStats(raw, denominator) {
2353
+ return {
2354
+ queued: percentText(numericStat(raw.queued), denominator),
2355
+ running: percentText(numericStat(raw.running), denominator),
2356
+ "completed:executed": percentText(numericStat(raw.completed), denominator),
2357
+ "completed:reused": percentText(numericStat(raw.cached), denominator),
2358
+ "skipped:condition": percentText(numericStat(raw.skipped), denominator),
2359
+ "skipped:missed": percentText(numericStat(raw.missed), denominator),
2360
+ failed: percentText(numericStat(raw.failed), denominator)
2361
+ };
2362
+ }
2067
2363
  function countPercentText(count, denominator) {
2068
2364
  return denominator > 0 ? `${count} (${Math.round(100 * count / denominator)}%)` : "0 (0%)";
2069
2365
  }
@@ -2102,13 +2398,13 @@ function summarizeSampleValue(value, depth = 0) {
2102
2398
  if (typeof parsed === "number" || typeof parsed === "boolean") return parsed;
2103
2399
  if (depth >= 3) {
2104
2400
  if (Array.isArray(parsed)) return [];
2105
- if (isRecord(parsed)) return {};
2401
+ if (isRecord2(parsed)) return {};
2106
2402
  return compactScalar(parsed);
2107
2403
  }
2108
2404
  if (Array.isArray(parsed)) {
2109
2405
  return parsed.slice(0, 3).map((item) => summarizeSampleValue(item, depth + 1));
2110
2406
  }
2111
- if (isRecord(parsed)) {
2407
+ if (isRecord2(parsed)) {
2112
2408
  const out = {};
2113
2409
  for (const [key, nested] of Object.entries(parsed)) {
2114
2410
  if (["__dl", "meta", "metadata"].includes(key)) {
@@ -2138,7 +2434,7 @@ function compactCell(value) {
2138
2434
  }
2139
2435
  return `[${parsed.length} items]`;
2140
2436
  }
2141
- if (isRecord(parsed)) {
2437
+ if (isRecord2(parsed)) {
2142
2438
  for (const key of ["matched_result", "output"]) {
2143
2439
  if (parsed[key] !== null && parsed[key] !== void 0 && parsed[key] !== "") {
2144
2440
  return compactCell(parsed[key]);
@@ -2156,15 +2452,16 @@ function compactCell(value) {
2156
2452
  }
2157
2453
  return compactScalar(parsed, 120);
2158
2454
  }
2159
- function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows)) {
2455
+ function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows), executionStats) {
2456
+ const sanitized = sanitizeCsvProjectionInfo({ rows, columns });
2160
2457
  const columnStats = {};
2161
- for (const column of columns) {
2458
+ for (const column of sanitized.columns) {
2162
2459
  let nonEmpty = 0;
2163
2460
  let empty = 0;
2164
2461
  let sampleValue;
2165
2462
  let sampleValueType = null;
2166
2463
  const valueCounts = /* @__PURE__ */ new Map();
2167
- for (const row of rows) {
2464
+ for (const row of sanitized.rows) {
2168
2465
  const raw = row[column];
2169
2466
  const value = compactCell(raw);
2170
2467
  if (value) {
@@ -2183,6 +2480,10 @@ function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns
2183
2480
  non_empty: percentText(nonEmpty, denominator),
2184
2481
  unique: valueCounts.size
2185
2482
  };
2483
+ const rawExecutionStats = executionStats?.columnStats[column];
2484
+ if (rawExecutionStats) {
2485
+ stat3.execution = formatExecutionStats(rawExecutionStats, totalRows);
2486
+ }
2186
2487
  if (sampleValue !== void 0 && sampleValueType) {
2187
2488
  stat3.sample_value = sampleValue;
2188
2489
  stat3.sample_type = sampleValueType;
@@ -2216,10 +2517,14 @@ function writeCanonicalRowsCsv(rowsInfo, outPath) {
2216
2517
  `Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; cannot export a complete CSV from this status payload yet.`
2217
2518
  );
2218
2519
  }
2520
+ const sanitized = sanitizeCsvProjectionInfo({
2521
+ rows: rowsInfo.rows,
2522
+ columns: rowsInfo.columns
2523
+ });
2219
2524
  const resolved = (0, import_node_path4.resolve)(outPath);
2220
2525
  (0, import_node_fs4.writeFileSync)(
2221
2526
  resolved,
2222
- csvStringFromRows(rowsInfo.rows, rowsInfo.columns),
2527
+ csvStringFromRows(sanitized.rows, sanitized.columns),
2223
2528
  "utf-8"
2224
2529
  );
2225
2530
  return resolved;
@@ -2511,7 +2816,6 @@ function registerOrgCommands(program) {
2511
2816
  var import_node_crypto3 = require("crypto");
2512
2817
  var import_node_fs6 = require("fs");
2513
2818
  var import_node_path8 = require("path");
2514
- var import_commander = require("commander");
2515
2819
 
2516
2820
  // src/plays/bundle-play-file.ts
2517
2821
  var import_node_os5 = require("os");
@@ -2526,7 +2830,6 @@ var import_node_os4 = require("os");
2526
2830
  var import_node_path5 = require("path");
2527
2831
  var import_node_module = require("module");
2528
2832
  var import_esbuild = require("esbuild");
2529
- var import_typescript = __toESM(require("typescript"));
2530
2833
 
2531
2834
  // ../shared_libs/play-runtime/backend.ts
2532
2835
  var PLAY_RUNTIME_BACKENDS = {
@@ -2596,6 +2899,14 @@ var PLAY_SOURCE_FILE_PATTERN = /\.play\.(?:[cm]?[jt]sx?)$/i;
2596
2899
  var NODE_BUILTIN_SET = new Set(
2597
2900
  import_node_module.builtinModules.flatMap((name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`])
2598
2901
  );
2902
+ function assertValidExportName(exportName) {
2903
+ if (exportName === "default") return;
2904
+ if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(exportName)) {
2905
+ throw new Error(
2906
+ `Invalid play export name "${exportName}". Named prebuilt exports must be valid JavaScript identifiers.`
2907
+ );
2908
+ }
2909
+ }
2599
2910
  function sha256(value) {
2600
2911
  return (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
2601
2912
  }
@@ -2603,56 +2914,6 @@ function formatEsbuildMessage(message) {
2603
2914
  const location = message.location ? `${message.location.file}:${message.location.line}:${message.location.column}` : null;
2604
2915
  return location ? `${location} ${message.text}` : message.text;
2605
2916
  }
2606
- function formatTypeScriptDiagnostic(diagnostic) {
2607
- if (!diagnostic.file) {
2608
- const message2 = import_typescript.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n").trim();
2609
- return message2 || null;
2610
- }
2611
- const start = diagnostic.start ?? 0;
2612
- const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(start);
2613
- const message = import_typescript.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n").trim();
2614
- if (!message) {
2615
- return null;
2616
- }
2617
- return `${diagnostic.file.fileName}:${line + 1}:${character + 1} ${message}`;
2618
- }
2619
- function resolveBundledTypeRoots() {
2620
- try {
2621
- return [(0, import_node_path5.dirname)((0, import_node_path5.dirname)(playArtifactRequire.resolve("@types/node/package.json")))];
2622
- } catch {
2623
- return [];
2624
- }
2625
- }
2626
- function typecheckPlaySource(input, adapter) {
2627
- const rootNames = Array.from(
2628
- /* @__PURE__ */ new Set([
2629
- ...input.importPolicy.localFiles,
2630
- ...input.importedPlayDependencies.map((dependency) => dependency.filePath)
2631
- ])
2632
- );
2633
- const sdkTypesPath = adapter.sdkTypesEntryFile ?? adapter.sdkEntryFile;
2634
- const program = import_typescript.default.createProgram(rootNames, {
2635
- target: import_typescript.default.ScriptTarget.ES2023,
2636
- // SDK source uses fetch/RequestInit/URL and node-aware config helpers.
2637
- // The play runtime import policy below still bans Node modules from play
2638
- // source for workers_edge bundles.
2639
- lib: ["lib.es2023.d.ts", "lib.dom.d.ts"],
2640
- module: import_typescript.default.ModuleKind.ESNext,
2641
- moduleResolution: import_typescript.default.ModuleResolutionKind.Bundler,
2642
- paths: { deepline: [sdkTypesPath] },
2643
- strict: true,
2644
- skipLibCheck: true,
2645
- noEmit: true,
2646
- esModuleInterop: true,
2647
- allowSyntheticDefaultImports: true,
2648
- allowImportingTsExtensions: true,
2649
- allowJs: true,
2650
- resolveJsonModule: true,
2651
- types: ["node"],
2652
- typeRoots: resolveBundledTypeRoots()
2653
- });
2654
- return import_typescript.default.getPreEmitDiagnostics(program).map(formatTypeScriptDiagnostic).filter((message) => Boolean(message));
2655
- }
2656
2917
  function isLocalSpecifier(specifier) {
2657
2918
  return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/") || specifier.startsWith("file:");
2658
2919
  }
@@ -2676,11 +2937,8 @@ function assertWithinPlayWorkspace(input) {
2676
2937
  if (isPathInsideDirectory(input.resolvedPath, input.workspace.rootDir)) {
2677
2938
  return;
2678
2939
  }
2679
- const position = input.sourceFile.getLineAndCharacterOfPosition(
2680
- input.node.getStart(input.sourceFile)
2681
- );
2682
2940
  throw new Error(
2683
- `${input.importer}:${position.line + 1}:${position.character + 1} Local play imports must stay inside the play workspace (${input.workspace.rootDir}). Import "${input.specifier}" resolved to ${input.resolvedPath}, which crosses into app/backend code. Use the public SDK/API surface or move shared helpers into the play workspace.`
2941
+ `${input.importer}:${input.line}:${input.column} Local play imports must stay inside the play workspace (${input.workspace.rootDir}). Import "${input.specifier}" resolved to ${input.resolvedPath}, which crosses into app/backend code. Use the public SDK/API surface or move shared helpers into the play workspace.`
2684
2942
  );
2685
2943
  }
2686
2944
  function getPackageName(specifier) {
@@ -2690,72 +2948,135 @@ function getPackageName(specifier) {
2690
2948
  }
2691
2949
  return specifier.split("/")[0] ?? specifier;
2692
2950
  }
2693
- function scriptKindForFile(filePath) {
2694
- const extension = (0, import_node_path5.extname)(filePath).toLowerCase();
2695
- switch (extension) {
2696
- case ".tsx":
2697
- return import_typescript.default.ScriptKind.TSX;
2698
- case ".jsx":
2699
- return import_typescript.default.ScriptKind.JSX;
2700
- case ".js":
2701
- case ".mjs":
2702
- case ".cjs":
2703
- return import_typescript.default.ScriptKind.JS;
2704
- case ".json":
2705
- return import_typescript.default.ScriptKind.JSON;
2706
- default:
2707
- return import_typescript.default.ScriptKind.TS;
2708
- }
2709
- }
2710
2951
  function isPlaySourceFile(filePath) {
2711
2952
  return PLAY_SOURCE_FILE_PATTERN.test(filePath);
2712
2953
  }
2713
- function extractStringLiteralProperty(objectLiteral, propertyName) {
2714
- for (const property of objectLiteral.properties) {
2715
- if (!import_typescript.default.isPropertyAssignment(property)) {
2954
+ function stripCommentsToSpaces(source) {
2955
+ return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
2956
+ /(^|[^:])\/\/.*$/gm,
2957
+ (match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
2958
+ );
2959
+ }
2960
+ function lineAndColumnAt(source, index) {
2961
+ const prefix = source.slice(0, index);
2962
+ const lines = prefix.split("\n");
2963
+ return { line: lines.length, column: lines[lines.length - 1].length + 1 };
2964
+ }
2965
+ function findSourceImportReferences(sourceCode) {
2966
+ const source = stripCommentsToSpaces(sourceCode);
2967
+ const references = [];
2968
+ const addReference = (specifier, specifierIndex, kind) => {
2969
+ if (!specifier) return;
2970
+ const position = lineAndColumnAt(sourceCode, specifierIndex);
2971
+ references.push({
2972
+ specifier,
2973
+ line: position.line,
2974
+ column: position.column,
2975
+ kind
2976
+ });
2977
+ };
2978
+ const staticImportPattern = /\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?(['"])([^'"\n]+)\1/g;
2979
+ for (const match of source.matchAll(staticImportPattern)) {
2980
+ addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "static");
2981
+ }
2982
+ const dynamicImportPattern = /\bimport\s*\(\s*(['"])([^'"\n]+)\1/g;
2983
+ for (const match of source.matchAll(dynamicImportPattern)) {
2984
+ addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "dynamic-import");
2985
+ }
2986
+ const requirePattern = /\brequire\s*\(\s*(['"])([^'"\n]+)\1/g;
2987
+ for (const match of source.matchAll(requirePattern)) {
2988
+ addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "require");
2989
+ }
2990
+ const literalDynamicImportIndexes = new Set(
2991
+ [...source.matchAll(dynamicImportPattern)].map((match) => match.index)
2992
+ );
2993
+ for (const match of source.matchAll(/\bimport\s*\(/g)) {
2994
+ if (literalDynamicImportIndexes.has(match.index)) continue;
2995
+ const position = lineAndColumnAt(sourceCode, match.index);
2996
+ throw new Error(
2997
+ `:${position.line}:${position.column} Dynamic import() is not allowed in plays. Use static imports instead.`
2998
+ );
2999
+ }
3000
+ const literalRequireIndexes = new Set(
3001
+ [...source.matchAll(requirePattern)].map((match) => match.index)
3002
+ );
3003
+ for (const match of source.matchAll(/\brequire\s*\(/g)) {
3004
+ if (literalRequireIndexes.has(match.index)) continue;
3005
+ const position = lineAndColumnAt(sourceCode, match.index);
3006
+ throw new Error(
3007
+ `:${position.line}:${position.column} Dynamic require() is not allowed in plays. Use static imports or require("literal") only.`
3008
+ );
3009
+ }
3010
+ return references.sort(
3011
+ (left, right) => left.line === right.line ? left.column - right.column : left.line - right.line
3012
+ );
3013
+ }
3014
+ function unquoteStringLiteral(literal) {
3015
+ const trimmed = literal.trim();
3016
+ const quote = trimmed[0];
3017
+ if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
3018
+ return null;
3019
+ }
3020
+ try {
3021
+ return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
3022
+ } catch {
3023
+ return trimmed.slice(1, -1);
3024
+ }
3025
+ }
3026
+ function findMatchingBrace(source, openIndex) {
3027
+ let depth = 0;
3028
+ let quote = null;
3029
+ let escaped = false;
3030
+ for (let index = openIndex; index < source.length; index += 1) {
3031
+ const char = source[index];
3032
+ if (quote) {
3033
+ if (escaped) {
3034
+ escaped = false;
3035
+ } else if (char === "\\") {
3036
+ escaped = true;
3037
+ } else if (char === quote) {
3038
+ quote = null;
3039
+ }
2716
3040
  continue;
2717
3041
  }
2718
- const name = property.name;
2719
- const matches = import_typescript.default.isIdentifier(name) && name.text === propertyName || import_typescript.default.isStringLiteralLike(name) && name.text === propertyName;
2720
- if (!matches) {
3042
+ if (char === '"' || char === "'" || char === "`") {
3043
+ quote = char;
2721
3044
  continue;
2722
3045
  }
2723
- return import_typescript.default.isStringLiteralLike(property.initializer) ? property.initializer.text.trim() : null;
2724
- }
2725
- return null;
2726
- }
2727
- function extractDefinedPlayName(sourceCode, filePath) {
2728
- const sourceFile = import_typescript.default.createSourceFile(
2729
- filePath,
2730
- sourceCode,
2731
- import_typescript.default.ScriptTarget.Latest,
2732
- true,
2733
- scriptKindForFile(filePath)
2734
- );
2735
- let detectedPlayName = null;
2736
- const visit = (node) => {
2737
- if (detectedPlayName) {
2738
- return;
2739
- }
2740
- if (import_typescript.default.isCallExpression(node)) {
2741
- const expression = node.expression;
2742
- const isDefinePlayCall = import_typescript.default.isIdentifier(expression) && (expression.text === "definePlay" || expression.text === "defineWorkflow") || import_typescript.default.isPropertyAccessExpression(expression) && (expression.name.text === "definePlay" || expression.name.text === "defineWorkflow");
2743
- if (isDefinePlayCall) {
2744
- const firstArgument = node.arguments[0];
2745
- if (firstArgument && import_typescript.default.isStringLiteralLike(firstArgument)) {
2746
- detectedPlayName = firstArgument.text.trim() || null;
2747
- return;
2748
- }
2749
- if (firstArgument && import_typescript.default.isObjectLiteralExpression(firstArgument)) {
2750
- detectedPlayName = extractStringLiteralProperty(firstArgument, "id");
2751
- return;
2752
- }
3046
+ if (char === "{") depth += 1;
3047
+ if (char === "}") {
3048
+ depth -= 1;
3049
+ if (depth === 0) return index;
3050
+ }
3051
+ }
3052
+ return -1;
3053
+ }
3054
+ function extractDefinedPlayName(sourceCode, _filePath) {
3055
+ const source = stripCommentsToSpaces(sourceCode);
3056
+ const callPattern = /(?:\b[A-Za-z_$][\w$]*\s*\.\s*)?\b(?:definePlay|defineWorkflow)\s*\(/g;
3057
+ for (const match of source.matchAll(callPattern)) {
3058
+ const openParen = match.index + match[0].length - 1;
3059
+ const firstArgStart = openParen + 1;
3060
+ const firstNonSpace = source.slice(firstArgStart).search(/\S/);
3061
+ if (firstNonSpace < 0) continue;
3062
+ const argIndex = firstArgStart + firstNonSpace;
3063
+ const quote = source[argIndex];
3064
+ if (quote === '"' || quote === "'") {
3065
+ const literalMatch = source.slice(argIndex).match(/^(['"])(?:\\.|(?!\1)[\s\S])*\1/);
3066
+ const value = literalMatch ? unquoteStringLiteral(literalMatch[0]) : null;
3067
+ if (value?.trim()) return value.trim();
3068
+ }
3069
+ if (quote === "{") {
3070
+ const closeBrace = findMatchingBrace(source, argIndex);
3071
+ if (closeBrace < 0) continue;
3072
+ const objectSource = source.slice(argIndex + 1, closeBrace);
3073
+ const idMatch = objectSource.match(/(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/);
3074
+ if (idMatch?.[2]?.trim()) {
3075
+ return idMatch[2].trim();
2753
3076
  }
2754
3077
  }
2755
- import_typescript.default.forEachChild(node, visit);
2756
- };
2757
- visit(sourceFile);
2758
- return detectedPlayName;
3078
+ }
3079
+ return null;
2759
3080
  }
2760
3081
  function getPackageRequireCandidates(fromFile) {
2761
3082
  const candidates = [
@@ -2790,6 +3111,29 @@ function workersPlayEntryAliasPlugin(playFilePath) {
2790
3111
  }
2791
3112
  };
2792
3113
  }
3114
+ function workersNamedPlayEntryAliasPlugin(playFilePath, exportName) {
3115
+ return {
3116
+ name: "deepline-workers-named-play-entry-alias",
3117
+ setup(buildContext) {
3118
+ buildContext.onResolve(
3119
+ { filter: new RegExp(`^${WORKERS_PLAY_ENTRY_VIRTUAL}$`) },
3120
+ () => ({
3121
+ path: `${playFilePath}.${exportName}.entry.ts`,
3122
+ namespace: "deepline-named-play-entry"
3123
+ })
3124
+ );
3125
+ buildContext.onLoad(
3126
+ { filter: /.*/, namespace: "deepline-named-play-entry" },
3127
+ () => ({
3128
+ contents: `export { ${exportName} as default } from ${JSON.stringify(playFilePath)};
3129
+ `,
3130
+ loader: "ts",
3131
+ resolveDir: (0, import_node_path5.dirname)(playFilePath)
3132
+ })
3133
+ );
3134
+ }
3135
+ };
3136
+ }
2793
3137
  function workersNodeBuiltinStubPlugin() {
2794
3138
  const UNSUPPORTED = /* @__PURE__ */ new Set(["node:fs", "node:fs/promises", "node:os", "node:child_process"]);
2795
3139
  return {
@@ -3042,18 +3386,10 @@ async function analyzeSourceGraph(entryFile, adapter) {
3042
3386
  if ((0, import_node_path5.extname)(absolutePath).toLowerCase() === ".json") {
3043
3387
  return;
3044
3388
  }
3045
- const sourceFile = import_typescript.default.createSourceFile(
3046
- absolutePath,
3047
- sourceCode2,
3048
- import_typescript.default.ScriptTarget.Latest,
3049
- true,
3050
- scriptKindForFile(absolutePath)
3051
- );
3052
- const handleSpecifier = async (specifier, node, kind) => {
3389
+ const handleSpecifier = async (specifier, line, column, kind) => {
3053
3390
  if (kind === "dynamic-import") {
3054
- const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
3055
3391
  throw new Error(
3056
- `${absolutePath}:${position.line + 1}:${position.character + 1} Dynamic import() is not allowed in plays. Use static imports instead.`
3392
+ `${absolutePath}:${line}:${column} Dynamic import() is not allowed in plays. Use static imports instead.`
3057
3393
  );
3058
3394
  }
3059
3395
  if (NODE_BUILTIN_SET.has(specifier)) {
@@ -3067,16 +3403,15 @@ async function analyzeSourceGraph(entryFile, adapter) {
3067
3403
  specifier,
3068
3404
  resolvedPath: resolved,
3069
3405
  workspace,
3070
- sourceFile,
3071
- node
3406
+ line,
3407
+ column
3072
3408
  });
3073
3409
  if (resolved !== absoluteEntryFile && isPlaySourceFile(resolved)) {
3074
3410
  const importedSource = await (0, import_promises2.readFile)(resolved, "utf-8");
3075
3411
  const importedPlayName = extractDefinedPlayName(importedSource, resolved);
3076
3412
  if (!importedPlayName) {
3077
- const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
3078
3413
  throw new Error(
3079
- `${absolutePath}:${position.line + 1}:${position.character + 1} Imported play file "${specifier}" must export definePlay(...) so it can be runtime-composed.`
3414
+ `${absolutePath}:${line}:${column} Imported play file "${specifier}" must export definePlay(...) so it can be runtime-composed.`
3080
3415
  );
3081
3416
  }
3082
3417
  importedPlayDependencies.set(resolved, {
@@ -3089,44 +3424,28 @@ async function analyzeSourceGraph(entryFile, adapter) {
3089
3424
  return;
3090
3425
  }
3091
3426
  if (specifier.includes(":")) {
3092
- const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
3093
3427
  throw new Error(
3094
- `${absolutePath}:${position.line + 1}:${position.character + 1} Unsupported import specifier "${specifier}". Allowed imports are relative files, Node builtins, and installed packages.`
3428
+ `${absolutePath}:${line}:${column} Unsupported import specifier "${specifier}". Allowed imports are relative files, Node builtins, and installed packages.`
3095
3429
  );
3096
3430
  }
3097
3431
  const packageImport = resolvePackageImport(specifier, absolutePath, adapter);
3098
3432
  packages.set(packageImport.name, packageImport.version);
3099
3433
  };
3100
- const walk = async (node) => {
3101
- if ((import_typescript.default.isImportDeclaration(node) || import_typescript.default.isExportDeclaration(node)) && node.moduleSpecifier && !(import_typescript.default.isImportDeclaration(node) && node.importClause?.isTypeOnly || import_typescript.default.isExportDeclaration(node) && node.isTypeOnly) && import_typescript.default.isStringLiteralLike(node.moduleSpecifier)) {
3102
- await handleSpecifier(node.moduleSpecifier.text, node, "static");
3103
- }
3104
- if (import_typescript.default.isCallExpression(node)) {
3105
- if (node.expression.kind === import_typescript.default.SyntaxKind.ImportKeyword) {
3106
- if (node.arguments.length !== 1 || !import_typescript.default.isStringLiteralLike(node.arguments[0])) {
3107
- const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
3108
- throw new Error(
3109
- `${absolutePath}:${position.line + 1}:${position.character + 1} Dynamic import() is not allowed in plays. Use static imports instead.`
3110
- );
3111
- }
3112
- await handleSpecifier(node.arguments[0].text, node, "dynamic-import");
3113
- }
3114
- if (import_typescript.default.isIdentifier(node.expression) && node.expression.text === "require") {
3115
- const firstArgument = node.arguments[0];
3116
- if (node.arguments.length !== 1 || !firstArgument || !import_typescript.default.isStringLiteralLike(firstArgument)) {
3117
- const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
3118
- throw new Error(
3119
- `${absolutePath}:${position.line + 1}:${position.character + 1} Dynamic require() is not allowed in plays. Use static imports or require("literal") only.`
3120
- );
3121
- }
3122
- await handleSpecifier(firstArgument.text, node, "require");
3123
- }
3434
+ try {
3435
+ for (const reference of findSourceImportReferences(sourceCode2)) {
3436
+ await handleSpecifier(
3437
+ reference.specifier,
3438
+ reference.line,
3439
+ reference.column,
3440
+ reference.kind
3441
+ );
3124
3442
  }
3125
- for (const child of node.getChildren(sourceFile)) {
3126
- await walk(child);
3443
+ } catch (error) {
3444
+ if (error instanceof Error && error.message.startsWith(":")) {
3445
+ throw new Error(`${absolutePath}${error.message}`);
3127
3446
  }
3128
- };
3129
- await walk(sourceFile);
3447
+ throw error;
3448
+ }
3130
3449
  };
3131
3450
  await visitFile(absoluteEntryFile);
3132
3451
  const sourceCode = localFiles.get(absoluteEntryFile) ?? "";
@@ -3146,6 +3465,11 @@ async function analyzeSourceGraph(entryFile, adapter) {
3146
3465
  const playName = extractDefinedPlayName(sourceCode, absoluteEntryFile);
3147
3466
  return {
3148
3467
  sourceCode,
3468
+ sourceFiles: Object.fromEntries(
3469
+ [...localFiles.entries()].sort(
3470
+ (left, right) => left[0].localeCompare(right[0])
3471
+ )
3472
+ ),
3149
3473
  sourceHash,
3150
3474
  graphHash,
3151
3475
  importPolicy: {
@@ -3209,8 +3533,7 @@ function normalizeSourceMapForRuntime(sourceMapText) {
3209
3533
  parsed.sourceRoot = void 0;
3210
3534
  return JSON.stringify(parsed);
3211
3535
  }
3212
- function getBundleSizeError(filePath, bundledCode, artifactKind) {
3213
- const bundleBytes = Buffer.byteLength(bundledCode, "utf8");
3536
+ function getBundleSizeErrorForBytes(filePath, bundleBytes, artifactKind) {
3214
3537
  if (bundleBytes > MAX_PLAY_BUNDLE_BYTES) {
3215
3538
  return `${filePath} Play bundle exceeds the 30 MiB limit (${bundleBytes} bytes > ${MAX_PLAY_BUNDLE_BYTES} bytes).`;
3216
3539
  }
@@ -3221,11 +3544,27 @@ function getBundleSizeError(filePath, bundledCode, artifactKind) {
3221
3544
  }
3222
3545
  return null;
3223
3546
  }
3224
- async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter) {
3547
+ function getBundleSizeError(filePath, bundledCode, artifactKind) {
3548
+ return getBundleSizeErrorForBytes(
3549
+ filePath,
3550
+ Buffer.byteLength(bundledCode, "utf8"),
3551
+ artifactKind
3552
+ );
3553
+ }
3554
+ async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter, exportName) {
3225
3555
  const sdkAliasPlugin = localSdkAliasPlugin(adapter);
3226
3556
  const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
3557
+ const namedExportShim = exportName === "default" ? null : `export { ${exportName} as default } from ${JSON.stringify(entryFile)};
3558
+ `;
3227
3559
  const result = await (0, import_esbuild.build)({
3228
- entryPoints: [entryFile],
3560
+ ...namedExportShim ? {
3561
+ stdin: {
3562
+ contents: namedExportShim,
3563
+ resolveDir: (0, import_node_path5.dirname)(entryFile),
3564
+ sourcefile: `${(0, import_node_path5.basename)(entryFile)}.${exportName}.entry.ts`,
3565
+ loader: "ts"
3566
+ }
3567
+ } : { entryPoints: [entryFile] },
3229
3568
  absWorkingDir: adapter.projectRoot,
3230
3569
  bundle: true,
3231
3570
  format: "cjs",
@@ -3253,10 +3592,10 @@ async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter
3253
3592
  outputExtension: "cjs"
3254
3593
  };
3255
3594
  }
3256
- async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter) {
3595
+ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter, exportName) {
3257
3596
  const sdkAliasPlugin = localSdkAliasPlugin(adapter, { workersRuntime: true });
3258
3597
  const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
3259
- const playEntryAlias = workersPlayEntryAliasPlugin(playEntryFile);
3598
+ const playEntryAlias = exportName === "default" ? workersPlayEntryAliasPlugin(playEntryFile) : workersNamedPlayEntryAliasPlugin(playEntryFile, exportName);
3260
3599
  const result = await (0, import_esbuild.build)({
3261
3600
  // Entry is the Workers harness; it imports the play via the virtual
3262
3601
  // `deepline-play-entry` alias resolved by workersPlayEntryAliasPlugin.
@@ -3327,10 +3666,16 @@ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies,
3327
3666
  async function bundlePlayFile(filePath, options) {
3328
3667
  const adapter = options.adapter;
3329
3668
  const target = options.target ?? PLAY_ARTIFACT_KINDS.cjsNode20;
3669
+ const exportName = options.exportName?.trim() || "default";
3670
+ assertValidExportName(exportName);
3330
3671
  const absolutePath = await normalizeLocalPath(filePath);
3331
3672
  adapter.warnAboutNonDevelopmentBundling?.(absolutePath);
3332
3673
  try {
3333
3674
  const analysis = await analyzeSourceGraph(absolutePath, adapter);
3675
+ analysis.graphHash = sha256(
3676
+ `${analysis.graphHash}
3677
+ entry-export:${exportName}`
3678
+ );
3334
3679
  if (target === PLAY_ARTIFACT_KINDS.esmWorkers) {
3335
3680
  const harnessFingerprint = await computeWorkersHarnessFingerprintWithAdapter(adapter);
3336
3681
  analysis.graphHash = sha256(
@@ -3338,6 +3683,23 @@ async function bundlePlayFile(filePath, options) {
3338
3683
  workers-harness:${harnessFingerprint}`
3339
3684
  );
3340
3685
  }
3686
+ const typecheckErrors = [
3687
+ ...await adapter.typecheckPlaySource?.({
3688
+ sourceCode: analysis.sourceCode,
3689
+ sourcePath: absolutePath,
3690
+ importedFilePaths: [
3691
+ ...analysis.importPolicy.localFiles,
3692
+ ...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
3693
+ ]
3694
+ }) ?? []
3695
+ ];
3696
+ if (typecheckErrors.length > 0) {
3697
+ return {
3698
+ success: false,
3699
+ filePath: absolutePath,
3700
+ errors: typecheckErrors
3701
+ };
3702
+ }
3341
3703
  const cachedArtifact = await readArtifactCache(analysis.graphHash, target, adapter);
3342
3704
  const discoveredFiles = await adapter.discoverPackagedLocalFiles(absolutePath);
3343
3705
  if (cachedArtifact) {
@@ -3357,6 +3719,7 @@ workers-harness:${harnessFingerprint}`
3357
3719
  success: true,
3358
3720
  artifact: { ...cachedArtifact, cacheHit: true },
3359
3721
  sourceCode: analysis.sourceCode,
3722
+ sourceFiles: analysis.sourceFiles,
3360
3723
  filePath: absolutePath,
3361
3724
  playName: analysis.playName,
3362
3725
  packagedFiles: discoveredFiles.files,
@@ -3364,25 +3727,7 @@ workers-harness:${harnessFingerprint}`
3364
3727
  importedPlayDependencies: analysis.importedPlayDependencies
3365
3728
  };
3366
3729
  }
3367
- const typecheckErrors = [
3368
- ...adapter.typecheckSdkTypes === false ? [] : typecheckPlaySource(analysis, adapter),
3369
- ...await adapter.typecheckPlaySource?.({
3370
- sourceCode: analysis.sourceCode,
3371
- sourcePath: absolutePath,
3372
- importedFilePaths: [
3373
- ...analysis.importPolicy.localFiles,
3374
- ...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
3375
- ]
3376
- }) ?? []
3377
- ];
3378
- if (typecheckErrors.length > 0) {
3379
- return {
3380
- success: false,
3381
- filePath: absolutePath,
3382
- errors: typecheckErrors
3383
- };
3384
- }
3385
- const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter);
3730
+ const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
3386
3731
  if (Array.isArray(buildOutcome)) {
3387
3732
  return {
3388
3733
  success: false,
@@ -3392,7 +3737,8 @@ workers-harness:${harnessFingerprint}`
3392
3737
  }
3393
3738
  const { bundledCode, sourceMapText, outputExtension } = buildOutcome;
3394
3739
  const normalizedSourceMap = normalizeSourceMapForRuntime(sourceMapText);
3395
- const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${(0, import_node_path5.basename)(absolutePath).replace(/\.[^.]+$/, "")}.${outputExtension}`;
3740
+ const virtualBaseName = exportName === "default" ? (0, import_node_path5.basename)(absolutePath).replace(/\.[^.]+$/, "") : `${(0, import_node_path5.basename)(absolutePath).replace(/\.[^.]+$/, "")}.${exportName}`;
3741
+ const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${virtualBaseName}.${outputExtension}`;
3396
3742
  const executableCode = `${bundledCode}
3397
3743
  //# sourceMappingURL=${(0, import_node_path5.basename)(virtualFilename)}.map
3398
3744
  `;
@@ -3430,6 +3776,7 @@ workers-harness:${harnessFingerprint}`
3430
3776
  success: true,
3431
3777
  artifact,
3432
3778
  sourceCode: analysis.sourceCode,
3779
+ sourceFiles: analysis.sourceFiles,
3433
3780
  filePath: absolutePath,
3434
3781
  playName: analysis.playName,
3435
3782
  packagedFiles: discoveredFiles.files,
@@ -3511,7 +3858,6 @@ function resolveExecutionProfile(override) {
3511
3858
  var import_node_crypto2 = require("crypto");
3512
3859
  var import_promises3 = require("fs/promises");
3513
3860
  var import_node_path6 = require("path");
3514
- var import_typescript2 = __toESM(require("typescript"));
3515
3861
  var SOURCE_EXTENSIONS2 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
3516
3862
  function sha2562(buffer) {
3517
3863
  return (0, import_node_crypto2.createHash)("sha256").update(buffer).digest("hex");
@@ -3523,94 +3869,181 @@ function contentTypeForFile(filePath) {
3523
3869
  if (extension === ".txt") return "text/plain";
3524
3870
  return "application/octet-stream";
3525
3871
  }
3526
- function isCtxCsvCall(node) {
3527
- if (!import_typescript2.default.isPropertyAccessExpression(node.expression)) {
3528
- return false;
3529
- }
3530
- const target = node.expression.expression;
3531
- return import_typescript2.default.isIdentifier(target) && (target.text === "ctx" || target.text.endsWith("Ctx")) && node.expression.name.text === "csv";
3532
- }
3533
- function extractSourceFragment(source, node) {
3534
- return source.slice(node.getStart(), node.getEnd()).trim();
3535
- }
3536
- function referencesInputIdentifier(node) {
3537
- if (import_typescript2.default.isIdentifier(node) && node.text === "input") {
3538
- return true;
3539
- }
3540
- return node.getChildren().some((child) => referencesInputIdentifier(child));
3872
+ function stripCommentsToSpaces2(source) {
3873
+ return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
3874
+ /(^|[^:])\/\/.*$/gm,
3875
+ (match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
3876
+ );
3541
3877
  }
3542
- function isRuntimeInputExpression(node) {
3543
- if (import_typescript2.default.isPropertyAccessExpression(node)) {
3544
- return import_typescript2.default.isIdentifier(node.expression) && node.expression.text === "input";
3545
- }
3546
- if (import_typescript2.default.isElementAccessExpression(node)) {
3547
- return import_typescript2.default.isIdentifier(node.expression) && node.expression.text === "input";
3548
- }
3549
- if (import_typescript2.default.isIdentifier(node)) {
3550
- return node.text === "input";
3551
- }
3552
- if (import_typescript2.default.isParenthesizedExpression(node)) {
3553
- return isRuntimeInputExpression(node.expression);
3878
+ function unquoteStringLiteral2(literal) {
3879
+ const trimmed = literal.trim();
3880
+ const quote = trimmed[0];
3881
+ if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
3882
+ return null;
3554
3883
  }
3555
- if (import_typescript2.default.isBinaryExpression(node) && (node.operatorToken.kind === import_typescript2.default.SyntaxKind.QuestionQuestionToken || node.operatorToken.kind === import_typescript2.default.SyntaxKind.BarBarToken)) {
3556
- return isRuntimeInputExpression(node.left) || isRuntimeInputExpression(node.right);
3884
+ try {
3885
+ return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
3886
+ } catch {
3887
+ return trimmed.slice(1, -1);
3557
3888
  }
3558
- if (import_typescript2.default.isConditionalExpression(node)) {
3559
- return isRuntimeInputExpression(node.condition) || isRuntimeInputExpression(node.whenTrue) || isRuntimeInputExpression(node.whenFalse);
3889
+ }
3890
+ function splitTopLevelPlus(expression) {
3891
+ const parts = [];
3892
+ let start = 0;
3893
+ let depth = 0;
3894
+ let quote = null;
3895
+ let escaped = false;
3896
+ for (let index = 0; index < expression.length; index += 1) {
3897
+ const char = expression[index];
3898
+ if (quote) {
3899
+ if (escaped) {
3900
+ escaped = false;
3901
+ } else if (char === "\\") {
3902
+ escaped = true;
3903
+ } else if (char === quote) {
3904
+ quote = null;
3905
+ }
3906
+ continue;
3907
+ }
3908
+ if (char === '"' || char === "'" || char === "`") {
3909
+ quote = char;
3910
+ continue;
3911
+ }
3912
+ if (char === "(" || char === "[" || char === "{") depth += 1;
3913
+ if (char === ")" || char === "]" || char === "}") depth -= 1;
3914
+ if (char === "+" && depth === 0) {
3915
+ parts.push(expression.slice(start, index));
3916
+ start = index + 1;
3917
+ }
3560
3918
  }
3561
- return referencesInputIdentifier(node);
3919
+ if (parts.length === 0) return null;
3920
+ parts.push(expression.slice(start));
3921
+ return parts;
3562
3922
  }
3563
- function resolveStringExpression(node, constants) {
3564
- if (import_typescript2.default.isStringLiteralLike(node) || import_typescript2.default.isNoSubstitutionTemplateLiteral(node)) {
3565
- return node.text;
3923
+ function stripOuterParens(expression) {
3924
+ let value = expression.trim();
3925
+ while (value.startsWith("(") && value.endsWith(")")) {
3926
+ value = value.slice(1, -1).trim();
3566
3927
  }
3567
- if (import_typescript2.default.isParenthesizedExpression(node)) {
3568
- return resolveStringExpression(node.expression, constants);
3928
+ return value;
3929
+ }
3930
+ function isRuntimeInputExpression(expression) {
3931
+ return /(^|[^\w$])input([^\w$]|$)/.test(expression);
3932
+ }
3933
+ function resolveStringExpression(expression, constants) {
3934
+ const value = stripOuterParens(expression);
3935
+ if (/^(['"])(?:\\.|(?!\1)[\s\S])*\1$/.test(value)) {
3936
+ return unquoteStringLiteral2(value);
3569
3937
  }
3570
- if (import_typescript2.default.isIdentifier(node)) {
3571
- return constants.get(node.text) ?? null;
3938
+ if (/^`(?:\\.|[^`$]|\$(?!\{))*`$/.test(value)) {
3939
+ return value.slice(1, -1);
3572
3940
  }
3573
- if (import_typescript2.default.isTemplateExpression(node)) {
3574
- let value = node.head.text;
3575
- for (const span of node.templateSpans) {
3576
- const resolved = resolveStringExpression(span.expression, constants);
3577
- if (resolved == null) {
3578
- return null;
3579
- }
3580
- value += resolved + span.literal.text;
3581
- }
3582
- return value;
3941
+ if (/^[A-Za-z_$][\w$]*$/.test(value)) {
3942
+ return constants.get(value) ?? null;
3583
3943
  }
3584
- if (import_typescript2.default.isBinaryExpression(node) && node.operatorToken.kind === import_typescript2.default.SyntaxKind.PlusToken) {
3585
- const left = resolveStringExpression(node.left, constants);
3586
- const right = resolveStringExpression(node.right, constants);
3587
- if (left == null || right == null) {
3588
- return null;
3589
- }
3590
- return left + right;
3944
+ const parts = splitTopLevelPlus(value);
3945
+ if (parts) {
3946
+ const resolved = parts.map((part) => resolveStringExpression(part, constants));
3947
+ return resolved.every((part) => part != null) ? resolved.join("") : null;
3591
3948
  }
3592
3949
  return null;
3593
3950
  }
3594
- function collectTopLevelStringConstants(sourceFile) {
3951
+ function collectTopLevelStringConstants(sourceCode) {
3595
3952
  const constants = /* @__PURE__ */ new Map();
3596
- for (const statement of sourceFile.statements) {
3597
- if (!import_typescript2.default.isVariableStatement(statement)) {
3953
+ const source = stripCommentsToSpaces2(sourceCode);
3954
+ for (const match of source.matchAll(/(?:^|\n)\s*const\s+([A-Za-z_$][\w$]*)\s*=\s*([^;\n]+)/g)) {
3955
+ const resolved = resolveStringExpression(match[2], constants);
3956
+ if (resolved != null) {
3957
+ constants.set(match[1], resolved);
3958
+ }
3959
+ }
3960
+ return constants;
3961
+ }
3962
+ function findMatchingGenericEnd(source, openIndex) {
3963
+ let depth = 0;
3964
+ let quote = null;
3965
+ let escaped = false;
3966
+ for (let index = openIndex; index < source.length; index += 1) {
3967
+ const char = source[index];
3968
+ if (quote) {
3969
+ if (escaped) {
3970
+ escaped = false;
3971
+ } else if (char === "\\") {
3972
+ escaped = true;
3973
+ } else if (char === quote) {
3974
+ quote = null;
3975
+ }
3598
3976
  continue;
3599
3977
  }
3600
- if (!(statement.declarationList.flags & import_typescript2.default.NodeFlags.Const)) {
3978
+ if (char === '"' || char === "'" || char === "`") {
3979
+ quote = char;
3601
3980
  continue;
3602
3981
  }
3603
- for (const declaration of statement.declarationList.declarations) {
3604
- if (!import_typescript2.default.isIdentifier(declaration.name) || !declaration.initializer) {
3605
- continue;
3606
- }
3607
- const resolved = resolveStringExpression(declaration.initializer, constants);
3608
- if (resolved != null) {
3609
- constants.set(declaration.name.text, resolved);
3982
+ if (char === "<") depth += 1;
3983
+ if (char === ">") {
3984
+ depth -= 1;
3985
+ if (depth === 0) return index;
3986
+ }
3987
+ }
3988
+ return -1;
3989
+ }
3990
+ function findCallOpenParen(source, afterCsvIndex) {
3991
+ let index = afterCsvIndex;
3992
+ while (/\s/.test(source[index] ?? "")) index += 1;
3993
+ if (source[index] === "<") {
3994
+ const genericEnd = findMatchingGenericEnd(source, index);
3995
+ if (genericEnd < 0) return -1;
3996
+ index = genericEnd + 1;
3997
+ while (/\s/.test(source[index] ?? "")) index += 1;
3998
+ }
3999
+ return source[index] === "(" ? index : -1;
4000
+ }
4001
+ function firstCallArgument(source, openParen) {
4002
+ let depth = 0;
4003
+ let quote = null;
4004
+ let escaped = false;
4005
+ const start = openParen + 1;
4006
+ for (let index = start; index < source.length; index += 1) {
4007
+ const char = source[index];
4008
+ if (quote) {
4009
+ if (escaped) {
4010
+ escaped = false;
4011
+ } else if (char === "\\") {
4012
+ escaped = true;
4013
+ } else if (char === quote) {
4014
+ quote = null;
3610
4015
  }
4016
+ continue;
4017
+ }
4018
+ if (char === '"' || char === "'" || char === "`") {
4019
+ quote = char;
4020
+ continue;
4021
+ }
4022
+ if (char === "(" || char === "[" || char === "{") depth += 1;
4023
+ if (char === ")" && depth === 0) {
4024
+ const text = source.slice(start, index).trim();
4025
+ return text ? { text, start, end: index } : null;
3611
4026
  }
4027
+ if (char === "," && depth === 0) {
4028
+ const text = source.slice(start, index).trim();
4029
+ return text ? { text, start, end: index } : null;
4030
+ }
4031
+ if (char === ")" || char === "]" || char === "}") depth -= 1;
3612
4032
  }
3613
- return constants;
4033
+ return null;
4034
+ }
4035
+ function localImportSpecifiers(sourceCode) {
4036
+ const source = stripCommentsToSpaces2(sourceCode);
4037
+ const specifiers = [];
4038
+ for (const match of source.matchAll(
4039
+ /\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?['"]([^'"]+)['"]/g
4040
+ )) {
4041
+ if (match[1]?.startsWith(".")) specifiers.push(match[1]);
4042
+ }
4043
+ for (const match of source.matchAll(/\brequire\s*\(\s*(['"])(\.[^'"]*)\1\s*\)/g)) {
4044
+ specifiers.push(match[2]);
4045
+ }
4046
+ return specifiers;
3614
4047
  }
3615
4048
  async function fileExists2(filePath) {
3616
4049
  try {
@@ -3655,69 +4088,60 @@ async function discoverPackagedLocalFiles(entryFile) {
3655
4088
  }
3656
4089
  visitedFiles.add(absolutePath);
3657
4090
  const sourceCode = await (0, import_promises3.readFile)(absolutePath, "utf-8");
3658
- const sourceFile = import_typescript2.default.createSourceFile(
3659
- absolutePath,
3660
- sourceCode,
3661
- import_typescript2.default.ScriptTarget.Latest,
3662
- true,
3663
- import_typescript2.default.ScriptKind.TS
3664
- );
3665
- const constants = collectTopLevelStringConstants(sourceFile);
4091
+ const scanSource = stripCommentsToSpaces2(sourceCode);
4092
+ const constants = collectTopLevelStringConstants(sourceCode);
3666
4093
  const childVisits = [];
3667
- const visitNode = async (node) => {
3668
- if (import_typescript2.default.isCallExpression(node) && isCtxCsvCall(node)) {
3669
- const argument = node.arguments[0];
3670
- if (!argument) {
4094
+ for (const match of scanSource.matchAll(/\b([A-Za-z_$][\w$]*)\s*\.\s*csv\b/g)) {
4095
+ const target = match[1];
4096
+ if (target !== "ctx" && !target.endsWith("Ctx")) {
4097
+ continue;
4098
+ }
4099
+ const openParen = findCallOpenParen(scanSource, match.index + match[0].length);
4100
+ if (openParen < 0) {
4101
+ continue;
4102
+ }
4103
+ const argument = firstCallArgument(scanSource, openParen);
4104
+ if (!argument) {
4105
+ unresolved.push({
4106
+ sourceFragment: "ctx.csv()",
4107
+ message: "ctx.csv() requires a file path string or input reference."
4108
+ });
4109
+ } else if (!isRuntimeInputExpression(argument.text)) {
4110
+ const resolvedPath = resolveStringExpression(argument.text, constants);
4111
+ if (resolvedPath == null) {
3671
4112
  unresolved.push({
3672
- sourceFragment: "ctx.csv()",
3673
- message: "ctx.csv() requires a file path string or input reference."
4113
+ sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
4114
+ message: "Could not resolve this ctx.csv(...) path at submit time. Use a string literal, a top-level const string, or pass a runtime input like input.file."
3674
4115
  });
3675
- } else if (!isRuntimeInputExpression(argument)) {
3676
- const resolvedPath = resolveStringExpression(argument, constants);
3677
- if (resolvedPath == null) {
4116
+ } else {
4117
+ const absoluteCsvPath = (0, import_node_path6.resolve)((0, import_node_path6.dirname)(absolutePath), resolvedPath);
4118
+ if ((0, import_node_path6.isAbsolute)(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
3678
4119
  unresolved.push({
3679
- sourceFragment: extractSourceFragment(sourceCode, argument),
3680
- message: "Could not resolve this ctx.csv(...) path at submit time. Use a string literal, a top-level const string, or pass a runtime input like input.file."
3681
- });
3682
- } else {
3683
- const absoluteCsvPath = (0, import_node_path6.resolve)((0, import_node_path6.dirname)(absolutePath), resolvedPath);
3684
- if ((0, import_node_path6.isAbsolute)(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
3685
- unresolved.push({
3686
- sourceFragment: extractSourceFragment(sourceCode, argument),
3687
- message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
3688
- });
3689
- return;
3690
- }
3691
- const buffer = await (0, import_promises3.readFile)(absoluteCsvPath);
3692
- const stats = await (0, import_promises3.stat)(absoluteCsvPath);
3693
- files.set(absoluteCsvPath, {
3694
- sourceFragment: extractSourceFragment(sourceCode, argument),
3695
- logicalPath: resolvedPath,
3696
- absolutePath: absoluteCsvPath,
3697
- bytes: stats.size,
3698
- contentHash: sha2562(buffer),
3699
- contentType: contentTypeForFile(absoluteCsvPath)
4120
+ sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
4121
+ message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
3700
4122
  });
4123
+ continue;
3701
4124
  }
4125
+ const buffer = await (0, import_promises3.readFile)(absoluteCsvPath);
4126
+ const stats = await (0, import_promises3.stat)(absoluteCsvPath);
4127
+ files.set(absoluteCsvPath, {
4128
+ sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
4129
+ logicalPath: resolvedPath,
4130
+ absolutePath: absoluteCsvPath,
4131
+ bytes: stats.size,
4132
+ contentHash: sha2562(buffer),
4133
+ contentType: contentTypeForFile(absoluteCsvPath)
4134
+ });
3702
4135
  }
3703
4136
  }
3704
- if (import_typescript2.default.isImportDeclaration(node) && !node.importClause?.isTypeOnly && import_typescript2.default.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text.startsWith(".")) {
3705
- childVisits.push(
3706
- resolveLocalImport2(absolutePath, node.moduleSpecifier.text).then(
3707
- (resolvedImport) => visitSourceFile(resolvedImport)
3708
- )
3709
- );
3710
- }
3711
- if (import_typescript2.default.isCallExpression(node) && import_typescript2.default.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length === 1 && import_typescript2.default.isStringLiteral(node.arguments[0]) && node.arguments[0].text.startsWith(".")) {
3712
- childVisits.push(
3713
- resolveLocalImport2(absolutePath, node.arguments[0].text).then(
3714
- (resolvedImport) => visitSourceFile(resolvedImport)
3715
- )
3716
- );
3717
- }
3718
- await Promise.all(node.getChildren(sourceFile).map((child) => visitNode(child)));
3719
- };
3720
- await visitNode(sourceFile);
4137
+ }
4138
+ for (const specifier of localImportSpecifiers(sourceCode)) {
4139
+ childVisits.push(
4140
+ resolveLocalImport2(absolutePath, specifier).then(
4141
+ (resolvedImport) => visitSourceFile(resolvedImport)
4142
+ )
4143
+ );
4144
+ }
3721
4145
  await Promise.all(childVisits);
3722
4146
  };
3723
4147
  await visitSourceFile(absoluteEntryFile);
@@ -3729,7 +4153,7 @@ async function discoverPackagedLocalFiles(entryFile) {
3729
4153
 
3730
4154
  // src/plays/bundle-play-file.ts
3731
4155
  var import_meta2 = {};
3732
- var PLAY_BUNDLE_CACHE_VERSION2 = 24;
4156
+ var PLAY_BUNDLE_CACHE_VERSION2 = 26;
3733
4157
  var MODULE_DIR = (0, import_node_path7.dirname)((0, import_node_url.fileURLToPath)(import_meta2.url));
3734
4158
  var SDK_PACKAGE_ROOT = (0, import_node_path7.resolve)(MODULE_DIR, "..", "..");
3735
4159
  var SOURCE_REPO_ROOT = (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "..");
@@ -3744,7 +4168,7 @@ var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED
3744
4168
  var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? (0, import_node_path7.resolve)(SOURCE_REPO_ROOT, "sdk", "src") : HAS_PACKAGED_BUNDLING_SOURCES ? (0, import_node_path7.resolve)(PACKAGED_REPO_ROOT, "sdk", "src") : (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "src");
3745
4169
  var SDK_PACKAGE_JSON = (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "package.json");
3746
4170
  var SDK_ENTRY_FILE = (0, import_node_path7.resolve)(SDK_SOURCE_ROOT, "index.ts");
3747
- var SDK_TYPES_ENTRY_FILE = (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
4171
+ var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
3748
4172
  var SDK_WORKERS_ENTRY_FILE = (0, import_node_path7.resolve)(SDK_SOURCE_ROOT, "worker-play-entry.ts");
3749
4173
  var WORKERS_HARNESS_ENTRY_FILE = (0, import_node_path7.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
3750
4174
  var WORKERS_HARNESS_FILES_DIR = (0, import_node_path7.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src");
@@ -3776,7 +4200,7 @@ function createSdkPlayBundlingAdapter() {
3776
4200
  sdkSourceRoot: SDK_SOURCE_ROOT,
3777
4201
  sdkPackageJson: SDK_PACKAGE_JSON,
3778
4202
  sdkEntryFile: SDK_ENTRY_FILE,
3779
- sdkTypesEntryFile: (0, import_node_fs5.existsSync)(SDK_TYPES_ENTRY_FILE) ? SDK_TYPES_ENTRY_FILE : SDK_ENTRY_FILE,
4203
+ sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES || !(0, import_node_fs5.existsSync)(SDK_TYPES_ENTRY_FILE) ? SDK_ENTRY_FILE : SDK_TYPES_ENTRY_FILE,
3780
4204
  sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
3781
4205
  workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
3782
4206
  workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
@@ -3787,6 +4211,7 @@ function createSdkPlayBundlingAdapter() {
3787
4211
  async function bundlePlayFile2(filePath, options = {}) {
3788
4212
  return bundlePlayFile(filePath, {
3789
4213
  target: options.target ?? defaultPlayBundleTarget(),
4214
+ exportName: options.exportName,
3790
4215
  adapter: createSdkPlayBundlingAdapter()
3791
4216
  });
3792
4217
  }
@@ -3936,54 +4361,6 @@ function createCliProgress(enabled) {
3936
4361
  return progress;
3937
4362
  }
3938
4363
 
3939
- // src/cli/trace.ts
3940
- var cliTraceStartedAt = Date.now();
3941
- function isTruthyEnv(value) {
3942
- return value === "1" || value === "true" || value === "yes";
3943
- }
3944
- function isCliTraceEnabled() {
3945
- return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
3946
- }
3947
- function recordCliTrace(event) {
3948
- if (!isCliTraceEnabled()) {
3949
- return;
3950
- }
3951
- const now = Date.now();
3952
- const payload = {
3953
- ts: now,
3954
- source: "cli",
3955
- sinceStartMs: now - cliTraceStartedAt,
3956
- ...event
3957
- };
3958
- process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
3959
- `);
3960
- }
3961
- async function traceCliSpan(phase, fields, run) {
3962
- if (!isCliTraceEnabled()) {
3963
- return run();
3964
- }
3965
- const startedAt = Date.now();
3966
- try {
3967
- const result = await run();
3968
- recordCliTrace({
3969
- phase,
3970
- ms: Date.now() - startedAt,
3971
- ok: true,
3972
- ...fields
3973
- });
3974
- return result;
3975
- } catch (error) {
3976
- recordCliTrace({
3977
- phase,
3978
- ms: Date.now() - startedAt,
3979
- ok: false,
3980
- error: error instanceof Error ? error.message : String(error),
3981
- ...fields
3982
- });
3983
- throw error;
3984
- }
3985
- }
3986
-
3987
4364
  // src/cli/commands/play.ts
3988
4365
  function parseReferencedPlayTarget(target) {
3989
4366
  const trimmed = target.trim();
@@ -4031,67 +4408,6 @@ function defaultMaterializedPlayPath(reference) {
4031
4408
  const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
4032
4409
  return (0, import_node_path8.resolve)(`${safeName || "play"}.play.ts`);
4033
4410
  }
4034
- function sanitizeGeneratedPlayName(value) {
4035
- return value.trim().toLowerCase().replace(/^prebuilt\//, "").replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "play";
4036
- }
4037
- function buildGeneratedCsvWrapperSource(input) {
4038
- return `import { definePlay } from 'deepline';
4039
-
4040
- export default definePlay(
4041
- ${JSON.stringify(input.wrapperName)},
4042
- async (ctx, input: Record<string, unknown> & { file: string }) => {
4043
- const rows = await ctx.csv<Record<string, unknown>>(input.file);
4044
- const constants = Object.fromEntries(
4045
- Object.entries(input).filter(([key]) => key !== 'file'),
4046
- );
4047
-
4048
- const mappedRows = await ctx
4049
- .map('csv_rows', rows, {
4050
- key: (row, index) =>
4051
- String(
4052
- row.id ??
4053
- row.lead_id ??
4054
- row.email ??
4055
- row.linkedin_url ??
4056
- row.domain ??
4057
- index,
4058
- ),
4059
- })
4060
- .step('result', (row, rowCtx) =>
4061
- rowCtx.runPlay(
4062
- 'row_play',
4063
- ${JSON.stringify(input.playRef)},
4064
- {
4065
- ...constants,
4066
- ...row,
4067
- },
4068
- {
4069
- description: 'Run the source play for this CSV row.',
4070
- },
4071
- ),
4072
- )
4073
- .run({ description: 'Run the source play once per CSV row.' });
4074
-
4075
- return { rows: mappedRows };
4076
- },
4077
- );
4078
- `;
4079
- }
4080
- function writeGeneratedCsvWrapperPlay(playRef) {
4081
- const baseName = sanitizeGeneratedPlayName(
4082
- parseReferencedPlayTarget(playRef).unqualifiedPlayName
4083
- );
4084
- const wrapperName = `${baseName}-csv`;
4085
- const outputDir = (0, import_node_path8.resolve)(".deepline", "generated");
4086
- const outputPath = (0, import_node_path8.join)(outputDir, `${wrapperName}.play.ts`);
4087
- (0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
4088
- (0, import_node_fs6.writeFileSync)(
4089
- outputPath,
4090
- buildGeneratedCsvWrapperSource({ wrapperName, playRef }),
4091
- "utf-8"
4092
- );
4093
- return outputPath;
4094
- }
4095
4411
  function materializeRemotePlaySource(input) {
4096
4412
  if (isFileTarget(input.target)) {
4097
4413
  return null;
@@ -4121,13 +4437,15 @@ function formatLoadedPlayMessage(materializedFile) {
4121
4437
  return `Loaded play here: ${materializedFile.path}`;
4122
4438
  }
4123
4439
  function buildReadonlyPrebuiltPlayError(reference) {
4440
+ const localName = reference.split("/").slice(1).join("/") || "custom-play";
4124
4441
  return new Error(
4125
4442
  `Cannot edit or push ${reference} because Deepline prebuilt plays are read-only.
4126
4443
  To make your own version:
4127
- 1. Copy the source into a new local file.
4128
- 2. Change definePlay('${reference.split("/").slice(1).join("/")}', ...) to a new play name you own.
4129
- 3. Run: deepline plays publish <your-file.play.ts>
4130
- 4. Your play will then live under your workspace namespace.`
4444
+ 1. Run: deepline plays get ${reference} --source --out ./${localName}.play.ts
4445
+ 2. Change definePlay('${localName}', ...) to a new play name you own.
4446
+ 3. Run: deepline plays check ./${localName}.play.ts
4447
+ 4. Run: deepline plays publish ./${localName}.play.ts
4448
+ 5. Your play will then live under your workspace namespace.`
4131
4449
  );
4132
4450
  }
4133
4451
  async function ensureEditableRemotePlay(client, target) {
@@ -4157,7 +4475,10 @@ function looksLikeFilePath(target) {
4157
4475
  if (target.trim().toLowerCase().startsWith("prebuilt/")) {
4158
4476
  return false;
4159
4477
  }
4160
- return target.includes("/") || target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
4478
+ if (target.startsWith("./") || target.startsWith("../") || target.startsWith("/") || target.startsWith("~/")) {
4479
+ return true;
4480
+ }
4481
+ return target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
4161
4482
  }
4162
4483
  function parsePositiveInteger2(value, flagName) {
4163
4484
  const parsed = Number.parseInt(value, 10);
@@ -4174,6 +4495,142 @@ function parseJsonInput(raw) {
4174
4495
  }
4175
4496
  return parsed;
4176
4497
  }
4498
+ function parseInputFieldFlag(rawFlag, nextArg) {
4499
+ const flag = rawFlag.slice(2);
4500
+ const equalsIndex = flag.indexOf("=");
4501
+ if (equalsIndex > 0) {
4502
+ const path = flag.slice(0, equalsIndex).trim();
4503
+ const value = flag.slice(equalsIndex + 1);
4504
+ if (!path) {
4505
+ throw new Error(`Invalid play input flag: ${rawFlag}`);
4506
+ }
4507
+ return { path, value };
4508
+ }
4509
+ if (!nextArg || nextArg.startsWith("--")) {
4510
+ throw new Error(`Play input flag ${rawFlag} requires a value.`);
4511
+ }
4512
+ return { path: flag, value: nextArg };
4513
+ }
4514
+ function parseInputFlagValue(raw) {
4515
+ const trimmed = raw.trim();
4516
+ if (!trimmed) return "";
4517
+ if (trimmed === "true" || trimmed === "false" || trimmed === "null" || trimmed.startsWith("{") || trimmed.startsWith("[") || /^-?\d+(\.\d+)?$/.test(trimmed)) {
4518
+ try {
4519
+ return JSON.parse(trimmed);
4520
+ } catch {
4521
+ return raw;
4522
+ }
4523
+ }
4524
+ return raw;
4525
+ }
4526
+ function getDottedInputValue(input, path) {
4527
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
4528
+ let cursor = input;
4529
+ for (const part of parts) {
4530
+ if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
4531
+ return void 0;
4532
+ }
4533
+ cursor = cursor[part];
4534
+ }
4535
+ return cursor;
4536
+ }
4537
+ function setDottedInputValue(input, path, value) {
4538
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
4539
+ if (parts.length === 0) {
4540
+ throw new Error(`Invalid play input flag path: ${path}`);
4541
+ }
4542
+ let cursor = input;
4543
+ for (const part of parts.slice(0, -1)) {
4544
+ const existing = cursor[part];
4545
+ if (existing !== void 0 && (!existing || typeof existing !== "object" || Array.isArray(existing))) {
4546
+ throw new Error(
4547
+ `Cannot set --${path}; input.${part} is already a non-object value.`
4548
+ );
4549
+ }
4550
+ if (!existing) {
4551
+ cursor[part] = {};
4552
+ }
4553
+ cursor = cursor[part];
4554
+ }
4555
+ cursor[parts[parts.length - 1]] = value;
4556
+ }
4557
+ function schemaMetadata(schema, key) {
4558
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return null;
4559
+ const value = schema[key];
4560
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
4561
+ }
4562
+ function stringMetadata(metadata, key) {
4563
+ const value = metadata?.[key];
4564
+ return typeof value === "string" && value.trim() ? value.trim() : null;
4565
+ }
4566
+ function inputFieldFromCsvArg(csvArg) {
4567
+ if (typeof csvArg !== "string") return null;
4568
+ const match = /^input\.([A-Za-z_$][\w$]*)$/.exec(csvArg.trim());
4569
+ return match?.[1] ?? null;
4570
+ }
4571
+ function fileInputBindingsFromPlaySchema(inputSchema) {
4572
+ const csvInput = schemaMetadata(inputSchema, "csvInput");
4573
+ if (!csvInput) return [];
4574
+ return [
4575
+ {
4576
+ inputPath: stringMetadata(csvInput, "inputField") ?? "csv"
4577
+ }
4578
+ ];
4579
+ }
4580
+ function fileInputBindingsFromStaticPipeline(staticPipeline) {
4581
+ if (!staticPipeline || typeof staticPipeline !== "object" || Array.isArray(staticPipeline)) {
4582
+ return [];
4583
+ }
4584
+ const inputField = inputFieldFromCsvArg(
4585
+ staticPipeline.csvArg
4586
+ );
4587
+ return inputField ? [{ inputPath: inputField }] : [];
4588
+ }
4589
+ function applyCsvShortcutInput(input) {
4590
+ const csvValue = getDottedInputValue(input.runtimeInput, "csv");
4591
+ if (csvValue == null || csvValue === "") return;
4592
+ const candidate = input.bindings.find((binding) => binding.inputPath !== "csv")?.inputPath ?? input.fallbackInputPath ?? null;
4593
+ if (!candidate || candidate === "csv") return;
4594
+ const existing = getDottedInputValue(input.runtimeInput, candidate);
4595
+ if (existing != null && existing !== "") return;
4596
+ setDottedInputValue(input.runtimeInput, candidate, csvValue);
4597
+ }
4598
+ function isLocalFilePathValue(value) {
4599
+ if (typeof value !== "string" || !value.trim()) return false;
4600
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
4601
+ return (0, import_node_fs6.existsSync)((0, import_node_path8.resolve)(value));
4602
+ }
4603
+ async function stageFileInputArgs(input) {
4604
+ const uniqueBindings = [
4605
+ ...new Map(input.bindings.map((binding) => [binding.inputPath, binding])).values()
4606
+ ];
4607
+ const localFiles = uniqueBindings.flatMap((binding) => {
4608
+ const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
4609
+ if (!isLocalFilePathValue(value)) return [];
4610
+ const absolutePath = (0, import_node_path8.resolve)(value);
4611
+ return [{ binding, absolutePath, logicalPath: (0, import_node_path8.basename)(absolutePath) }];
4612
+ });
4613
+ if (localFiles.length === 0) {
4614
+ return { inputFile: null, packagedFiles: [] };
4615
+ }
4616
+ input.progress.phase(
4617
+ localFiles.length === 1 ? "staging input file" : "staging input files"
4618
+ );
4619
+ const staged = await input.client.stagePlayFiles(
4620
+ localFiles.map((file) => stageFile(file.logicalPath, file.absolutePath))
4621
+ );
4622
+ for (const [index, file] of localFiles.entries()) {
4623
+ setDottedInputValue(input.runtimeInput, file.binding.inputPath, file.logicalPath);
4624
+ const stagedFile = staged[index];
4625
+ if (stagedFile && stagedFile.logicalPath !== file.logicalPath) {
4626
+ setDottedInputValue(input.runtimeInput, file.binding.inputPath, stagedFile.logicalPath);
4627
+ }
4628
+ }
4629
+ return {
4630
+ inputFile: staged[0] ?? null,
4631
+ packagedFiles: staged.slice(1)
4632
+ };
4633
+ }
4177
4634
  function stageFile(logicalPath, absolutePath) {
4178
4635
  const buffer = (0, import_node_fs6.readFileSync)(absolutePath);
4179
4636
  return {
@@ -4261,6 +4718,7 @@ async function compileBundledPlayGraphManifests(client, graph) {
4261
4718
  node.compilerManifest = await client.compilePlayManifest({
4262
4719
  name,
4263
4720
  sourceCode: node.sourceCode,
4721
+ sourceFiles: node.sourceFiles,
4264
4722
  artifact: node.artifact,
4265
4723
  importedPlayDependencies: node.importedPlayDependencies.map(
4266
4724
  (dependency) => {
@@ -4310,6 +4768,7 @@ async function publishImportedPlayDependencies(client, graph) {
4310
4768
  await client.registerPlayArtifact({
4311
4769
  name: node.playName,
4312
4770
  sourceCode: node.sourceCode,
4771
+ sourceFiles: node.sourceFiles,
4313
4772
  artifact: node.artifact,
4314
4773
  compilerManifest: requireCompilerManifest(node),
4315
4774
  publish: true
@@ -4327,67 +4786,6 @@ function formatTimestamp(value) {
4327
4786
  function formatRunLine(run) {
4328
4787
  return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)}`;
4329
4788
  }
4330
- function parsePlayRunTarget(input) {
4331
- const { args, usage } = input;
4332
- let runId = null;
4333
- let playName = null;
4334
- for (let index = 0; index < args.length; index += 1) {
4335
- const arg = args[index];
4336
- if (arg === "--json") {
4337
- continue;
4338
- }
4339
- if (arg === "--reason" || arg === "--interval-ms" || arg === "--poll-interval-ms") {
4340
- index += 1;
4341
- continue;
4342
- }
4343
- if (arg === "--run-id" && args[index + 1]) {
4344
- runId = args[++index].trim();
4345
- continue;
4346
- }
4347
- if (arg === "--name" && args[index + 1] && input.allowName) {
4348
- playName = parseReferencedPlayTarget(args[++index]).playName;
4349
- continue;
4350
- }
4351
- if (arg.startsWith("--")) {
4352
- continue;
4353
- }
4354
- throw new DeeplineError(
4355
- `Unexpected positional target "${arg}". Use --run-id for run ids.
4356
- ${usage}`
4357
- );
4358
- }
4359
- const explicitTargets = [runId, playName].filter(Boolean).length;
4360
- if (explicitTargets > 1) {
4361
- throw new DeeplineError(`Choose exactly one play run target.
4362
- ${usage}`);
4363
- }
4364
- if (runId) {
4365
- return { kind: "run", runId };
4366
- }
4367
- if (playName) {
4368
- return { kind: "name", name: playName };
4369
- }
4370
- throw new DeeplineError(usage);
4371
- }
4372
- async function resolvePlayRunId(client, target) {
4373
- if (target.kind === "run") {
4374
- try {
4375
- const status = await client.getPlayStatus(target.runId);
4376
- return status.runId;
4377
- } catch (error) {
4378
- if (!(error instanceof DeeplineError) || error.statusCode !== 404) {
4379
- throw error;
4380
- }
4381
- throw new DeeplineError(`No play run found for run id: ${target.runId}`);
4382
- }
4383
- }
4384
- const runs = await client.listPlayRuns(target.name);
4385
- const workflowId = runs[0]?.workflowId ?? "";
4386
- if (!workflowId) {
4387
- throw new DeeplineError(`No runs found for play: ${target.name}`);
4388
- }
4389
- return workflowId;
4390
- }
4391
4789
  function isTransientPlayStatusPollError(error) {
4392
4790
  if (error instanceof DeeplineError && typeof error.statusCode === "number") {
4393
4791
  return error.statusCode >= 500 && error.statusCode < 600;
@@ -4491,7 +4889,7 @@ function assertPlayWaitNotTimedOut(input) {
4491
4889
  if (input.waitTimeoutMs !== null && Date.now() - input.startedAt >= input.waitTimeoutMs) {
4492
4890
  const hasRealRunId = input.workflowId.length > 0 && input.workflowId !== "pending";
4493
4891
  const phaseSuffix = input.lastPhase && input.lastPhase.trim() ? ` (last observed phase: ${input.lastPhase.trim()})` : "";
4494
- const tailHint = hasRealRunId ? ` Run 'deepline play tail --run-id ${input.workflowId} --json' to inspect it, or rerun with a larger --tail-timeout-ms.` : ` The run never reported a workflow id \u2014 the start request likely failed before reaching the scheduler. Check server logs and rerun with a larger --tail-timeout-ms.`;
4892
+ const tailHint = hasRealRunId ? ` Run 'deepline runs tail ${input.workflowId} --json' to inspect it, or rerun with a larger --tail-timeout-ms.` : ` The run never reported a workflow id \u2014 the start request likely failed before reaching the scheduler. Check server logs and rerun with a larger --tail-timeout-ms.`;
4495
4893
  throw new DeeplineError(
4496
4894
  `Timed out waiting for play ${hasRealRunId ? input.workflowId : "<no run id>"} after ${Math.ceil(input.waitTimeoutMs / 1e3)}s${phaseSuffix}.${tailHint}`,
4497
4895
  void 0,
@@ -4504,66 +4902,6 @@ function assertPlayWaitNotTimedOut(input) {
4504
4902
  );
4505
4903
  }
4506
4904
  }
4507
- async function waitForPlayCompletionByStream(input) {
4508
- const controller = new AbortController();
4509
- let timedOut = false;
4510
- let lastPhase = null;
4511
- const timeout = input.waitTimeoutMs === null ? null : setTimeout(
4512
- () => {
4513
- timedOut = true;
4514
- controller.abort();
4515
- },
4516
- Math.max(1, input.waitTimeoutMs - (Date.now() - input.startedAt))
4517
- );
4518
- try {
4519
- for await (const event of input.client.streamPlayRunEvents(
4520
- input.workflowId,
4521
- { signal: controller.signal }
4522
- )) {
4523
- assertPlayWaitNotTimedOut({ ...input, lastPhase });
4524
- const phase = describeLiveEventPhase(event);
4525
- if (phase) {
4526
- lastPhase = phase;
4527
- input.progress.phase(phase);
4528
- }
4529
- printPlayLogLines({
4530
- lines: getLogLinesFromLiveEvent(event),
4531
- status: null,
4532
- jsonOutput: input.jsonOutput,
4533
- emitLogs: input.emitLogs,
4534
- state: input.state,
4535
- progress: input.progress
4536
- });
4537
- const status = getStatusFromLiveEvent(event);
4538
- if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
4539
- const finalStatus = await input.client.getPlayStatus(input.workflowId);
4540
- if (TERMINAL_PLAY_STATUSES2.has(finalStatus.status)) {
4541
- return finalStatus;
4542
- }
4543
- }
4544
- }
4545
- } catch (error) {
4546
- if (timedOut) {
4547
- assertPlayWaitNotTimedOut({ ...input, lastPhase });
4548
- }
4549
- throw error;
4550
- } finally {
4551
- if (timeout) {
4552
- clearTimeout(timeout);
4553
- }
4554
- }
4555
- const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
4556
- throw new DeeplineError(
4557
- `Play live stream ended before the run reached a terminal state runId=${input.workflowId}${phaseSuffix}.`,
4558
- void 0,
4559
- "PLAY_LIVE_STREAM_ENDED",
4560
- {
4561
- runId: input.workflowId,
4562
- workflowId: input.workflowId,
4563
- ...lastPhase ? { phase: lastPhase } : {}
4564
- }
4565
- );
4566
- }
4567
4905
  async function startAndWaitForPlayCompletionByStream(input) {
4568
4906
  const startedAt = Date.now();
4569
4907
  const state = {
@@ -4582,24 +4920,10 @@ async function startAndWaitForPlayCompletionByStream(input) {
4582
4920
  },
4583
4921
  Math.max(1, input.waitTimeoutMs)
4584
4922
  );
4585
- recordCliTrace({
4586
- phase: "cli.start_stream_request",
4587
- playName: input.playName
4588
- });
4589
4923
  try {
4590
- let eventCount = 0;
4591
4924
  for await (const event of input.client.startPlayRunStream(input.request, {
4592
4925
  signal: controller.signal
4593
- })) {
4594
- eventCount += 1;
4595
- if (eventCount === 1) {
4596
- recordCliTrace({
4597
- phase: "cli.start_stream_first_event",
4598
- ms: Date.now() - startedAt,
4599
- playName: input.playName,
4600
- eventType: event.type
4601
- });
4602
- }
4926
+ })) {
4603
4927
  const eventRunId = getEventPayload(event).runId;
4604
4928
  if (typeof eventRunId === "string" && eventRunId && eventRunId !== "pending") {
4605
4929
  lastKnownWorkflowId = eventRunId;
@@ -4640,14 +4964,6 @@ async function startAndWaitForPlayCompletionByStream(input) {
4640
4964
  });
4641
4965
  const finalStatus = getFinalStatusFromLiveEvent(event);
4642
4966
  if (finalStatus) {
4643
- recordCliTrace({
4644
- phase: "cli.start_stream_final_event",
4645
- ms: Date.now() - startedAt,
4646
- playName: input.playName,
4647
- runId: finalStatus.runId,
4648
- status: finalStatus.status,
4649
- eventCount
4650
- });
4651
4967
  return finalStatus;
4652
4968
  }
4653
4969
  }
@@ -4665,10 +4981,12 @@ async function startAndWaitForPlayCompletionByStream(input) {
4665
4981
  clearTimeout(timeout);
4666
4982
  }
4667
4983
  const reason = error instanceof Error ? error.message : String(error);
4668
- process.stderr.write(
4669
- `[play watch] start stream failed after run ${lastKnownWorkflowId}; falling back to polling (${reason})
4984
+ if (!input.jsonOutput) {
4985
+ process.stderr.write(
4986
+ `[play watch] start stream failed after run ${lastKnownWorkflowId}; falling back to polling (${reason})
4670
4987
  `
4671
- );
4988
+ );
4989
+ }
4672
4990
  return waitForPlayCompletionByPolling({
4673
4991
  client: input.client,
4674
4992
  workflowId: lastKnownWorkflowId,
@@ -4687,6 +5005,24 @@ async function startAndWaitForPlayCompletionByStream(input) {
4687
5005
  clearTimeout(timeout);
4688
5006
  }
4689
5007
  }
5008
+ if (lastKnownWorkflowId) {
5009
+ if (!input.jsonOutput) {
5010
+ input.progress.writeLine(
5011
+ `[play watch] start stream ended after run ${lastKnownWorkflowId}; falling back to polling`
5012
+ );
5013
+ }
5014
+ return waitForPlayCompletionByPolling({
5015
+ client: input.client,
5016
+ workflowId: lastKnownWorkflowId,
5017
+ pollIntervalMs: 500,
5018
+ jsonOutput: input.jsonOutput,
5019
+ emitLogs: input.emitLogs,
5020
+ waitTimeoutMs: input.waitTimeoutMs,
5021
+ startedAt,
5022
+ state,
5023
+ progress: input.progress
5024
+ });
5025
+ }
4690
5026
  const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
4691
5027
  const idSuffix = lastKnownWorkflowId ? ` runId=${lastKnownWorkflowId}` : "";
4692
5028
  throw new DeeplineError(
@@ -4764,38 +5100,6 @@ async function waitForPlayCompletionByPolling(input) {
4764
5100
  }
4765
5101
  }
4766
5102
  }
4767
- async function waitForPlayCompletion(input) {
4768
- const startedAt = Date.now();
4769
- const state = {
4770
- lastLogIndex: 0,
4771
- emittedRunnerStarted: false
4772
- };
4773
- try {
4774
- return await waitForPlayCompletionByStream({
4775
- ...input,
4776
- startedAt,
4777
- state,
4778
- progress: input.progress
4779
- });
4780
- } catch (error) {
4781
- assertPlayWaitNotTimedOut({
4782
- workflowId: input.workflowId,
4783
- startedAt,
4784
- waitTimeoutMs: input.waitTimeoutMs
4785
- });
4786
- const reason = error instanceof Error ? error.message : String(error);
4787
- process.stderr.write(
4788
- `[play watch] SSE stream failed; falling back to polling (${reason})
4789
- `
4790
- );
4791
- return waitForPlayCompletionByPolling({
4792
- ...input,
4793
- startedAt,
4794
- state,
4795
- progress: input.progress
4796
- });
4797
- }
4798
- }
4799
5103
  function formatInteger(value) {
4800
5104
  return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
4801
5105
  }
@@ -4924,18 +5228,153 @@ function buildRunWarnings(status, rowsInfo) {
4924
5228
  }
4925
5229
  function buildRunNextCommands(runId) {
4926
5230
  return {
4927
- exportCsv: `deepline runs export ${runId} --out output.csv`,
4928
- status: `deepline runs status ${runId} --json`,
4929
- fullStatus: `deepline runs status ${runId} --json --full`,
4930
- logs: `deepline runs logs ${runId}`
5231
+ get: `deepline runs get ${runId} --json`,
5232
+ tail: `deepline runs tail ${runId} --json`,
5233
+ stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
5234
+ logs: `deepline runs logs ${runId} --out run.log --json`,
5235
+ exportCsv: `deepline runs export ${runId} --out output.csv`
5236
+ };
5237
+ }
5238
+ var RUN_LOG_PREVIEW_LIMIT = 20;
5239
+ function getRecordField(value, key) {
5240
+ return value && typeof value === "object" && !Array.isArray(value) ? value[key] : void 0;
5241
+ }
5242
+ function getNumericField(value, key) {
5243
+ const field = getRecordField(value, key);
5244
+ return typeof field === "number" && Number.isFinite(field) ? field : null;
5245
+ }
5246
+ function getStringField(value, key) {
5247
+ const field = getRecordField(value, key);
5248
+ return typeof field === "string" && field.trim() ? field : null;
5249
+ }
5250
+ function normalizeRunStatusForEnvelope(status) {
5251
+ const run = status.run ?? null;
5252
+ return {
5253
+ id: status.runId,
5254
+ playName: status.playName ?? status.name ?? getStringField(run, "playName") ?? null,
5255
+ status: status.status,
5256
+ runtime: getStringField(status, "runtime") ?? getStringField(status, "runtimeBackend") ?? getStringField(run, "runtime") ?? null,
5257
+ startedAt: getStringField(run, "startTime") ?? getStringField(run, "startedAt") ?? null,
5258
+ updatedAt: getStringField(status, "updatedAt") ?? getStringField(run, "updatedAt") ?? null,
5259
+ finishedAt: getStringField(run, "closeTime") ?? getStringField(run, "finishedAt") ?? null,
5260
+ source: getRecordField(status, "source") ?? getRecordField(status, "artifact") ?? null
5261
+ };
5262
+ }
5263
+ function normalizeProgressForEnvelope(status, rowsInfo) {
5264
+ const progress = status.progress;
5265
+ const total = getNumericField(progress, "totalRows") ?? getNumericField(progress, "total") ?? rowsInfo?.totalRows ?? null;
5266
+ const failed = getNumericField(progress, "failed") ?? getNumericField(progress, "failedRows") ?? null;
5267
+ const completed = getNumericField(progress, "completed") ?? getNumericField(progress, "completedRows") ?? (status.status === "completed" ? total : null);
5268
+ const pending = getNumericField(progress, "pending") ?? (typeof total === "number" && typeof completed === "number" && typeof failed === "number" ? Math.max(0, total - completed - failed) : null);
5269
+ return {
5270
+ total,
5271
+ completed,
5272
+ pending,
5273
+ failed,
5274
+ executed: getNumericField(progress, "executed"),
5275
+ reused: getNumericField(progress, "reused"),
5276
+ skipped: getNumericField(progress, "skipped"),
5277
+ retried: getNumericField(progress, "retried"),
5278
+ degraded: typeof getRecordField(progress, "degraded") === "boolean" ? getRecordField(progress, "degraded") : null,
5279
+ duplicates: getRecordField(progress, "duplicates") ?? null,
5280
+ active: getStringField(progress, "status") ?? getStringField(status, "activeStep") ?? getStringField(status, "activeNodeId") ?? null,
5281
+ wait: status.wait ?? null
5282
+ };
5283
+ }
5284
+ function normalizeOutputsForEnvelope(rowsInfo, exportedPath) {
5285
+ if (!rowsInfo) {
5286
+ return exportedPath ? [{ name: "output", kind: "file", path: exportedPath }] : [];
5287
+ }
5288
+ return [
5289
+ {
5290
+ name: "rows",
5291
+ kind: "dataset",
5292
+ rowCount: rowsInfo.totalRows,
5293
+ columns: rowsInfo.columns,
5294
+ preview: rowsInfo.rows.slice(0, 5),
5295
+ previewRowCount: Math.min(rowsInfo.rows.length, 5),
5296
+ previewLimit: 5,
5297
+ complete: rowsInfo.complete,
5298
+ source: rowsInfo.source,
5299
+ ...exportedPath ? { csv_path: exportedPath } : {}
5300
+ }
5301
+ ];
5302
+ }
5303
+ function normalizeStepsForEnvelope(status) {
5304
+ const directSteps = getRecordField(status, "steps");
5305
+ if (Array.isArray(directSteps)) {
5306
+ return directSteps;
5307
+ }
5308
+ const timeline = getRecordField(status, "timeline");
5309
+ if (Array.isArray(timeline)) {
5310
+ return timeline;
5311
+ }
5312
+ return [];
5313
+ }
5314
+ function normalizeErrorsForEnvelope(status, error) {
5315
+ const directErrors = getRecordField(status, "errors");
5316
+ if (Array.isArray(directErrors)) {
5317
+ return directErrors.filter(
5318
+ (entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)
5319
+ );
5320
+ }
5321
+ if (!error) {
5322
+ return [];
5323
+ }
5324
+ return [
5325
+ {
5326
+ code: getStringField(status, "errorCode") ?? "RUN_FAILED",
5327
+ phase: getStringField(status, "errorPhase") ?? "runtime",
5328
+ message: error,
5329
+ retryable: typeof getRecordField(status, "retryable") === "boolean" ? getRecordField(status, "retryable") : null,
5330
+ nextAction: `deepline runs get ${status.runId} --json`
5331
+ }
5332
+ ];
5333
+ }
5334
+ function normalizeLogsForEnvelope(status) {
5335
+ const logs = Array.isArray(status.progress?.logs) ? status.progress.logs : [];
5336
+ const offset = typeof status.progress?.logOffset === "number" && Number.isFinite(status.progress.logOffset) ? Math.max(0, Math.trunc(status.progress.logOffset)) : 0;
5337
+ const totalCount = offset + logs.length;
5338
+ const entries = logs.slice(Math.max(0, logs.length - RUN_LOG_PREVIEW_LIMIT));
5339
+ const firstSequence = entries.length === 0 ? null : offset + logs.length - entries.length + 1;
5340
+ const lastSequence = totalCount === 0 ? null : totalCount;
5341
+ return {
5342
+ totalCount,
5343
+ returnedCount: entries.length,
5344
+ firstSequence,
5345
+ lastSequence,
5346
+ truncated: totalCount > entries.length,
5347
+ hasMore: totalCount > entries.length,
5348
+ entries,
5349
+ nextCursor: lastSequence
4931
5350
  };
4932
5351
  }
5352
+ function stripProviderSpendFromBilling(value) {
5353
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
5354
+ return value;
5355
+ }
5356
+ const next = {};
5357
+ for (const [key, item] of Object.entries(value)) {
5358
+ if (key === "providerCostUsd" || key === "totalProviderCostUsd") {
5359
+ continue;
5360
+ }
5361
+ next[key] = item;
5362
+ }
5363
+ return next;
5364
+ }
4933
5365
  function compactPlayStatus(status, options) {
4934
5366
  const rowsInfo = extractCanonicalRowsInfo(status);
4935
5367
  const result = status && typeof status === "object" ? status.result : null;
4936
5368
  const warnings = buildRunWarnings(status, rowsInfo);
4937
- const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(rowsInfo.rows, rowsInfo.totalRows, rowsInfo.columns) : null;
4938
- const billing = status && typeof status === "object" ? status.billing : null;
5369
+ const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
5370
+ rowsInfo.rows,
5371
+ rowsInfo.totalRows,
5372
+ rowsInfo.columns,
5373
+ extractDatasetExecutionStats(status)
5374
+ ) : null;
5375
+ const billing = status && typeof status === "object" ? stripProviderSpendFromBilling(
5376
+ status.billing
5377
+ ) : null;
4939
5378
  const progressError = status.progress?.error;
4940
5379
  const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
4941
5380
  return {
@@ -4944,18 +5383,38 @@ function compactPlayStatus(status, options) {
4944
5383
  ...typeof status.name === "string" ? { name: status.name } : {},
4945
5384
  ...typeof status.playName === "string" ? { playName: status.playName } : {},
4946
5385
  status: status.status,
5386
+ run: normalizeRunStatusForEnvelope(status),
5387
+ progress: normalizeProgressForEnvelope(status, rowsInfo),
5388
+ outputs: normalizeOutputsForEnvelope(rowsInfo, options?.exportedPath),
5389
+ steps: normalizeStepsForEnvelope(status),
5390
+ errors: normalizeErrorsForEnvelope(status, error),
5391
+ logs: normalizeLogsForEnvelope(status),
4947
5392
  ...error ? { error } : {},
4948
5393
  ...warnings.length > 0 ? { warnings } : {},
4949
5394
  output: buildOutputSummary(rowsInfo, options?.exportedPath) ?? result ?? null,
4950
5395
  ...result !== void 0 ? { result } : {},
4951
5396
  ...status.resultView ? { resultView: status.resultView } : {},
4952
5397
  ...datasetStats ? { dataset_stats: datasetStats } : {},
4953
- ...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 10) } : {},
5398
+ ...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
4954
5399
  ...billing ? { billing } : {},
4955
- ...status.run ? { run: status.run } : {},
4956
5400
  next: buildRunNextCommands(status.runId)
4957
5401
  };
4958
5402
  }
5403
+ function enrichPlayStatusWithDatasetStats(status) {
5404
+ const rowsInfo = extractCanonicalRowsInfo(status);
5405
+ if (!rowsInfo?.complete) {
5406
+ return status;
5407
+ }
5408
+ return {
5409
+ ...status,
5410
+ dataset_stats: buildDatasetStats(
5411
+ rowsInfo.rows,
5412
+ rowsInfo.totalRows,
5413
+ rowsInfo.columns,
5414
+ extractDatasetExecutionStats(status)
5415
+ )
5416
+ };
5417
+ }
4959
5418
  function formatDatasetStatsLines(datasetStats) {
4960
5419
  if (!datasetStats) {
4961
5420
  return [];
@@ -4965,10 +5424,11 @@ function formatDatasetStatsLines(datasetStats) {
4965
5424
  0,
4966
5425
  12
4967
5426
  )) {
4968
- const topValues = stat3.top_values ? `, ${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
4969
- const sample = stat3.sample_value !== void 0 ? `, sample=${JSON.stringify(stat3.sample_value)}` : "";
5427
+ const topValues = stat3.top_values ? `, top_values=${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
5428
+ const sample = stat3.sample_value !== void 0 ? `, sample_value=${JSON.stringify(stat3.sample_value)}` : "";
5429
+ const execution = stat3.execution ? `, execution=${Object.entries(stat3.execution).map(([bucket, count]) => `${bucket}=${count}`).join(", ")}` : "";
4970
5430
  lines.push(
4971
- ` ${column}: ${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}`
5431
+ ` ${column}: non_empty=${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}${execution}`
4972
5432
  );
4973
5433
  }
4974
5434
  return lines;
@@ -4977,7 +5437,7 @@ function writePlayResult(status, jsonOutput, options) {
4977
5437
  if (jsonOutput) {
4978
5438
  process.stdout.write(
4979
5439
  `${JSON.stringify(
4980
- options?.fullJson ? status : compactPlayStatus(status, options)
5440
+ options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status, options)
4981
5441
  )}
4982
5442
  `
4983
5443
  );
@@ -4991,7 +5451,12 @@ function writePlayResult(status, jsonOutput, options) {
4991
5451
  lines.push(`${success ? "\u2713" : "\u2717"} ${publicStatus} ${runId}`);
4992
5452
  const rowsInfo = extractCanonicalRowsInfo(status);
4993
5453
  const warnings = buildRunWarnings(status, rowsInfo);
4994
- const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(rowsInfo.rows, rowsInfo.totalRows, rowsInfo.columns) : null;
5454
+ const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
5455
+ rowsInfo.rows,
5456
+ rowsInfo.totalRows,
5457
+ rowsInfo.columns,
5458
+ extractDatasetExecutionStats(status)
5459
+ ) : null;
4995
5460
  const outputSummary = buildOutputSummary(rowsInfo, options?.exportedPath);
4996
5461
  if (outputSummary) {
4997
5462
  const columns = Array.isArray(outputSummary.columns) ? outputSummary.columns.length : 0;
@@ -5110,10 +5575,10 @@ function writeStartedPlayRun(input) {
5110
5575
  const lines = [
5111
5576
  `Started ${input.playName}`,
5112
5577
  ` run id: ${input.runId}`,
5113
- ` check status: deepline play status --run-id ${input.runId}`,
5114
- ` tail logs: deepline play tail --run-id ${input.runId}`,
5115
- ` stop run: deepline play stop --run-id ${input.runId}`,
5116
- ` result JSON: deepline play status --run-id ${input.runId} --json`
5578
+ ` get status: deepline runs get ${input.runId} --json`,
5579
+ ` tail logs: deepline runs tail ${input.runId} --json`,
5580
+ ` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`,
5581
+ ` result JSON: deepline runs get ${input.runId} --json`
5117
5582
  ];
5118
5583
  if (input.dashboardUrl) {
5119
5584
  lines.push(` play page: ${input.dashboardUrl}`);
@@ -5126,10 +5591,9 @@ function writeStartedPlayRun(input) {
5126
5591
  console.log(output);
5127
5592
  }
5128
5593
  function parsePlayRunOptions(args) {
5129
- const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--csv file.csv] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force]\n deepline plays run <play-file.ts> [--input '{...}'] [--csv file.csv] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--csv file.csv] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force]\n deepline plays run --name <name> [--input '{...}'] [--csv file.csv] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--json]";
5594
+ const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--json] [--<input> value]";
5130
5595
  let filePath = null;
5131
5596
  let playName = null;
5132
- let csvPath = null;
5133
5597
  let input = null;
5134
5598
  let revisionId = null;
5135
5599
  let revisionSelector = null;
@@ -5150,10 +5614,6 @@ function parsePlayRunOptions(args) {
5150
5614
  playName = parseReferencedPlayTarget(args[++index]).playName;
5151
5615
  continue;
5152
5616
  }
5153
- if (arg === "--csv" && args[index + 1]) {
5154
- csvPath = (0, import_node_path8.resolve)(args[++index]);
5155
- continue;
5156
- }
5157
5617
  if ((arg === "--input" || arg === "-i") && args[index + 1]) {
5158
5618
  input = parseJsonInput(args[++index]);
5159
5619
  continue;
@@ -5203,8 +5663,13 @@ function parsePlayRunOptions(args) {
5203
5663
  continue;
5204
5664
  }
5205
5665
  if (arg.startsWith("--")) {
5206
- throw new Error(`Unexpected flag: ${arg}
5207
- ${usage}`);
5666
+ const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
5667
+ input ??= {};
5668
+ setDottedInputValue(input, path, parseInputFlagValue(value));
5669
+ if (!arg.includes("=")) {
5670
+ index += 1;
5671
+ }
5672
+ continue;
5208
5673
  }
5209
5674
  if (!arg.startsWith("--") && !filePath && !playName) {
5210
5675
  if (isFileTarget(arg) || looksLikeFilePath(arg)) {
@@ -5240,7 +5705,6 @@ ${usage}`);
5240
5705
  }
5241
5706
  return {
5242
5707
  target: filePath ? { kind: "file", path: filePath } : { kind: "name", name: playName },
5243
- csvPath,
5244
5708
  input,
5245
5709
  revisionId,
5246
5710
  revisionSelector,
@@ -5261,6 +5725,10 @@ function parsePlayCheckOptions(args) {
5261
5725
  const jsonOutput = argsWantJson(args);
5262
5726
  return { target, jsonOutput };
5263
5727
  }
5728
+ function shouldUseLocalOnlyPlayCheck() {
5729
+ const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
5730
+ return value === "1" || value === "true" || value === "yes" || value === "on";
5731
+ }
5264
5732
  async function handlePlayCheck(args) {
5265
5733
  const options = parsePlayCheckOptions(args);
5266
5734
  if (!isFileTarget(options.target)) {
@@ -5286,10 +5754,28 @@ async function handlePlayCheck(args) {
5286
5754
  return 1;
5287
5755
  }
5288
5756
  const playName = graph.root.playName ?? extractPlayName(sourceCode, absolutePlayPath);
5757
+ if (shouldUseLocalOnlyPlayCheck()) {
5758
+ const result2 = {
5759
+ valid: true,
5760
+ errors: [],
5761
+ staticPipeline: graph.root.compilerManifest?.staticPipeline ?? null,
5762
+ artifactHash: graph.root.artifact.artifactHash,
5763
+ graphHash: graph.root.artifact.graphHash
5764
+ };
5765
+ if (options.jsonOutput) {
5766
+ process.stdout.write(`${JSON.stringify({ name: playName, ...result2 })}
5767
+ `);
5768
+ } else {
5769
+ console.log(`\u2713 ${playName} passed local play check`);
5770
+ console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
5771
+ }
5772
+ return 0;
5773
+ }
5289
5774
  const client = new DeeplineClient();
5290
5775
  const result = await client.checkPlayArtifact({
5291
5776
  name: playName,
5292
5777
  sourceCode: graph.root.sourceCode,
5778
+ sourceFiles: graph.root.sourceFiles,
5293
5779
  artifact: graph.root.artifact
5294
5780
  });
5295
5781
  if (options.jsonOutput) {
@@ -5315,34 +5801,12 @@ async function handleFileBackedRun(options) {
5315
5801
  const client = new DeeplineClient();
5316
5802
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
5317
5803
  const absolutePlayPath = (0, import_node_path8.resolve)(options.target.path);
5318
- recordCliTrace({
5319
- phase: "cli.play_run_file_start",
5320
- playPath: absolutePlayPath,
5321
- watch: options.watch,
5322
- hasCsv: Boolean(options.csvPath),
5323
- force: options.force
5324
- });
5325
5804
  progress.phase("compiling play");
5326
- const readSourceStartedAt = Date.now();
5327
5805
  const sourceCode = (0, import_node_fs6.readFileSync)(absolutePlayPath, "utf-8");
5328
- recordCliTrace({
5329
- phase: "cli.read_play_source",
5330
- ms: Date.now() - readSourceStartedAt,
5331
- bytes: sourceCode.length,
5332
- playPath: absolutePlayPath
5333
- });
5334
5806
  let graph;
5335
5807
  try {
5336
- graph = await traceCliSpan(
5337
- "cli.bundle_play_graph",
5338
- { playPath: absolutePlayPath },
5339
- () => collectBundledPlayGraph(absolutePlayPath)
5340
- );
5341
- await traceCliSpan(
5342
- "cli.compile_play_manifest",
5343
- { playPath: absolutePlayPath, nodeCount: graph.nodes.size },
5344
- () => compileBundledPlayGraphManifests(client, graph)
5345
- );
5808
+ graph = await collectBundledPlayGraph(absolutePlayPath);
5809
+ await compileBundledPlayGraphManifests(client, graph);
5346
5810
  progress.phase("compiled play");
5347
5811
  } catch (error) {
5348
5812
  progress.fail();
@@ -5353,87 +5817,65 @@ async function handleFileBackedRun(options) {
5353
5817
  const playName = bundleResult.playName ?? extractPlayName(sourceCode, absolutePlayPath);
5354
5818
  try {
5355
5819
  progress.phase("publishing imported plays");
5356
- await traceCliSpan(
5357
- "cli.publish_imported_plays",
5358
- { playName, nodeCount: graph.nodes.size },
5359
- () => publishImportedPlayDependencies(client, graph)
5360
- );
5820
+ await publishImportedPlayDependencies(client, graph);
5361
5821
  } catch (error) {
5362
5822
  progress.fail();
5363
5823
  console.error(error instanceof Error ? error.message : String(error));
5364
5824
  return 1;
5365
5825
  }
5366
5826
  const runtimeInput = options.input ? { ...options.input } : {};
5367
- const prepareFilesStartedAt = Date.now();
5368
5827
  const packagedFileUploads = bundleResult.packagedFiles.map(
5369
5828
  (file) => stageFile(file.logicalPath, file.absolutePath)
5370
5829
  );
5371
- const inputFileUpload = options.csvPath ? stageFile((0, import_node_path8.basename)(options.csvPath), options.csvPath) : packagedFileUploads[0] ?? null;
5372
- if (options.csvPath && typeof runtimeInput.file !== "string" && typeof runtimeInput.csv !== "string") {
5373
- runtimeInput.file = (0, import_node_path8.basename)(options.csvPath);
5374
- }
5375
- recordCliTrace({
5376
- phase: "cli.prepare_input_files",
5377
- ms: Date.now() - prepareFilesStartedAt,
5378
- playName,
5379
- packagedFileCount: packagedFileUploads.length,
5380
- hasInputFile: Boolean(inputFileUpload)
5830
+ const fileInputBindings = fileInputBindingsFromStaticPipeline(
5831
+ requireCompilerManifest(bundleResult).staticPipeline
5832
+ );
5833
+ applyCsvShortcutInput({
5834
+ runtimeInput,
5835
+ bindings: fileInputBindings,
5836
+ fallbackInputPath: "file"
5837
+ });
5838
+ const stagedFileInputs = await stageFileInputArgs({
5839
+ client,
5840
+ runtimeInput,
5841
+ bindings: fileInputBindings,
5842
+ progress
5381
5843
  });
5382
5844
  const startRequest = {
5383
5845
  name: playName,
5384
5846
  sourceCode: bundleResult.sourceCode,
5847
+ sourceFiles: bundleResult.sourceFiles,
5385
5848
  runtimeArtifact: bundleResult.artifact,
5386
5849
  compilerManifest: requireCompilerManifest(bundleResult),
5387
- inputFileUpload,
5388
5850
  packagedFileUploads,
5389
5851
  ...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
5852
+ ...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
5853
+ ...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
5390
5854
  ...options.force ? { force: true } : {}
5391
5855
  };
5392
5856
  if (options.watch) {
5393
5857
  progress.phase("starting run");
5394
- const finalStatus = await traceCliSpan(
5395
- "cli.start_and_watch",
5396
- { playName },
5397
- () => startAndWaitForPlayCompletionByStream({
5398
- client,
5399
- request: startRequest,
5400
- playName,
5401
- jsonOutput: options.jsonOutput,
5402
- emitLogs: options.emitLogs,
5403
- waitTimeoutMs: options.waitTimeoutMs,
5404
- progress
5405
- })
5406
- );
5407
- const exportStartedAt = Date.now();
5408
- const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
5409
- recordCliTrace({
5410
- phase: "cli.export_rows",
5411
- ms: Date.now() - exportStartedAt,
5858
+ const finalStatus = await startAndWaitForPlayCompletionByStream({
5859
+ client,
5860
+ request: startRequest,
5412
5861
  playName,
5413
- exported: Boolean(exportedPath)
5862
+ jsonOutput: options.jsonOutput,
5863
+ emitLogs: options.emitLogs,
5864
+ waitTimeoutMs: options.waitTimeoutMs,
5865
+ progress
5414
5866
  });
5867
+ const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
5415
5868
  if (finalStatus.status === "completed") {
5416
5869
  progress.complete();
5417
5870
  } else {
5418
5871
  progress.fail();
5419
5872
  }
5420
- recordCliTrace({
5421
- phase: "cli.write_play_result",
5422
- playName,
5423
- status: finalStatus.status,
5424
- runId: finalStatus.runId
5425
- });
5426
5873
  writePlayResult(finalStatus, options.jsonOutput, { exportedPath });
5427
5874
  return finalStatus.status === "completed" ? 0 : 1;
5428
5875
  }
5429
5876
  progress.phase("starting run");
5430
- const started = await traceCliSpan(
5431
- "cli.start_run",
5432
- { playName },
5433
- () => client.startPlayRun(startRequest)
5434
- );
5435
- const fallbackDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
5436
- const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
5877
+ const started = await client.startPlayRun(startRequest);
5878
+ const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
5437
5879
  progress.phase(`loading play on ${dashboardUrl}`);
5438
5880
  progress.complete();
5439
5881
  writeStartedPlayRun({
@@ -5441,7 +5883,7 @@ async function handleFileBackedRun(options) {
5441
5883
  playName,
5442
5884
  status: started.status,
5443
5885
  statusUrl: started.statusUrl,
5444
- dashboardUrl,
5886
+ dashboardUrl: started.dashboardUrl ?? dashboardUrl,
5445
5887
  jsonOutput: options.jsonOutput,
5446
5888
  progress
5447
5889
  });
@@ -5467,9 +5909,8 @@ async function handleNamedRun(options) {
5467
5909
  }
5468
5910
  const client = new DeeplineClient();
5469
5911
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
5470
- let stagedInputFile = null;
5471
5912
  progress.phase("loading play definition");
5472
- await assertCanonicalNamedPlayReference(client, options.target.name);
5913
+ const playDetail = await assertCanonicalNamedPlayReference(client, options.target.name);
5473
5914
  progress.phase("selecting revision");
5474
5915
  const selectedRevisionId = await resolveNamedRunRevisionId({
5475
5916
  client,
@@ -5477,19 +5918,27 @@ async function handleNamedRun(options) {
5477
5918
  revisionId: options.revisionId,
5478
5919
  selector: options.revisionSelector
5479
5920
  });
5480
- if (options.csvPath) {
5481
- progress.phase("staging input file");
5482
- const [staged] = await client.stagePlayFiles([
5483
- stageFile((0, import_node_path8.basename)(options.csvPath), options.csvPath)
5484
- ]);
5485
- stagedInputFile = staged ?? null;
5486
- }
5487
5921
  const runtimeInput = options.input ? { ...options.input } : {};
5922
+ const fileInputBindings = [
5923
+ ...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
5924
+ ...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
5925
+ ];
5926
+ applyCsvShortcutInput({
5927
+ runtimeInput,
5928
+ bindings: fileInputBindings
5929
+ });
5930
+ const stagedFileInputs = await stageFileInputArgs({
5931
+ client,
5932
+ runtimeInput,
5933
+ bindings: fileInputBindings,
5934
+ progress
5935
+ });
5488
5936
  const startRequest = {
5489
5937
  name: options.target.name,
5490
5938
  ...selectedRevisionId ? { revisionId: selectedRevisionId } : {},
5491
5939
  ...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
5492
- ...stagedInputFile ? { inputFile: stagedInputFile } : {},
5940
+ ...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
5941
+ ...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
5493
5942
  ...options.force ? { force: true } : {}
5494
5943
  };
5495
5944
  if (options.watch) {
@@ -5503,22 +5952,6 @@ async function handleNamedRun(options) {
5503
5952
  waitTimeoutMs: options.waitTimeoutMs,
5504
5953
  progress
5505
5954
  });
5506
- if (finalStatus.status !== "completed" && options.csvPath) {
5507
- progress.phase("generating csv wrapper play");
5508
- const generatedPlayPath = writeGeneratedCsvWrapperPlay(
5509
- options.target.name
5510
- );
5511
- progress.writeLogLine(
5512
- `Generated CSV wrapper play: ${generatedPlayPath}`
5513
- );
5514
- progress.phase("running generated csv wrapper play");
5515
- return handleFileBackedRun({
5516
- ...options,
5517
- target: { kind: "file", path: generatedPlayPath },
5518
- revisionId: null,
5519
- revisionSelector: null
5520
- });
5521
- }
5522
5955
  const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
5523
5956
  if (finalStatus.status === "completed") {
5524
5957
  progress.complete();
@@ -5530,11 +5963,10 @@ async function handleNamedRun(options) {
5530
5963
  }
5531
5964
  progress.phase("starting run");
5532
5965
  const started = await client.startPlayRun(startRequest);
5533
- const fallbackDashboardUrl = buildPlayDashboardUrl(
5966
+ const dashboardUrl = buildPlayDashboardUrl(
5534
5967
  client.baseUrl,
5535
5968
  options.target.name
5536
5969
  );
5537
- const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
5538
5970
  progress.phase(`loading play on ${dashboardUrl}`);
5539
5971
  progress.complete();
5540
5972
  writeStartedPlayRun({
@@ -5542,7 +5974,7 @@ async function handleNamedRun(options) {
5542
5974
  playName: started.name ?? options.target.name,
5543
5975
  status: started.status,
5544
5976
  statusUrl: started.statusUrl,
5545
- dashboardUrl,
5977
+ dashboardUrl: started.dashboardUrl ?? dashboardUrl,
5546
5978
  jsonOutput: options.jsonOutput,
5547
5979
  progress
5548
5980
  });
@@ -5576,80 +6008,101 @@ async function handlePlayRun(args) {
5576
6008
  }
5577
6009
  return handleNamedRun(options);
5578
6010
  }
5579
- async function handlePlayTail(args) {
5580
- const usage = "Usage: deepline play tail --run-id <run-id> [--interval-ms 1000] [--json]\n deepline play tail --name <name> [--interval-ms 1000] [--json]";
5581
- let target;
5582
- try {
5583
- target = parsePlayRunTarget({ args, usage, allowName: true });
5584
- } catch (error) {
5585
- console.error(error instanceof Error ? error.message : usage);
5586
- return 1;
5587
- }
5588
- const client = new DeeplineClient();
5589
- const jsonOutput = argsWantJson(args);
5590
- const emitLogs = !jsonOutput || args.includes("--logs");
5591
- let intervalMs = 500;
6011
+ function parseRunIdPositional(args, usage) {
5592
6012
  for (let index = 0; index < args.length; index += 1) {
5593
6013
  const arg = args[index];
5594
- if ((arg === "--interval-ms" || arg === "--poll-interval-ms") && args[index + 1]) {
5595
- intervalMs = parsePositiveInteger2(args[++index], arg);
6014
+ if (arg === "--json" || arg === "--full" || arg === "--logs" || arg === "--compact" || arg === "--limit") {
6015
+ if (arg === "--limit" && args[index + 1]) {
6016
+ index += 1;
6017
+ }
6018
+ continue;
6019
+ }
6020
+ if ((arg === "--out" || arg === "--cursor" || arg === "--reason" || arg === "--interval-ms" || arg === "--poll-interval-ms") && args[index + 1]) {
6021
+ index += 1;
6022
+ continue;
6023
+ }
6024
+ if (!arg.startsWith("--")) {
6025
+ return arg;
5596
6026
  }
5597
6027
  }
5598
- const workflowId = await resolvePlayRunId(client, target);
5599
- const progress = getActiveCliProgress() ?? createCliProgress(!jsonOutput);
5600
- progress.phase(`tailing ${workflowId}`);
5601
- const finalStatus = await waitForPlayCompletion({
5602
- client,
5603
- workflowId,
5604
- pollIntervalMs: intervalMs,
5605
- jsonOutput,
5606
- emitLogs,
5607
- waitTimeoutMs: null,
5608
- progress
5609
- });
5610
- if (finalStatus.status === "completed") {
5611
- progress.complete();
5612
- } else {
5613
- progress.fail();
5614
- }
5615
- writePlayResult(finalStatus, jsonOutput);
5616
- return finalStatus.status === "completed" ? 0 : 1;
6028
+ throw new DeeplineError(usage);
5617
6029
  }
5618
- async function handlePlayStatus(args) {
5619
- const usage = "Usage: deepline play status --run-id <run-id> [--json] [--full]\n deepline play status --name <name> [--json] [--full]";
5620
- let target;
6030
+ async function handleRunGet(args) {
6031
+ const usage = "Usage: deepline runs get <run-id> [--json] [--full]";
6032
+ let runId;
5621
6033
  try {
5622
- target = parsePlayRunTarget({ args, usage, allowName: true });
6034
+ runId = parseRunIdPositional(args, usage);
5623
6035
  } catch (error) {
5624
6036
  console.error(error instanceof Error ? error.message : usage);
5625
6037
  return 1;
5626
6038
  }
5627
6039
  const client = new DeeplineClient();
5628
- const workflowId = await resolvePlayRunId(client, target);
5629
- const status = await client.getPlayStatus(workflowId);
6040
+ const status = await client.runs.get(runId);
5630
6041
  writePlayResult(status, argsWantJson(args), {
5631
6042
  fullJson: args.includes("--full")
5632
6043
  });
5633
6044
  return 0;
5634
6045
  }
5635
- function parseRunIdPositional(args, usage) {
6046
+ async function handleRunsList(args) {
6047
+ const usage = "Usage: deepline runs list --play <play-name> [--status <status>] [--json]";
6048
+ let playName = null;
6049
+ let statusFilter = null;
5636
6050
  for (let index = 0; index < args.length; index += 1) {
5637
6051
  const arg = args[index];
5638
- if (arg === "--json" || arg === "--full" || arg === "--logs") {
6052
+ if ((arg === "--play" || arg === "--name") && args[index + 1]) {
6053
+ playName = parseReferencedPlayTarget(args[++index]).playName;
5639
6054
  continue;
5640
6055
  }
5641
- if (arg === "--out" && args[index + 1]) {
5642
- index += 1;
6056
+ if (arg === "--status" && args[index + 1]) {
6057
+ statusFilter = args[++index].trim().toLowerCase();
5643
6058
  continue;
5644
6059
  }
5645
- if (!arg.startsWith("--")) {
5646
- return arg;
6060
+ if (arg === "--json" || arg === "--compact") {
6061
+ continue;
5647
6062
  }
5648
6063
  }
5649
- throw new DeeplineError(usage);
6064
+ if (!playName) {
6065
+ console.error(usage);
6066
+ return 1;
6067
+ }
6068
+ const client = new DeeplineClient();
6069
+ const runs = (await client.runs.list({
6070
+ play: playName,
6071
+ ...statusFilter ? { status: statusFilter } : {}
6072
+ })).map((run) => ({
6073
+ runId: run.workflowId,
6074
+ workflowId: run.workflowId,
6075
+ temporalRunId: run.runId,
6076
+ status: String(run.status ?? "").toLowerCase(),
6077
+ startedAt: run.startTime,
6078
+ finishedAt: run.closeTime,
6079
+ executionTime: run.executionTime,
6080
+ playName: run.memo?.playName ?? playName
6081
+ }));
6082
+ if (argsWantJson(args)) {
6083
+ process.stdout.write(
6084
+ `${JSON.stringify({
6085
+ runs,
6086
+ count: runs.length,
6087
+ next: {
6088
+ get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
6089
+ }
6090
+ })}
6091
+ `
6092
+ );
6093
+ } else {
6094
+ if (runs.length === 0) {
6095
+ console.log(`No runs found for ${playName}.`);
6096
+ } else {
6097
+ for (const run of runs) {
6098
+ console.log(`${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
6099
+ }
6100
+ }
6101
+ }
6102
+ return 0;
5650
6103
  }
5651
- async function handleRunStatus(args) {
5652
- const usage = "Usage: deepline runs status <run-id> [--json] [--full]";
6104
+ async function handleRunTail(args) {
6105
+ const usage = "Usage: deepline runs tail <run-id> [--json] [--compact] [--cursor <cursor>]";
5653
6106
  let runId;
5654
6107
  try {
5655
6108
  runId = parseRunIdPositional(args, usage);
@@ -5658,14 +6111,24 @@ async function handleRunStatus(args) {
5658
6111
  return 1;
5659
6112
  }
5660
6113
  const client = new DeeplineClient();
5661
- const status = await client.getPlayStatus(runId);
5662
- writePlayResult(status, argsWantJson(args), {
5663
- fullJson: args.includes("--full")
6114
+ let afterLogIndex;
6115
+ for (let index = 0; index < args.length; index += 1) {
6116
+ const arg = args[index];
6117
+ if (arg === "--cursor" && args[index + 1]) {
6118
+ const parsed = Number(args[++index]);
6119
+ if (Number.isInteger(parsed) && parsed >= 0) {
6120
+ afterLogIndex = parsed;
6121
+ }
6122
+ }
6123
+ }
6124
+ const status = await client.runs.tail(runId, {
6125
+ ...afterLogIndex !== void 0 ? { afterLogIndex } : {}
5664
6126
  });
5665
- return 0;
6127
+ writePlayResult(status, argsWantJson(args));
6128
+ return status.status === "failed" ? 1 : 0;
5666
6129
  }
5667
6130
  async function handleRunLogs(args) {
5668
- const usage = "Usage: deepline runs logs <run-id> [--json]";
6131
+ const usage = "Usage: deepline runs logs <run-id> [--limit 200] [--out run.log] [--json]";
5669
6132
  let runId;
5670
6133
  try {
5671
6134
  runId = parseRunIdPositional(args, usage);
@@ -5673,14 +6136,86 @@ async function handleRunLogs(args) {
5673
6136
  console.error(error instanceof Error ? error.message : usage);
5674
6137
  return 1;
5675
6138
  }
6139
+ let limit = 200;
6140
+ let outPath = null;
6141
+ for (let index = 0; index < args.length; index += 1) {
6142
+ const arg = args[index];
6143
+ if (arg === "--limit" && args[index + 1]) {
6144
+ limit = parsePositiveInteger2(args[++index], "--limit");
6145
+ continue;
6146
+ }
6147
+ if (arg === "--out" && args[index + 1]) {
6148
+ outPath = (0, import_node_path8.resolve)(args[++index]);
6149
+ }
6150
+ }
5676
6151
  const client = new DeeplineClient();
5677
- const status = await client.getPlayStatus(runId);
6152
+ const status = await client.runs.get(runId);
5678
6153
  const logs = status.progress?.logs ?? [];
6154
+ if (outPath) {
6155
+ (0, import_node_fs6.writeFileSync)(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
6156
+ if (argsWantJson(args)) {
6157
+ process.stdout.write(
6158
+ `${JSON.stringify({
6159
+ runId: status.runId,
6160
+ log_path: outPath,
6161
+ lineCount: logs.length
6162
+ })}
6163
+ `
6164
+ );
6165
+ } else {
6166
+ console.log(`Wrote ${logs.length} log lines to ${outPath}`);
6167
+ }
6168
+ return 0;
6169
+ }
6170
+ const entries = logs.slice(Math.max(0, logs.length - limit));
5679
6171
  if (argsWantJson(args)) {
5680
- process.stdout.write(`${JSON.stringify({ runId: status.runId, logs })}
6172
+ process.stdout.write(
6173
+ `${JSON.stringify({
6174
+ runId: status.runId,
6175
+ totalCount: logs.length,
6176
+ returnedCount: entries.length,
6177
+ firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
6178
+ lastSequence: logs.length === 0 ? null : logs.length,
6179
+ truncated: logs.length > entries.length,
6180
+ hasMore: logs.length > entries.length,
6181
+ entries,
6182
+ next: {
6183
+ export: `deepline runs logs ${status.runId} --out run.log --json`
6184
+ }
6185
+ })}
6186
+ `
6187
+ );
6188
+ } else {
6189
+ process.stdout.write(`${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`);
6190
+ }
6191
+ return 0;
6192
+ }
6193
+ async function handleRunStop(args) {
6194
+ const usage = 'Usage: deepline runs stop <run-id> [--reason "text"] [--json]';
6195
+ let runId;
6196
+ try {
6197
+ runId = parseRunIdPositional(args, usage);
6198
+ } catch (error) {
6199
+ console.error(error instanceof Error ? error.message : usage);
6200
+ return 1;
6201
+ }
6202
+ let reason;
6203
+ for (let index = 0; index < args.length; index += 1) {
6204
+ const arg = args[index];
6205
+ if (arg === "--reason" && args[index + 1]) {
6206
+ reason = args[++index];
6207
+ }
6208
+ }
6209
+ const client = new DeeplineClient();
6210
+ const result = await client.runs.stop(runId, { reason });
6211
+ if (argsWantJson(args)) {
6212
+ process.stdout.write(`${JSON.stringify(result)}
5681
6213
  `);
5682
6214
  } else {
5683
- process.stdout.write(`${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
6215
+ console.log(`Stopped ${result.runId}`);
6216
+ if (result.hitlCancelledCount > 0) {
6217
+ console.log(` cancelled HITL waits: ${result.hitlCancelledCount}`);
6218
+ }
5684
6219
  }
5685
6220
  return 0;
5686
6221
  }
@@ -5723,37 +6258,6 @@ async function handleRunExport(args) {
5723
6258
  }
5724
6259
  return 0;
5725
6260
  }
5726
- async function handlePlayStop(args) {
5727
- const usage = 'Usage: deepline play stop --run-id <run-id> [--reason "text"] [--json]';
5728
- let target;
5729
- try {
5730
- target = parsePlayRunTarget({ args, usage, allowName: false });
5731
- } catch (error) {
5732
- console.error(error instanceof Error ? error.message : usage);
5733
- return 1;
5734
- }
5735
- const client = new DeeplineClient();
5736
- const jsonOutput = argsWantJson(args);
5737
- let reason;
5738
- for (let index = 0; index < args.length; index += 1) {
5739
- const arg = args[index];
5740
- if (arg === "--reason" && args[index + 1]) {
5741
- reason = args[++index];
5742
- }
5743
- }
5744
- const workflowId = await resolvePlayRunId(client, target);
5745
- const result = await client.stopPlay(workflowId, { reason });
5746
- if (jsonOutput) {
5747
- process.stdout.write(`${JSON.stringify(result)}
5748
- `);
5749
- } else {
5750
- console.log(`Stopped ${result.runId}`);
5751
- if (result.hitlCancelledCount > 0) {
5752
- console.log(` cancelled HITL waits: ${result.hitlCancelledCount}`);
5753
- }
5754
- }
5755
- return 0;
5756
- }
5757
6261
  async function handlePlayGet(args) {
5758
6262
  const target = args[0];
5759
6263
  if (!target) {
@@ -5761,8 +6265,9 @@ async function handlePlayGet(args) {
5761
6265
  return 1;
5762
6266
  }
5763
6267
  const client = new DeeplineClient();
5764
- const jsonOutput = argsWantJson(args);
6268
+ const explicitJson = args.includes("--json");
5765
6269
  const sourceOutput = args.includes("--source");
6270
+ const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
5766
6271
  let outPath = null;
5767
6272
  for (let index = 1; index < args.length; index += 1) {
5768
6273
  const arg = args[index];
@@ -5773,7 +6278,7 @@ async function handlePlayGet(args) {
5773
6278
  const playName = isFileTarget(target) ? extractPlayName((0, import_node_fs6.readFileSync)((0, import_node_path8.resolve)(target), "utf-8"), (0, import_node_path8.resolve)(target)) : parseReferencedPlayTarget(target).playName;
5774
6279
  const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
5775
6280
  const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
5776
- const materializedFile = sourceOutput || outPath ? materializeRemotePlaySource({
6281
+ const materializedFile = outPath ? materializeRemotePlaySource({
5777
6282
  target,
5778
6283
  playName,
5779
6284
  sourceCode: resolvedSource,
@@ -5813,6 +6318,10 @@ async function handlePlayGet(args) {
5813
6318
  }
5814
6319
  return 0;
5815
6320
  }
6321
+ if (outPath && loadedMessage) {
6322
+ console.log(loadedMessage);
6323
+ return 0;
6324
+ }
5816
6325
  console.log(`Play: ${formatPlayReference(detail.play)}`);
5817
6326
  console.log(
5818
6327
  `Working version: ${detail.play.workingRevision?.version ?? "\u2014"}`
@@ -5836,33 +6345,6 @@ async function handlePlayGet(args) {
5836
6345
  }
5837
6346
  return 0;
5838
6347
  }
5839
- async function handlePlayRuns(args) {
5840
- const nameIndex = args.indexOf("--name");
5841
- const name = nameIndex >= 0 ? args[nameIndex + 1] : void 0;
5842
- if (!name) {
5843
- console.error("Usage: deepline play runs --name <name> [--json]");
5844
- return 1;
5845
- }
5846
- const client = new DeeplineClient();
5847
- const jsonOutput = argsWantJson(args);
5848
- await assertCanonicalNamedPlayReference(client, name);
5849
- const runs = await client.listPlayRuns(
5850
- parseReferencedPlayTarget(name).playName
5851
- );
5852
- if (jsonOutput) {
5853
- process.stdout.write(`${JSON.stringify({ runs })}
5854
- `);
5855
- return 0;
5856
- }
5857
- if (runs.length === 0) {
5858
- console.log(`No runs found for ${name}.`);
5859
- return 0;
5860
- }
5861
- for (const run of runs) {
5862
- console.log(formatRunLine(run));
5863
- }
5864
- return 0;
5865
- }
5866
6348
  function formatVersionLine(version) {
5867
6349
  const revisionLabel = version.artifactHash?.slice(0, 12) ?? "unknown-revision";
5868
6350
  return `v${version.version} ${revisionLabel} ${formatTimestamp(version.createdAt)}`;
@@ -5979,7 +6461,26 @@ function printPlayDescription(play) {
5979
6461
  console.log(` ${line}`);
5980
6462
  }
5981
6463
  }
6464
+ if (play.csvInput) {
6465
+ console.log(" CSV input:");
6466
+ const rendered = JSON.stringify(play.csvInput, null, 2);
6467
+ for (const line of rendered.split("\n")) {
6468
+ console.log(` ${line}`);
6469
+ }
6470
+ }
6471
+ if (play.rowOutputSchema) {
6472
+ console.log(" Row output schema:");
6473
+ const rendered = JSON.stringify(play.rowOutputSchema, null, 2);
6474
+ for (const line of rendered.split("\n")) {
6475
+ console.log(` ${line}`);
6476
+ }
6477
+ }
5982
6478
  console.log(` Run: ${play.runCommand}`);
6479
+ if (play.origin === "prebuilt" && !play.canEdit) {
6480
+ console.log(
6481
+ ` Customize: deepline plays get ${reference} --source --out ./my-play.play.ts`
6482
+ );
6483
+ }
5983
6484
  }
5984
6485
  async function handlePlaySearch(args) {
5985
6486
  let options;
@@ -6077,6 +6578,7 @@ async function handlePlayPublish(args) {
6077
6578
  const published = await client.registerPlayArtifact({
6078
6579
  name: rootPlayName,
6079
6580
  sourceCode: graph.root.sourceCode,
6581
+ sourceFiles: graph.root.sourceFiles,
6080
6582
  artifact: graph.root.artifact,
6081
6583
  compilerManifest: requireCompilerManifest(graph.root),
6082
6584
  publish: true
@@ -6118,6 +6620,45 @@ async function handlePlayPublish(args) {
6118
6620
  `);
6119
6621
  return result.success ? 0 : 1;
6120
6622
  }
6623
+ async function handlePlayDelete(args) {
6624
+ const playName = args[0];
6625
+ if (!playName) {
6626
+ console.error("Usage: deepline plays delete <play-name> --yes [--json]");
6627
+ return 1;
6628
+ }
6629
+ const confirmed = args.includes("--yes") || args.includes("-y") || args.includes("--force");
6630
+ if (!confirmed) {
6631
+ console.error(
6632
+ "Refusing to delete without --yes. This deletes the org-owned play, its revisions, trigger bindings, and local run records."
6633
+ );
6634
+ return 1;
6635
+ }
6636
+ const client = new DeeplineClient();
6637
+ let detail;
6638
+ try {
6639
+ detail = await client.getPlay(parseReferencedPlayTarget(playName).playName);
6640
+ } catch (error) {
6641
+ console.error(error instanceof Error ? error.message : String(error));
6642
+ return 1;
6643
+ }
6644
+ if (detail.play.ownerType === "deepline" || detail.play.origin === "prebuilt") {
6645
+ console.error(`Cannot delete prebuilt play: ${formatPlayReference(detail.play)}`);
6646
+ return 1;
6647
+ }
6648
+ const result = await client.deletePlay(
6649
+ parseReferencedPlayTarget(formatPlayReference(detail.play)).playName
6650
+ );
6651
+ if (argsWantJson(args)) {
6652
+ process.stdout.write(`${JSON.stringify(result)}
6653
+ `);
6654
+ return result.deleted ? 0 : 1;
6655
+ }
6656
+ process.stdout.write(
6657
+ `Deleted ${result.name}: revisions=${result.deletedRevisionCount}, bindings=${result.deletedBindingCount}, runs=${result.deletedRunCount}
6658
+ `
6659
+ );
6660
+ return result.deleted ? 0 : 1;
6661
+ }
6121
6662
  function registerPlayCommands(program) {
6122
6663
  const play = program.command("plays").alias("play").description("Search, validate, run, and manage cloud plays.").addHelpText(
6123
6664
  "after",
@@ -6151,21 +6692,23 @@ Examples:
6151
6692
  ...options.json ? ["--json"] : []
6152
6693
  ]);
6153
6694
  });
6154
- play.command("run [target]").description("Run a play file or named play.").addHelpText(
6695
+ play.command("run [target]").description("Run a play file or named play.").allowUnknownOption(true).allowExcessArguments(true).addHelpText(
6155
6696
  "after",
6156
6697
  `
6157
6698
  Notes:
6158
- Local play files are bundled locally, then validated and executed in Deepline cloud.
6159
- Named plays run the stored live cloud revision.
6160
- Run performs server preflight automatically. Use \`deepline plays check <file>\`
6161
- to validate without starting a run.
6699
+ Local play files are bundled locally, then validated and executed in Deepline cloud.
6700
+ Named plays run the stored live cloud revision.
6701
+ Unknown --foo and --foo.bar flags are treated as play input args.
6702
+ File-like input args accept local paths; the CLI stages those files before submit.
6703
+ Run performs server preflight automatically. Use \`deepline plays check <file>\`
6704
+ to validate without starting a run.
6162
6705
 
6163
6706
  Examples:
6164
6707
  deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
6165
6708
  deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
6166
6709
  deepline plays run enrich.play.ts --csv leads.csv --watch --out leads-enriched.csv
6167
6710
  `
6168
- ).option("--file <path>", "Local play file to run").option("--name <name>", "Saved play name to run").option("--csv <path>", "Attach a CSV file").option("-i, --input <json>", "Input JSON object or @file path").option("--live", "Run the current live revision explicitly").option("--latest", "Run the newest saved revision, even if it is not live").option(
6711
+ ).option("--file <path>", "Local play file to run").option("--name <name>", "Saved play name to run").option("-i, --input <json>", "Input JSON object or @file path").option("--live", "Run the current live revision explicitly").option("--latest", "Run the newest saved revision, even if it is not live").option(
6169
6712
  "--revision-id <id>",
6170
6713
  "Run a specific saved revision instead of the live revision"
6171
6714
  ).option(
@@ -6174,12 +6717,21 @@ Examples:
6174
6717
  ).option("--watch", "Stream logs until completion").option(
6175
6718
  "--logs",
6176
6719
  "When output is non-interactive, stream play logs to stderr while waiting"
6177
- ).option("--poll-interval-ms <ms>", "Polling interval while tailing").option("--tail-timeout-ms <ms>", "Timeout while tailing").option("--force", "Supersede any active runs for this play").option("--json", "Emit JSON output").action(async (target, options) => {
6720
+ ).option("--poll-interval-ms <ms>", "Polling interval while tailing").option("--tail-timeout-ms <ms>", "Timeout while tailing").option("--force", "Supersede any active runs for this play").option("--json", "Emit JSON output").action(async (target, options, command) => {
6721
+ const passthroughArgs = [...command.args];
6722
+ const explicitTarget = options.file || options.name;
6723
+ const targetIsInputFlag = typeof target === "string" && target.startsWith("--");
6724
+ const effectiveTarget = explicitTarget || targetIsInputFlag ? null : target;
6725
+ if (explicitTarget && typeof target === "string" && !targetIsInputFlag && !passthroughArgs.includes(target)) {
6726
+ passthroughArgs.push(target);
6727
+ }
6728
+ if (effectiveTarget && passthroughArgs[0] === effectiveTarget) {
6729
+ passthroughArgs.shift();
6730
+ }
6178
6731
  process.exitCode = await handlePlayRun([
6179
- ...target ? [target] : [],
6732
+ ...effectiveTarget ? [effectiveTarget] : [],
6180
6733
  ...options.file ? ["--file", options.file] : [],
6181
6734
  ...options.name ? ["--name", options.name] : [],
6182
- ...options.csv ? ["--csv", options.csv] : [],
6183
6735
  ...options.input ? ["--input", options.input] : [],
6184
6736
  ...options.live ? ["--live"] : [],
6185
6737
  ...options.latest ? ["--latest"] : [],
@@ -6190,7 +6742,8 @@ Examples:
6190
6742
  ...options.pollIntervalMs ? ["--poll-interval-ms", options.pollIntervalMs] : [],
6191
6743
  ...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
6192
6744
  ...options.force ? ["--force"] : [],
6193
- ...options.json ? ["--json"] : []
6745
+ ...options.json ? ["--json"] : [],
6746
+ ...passthroughArgs
6194
6747
  ]);
6195
6748
  });
6196
6749
  play.command("get <target>").description("Fetch full play details.").addHelpText(
@@ -6204,12 +6757,10 @@ Notes:
6204
6757
  Examples:
6205
6758
  deepline plays get person-linkedin-to-email
6206
6759
  deepline plays get person-linkedin-to-email --json | jq '.play.liveRevision'
6760
+ deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source > email-waterfall.play.ts
6761
+ deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source --out ./email-waterfall.play.ts
6207
6762
  `
6208
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").addOption(
6209
- new import_commander.Option("--source", "Materialize or print the source code").hideHelp()
6210
- ).addOption(
6211
- new import_commander.Option("--out <path>", "Write source to a specific path").hideHelp()
6212
- ).action(async (target, options) => {
6763
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--source", "Print raw source code; combine with --out to write a file").option("--out <path>", "Write source to a specific path").action(async (target, options) => {
6213
6764
  process.exitCode = await handlePlayGet([
6214
6765
  target,
6215
6766
  ...options.json ? ["--json"] : [],
@@ -6248,50 +6799,12 @@ Examples:
6248
6799
  ...options.json ? ["--json"] : []
6249
6800
  ]);
6250
6801
  });
6251
- play.command("runs").description("List runs for a named play.").option("--name <name>", "Saved play name").option("--json", "Emit JSON output").action(async (options) => {
6252
- process.exitCode = await handlePlayRuns([
6253
- ...options.name ? ["--name", options.name] : [],
6254
- ...options.json ? ["--json"] : []
6255
- ]);
6256
- });
6257
6802
  play.command("versions").description("List revisions for a named play.").option("--name <name>", "Saved play name").option("--json", "Emit JSON output").action(async (options) => {
6258
6803
  process.exitCode = await handlePlayVersions([
6259
6804
  ...options.name ? ["--name", options.name] : [],
6260
6805
  ...options.json ? ["--json"] : []
6261
6806
  ]);
6262
6807
  });
6263
- play.command("tail").description("Tail events for a play run.").option("--run-id <runId>", "Run id to tail").option("--name <name>", "Tail the latest run for a named play").option("--interval-ms <ms>", "Polling interval while tailing").option("--logs", "With --json, stream play logs to stderr while waiting").option("--json", "Emit JSON output").action(async (options) => {
6264
- process.exitCode = await handlePlayTail([
6265
- ...options.runId ? ["--run-id", options.runId] : [],
6266
- ...options.name ? ["--name", options.name] : [],
6267
- ...options.intervalMs ? ["--interval-ms", options.intervalMs] : [],
6268
- ...options.logs ? ["--logs"] : [],
6269
- ...options.json ? ["--json"] : []
6270
- ]);
6271
- });
6272
- play.command("status").description("Show status for a play run.").option("--run-id <runId>", "Run id to inspect").option("--name <name>", "Inspect the latest run for a named play").option("--json", "Emit JSON output").option("--full", "With --json, emit the full raw status payload").action(async (options) => {
6273
- process.exitCode = await handlePlayStatus([
6274
- ...options.runId ? ["--run-id", options.runId] : [],
6275
- ...options.name ? ["--name", options.name] : [],
6276
- ...options.json ? ["--json"] : [],
6277
- ...options.full ? ["--full"] : []
6278
- ]);
6279
- });
6280
- play.command("export").description("Export a completed play run to CSV.").requiredOption("--run-id <runId>", "Run id to export").requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
6281
- process.exitCode = await handleRunExport([
6282
- options.runId,
6283
- "--out",
6284
- options.out,
6285
- ...options.json ? ["--json"] : []
6286
- ]);
6287
- });
6288
- play.command("stop").description("Stop a play run.").option("--run-id <runId>", "Run id to stop").option("--reason <text>", "Reason to include with the stop request").option("--json", "Emit JSON output").action(async (options) => {
6289
- process.exitCode = await handlePlayStop([
6290
- ...options.runId ? ["--run-id", options.runId] : [],
6291
- ...options.reason ? ["--reason", options.reason] : [],
6292
- ...options.json ? ["--json"] : []
6293
- ]);
6294
- });
6295
6808
  play.command("publish <target>").description("Bundle, validate, save, and publish a play.").option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option("--json", "Emit JSON output").action(async (target, options) => {
6296
6809
  process.exitCode = await handlePlayPublish([
6297
6810
  target,
@@ -6300,38 +6813,79 @@ Examples:
6300
6813
  ...options.json ? ["--json"] : []
6301
6814
  ]);
6302
6815
  });
6303
- const runs = program.command("runs").description("Inspect and export play runs.").addHelpText(
6816
+ play.command("delete <target>").description("Delete an org-owned play and its saved revisions/runs.").option("-y, --yes", "Confirm deletion").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
6817
+ process.exitCode = await handlePlayDelete([
6818
+ target,
6819
+ ...options.yes ? ["--yes"] : [],
6820
+ ...options.json ? ["--json"] : []
6821
+ ]);
6822
+ });
6823
+ const runs = program.command("runs").description("Inspect, tail, stop, and export play runs.").addHelpText(
6304
6824
  "after",
6305
6825
  `
6306
6826
  Examples:
6307
- deepline runs status play/my-play/run/20260501t000000-000
6827
+ deepline runs get play/my-play/run/20260501t000000-000 --json
6828
+ deepline runs tail play/my-play/run/20260501t000000-000
6829
+ deepline runs logs play/my-play/run/20260501t000000-000 --out run.log --json
6830
+ deepline runs list --play my-play --status failed --json
6831
+ deepline runs stop play/my-play/run/20260501t000000-000 --reason "stale lock" --json
6308
6832
  deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
6309
- deepline runs logs play/my-play/run/20260501t000000-000
6310
6833
  `
6311
6834
  );
6312
- runs.command("status <runId>").description("Show compact status for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--full", "With --json, emit the full raw status payload").action(async (runId, options) => {
6313
- process.exitCode = await handleRunStatus([
6835
+ runs.command("get <runId>").description("Get status, progress, outputs, errors, and recovery metadata for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--full", "Debug only: with --json, emit the raw status payload").action(async (runId, options) => {
6836
+ process.exitCode = await handleRunGet([
6314
6837
  runId,
6315
6838
  ...options.json ? ["--json"] : [],
6316
6839
  ...options.full ? ["--full"] : []
6317
6840
  ]);
6318
6841
  });
6319
- runs.command("export <runId>").description("Export the completed row output for a play run to CSV.").requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
6320
- process.exitCode = await handleRunExport([
6321
- runId,
6322
- "--out",
6323
- options.out,
6842
+ runs.command("list").description("List play runs.").requiredOption("--play <name>", "Play name to filter runs").option("--status <status>", "Filter by run status").option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
6843
+ process.exitCode = await handleRunsList([
6844
+ "--play",
6845
+ options.play,
6846
+ ...options.status ? ["--status", options.status] : [],
6847
+ ...options.compact ? ["--compact"] : [],
6324
6848
  ...options.json ? ["--json"] : []
6325
6849
  ]);
6326
6850
  });
6327
- runs.command("logs <runId>").description("Print logs for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
6851
+ runs.command("tail <runId>").description("Tail or fetch recent live events for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--compact", "Drop verbose fields from JSON output").option("--cursor <cursor>", "Resume after a previously returned event cursor").action(async (runId, options) => {
6852
+ process.exitCode = await handleRunTail([
6853
+ runId,
6854
+ ...options.json ? ["--json"] : [],
6855
+ ...options.compact ? ["--compact"] : [],
6856
+ ...options.cursor ? ["--cursor", options.cursor] : []
6857
+ ]);
6858
+ });
6859
+ runs.command("logs <runId>").description("Fetch persisted logs for a play run.").option("--limit <count>", "Maximum recent log lines to print without --out", "200").option("--out <path>", "Write the full persisted log stream to a file").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
6328
6860
  process.exitCode = await handleRunLogs([
6329
6861
  runId,
6862
+ ...options.limit ? ["--limit", options.limit] : [],
6863
+ ...options.out ? ["--out", options.out] : [],
6864
+ ...options.json ? ["--json"] : []
6865
+ ]);
6866
+ });
6867
+ runs.command("stop <runId>").description("Stop a play run.").option("--reason <text>", "Reason to include with the stop request").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
6868
+ process.exitCode = await handleRunStop([
6869
+ runId,
6870
+ ...options.reason ? ["--reason", options.reason] : [],
6871
+ ...options.json ? ["--json"] : []
6872
+ ]);
6873
+ });
6874
+ runs.command("export <runId>").description("Export the completed row output for a play run to CSV.").requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
6875
+ process.exitCode = await handleRunExport([
6876
+ runId,
6877
+ "--out",
6878
+ options.out,
6330
6879
  ...options.json ? ["--json"] : []
6331
6880
  ]);
6332
6881
  });
6333
6882
  }
6334
6883
 
6884
+ // src/cli/commands/tools.ts
6885
+ var import_node_fs8 = require("fs");
6886
+ var import_node_os7 = require("os");
6887
+ var import_node_path10 = require("path");
6888
+
6335
6889
  // src/tool-output.ts
6336
6890
  var import_node_fs7 = require("fs");
6337
6891
  var import_node_os6 = require("os");
@@ -6500,31 +7054,23 @@ async function listTools(args) {
6500
7054
  }
6501
7055
  return 0;
6502
7056
  }
6503
- function toolMatchesQuery(tool, terms) {
6504
- const haystack = [
6505
- tool.toolId,
6506
- tool.provider,
6507
- tool.displayName,
6508
- tool.description,
6509
- tool.operation,
6510
- tool.operationId,
6511
- ...tool.operationAliases ?? [],
6512
- ...tool.categories ?? [],
6513
- tool.inputSchema ? JSON.stringify(tool.inputSchema) : ""
6514
- ].filter(Boolean).join(" ").toLowerCase();
6515
- return terms.every((term) => haystack.includes(term));
6516
- }
6517
- async function searchTools(args) {
6518
- const query = args[0]?.trim();
7057
+ async function searchTools(queryInput, options = {}) {
7058
+ const query = queryInput.trim();
6519
7059
  if (!query) {
6520
7060
  console.error("Usage: deepline tools search <query> [--json]");
6521
7061
  return 1;
6522
7062
  }
6523
- const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
6524
7063
  const client = new DeeplineClient();
6525
- const items = (await client.listTools()).filter((tool) => toolMatchesQuery(tool, terms)).map(toListedTool);
6526
- if (argsWantJson(args)) {
6527
- process.stdout.write(`${JSON.stringify({ tools: items })}
7064
+ const result = await client.searchTools({
7065
+ query,
7066
+ categories: options.categories,
7067
+ searchTerms: options.searchTerms,
7068
+ searchMode: options.searchMode,
7069
+ includeSearchDebug: options.includeSearchDebug
7070
+ });
7071
+ const items = result.tools.map(toListedTool);
7072
+ if (options.json || shouldEmitJson()) {
7073
+ process.stdout.write(`${JSON.stringify({ ...result, tools: items })}
6528
7074
  `);
6529
7075
  return 0;
6530
7076
  }
@@ -6578,11 +7124,14 @@ Common commands:
6578
7124
  ...options.json ? ["--json"] : []
6579
7125
  ]);
6580
7126
  });
6581
- tools.command("search <query>").description("Search available tools.").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
6582
- process.exitCode = await searchTools([
6583
- query,
6584
- ...options.json ? ["--json"] : []
6585
- ]);
7127
+ tools.command("search <query>").description("Search available tools.").option("--categories <categories>", "Comma-separated categories to filter ranked search").option("--search_terms <terms>", "Structured search terms for ranked search").option("--search-terms <terms>", "Structured search terms for ranked search").option("--search-mode <mode>", "Ranked search mode: v1 or v2").option("--include-search-debug", "Include ranked search debug metadata").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
7128
+ process.exitCode = await searchTools(query, {
7129
+ json: options.json,
7130
+ categories: options.categories,
7131
+ searchTerms: options.searchTerms ?? options.search_terms,
7132
+ searchMode: options.searchMode === "v1" || options.searchMode === "v2" ? options.searchMode : void 0,
7133
+ includeSearchDebug: Boolean(options.includeSearchDebug)
7134
+ });
6586
7135
  });
6587
7136
  tools.command("get <toolId>").alias("describe").description("Show metadata for a tool.").addHelpText(
6588
7137
  "after",
@@ -6664,7 +7213,7 @@ function printToolDetails(tool, requestedToolId) {
6664
7213
  const operation = typeof tool.operation === "string" ? tool.operation : "";
6665
7214
  const displayBase = operation && operation.startsWith(`${tool.provider}_`) ? operation.slice(String(tool.provider).length + 1) : operation ? `${tool.provider} ${operation}`.trim() : toolId;
6666
7215
  const displayName = titleCase(displayBase || String(tool.displayName || toolId));
6667
- const cost = isRecord2(tool.cost) ? tool.cost : null;
7216
+ const cost = isRecord3(tool.cost) ? tool.cost : null;
6668
7217
  const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
6669
7218
  const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
6670
7219
  const deeplineUsdPerCredit = numberField(tool, "deeplineUsdPerCredit", "deepline_usd_per_credit");
@@ -6709,7 +7258,7 @@ function printToolDetails(tool, requestedToolId) {
6709
7258
  if (stepContributions.length) {
6710
7259
  console.log(" step contributions:");
6711
7260
  for (const item of stepContributions) {
6712
- if (!isRecord2(item)) continue;
7261
+ if (!isRecord3(item)) continue;
6713
7262
  const stepTool = typeof item.tool === "string" ? item.tool.trim() : "";
6714
7263
  const low = typeof item.lowCredits === "number" ? item.lowCredits : null;
6715
7264
  const high = typeof item.highCredits === "number" ? item.highCredits : null;
@@ -6796,12 +7345,12 @@ function printToolCost(input) {
6796
7345
  return false;
6797
7346
  }
6798
7347
  function toolInputFieldsForDisplay(inputSchema) {
6799
- if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord2);
6800
- const jsonSchema = isRecord2(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
6801
- const properties = isRecord2(jsonSchema.properties) ? jsonSchema.properties : {};
7348
+ if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord3);
7349
+ const jsonSchema = isRecord3(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
7350
+ const properties = isRecord3(jsonSchema.properties) ? jsonSchema.properties : {};
6802
7351
  const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
6803
7352
  return Object.entries(properties).map(([name, value]) => {
6804
- const property = isRecord2(value) ? value : {};
7353
+ const property = isRecord3(value) ? value : {};
6805
7354
  return {
6806
7355
  name,
6807
7356
  type: typeof property.type === "string" ? property.type : "unknown",
@@ -6828,7 +7377,7 @@ function printJsonPreview(label, payload) {
6828
7377
  }
6829
7378
  function samplePayload(samples, key) {
6830
7379
  const entry = samples[key];
6831
- if (!isRecord2(entry)) return void 0;
7380
+ if (!isRecord3(entry)) return void 0;
6832
7381
  return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
6833
7382
  }
6834
7383
  function isPlayTool(tool) {
@@ -6849,7 +7398,7 @@ function formatDecimal(value) {
6849
7398
  function formatUsd(value) {
6850
7399
  return `$${formatDecimal(value)}`;
6851
7400
  }
6852
- function isRecord2(value) {
7401
+ function isRecord3(value) {
6853
7402
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
6854
7403
  }
6855
7404
  function stringField(source, ...keys) {
@@ -6876,7 +7425,7 @@ function arrayField(source, ...keys) {
6876
7425
  function recordField(source, ...keys) {
6877
7426
  for (const key of keys) {
6878
7427
  const value = source[key];
6879
- if (isRecord2(value)) return value;
7428
+ if (isRecord3(value)) return value;
6880
7429
  }
6881
7430
  return {};
6882
7431
  }
@@ -6940,6 +7489,61 @@ function parseExecuteOptions(args) {
6940
7489
  }
6941
7490
  return { toolId, params, outputFormat, noPreview };
6942
7491
  }
7492
+ function safeFileStem(value) {
7493
+ return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
7494
+ }
7495
+ function shellQuote(value) {
7496
+ return `'${value.replace(/'/g, `'\\''`)}'`;
7497
+ }
7498
+ function powerShellQuote(value) {
7499
+ return `'${value.replace(/'/g, "''")}'`;
7500
+ }
7501
+ function seedToolListScript(input) {
7502
+ const stem = safeFileStem(input.toolId);
7503
+ const fileName = `${stem}-workflow-seed-${Date.now()}.play.ts`;
7504
+ const scriptDir = (0, import_node_fs8.mkdtempSync)((0, import_node_path10.join)((0, import_node_os7.tmpdir)(), "deepline-workflow-seed-"));
7505
+ (0, import_node_fs8.chmodSync)(scriptDir, 448);
7506
+ const scriptPath = (0, import_node_path10.join)(scriptDir, fileName);
7507
+ const projectDir = `deepline/projects/${stem}-workflow`;
7508
+ const playName = `${stem}-workflow`;
7509
+ const sampleRows = input.rows.length > 0 ? `${JSON.stringify(input.rows.slice(0, 2)).replace(/\]$/, "")}, ...]` : "[]";
7510
+ const columns = Object.keys(input.rows[0] ?? {}).join(", ");
7511
+ const rowKey = Object.prototype.hasOwnProperty.call(input.rows[0] ?? {}, "id") ? '"id"' : "(row) => JSON.stringify(row)";
7512
+ const script = `import { definePlay } from 'deepline';
7513
+
7514
+ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
7515
+ const result = await ctx.tools.execute({
7516
+ id: ${JSON.stringify(input.toolId)},
7517
+ tool: ${JSON.stringify(input.toolId)},
7518
+ input: ${JSON.stringify(input.payload)},
7519
+ description: ${JSON.stringify(`Seed ${input.toolId} rows for workflow expansion.`)},
7520
+ });
7521
+
7522
+ const list = Object.values(result.lists)[0];
7523
+ const rows = (list?.get() ?? []).slice(0, 100);
7524
+ // ${sampleRows}
7525
+ // columns: ${columns}
7526
+ // .step('email_waterfall', (row, rowCtx) => rowCtx.runPlay('name_domain_email', 'name-and-domain-to-email', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), domain: String(row.domain ?? '') }, { description: 'Resolve email.' }))
7527
+ // .step('phone_waterfall', (row, rowCtx) => rowCtx.runPlay('contact_phone', 'contact-to-phone', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), email: String(row.email ?? '') }, { description: 'Resolve phone.' }))
7528
+ // ctx.map is idempotent by map key + row key; reruns reuse completed rows.
7529
+ const enrichedData = await ctx
7530
+ .map('enriched_data', rows, { key: ${rowKey} })
7531
+ .run({ description: 'Enrich seeded rows.' });
7532
+
7533
+ return {
7534
+ rows: enrichedData,
7535
+ count: await enrichedData.count(),
7536
+ };
7537
+ });
7538
+ `;
7539
+ (0, import_node_fs8.writeFileSync)(scriptPath, script, { encoding: "utf-8", mode: 384 });
7540
+ return {
7541
+ path: scriptPath,
7542
+ projectDir,
7543
+ macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
7544
+ windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
7545
+ };
7546
+ }
6943
7547
  async function executeTool(args) {
6944
7548
  let parsed;
6945
7549
  try {
@@ -6967,7 +7571,7 @@ async function executeTool(args) {
6967
7571
  }
6968
7572
  throw error;
6969
7573
  }
6970
- const rawResponse = await client.executeToolRaw(parsed.toolId, parsed.params);
7574
+ const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
6971
7575
  const listConversion = tryConvertToList(rawResponse, {
6972
7576
  listExtractorPaths: metadata.listExtractorPaths ?? []
6973
7577
  });
@@ -6993,6 +7597,11 @@ async function executeTool(args) {
6993
7597
  return 0;
6994
7598
  }
6995
7599
  const csv = writeCsvOutputFile(listConversion.rows, `${parsed.toolId}_output`);
7600
+ const seededScript = seedToolListScript({
7601
+ toolId: parsed.toolId,
7602
+ payload: parsed.params,
7603
+ rows: listConversion.rows
7604
+ });
6996
7605
  if (parsed.outputFormat === "csv_file") {
6997
7606
  process.stdout.write(`${JSON.stringify({
6998
7607
  extracted_csv: csv.path,
@@ -7001,6 +7610,12 @@ async function executeTool(args) {
7001
7610
  preview: csv.preview,
7002
7611
  list_strategy: listConversion.strategy,
7003
7612
  list_source_path: listConversion.sourcePath,
7613
+ starter_script: seededScript.path,
7614
+ project_dir: seededScript.projectDir,
7615
+ copy_to_project: {
7616
+ macos_linux: seededScript.macCopyCommand,
7617
+ windows_powershell: seededScript.windowsCopyCommand
7618
+ },
7004
7619
  summary
7005
7620
  })}
7006
7621
  `);
@@ -7018,14 +7633,20 @@ async function executeTool(args) {
7018
7633
  console.log(`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`);
7019
7634
  }
7020
7635
  console.log(`preview: ${JSON.stringify(csv.preview)}`);
7636
+ console.log(`starter script: ${seededScript.path}`);
7637
+ console.log(
7638
+ "next: Move the script into a project folder and expand it into a Deepline play. Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
7639
+ );
7640
+ console.log(`macOS/Linux: ${seededScript.macCopyCommand}`);
7641
+ console.log(`Windows PowerShell: ${seededScript.windowsCopyCommand}`);
7021
7642
  return 0;
7022
7643
  }
7023
7644
 
7024
7645
  // src/cli/skills-sync.ts
7025
7646
  var import_node_child_process2 = require("child_process");
7026
- var import_node_fs8 = require("fs");
7027
- var import_node_os7 = require("os");
7028
- var import_node_path10 = require("path");
7647
+ var import_node_fs9 = require("fs");
7648
+ var import_node_os8 = require("os");
7649
+ var import_node_path11 = require("path");
7029
7650
  var CHECK_TIMEOUT_MS2 = 3e3;
7030
7651
  var SDK_SKILL_NAME = "deepline-sdk";
7031
7652
  var SKILL_AGENTS = ["codex", "claude-code", "cursor"];
@@ -7035,22 +7656,22 @@ function shouldSkipSkillsSync() {
7035
7656
  return value === "1" || value === "true" || value === "yes" || value === "on";
7036
7657
  }
7037
7658
  function sdkSkillsVersionPath(baseUrl) {
7038
- const home = process.env.HOME?.trim() || (0, import_node_os7.homedir)();
7039
- return (0, import_node_path10.join)(home, ".local", "deepline", baseUrlSlug(baseUrl), "sdk-skills", ".version");
7659
+ const home = process.env.HOME?.trim() || (0, import_node_os8.homedir)();
7660
+ return (0, import_node_path11.join)(home, ".local", "deepline", baseUrlSlug(baseUrl), "sdk-skills", ".version");
7040
7661
  }
7041
7662
  function readLocalSkillsVersion(baseUrl) {
7042
7663
  const path = sdkSkillsVersionPath(baseUrl);
7043
- if (!(0, import_node_fs8.existsSync)(path)) return "";
7664
+ if (!(0, import_node_fs9.existsSync)(path)) return "";
7044
7665
  try {
7045
- return (0, import_node_fs8.readFileSync)(path, "utf-8").trim();
7666
+ return (0, import_node_fs9.readFileSync)(path, "utf-8").trim();
7046
7667
  } catch {
7047
7668
  return "";
7048
7669
  }
7049
7670
  }
7050
7671
  function writeLocalSkillsVersion(baseUrl, version) {
7051
7672
  const path = sdkSkillsVersionPath(baseUrl);
7052
- (0, import_node_fs8.mkdirSync)((0, import_node_path10.dirname)(path), { recursive: true });
7053
- (0, import_node_fs8.writeFileSync)(path, `${version}
7673
+ (0, import_node_fs9.mkdirSync)((0, import_node_path11.dirname)(path), { recursive: true });
7674
+ (0, import_node_fs9.writeFileSync)(path, `${version}
7054
7675
  `, "utf-8");
7055
7676
  }
7056
7677
  async function fetchSkillsUpdate(baseUrl, localVersion) {
@@ -7081,9 +7702,26 @@ async function fetchSkillsUpdate(baseUrl, localVersion) {
7081
7702
  clearTimeout(timeout);
7082
7703
  }
7083
7704
  }
7084
- function runSkillsInstall(baseUrl) {
7705
+ function buildSkillsInstallArgs(baseUrl) {
7706
+ const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
7707
+ return [
7708
+ "--yes",
7709
+ "skills",
7710
+ "add",
7711
+ packageUrl,
7712
+ "--agents",
7713
+ ...SKILL_AGENTS,
7714
+ "--global",
7715
+ "--yes",
7716
+ "--skill",
7717
+ SDK_SKILL_NAME,
7718
+ "--full-depth"
7719
+ ];
7720
+ }
7721
+ function buildBunxSkillsInstallArgs(baseUrl) {
7085
7722
  const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
7086
- const args = [
7723
+ return [
7724
+ "--bun",
7087
7725
  "skills",
7088
7726
  "add",
7089
7727
  packageUrl,
@@ -7095,8 +7733,40 @@ function runSkillsInstall(baseUrl) {
7095
7733
  SDK_SKILL_NAME,
7096
7734
  "--full-depth"
7097
7735
  ];
7736
+ }
7737
+ function hasCommand(command) {
7738
+ const result = (0, import_node_child_process2.spawnSync)(command, ["--version"], {
7739
+ stdio: "ignore",
7740
+ shell: process.platform === "win32"
7741
+ });
7742
+ return result.status === 0;
7743
+ }
7744
+ function shellQuote2(arg) {
7745
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
7746
+ }
7747
+ function resolveSkillsInstallCommands(baseUrl) {
7748
+ const npxArgs = buildSkillsInstallArgs(baseUrl);
7749
+ const npxInstall = {
7750
+ command: "npx",
7751
+ args: npxArgs,
7752
+ manualCommand: `npx ${npxArgs.map(shellQuote2).join(" ")}`
7753
+ };
7754
+ if (hasCommand("bunx")) {
7755
+ const bunxArgs = buildBunxSkillsInstallArgs(baseUrl);
7756
+ return [
7757
+ {
7758
+ command: "bunx",
7759
+ args: bunxArgs,
7760
+ manualCommand: `bunx ${bunxArgs.map(shellQuote2).join(" ")}`
7761
+ },
7762
+ npxInstall
7763
+ ];
7764
+ }
7765
+ return [npxInstall];
7766
+ }
7767
+ function runOneSkillsInstall(install) {
7098
7768
  return new Promise((resolve8) => {
7099
- const child = (0, import_node_child_process2.spawn)("npx", args, {
7769
+ const child = (0, import_node_child_process2.spawn)(install.command, install.args, {
7100
7770
  stdio: ["ignore", "ignore", "pipe"],
7101
7771
  env: process.env
7102
7772
  });
@@ -7105,37 +7775,111 @@ function runSkillsInstall(baseUrl) {
7105
7775
  stderr += chunk.toString("utf-8");
7106
7776
  });
7107
7777
  child.on("error", (error) => {
7108
- process.stderr.write(`SDK skills sync failed to start: ${error.message}
7109
- `);
7110
- resolve8(false);
7778
+ resolve8({
7779
+ ok: false,
7780
+ detail: `failed to start ${install.command}: ${error.message}`,
7781
+ manualCommand: install.manualCommand
7782
+ });
7111
7783
  });
7112
7784
  child.on("close", (code) => {
7113
7785
  if (code === 0) {
7114
- resolve8(true);
7786
+ resolve8({ ok: true, detail: "", manualCommand: install.manualCommand });
7115
7787
  return;
7116
7788
  }
7117
7789
  const detail = stderr.trim();
7118
- process.stderr.write(
7119
- `SDK skills sync failed${detail ? `: ${detail}` : ""}
7120
- Run manually: npx ${args.map((arg) => arg.includes(" ") ? JSON.stringify(arg) : arg).join(" ")}
7121
- `
7122
- );
7123
- resolve8(false);
7790
+ resolve8({
7791
+ ok: false,
7792
+ detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
7793
+ manualCommand: install.manualCommand
7794
+ });
7124
7795
  });
7125
7796
  });
7126
7797
  }
7798
+ async function runSkillsInstall(baseUrl) {
7799
+ const failures = [];
7800
+ for (const install of resolveSkillsInstallCommands(baseUrl)) {
7801
+ const result = await runOneSkillsInstall(install);
7802
+ if (result.ok) return true;
7803
+ failures.push(result);
7804
+ }
7805
+ const details = failures.map((failure) => failure.detail).filter(Boolean).join("\n");
7806
+ const manualCommand = failures.at(-1)?.manualCommand;
7807
+ process.stderr.write(
7808
+ `SDK skills sync failed${details ? `:
7809
+ ${details}` : ""}
7810
+ ` + (manualCommand ? `Run manually: ${manualCommand}
7811
+ ` : "")
7812
+ );
7813
+ return false;
7814
+ }
7815
+ function writeSdkSkillsStatusLine(line) {
7816
+ const progress = getActiveCliProgress();
7817
+ if (progress) {
7818
+ progress.writeLine(line);
7819
+ return;
7820
+ }
7821
+ process.stderr.write(`${line}
7822
+ `);
7823
+ }
7127
7824
  async function syncSdkSkillsIfNeeded(baseUrl) {
7128
7825
  if (attemptedSync || shouldSkipSkillsSync()) return;
7129
7826
  attemptedSync = true;
7130
7827
  const localVersion = readLocalSkillsVersion(baseUrl);
7131
7828
  const update = await fetchSkillsUpdate(baseUrl, localVersion);
7132
7829
  if (!update?.needsUpdate || !update.remoteVersion) return;
7133
- const progress = getActiveCliProgress();
7134
- progress?.writeLine("SDK skills changed; syncing deepline-sdk skill...") ?? process.stderr.write("SDK skills changed; syncing deepline-sdk skill...\n");
7830
+ writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-sdk skill...");
7135
7831
  const installed = await runSkillsInstall(baseUrl);
7136
7832
  if (!installed) return;
7137
7833
  writeLocalSkillsVersion(baseUrl, update.remoteVersion);
7138
- progress?.writeLine("SDK skills are up to date.") ?? process.stderr.write("SDK skills are up to date.\n");
7834
+ writeSdkSkillsStatusLine("SDK skills are up to date.");
7835
+ }
7836
+
7837
+ // src/cli/trace.ts
7838
+ var cliTraceStartedAt = Date.now();
7839
+ function isTruthyEnv(value) {
7840
+ return value === "1" || value === "true" || value === "yes";
7841
+ }
7842
+ function isCliTraceEnabled() {
7843
+ return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
7844
+ }
7845
+ function recordCliTrace(event) {
7846
+ if (!isCliTraceEnabled()) {
7847
+ return;
7848
+ }
7849
+ const now = Date.now();
7850
+ const payload = {
7851
+ ts: now,
7852
+ source: "cli",
7853
+ sinceStartMs: now - cliTraceStartedAt,
7854
+ ...event
7855
+ };
7856
+ process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
7857
+ `);
7858
+ }
7859
+ async function traceCliSpan(phase, fields, run) {
7860
+ if (!isCliTraceEnabled()) {
7861
+ return run();
7862
+ }
7863
+ const startedAt = Date.now();
7864
+ try {
7865
+ const result = await run();
7866
+ recordCliTrace({
7867
+ phase,
7868
+ ms: Date.now() - startedAt,
7869
+ ok: true,
7870
+ ...fields
7871
+ });
7872
+ return result;
7873
+ } catch (error) {
7874
+ recordCliTrace({
7875
+ phase,
7876
+ ms: Date.now() - startedAt,
7877
+ ok: false,
7878
+ error: error instanceof Error ? error.message : String(error),
7879
+ ...fields
7880
+ });
7881
+ throw error;
7882
+ }
7139
7883
  }
7140
7884
 
7141
7885
  // src/cli/index.ts
@@ -7159,7 +7903,7 @@ async function main() {
7159
7903
  if (printStartupPhase) {
7160
7904
  progress?.phase("loading deepline cli");
7161
7905
  }
7162
- const program = new import_commander2.Command();
7906
+ const program = new import_commander.Command();
7163
7907
  program.name("deepline").description("Deepline CLI (TypeScript SDK)").version(SDK_VERSION, "-v, --version", "Show version").showHelpAfterError().showSuggestionAfterError(true).addHelpText(
7164
7908
  "after",
7165
7909
  `
@@ -7249,4 +7993,3 @@ Output:
7249
7993
  process.exit(process.exitCode ?? 0);
7250
7994
  }
7251
7995
  main();
7252
- //# sourceMappingURL=index.js.map