deepline 0.1.10 → 0.1.12

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 (34) hide show
  1. package/README.md +4 -4
  2. package/dist/cli/index.js +509 -353
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/cli/index.mjs +513 -358
  5. package/dist/cli/index.mjs.map +1 -1
  6. package/dist/index.d.mts +250 -305
  7. package/dist/index.d.ts +250 -305
  8. package/dist/index.js +174 -286
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +174 -285
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +23 -13
  13. package/dist/repo/apps/play-runner-workers/src/entry.ts +581 -1220
  14. package/dist/repo/sdk/src/cli/commands/play.ts +381 -247
  15. package/dist/repo/sdk/src/cli/commands/tools.ts +1 -1
  16. package/dist/repo/sdk/src/cli/dataset-stats.ts +86 -12
  17. package/dist/repo/sdk/src/client.ts +54 -51
  18. package/dist/repo/sdk/src/index.ts +7 -16
  19. package/dist/repo/sdk/src/play.ts +122 -135
  20. package/dist/repo/sdk/src/plays/bundle-play-file.ts +6 -3
  21. package/dist/repo/sdk/src/tool-output.ts +0 -111
  22. package/dist/repo/sdk/src/types.ts +2 -0
  23. package/dist/repo/sdk/src/version.ts +1 -1
  24. package/dist/repo/sdk/src/worker-play-entry.ts +3 -0
  25. package/dist/repo/shared_libs/play-runtime/context.ts +510 -267
  26. package/dist/repo/shared_libs/play-runtime/csv-rename.ts +180 -0
  27. package/dist/repo/shared_libs/play-runtime/ctx-types.ts +13 -1
  28. package/dist/repo/shared_libs/play-runtime/tool-result.ts +139 -114
  29. package/dist/repo/shared_libs/plays/bundling/index.ts +68 -5
  30. package/dist/repo/shared_libs/plays/compiler-manifest.ts +1 -1
  31. package/dist/repo/shared_libs/plays/dataset.ts +1 -1
  32. package/dist/repo/shared_libs/plays/runtime-validation.ts +8 -28
  33. package/package.json +1 -1
  34. package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +0 -184
@@ -628,7 +628,7 @@ async function executeTool(args: string[]): Promise<number> {
628
628
  }
629
629
  throw error;
630
630
  }
631
- const rawResponse = await client.executeToolRaw(parsed.toolId, parsed.params);
631
+ const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
632
632
 
633
633
  const listConversion = tryConvertToList(rawResponse, {
634
634
  listExtractorPaths: metadata.listExtractorPaths ?? [],
@@ -35,6 +35,66 @@ export type CanonicalRowsInfo = {
35
35
  source: string | null;
36
36
  };
37
37
 
38
+ const CSV_PROJECTED_FIELDS_KEY = '__deeplineCsvProjectedFields';
39
+
40
+ function csvProjectedFields(row: Record<string, unknown>): Set<string> {
41
+ const serialized = row[CSV_PROJECTED_FIELDS_KEY];
42
+ if (!Array.isArray(serialized)) {
43
+ return new Set();
44
+ }
45
+ return new Set(
46
+ serialized.filter((field): field is string => typeof field === 'string'),
47
+ );
48
+ }
49
+
50
+ function stripCsvProjectionFields(
51
+ row: Record<string, unknown>,
52
+ ): Record<string, unknown> {
53
+ const projectedFields = csvProjectedFields(row);
54
+ if (projectedFields.size === 0 && !(CSV_PROJECTED_FIELDS_KEY in row)) {
55
+ return row;
56
+ }
57
+ const stripped = { ...row };
58
+ for (const field of projectedFields) {
59
+ delete stripped[field];
60
+ }
61
+ delete stripped[CSV_PROJECTED_FIELDS_KEY];
62
+ return stripped;
63
+ }
64
+
65
+ function stripCsvProjectionColumns(
66
+ columns: string[],
67
+ rows: Array<Record<string, unknown>>,
68
+ ): string[] {
69
+ const projectedFields = new Set<string>();
70
+ let hasProjectionMetadata = false;
71
+ for (const row of rows) {
72
+ for (const field of csvProjectedFields(row)) {
73
+ projectedFields.add(field);
74
+ }
75
+ hasProjectionMetadata ||= CSV_PROJECTED_FIELDS_KEY in row;
76
+ }
77
+ if (!hasProjectionMetadata && projectedFields.size === 0) {
78
+ return columns;
79
+ }
80
+ return columns.filter(
81
+ (column) =>
82
+ column !== CSV_PROJECTED_FIELDS_KEY && !projectedFields.has(column),
83
+ );
84
+ }
85
+
86
+ function sanitizeCsvProjectionInfo(input: {
87
+ rows: Array<Record<string, unknown>>;
88
+ columns: string[];
89
+ }): {
90
+ rows: Array<Record<string, unknown>>;
91
+ columns: string[];
92
+ } {
93
+ const columns = stripCsvProjectionColumns(input.columns, input.rows);
94
+ const rows = input.rows.map(stripCsvProjectionFields);
95
+ return { rows, columns };
96
+ }
97
+
38
98
  function isRecord(value: unknown): value is Record<string, unknown> {
39
99
  return Boolean(value && typeof value === 'object' && !Array.isArray(value));
40
100
  }
@@ -118,16 +178,21 @@ export function extractCanonicalRowsInfo(statusOrResult: unknown): CanonicalRows
118
178
 
119
179
  for (const candidate of candidates) {
120
180
  if (isSerializedDataset(candidate.value)) {
121
- const rows = rowArray(candidate.value.preview) ?? [];
122
- const totalRows = readNumber(candidate.value.count) ?? rows.length;
181
+ const rawRows = rowArray(candidate.value.preview) ?? [];
182
+ const totalRows = readNumber(candidate.value.count) ?? rawRows.length;
183
+ const rawColumns =
184
+ Array.isArray(candidate.value.columns) &&
185
+ candidate.value.columns.every((column) => typeof column === 'string')
186
+ ? (candidate.value.columns as string[])
187
+ : inferColumns(rawRows);
188
+ const { rows, columns } = sanitizeCsvProjectionInfo({
189
+ rows: rawRows,
190
+ columns: rawColumns,
191
+ });
123
192
  return {
124
193
  rows,
125
194
  totalRows,
126
- columns:
127
- Array.isArray(candidate.value.columns) &&
128
- candidate.value.columns.every((column) => typeof column === 'string')
129
- ? (candidate.value.columns as string[])
130
- : inferColumns(rows),
195
+ columns,
131
196
  complete: rows.length === totalRows,
132
197
  source: candidate.source,
133
198
  };
@@ -138,10 +203,14 @@ export function extractCanonicalRowsInfo(statusOrResult: unknown): CanonicalRows
138
203
  continue;
139
204
  }
140
205
  const totalRows = readNumber(candidate.total) ?? rows.length;
141
- return {
206
+ const sanitized = sanitizeCsvProjectionInfo({
142
207
  rows,
143
- totalRows,
144
208
  columns: inferColumns(rows),
209
+ });
210
+ return {
211
+ rows: sanitized.rows,
212
+ totalRows,
213
+ columns: sanitized.columns,
145
214
  complete: rows.length === totalRows,
146
215
  source: candidate.source,
147
216
  };
@@ -261,15 +330,16 @@ export function buildDatasetStats(
261
330
  totalRows = rows.length,
262
331
  columns = inferColumns(rows),
263
332
  ): DatasetStats {
333
+ const sanitized = sanitizeCsvProjectionInfo({ rows, columns });
264
334
  const columnStats: DatasetStats['columnStats'] = {};
265
- for (const column of columns) {
335
+ for (const column of sanitized.columns) {
266
336
  let nonEmpty = 0;
267
337
  let empty = 0;
268
338
  let sampleValue: unknown;
269
339
  let sampleValueType: string | null = null;
270
340
  const valueCounts = new Map<string, number>();
271
341
 
272
- for (const row of rows) {
342
+ for (const row of sanitized.rows) {
273
343
  const raw = row[column];
274
344
  const value = compactCell(raw);
275
345
  if (value) {
@@ -331,10 +401,14 @@ export function writeCanonicalRowsCsv(
331
401
  `Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; cannot export a complete CSV from this status payload yet.`,
332
402
  );
333
403
  }
404
+ const sanitized = sanitizeCsvProjectionInfo({
405
+ rows: rowsInfo.rows,
406
+ columns: rowsInfo.columns,
407
+ });
334
408
  const resolved = resolve(outPath);
335
409
  writeFileSync(
336
410
  resolved,
337
- csvStringFromRows(rowsInfo.rows, rowsInfo.columns),
411
+ csvStringFromRows(sanitized.rows, sanitized.columns),
338
412
  'utf-8',
339
413
  );
340
414
  return resolved;
@@ -64,6 +64,21 @@ import type { PlayCompilerManifest } from '../../shared_libs/plays/compiler-mani
64
64
 
65
65
  const TERMINAL_PLAY_STATUSES = new Set(['completed', 'failed', 'cancelled']);
66
66
 
67
+ export type ToolExecution<TData = unknown, TMeta = Record<string, unknown>> = {
68
+ status: string;
69
+ job_id?: string;
70
+ result: {
71
+ data: TData;
72
+ meta?: TMeta;
73
+ };
74
+ billing?: Record<string, unknown>;
75
+ [key: string]: unknown;
76
+ };
77
+
78
+ function isRecord(value: unknown): value is Record<string, unknown> {
79
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
80
+ }
81
+
67
82
  function normalizePlayStatus(raw: Record<string, unknown>): PlayStatus {
68
83
  const status =
69
84
  typeof raw.status === 'string'
@@ -159,8 +174,29 @@ export class DeeplineClient {
159
174
  return fields.length > 0 ? { fields } : schema;
160
175
  }
161
176
 
162
- private playRunCommand(name: string): string {
163
- return `deepline plays run ${name} --input '{...}' --watch`;
177
+ private schemaMetadata(
178
+ schema: Record<string, unknown> | null | undefined,
179
+ key: string,
180
+ ): Record<string, unknown> | null {
181
+ if (!isRecord(schema)) return null;
182
+ const value = schema[key];
183
+ return isRecord(value) ? value : null;
184
+ }
185
+
186
+ private playRunCommand(
187
+ play: Pick<PlayListItem, 'name' | 'reference'>,
188
+ options?: { csvInput?: Record<string, unknown> | null },
189
+ ): string {
190
+ const target = play.reference || play.name;
191
+ if (options?.csvInput) {
192
+ const inputField =
193
+ typeof options.csvInput.inputField === 'string' &&
194
+ options.csvInput.inputField.trim()
195
+ ? options.csvInput.inputField.trim()
196
+ : 'csv';
197
+ return `deepline plays run ${target} --${inputField} leads.csv --watch`;
198
+ }
199
+ return `deepline plays run ${target} --input '{...}' --watch`;
164
200
  }
165
201
 
166
202
  private summarizePlayListItem(
@@ -168,7 +204,12 @@ export class DeeplineClient {
168
204
  options?: { compact?: boolean },
169
205
  ): PlayDescription {
170
206
  const aliases = play.aliases?.length ? play.aliases : [play.name];
171
- const runCommand = this.playRunCommand(play.name);
207
+ const csvInput = this.schemaMetadata(play.inputSchema, 'csvInput');
208
+ const rowOutputSchema = this.schemaMetadata(
209
+ play.outputSchema,
210
+ 'rowOutputSchema',
211
+ );
212
+ const runCommand = this.playRunCommand(play, { csvInput });
172
213
  return {
173
214
  name: play.name,
174
215
  ...(play.reference ? { reference: play.reference } : {}),
@@ -184,6 +225,8 @@ export class DeeplineClient {
184
225
  outputSchema: options?.compact
185
226
  ? this.compactSchema(play.outputSchema)
186
227
  : (play.outputSchema ?? null),
228
+ ...(csvInput ? { csvInput } : {}),
229
+ ...(rowOutputSchema ? { rowOutputSchema } : {}),
187
230
  runCommand,
188
231
  examples: [runCommand],
189
232
  currentPublishedVersion: play.currentPublishedVersion ?? null,
@@ -259,58 +302,18 @@ export class DeeplineClient {
259
302
  }
260
303
 
261
304
  /**
262
- * Execute a tool and return the extracted result.
305
+ * Execute a tool and return the standard execution envelope.
263
306
  *
264
- * Sends the input payload to the tool and returns the `.result` field from the
265
- * response. For the full response envelope (including job_id, credits, etc.),
266
- * use {@link executeToolRaw}.
267
- *
268
- * @param toolId - Tool identifier (e.g. `"test_company_search"`)
269
- * @param input - Tool-specific input parameters
270
- * @returns The tool's output (shape varies by tool)
271
- * @throws {@link DeeplineError} if the tool execution fails
272
- *
273
- * @example
274
- * ```typescript
275
- * const company = await client.executeTool('test_company_search', {
276
- * domain: 'stripe.com',
277
- * });
278
- * console.log(company); // { name: "Stripe", industry: "Financial Services", ... }
279
- * ```
280
- */
281
- async executeTool(
282
- toolId: string,
283
- input: Record<string, unknown>,
284
- ): Promise<unknown> {
285
- const res = await this.http.post<{ result: unknown }>(
286
- `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
287
- { payload: input },
288
- );
289
- return res.result ?? res;
290
- }
291
-
292
- /**
293
- * Execute a tool and return the full response envelope.
294
- *
295
- * Unlike {@link executeTool}, this returns the complete API response including
296
- * `job_id`, `status`, `credits`, and the raw `result` object.
297
- *
298
- * @param toolId - Tool identifier
299
- * @param input - Tool-specific input parameters
300
- * @returns Full response with job metadata and result
301
- *
302
- * @example
303
- * ```typescript
304
- * const raw = await client.executeToolRaw('test_company_search', { domain: 'stripe.com' });
305
- * console.log(`Job: ${raw.job_id}, Credits: ${raw.credits}`);
306
- * console.log(`Result:`, raw.result);
307
- * ```
307
+ * The `result.data` field contains the provider payload. `result.meta`
308
+ * contains provider/upstream metadata such as HTTP status or paging details.
309
+ * Top-level fields such as `status`, `job_id`, and `billing` describe the
310
+ * Deepline execution.
308
311
  */
309
- async executeToolRaw(
312
+ async executeTool<TData = unknown, TMeta = Record<string, unknown>>(
310
313
  toolId: string,
311
314
  input: Record<string, unknown>,
312
- ): Promise<Record<string, unknown>> {
313
- return this.http.post<Record<string, unknown>>(
315
+ ): Promise<ToolExecution<TData, TMeta>> {
316
+ return this.http.post<ToolExecution<TData, TMeta>>(
314
317
  `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
315
318
  { payload: input },
316
319
  );
@@ -10,11 +10,8 @@
10
10
  * const ctx = await Deepline.connect();
11
11
  *
12
12
  * // Execute a tool
13
- * const result = await ctx.tools.execute({
14
- * tool: 'test_company_search',
15
- * input: { domain: 'stripe.com' },
16
- * });
17
- * const company = result.value;
13
+ * const result = await ctx.tools.execute('test_company_search', { domain: 'stripe.com' });
14
+ * const company = result.result.data;
18
15
  *
19
16
  * // Run a named play
20
17
  * const job = await ctx.play('email-waterfall').run({ domain: 'stripe.com' });
@@ -28,10 +25,7 @@
28
25
  *
29
26
  * export default definePlay('my-play', async (ctx, input: { domain: string }) => {
30
27
  * ctx.log(`Looking up ${input.domain}`);
31
- * const company = await ctx.tools.execute({
32
- * id: 'company_search',
33
- * tool: 'test_company_search',
34
- * input: { domain: input.domain },
28
+ * const company = await ctx.tools.execute('company_search', 'test_company_search', { domain: input.domain }, {
35
29
  * description: 'Look up company details by domain.',
36
30
  * });
37
31
  * return company;
@@ -58,7 +52,7 @@
58
52
 
59
53
  // ——— Client ———
60
54
  export { DeeplineClient } from './client.js';
61
- export type { PlayStatus } from './client.js';
55
+ export type { PlayStatus, ToolExecution } from './client.js';
62
56
  export { SDK_API_CONTRACT, SDK_VERSION } from './version.js';
63
57
 
64
58
  // ——— Errors ———
@@ -86,15 +80,12 @@ export {
86
80
 
87
81
  // ——— Tool output processing ———
88
82
  export {
89
- createToolCallResult,
90
83
  tryConvertToList,
91
84
  writeCsvOutputFile,
92
85
  writeJsonOutputFile,
93
86
  extractSummaryFields,
94
87
  } from './tool-output.js';
95
88
 
96
- export type { ToolCallResult } from './tool-output.js';
97
-
98
89
  // ——— Types (re-exported for consumers) ———
99
90
  export type {
100
91
  DeeplineClientOptions,
@@ -119,13 +110,13 @@ export type {
119
110
  DeeplineNamedPlay,
120
111
  DeeplinePlayRuntimeContext,
121
112
  DefinedPlay,
113
+ ColumnMap,
114
+ CsvInput,
122
115
  PlayBindings,
116
+ FileInput,
123
117
  PlayInputContract,
124
118
  PlayJob,
125
119
  ConditionalStepResolver,
126
- MapDefinitionOptions,
127
- MapRowKey,
128
- MapRunOptions,
129
120
  MapStepBuilder,
130
121
  MapStepResolver,
131
122
  PlayDataset,