deepline 0.1.12 → 0.1.20

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 (82) hide show
  1. package/README.md +14 -6
  2. package/dist/cli/index.js +1346 -717
  3. package/dist/cli/index.mjs +1342 -713
  4. package/dist/index.d.mts +199 -23
  5. package/dist/index.d.ts +199 -23
  6. package/dist/index.js +221 -14
  7. package/dist/index.mjs +221 -14
  8. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +214 -77
  9. package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +85 -60
  10. package/dist/repo/apps/play-runner-workers/src/entry.ts +385 -66
  11. package/dist/repo/sdk/src/client.ts +237 -0
  12. package/dist/repo/sdk/src/config.ts +125 -8
  13. package/dist/repo/sdk/src/http.ts +29 -5
  14. package/dist/repo/sdk/src/play.ts +19 -36
  15. package/dist/repo/sdk/src/plays/bundle-play-file.ts +22 -8
  16. package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
  17. package/dist/repo/sdk/src/types.ts +25 -0
  18. package/dist/repo/sdk/src/version.ts +2 -2
  19. package/dist/repo/shared_libs/play-runtime/tool-result.ts +237 -145
  20. package/dist/repo/shared_libs/plays/bundling/index.ts +206 -229
  21. package/dist/repo/shared_libs/plays/dataset.ts +28 -0
  22. package/dist/repo/shared_libs/plays/row-identity.ts +59 -4
  23. package/package.json +5 -4
  24. package/dist/cli/index.js.map +0 -1
  25. package/dist/cli/index.mjs.map +0 -1
  26. package/dist/index.js.map +0 -1
  27. package/dist/index.mjs.map +0 -1
  28. package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
  29. package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
  30. package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
  31. package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
  32. package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
  33. package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
  34. package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
  35. package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
  36. package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
  37. package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
  38. package/dist/repo/sdk/src/cli/commands/play.ts +0 -3441
  39. package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
  40. package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -415
  41. package/dist/repo/sdk/src/cli/index.ts +0 -148
  42. package/dist/repo/sdk/src/cli/progress.ts +0 -149
  43. package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
  44. package/dist/repo/sdk/src/cli/trace.ts +0 -61
  45. package/dist/repo/sdk/src/cli/utils.ts +0 -145
  46. package/dist/repo/sdk/src/compat.ts +0 -77
  47. package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
  48. package/dist/repo/shared_libs/observability/tracing.ts +0 -98
  49. package/dist/repo/shared_libs/play-runtime/context.ts +0 -4242
  50. package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
  51. package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -725
  52. package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
  53. package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
  54. package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
  55. package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
  56. package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
  57. package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
  58. package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
  59. package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
  60. package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
  61. package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
  62. package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
  63. package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
  64. package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
  65. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
  66. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
  67. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
  68. package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
  69. package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
  70. package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
  71. package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
  72. package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
  73. package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
  74. package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
  75. package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
  76. package/dist/repo/shared_libs/plays/definition.ts +0 -264
  77. package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
  78. package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
  79. package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
  80. package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -395
  81. package/dist/repo/shared_libs/temporal/constants.ts +0 -39
  82. package/dist/repo/shared_libs/temporal/preview-config.ts +0 -153
@@ -1,415 +0,0 @@
1
- import { writeFileSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
- import { csvStringFromRows } from './utils.js';
4
-
5
- export type DatasetStats = {
6
- total_rows: number;
7
- columnStats: Record<
8
- string,
9
- {
10
- non_empty: string;
11
- unique: number;
12
- sample_value?: unknown;
13
- sample_type?: string;
14
- top_values?: Record<string, string>;
15
- }
16
- >;
17
- miss_reasons?: Record<string, Record<string, number>>;
18
- miss_details?: Record<
19
- string,
20
- Record<
21
- string,
22
- {
23
- count: number;
24
- examples: Array<{ row: number; detail: string }>;
25
- }
26
- >
27
- >;
28
- };
29
-
30
- export type CanonicalRowsInfo = {
31
- rows: Array<Record<string, unknown>>;
32
- totalRows: number;
33
- columns: string[];
34
- complete: boolean;
35
- source: string | null;
36
- };
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
-
98
- function isRecord(value: unknown): value is Record<string, unknown> {
99
- return Boolean(value && typeof value === 'object' && !Array.isArray(value));
100
- }
101
-
102
- function isSerializedDataset(value: unknown): value is {
103
- kind: 'dataset';
104
- count: number;
105
- preview: unknown[];
106
- columns?: unknown[];
107
- tableNamespace?: unknown;
108
- } {
109
- return (
110
- isRecord(value) &&
111
- value.kind === 'dataset' &&
112
- typeof value.count === 'number' &&
113
- Array.isArray(value.preview)
114
- );
115
- }
116
-
117
- function rowArray(value: unknown): Array<Record<string, unknown>> | null {
118
- if (!Array.isArray(value)) {
119
- return null;
120
- }
121
- const rows = value.filter(
122
- (row): row is Record<string, unknown> => isRecord(row),
123
- );
124
- return rows.length === value.length ? rows : null;
125
- }
126
-
127
- function readNumber(value: unknown): number | null {
128
- return typeof value === 'number' && Number.isFinite(value) && value >= 0
129
- ? Math.trunc(value)
130
- : null;
131
- }
132
-
133
- function inferColumns(rows: Array<Record<string, unknown>>): string[] {
134
- const columns: string[] = [];
135
- const seen = new Set<string>();
136
- for (const row of rows) {
137
- for (const key of Object.keys(row)) {
138
- if (key.startsWith('_')) {
139
- continue;
140
- }
141
- if (!seen.has(key)) {
142
- seen.add(key);
143
- columns.push(key);
144
- }
145
- }
146
- }
147
- return columns;
148
- }
149
-
150
- export function extractCanonicalRowsInfo(statusOrResult: unknown): CanonicalRowsInfo | null {
151
- const root = isRecord(statusOrResult) ? statusOrResult : null;
152
- const result = isRecord(root?.result) ? root.result : root;
153
- if (!result) {
154
- return null;
155
- }
156
- const metadata = isRecord(result._metadata) ? result._metadata : null;
157
- const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
158
-
159
- const candidates: Array<{ source: string; value: unknown; total?: unknown }> = [
160
- { source: 'result.contacts', value: result.contacts, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
161
- { source: 'result.previewRows', value: result.previewRows, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
162
- { source: 'result.rows', value: result.rows, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
163
- { source: 'result.results', value: result.results, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
164
- ];
165
- if (isRecord(result.output)) {
166
- const outputMetadata = isRecord(result.output._metadata)
167
- ? result.output._metadata
168
- : null;
169
- const outputTotalFromMetadata =
170
- outputMetadata?.totalRows ?? outputMetadata?.rowCount ?? outputMetadata?.count;
171
- candidates.push(
172
- { source: 'result.output.contacts', value: result.output.contacts, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count },
173
- { source: 'result.output.previewRows', value: result.output.previewRows, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count },
174
- { source: 'result.output.rows', value: result.output.rows, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count },
175
- { source: 'result.output.results', value: result.output.results, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count },
176
- );
177
- }
178
-
179
- for (const candidate of candidates) {
180
- if (isSerializedDataset(candidate.value)) {
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
- });
192
- return {
193
- rows,
194
- totalRows,
195
- columns,
196
- complete: rows.length === totalRows,
197
- source: candidate.source,
198
- };
199
- }
200
-
201
- const rows = rowArray(candidate.value);
202
- if (!rows) {
203
- continue;
204
- }
205
- const totalRows = readNumber(candidate.total) ?? rows.length;
206
- const sanitized = sanitizeCsvProjectionInfo({
207
- rows,
208
- columns: inferColumns(rows),
209
- });
210
- return {
211
- rows: sanitized.rows,
212
- totalRows,
213
- columns: sanitized.columns,
214
- complete: rows.length === totalRows,
215
- source: candidate.source,
216
- };
217
- }
218
-
219
- return null;
220
- }
221
-
222
- function percentText(numerator: number, denominator: number): string {
223
- return denominator > 0
224
- ? `${numerator}/${denominator} (${Math.round((100 * numerator) / denominator)}%)`
225
- : '0/0 (0%)';
226
- }
227
-
228
- function countPercentText(count: number, denominator: number): string {
229
- return denominator > 0
230
- ? `${count} (${Math.round((100 * count) / denominator)}%)`
231
- : '0 (0%)';
232
- }
233
-
234
- function parseJsonLike(value: unknown): unknown {
235
- if (typeof value !== 'string') {
236
- return value;
237
- }
238
- const text = value.trim();
239
- if (!text || !['{', '['].includes(text[0] ?? '')) {
240
- return value;
241
- }
242
- try {
243
- return JSON.parse(text);
244
- } catch {
245
- return value;
246
- }
247
- }
248
-
249
- function sampleType(value: unknown): string {
250
- const parsed = parseJsonLike(value);
251
- if (parsed === null || parsed === undefined) return 'null';
252
- if (typeof parsed === 'boolean') return 'boolean';
253
- if (Number.isInteger(parsed)) return 'integer';
254
- if (typeof parsed === 'number') return 'number';
255
- if (Array.isArray(parsed)) return 'array';
256
- if (typeof parsed === 'object') return 'object';
257
- return 'string';
258
- }
259
-
260
- function compactScalar(value: unknown, maxLength = 200): string {
261
- const text = String(value).replace(/\s+/g, ' ').trim();
262
- return text.length <= maxLength ? text : `${text.slice(0, maxLength - 3)}...`;
263
- }
264
-
265
- function summarizeSampleValue(value: unknown, depth = 0): unknown {
266
- const parsed = parseJsonLike(value);
267
- if (parsed === null || parsed === undefined) return '';
268
- if (typeof parsed === 'string') return compactScalar(parsed);
269
- if (typeof parsed === 'number' || typeof parsed === 'boolean') return parsed;
270
- if (depth >= 3) {
271
- if (Array.isArray(parsed)) return [];
272
- if (isRecord(parsed)) return {};
273
- return compactScalar(parsed);
274
- }
275
- if (Array.isArray(parsed)) {
276
- return parsed.slice(0, 3).map((item) => summarizeSampleValue(item, depth + 1));
277
- }
278
- if (isRecord(parsed)) {
279
- const out: Record<string, unknown> = {};
280
- for (const [key, nested] of Object.entries(parsed)) {
281
- if (['__dl', 'meta', 'metadata'].includes(key)) {
282
- continue;
283
- }
284
- if (nested === null || nested === undefined || nested === '') {
285
- continue;
286
- }
287
- out[key] = summarizeSampleValue(nested, depth + 1);
288
- if (Object.keys(out).length >= 6) {
289
- break;
290
- }
291
- }
292
- return out;
293
- }
294
- return compactScalar(parsed);
295
- }
296
-
297
- function compactCell(value: unknown): string {
298
- const parsed = parseJsonLike(value);
299
- if (parsed === null || parsed === undefined) return '';
300
- if (typeof parsed === 'string') return compactScalar(parsed, 120);
301
- if (typeof parsed === 'number' || typeof parsed === 'boolean') return String(parsed);
302
- if (Array.isArray(parsed)) {
303
- if (parsed.length === 0) return '';
304
- if (parsed.slice(0, 3).every((item) => ['string', 'number', 'boolean'].includes(typeof item))) {
305
- return compactScalar(parsed.slice(0, 3).join(' | '), 120);
306
- }
307
- return `[${parsed.length} items]`;
308
- }
309
- if (isRecord(parsed)) {
310
- for (const key of ['matched_result', 'output']) {
311
- if (parsed[key] !== null && parsed[key] !== undefined && parsed[key] !== '') {
312
- return compactCell(parsed[key]);
313
- }
314
- }
315
- const preferred = ['email', 'status', 'name', 'full_name', 'title', 'domain', 'linkedin_url'];
316
- const parts: string[] = [];
317
- for (const key of preferred) {
318
- if (parsed[key] !== null && parsed[key] !== undefined && parsed[key] !== '') {
319
- parts.push(`${key}=${compactCell(parsed[key])}`);
320
- }
321
- if (parts.length >= 4) break;
322
- }
323
- return parts.length > 0 ? compactScalar(parts.join(' | '), 160) : '{}';
324
- }
325
- return compactScalar(parsed, 120);
326
- }
327
-
328
- export function buildDatasetStats(
329
- rows: Array<Record<string, unknown>>,
330
- totalRows = rows.length,
331
- columns = inferColumns(rows),
332
- ): DatasetStats {
333
- const sanitized = sanitizeCsvProjectionInfo({ rows, columns });
334
- const columnStats: DatasetStats['columnStats'] = {};
335
- for (const column of sanitized.columns) {
336
- let nonEmpty = 0;
337
- let empty = 0;
338
- let sampleValue: unknown;
339
- let sampleValueType: string | null = null;
340
- const valueCounts = new Map<string, number>();
341
-
342
- for (const row of sanitized.rows) {
343
- const raw = row[column];
344
- const value = compactCell(raw);
345
- if (value) {
346
- nonEmpty += 1;
347
- valueCounts.set(value, (valueCounts.get(value) ?? 0) + 1);
348
- if (sampleValue === undefined) {
349
- sampleValue = summarizeSampleValue(raw);
350
- sampleValueType = sampleType(raw);
351
- }
352
- } else {
353
- empty += 1;
354
- }
355
- }
356
-
357
- const denominator = nonEmpty + empty;
358
- const stat: DatasetStats['columnStats'][string] = {
359
- non_empty: percentText(nonEmpty, denominator),
360
- unique: valueCounts.size,
361
- };
362
- if (sampleValue !== undefined && sampleValueType) {
363
- stat.sample_value = sampleValue;
364
- stat.sample_type = sampleValueType;
365
- }
366
- if (valueCounts.size > 0 && valueCounts.size < nonEmpty) {
367
- const top = [...valueCounts.entries()]
368
- .sort((left, right) => right[1] - left[1])
369
- .slice(0, 3);
370
- const topKeys = new Set(top.map(([key]) => key));
371
- const otherCount = [...valueCounts.entries()]
372
- .filter(([key]) => !topKeys.has(key))
373
- .reduce((sum, [, count]) => sum + count, 0);
374
- stat.top_values = Object.fromEntries(
375
- top.map(([key, count]) => [key, countPercentText(count, denominator)]),
376
- );
377
- if (otherCount > 0) {
378
- stat.top_values['(other)'] = countPercentText(otherCount, denominator);
379
- }
380
- if (empty > 0) {
381
- stat.top_values['(null)'] = countPercentText(empty, denominator);
382
- }
383
- } else if (empty > 0 && nonEmpty > 0) {
384
- stat.top_values = { '(null)': countPercentText(empty, denominator) };
385
- }
386
- columnStats[column] = stat;
387
- }
388
-
389
- return {
390
- total_rows: totalRows,
391
- columnStats,
392
- };
393
- }
394
-
395
- export function writeCanonicalRowsCsv(
396
- rowsInfo: CanonicalRowsInfo,
397
- outPath: string,
398
- ): string {
399
- if (!rowsInfo.complete) {
400
- throw new Error(
401
- `Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; cannot export a complete CSV from this status payload yet.`,
402
- );
403
- }
404
- const sanitized = sanitizeCsvProjectionInfo({
405
- rows: rowsInfo.rows,
406
- columns: rowsInfo.columns,
407
- });
408
- const resolved = resolve(outPath);
409
- writeFileSync(
410
- resolved,
411
- csvStringFromRows(sanitized.rows, sanitized.columns),
412
- 'utf-8',
413
- );
414
- return resolved;
415
- }
@@ -1,148 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from 'commander';
4
- import { DeeplineClient } from '../client.js';
5
- import { autoDetectBaseUrl } from '../config.js';
6
- import { enforceSdkCompatibility } from '../compat.js';
7
- import { SDK_VERSION } from '../version.js';
8
- import { registerAuthCommands } from './commands/auth.js';
9
- import { registerBillingCommands } from './commands/billing.js';
10
- import { registerCsvCommands } from './commands/csv.js';
11
- import { registerDbCommands } from './commands/db.js';
12
- import { registerFeedbackCommands } from './commands/feedback.js';
13
- import { registerOrgCommands } from './commands/org.js';
14
- import { registerPlayCommands } from './commands/play.js';
15
- import { registerToolsCommands } from './commands/tools.js';
16
- import { createCliProgress } from './progress.js';
17
- import { syncSdkSkillsIfNeeded } from './skills-sync.js';
18
- import { recordCliTrace, traceCliSpan } from './trace.js';
19
- import { printJsonError } from './utils.js';
20
-
21
- function shouldPrintStartupPhase(): boolean {
22
- if (process.argv.includes('--json')) {
23
- return false;
24
- }
25
- const args = process.argv.slice(2);
26
- const command = args[0];
27
- const subcommand = args[1];
28
- return (command === 'play' || command === 'plays') && subcommand === 'run';
29
- }
30
-
31
- async function main(): Promise<void> {
32
- const mainStartedAt = Date.now();
33
- recordCliTrace({
34
- phase: 'cli.main_start',
35
- argv: process.argv.slice(2),
36
- });
37
- const printStartupPhase = shouldPrintStartupPhase();
38
- const progress = printStartupPhase ? createCliProgress(true) : null;
39
- if (printStartupPhase) {
40
- progress?.phase('loading deepline cli');
41
- }
42
- const program = new Command();
43
- program
44
- .name('deepline')
45
- .description('Deepline CLI (TypeScript SDK)')
46
- .version(SDK_VERSION, '-v, --version', 'Show version')
47
- .showHelpAfterError()
48
- .showSuggestionAfterError(true)
49
- .addHelpText(
50
- 'after',
51
- `
52
- Common commands:
53
- deepline health
54
- deepline auth status --json
55
- deepline plays search email --json
56
- deepline plays describe person-linkedin-to-email --json
57
- deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
58
- deepline tools call hunter_email_verifier --input '{"email":"a@b.com"}'
59
-
60
- Output:
61
- Structured commands print human-readable output in a terminal and JSON when stdout is piped.
62
- Use --json to force JSON in an interactive terminal.
63
- `,
64
- );
65
-
66
- program.hook('preAction', async (_thisCommand, actionCommand) => {
67
- if (actionCommand.name() === 'version') {
68
- return;
69
- }
70
- if (printStartupPhase) {
71
- progress?.phase('checking sdk compatibility');
72
- }
73
- const baseUrl = autoDetectBaseUrl().replace(/\/$/, '');
74
- await traceCliSpan(
75
- 'cli.sdk_compatibility',
76
- { baseUrl },
77
- () => enforceSdkCompatibility(baseUrl),
78
- );
79
- if (printStartupPhase) {
80
- progress?.phase('checking sdk skills');
81
- }
82
- await traceCliSpan(
83
- 'cli.sdk_skills_sync',
84
- { baseUrl },
85
- () => syncSdkSkillsIfNeeded(baseUrl),
86
- );
87
- });
88
-
89
- registerAuthCommands(program);
90
- registerToolsCommands(program);
91
- registerPlayCommands(program);
92
- registerBillingCommands(program);
93
- registerOrgCommands(program);
94
- registerCsvCommands(program);
95
- registerDbCommands(program);
96
- registerFeedbackCommands(program);
97
-
98
- program
99
- .command('health')
100
- .description('Check server health.')
101
- .action(async () => {
102
- try {
103
- const client = new DeeplineClient();
104
- const data = await client.health();
105
- process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
106
- } catch (error) {
107
- throw new Error(
108
- `Cannot reach Deepline API: ${error instanceof Error ? error.message : String(error)}`,
109
- );
110
- }
111
- });
112
-
113
- program
114
- .command('version')
115
- .description('Show version.')
116
- .action(() => {
117
- process.stdout.write(`deepline ${SDK_VERSION}\n`);
118
- });
119
-
120
- try {
121
- await program.parseAsync(process.argv);
122
- recordCliTrace({
123
- phase: 'cli.main_total',
124
- ms: Date.now() - mainStartedAt,
125
- ok: true,
126
- });
127
- } catch (error) {
128
- recordCliTrace({
129
- phase: 'cli.main_total',
130
- ms: Date.now() - mainStartedAt,
131
- ok: false,
132
- error: error instanceof Error ? error.message : String(error),
133
- });
134
- progress?.fail();
135
- if (process.argv.includes('--json')) {
136
- printJsonError(error);
137
- } else if (error instanceof Error) {
138
- console.error(`Error: ${error.message}`);
139
- } else {
140
- console.error(`Error: ${String(error)}`);
141
- }
142
- process.exitCode = 1;
143
- }
144
-
145
- process.exit(process.exitCode ?? 0);
146
- }
147
-
148
- main();
@@ -1,149 +0,0 @@
1
- import { Worker } from 'node:worker_threads';
2
-
3
- export class CliProgress {
4
- private lastMessage: string | null = null;
5
- private readonly interactive = Boolean(process.stderr.isTTY);
6
- private readonly grey = '\x1b[90m';
7
- private readonly cyan = '\x1b[36m';
8
- private readonly dim = '\x1b[2m';
9
- private readonly reset = '\x1b[0m';
10
- private worker: Worker | null = null;
11
-
12
- constructor(private readonly enabled: boolean) {}
13
-
14
- phase(message: string): void {
15
- if (!this.enabled || this.lastMessage === message) {
16
- return;
17
- }
18
- if (this.interactive && this.lastMessage) {
19
- this.settle('✓');
20
- }
21
- this.lastMessage = message;
22
- if (!this.interactive) {
23
- process.stderr.write(`${message}\n`);
24
- return;
25
- }
26
- this.ensureWorker();
27
- this.worker?.postMessage({ type: 'phase', message });
28
- }
29
-
30
- complete(): void {
31
- this.settle('✓');
32
- }
33
-
34
- fail(): void {
35
- this.settle('✗');
36
- }
37
-
38
- writeLogLine(line: string): void {
39
- if (!this.enabled) {
40
- process.stderr.write(`${line}\n`);
41
- return;
42
- }
43
- const message = this.interactive
44
- ? `${this.cyan}[logs]${this.reset} ${this.dim}${line}${this.reset}`
45
- : `[logs] ${line}`;
46
- if (!this.interactive) {
47
- process.stderr.write(`${message}\n`);
48
- return;
49
- }
50
- const activeMessage = this.lastMessage;
51
- this.worker?.terminate().catch(() => undefined);
52
- this.worker = null;
53
- process.stderr.write(`\r\x1b[2K${message}\n`);
54
- if (activeMessage) {
55
- this.startWorker().postMessage({ type: 'phase', message: activeMessage });
56
- }
57
- }
58
-
59
- writeLine(line: string, stream: NodeJS.WriteStream = process.stderr): void {
60
- if (!this.enabled || !this.interactive) {
61
- stream.write(`${line}\n`);
62
- return;
63
- }
64
- const activeMessage = this.lastMessage;
65
- this.worker?.terminate().catch(() => undefined);
66
- this.worker = null;
67
- stream.write(`\r\x1b[2K${line}\n`);
68
- if (activeMessage) {
69
- this.startWorker().postMessage({ type: 'phase', message: activeMessage });
70
- }
71
- }
72
-
73
- private settle(mark: '✓' | '✗'): void {
74
- if (!this.enabled || !this.lastMessage) {
75
- return;
76
- }
77
- if (this.interactive) {
78
- this.worker?.terminate().catch(() => undefined);
79
- this.worker = null;
80
- process.stderr.write(
81
- `\r\x1b[2K${this.grey}${mark} ${this.lastMessage}${this.reset}\n`,
82
- );
83
- }
84
- this.lastMessage = null;
85
- }
86
-
87
- private ensureWorker(): void {
88
- this.startWorker();
89
- }
90
-
91
- private startWorker(): Worker {
92
- if (this.worker) {
93
- return this.worker;
94
- }
95
- this.worker = new Worker(
96
- `
97
- const { parentPort } = require('node:worker_threads');
98
- const { writeSync } = require('node:fs');
99
- const frames = ['—', '\\\\', '|', '/'];
100
- const grey = '\\x1b[90m';
101
- const reset = '\\x1b[0m';
102
- let message = '';
103
- let frameIndex = 0;
104
- let timer = null;
105
- function render() {
106
- if (!message) return;
107
- const frame = frames[frameIndex % frames.length] || '—';
108
- frameIndex += 1;
109
- writeSync(2, '\\r\\x1b[2K' + grey + frame + ' ' + message + reset);
110
- }
111
- parentPort.on('message', (event) => {
112
- if (event.type === 'phase') {
113
- message = String(event.message || '');
114
- frameIndex = 0;
115
- render();
116
- if (!timer) {
117
- timer = setInterval(render, 120);
118
- timer.unref && timer.unref();
119
- }
120
- }
121
- if (event.type === 'stop') {
122
- if (timer) clearInterval(timer);
123
- timer = null;
124
- writeSync(2, '\\r\\x1b[2K');
125
- }
126
- });
127
- `,
128
- { eval: true },
129
- );
130
- this.worker.unref();
131
- return this.worker;
132
- }
133
- }
134
-
135
- let activeProgress: CliProgress | null = null;
136
-
137
- export function setActiveCliProgress(progress: CliProgress | null): void {
138
- activeProgress = progress;
139
- }
140
-
141
- export function getActiveCliProgress(): CliProgress | null {
142
- return activeProgress;
143
- }
144
-
145
- export function createCliProgress(enabled: boolean): CliProgress {
146
- const progress = new CliProgress(enabled);
147
- setActiveCliProgress(progress);
148
- return progress;
149
- }