deepline 0.0.1

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.
@@ -0,0 +1,745 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/cli/commands/auth.ts
5
+ var import_node_fs2 = require("fs");
6
+ var import_node_os2 = require("os");
7
+ var import_node_os3 = require("os");
8
+ var import_node_path2 = require("path");
9
+
10
+ // src/config.ts
11
+ var import_node_fs = require("fs");
12
+ var import_node_os = require("os");
13
+ var import_node_path = require("path");
14
+
15
+ // src/errors.ts
16
+ var DeeplineError = class extends Error {
17
+ constructor(message, statusCode, code, details) {
18
+ super(message);
19
+ this.statusCode = statusCode;
20
+ this.code = code;
21
+ this.details = details;
22
+ this.name = "DeeplineError";
23
+ }
24
+ statusCode;
25
+ code;
26
+ details;
27
+ };
28
+ var AuthError = class extends DeeplineError {
29
+ constructor(message = "Authentication failed. Check your DEEPLINE_API_KEY.") {
30
+ super(message, 401, "AUTH_ERROR");
31
+ this.name = "AuthError";
32
+ }
33
+ };
34
+ var RateLimitError = class extends DeeplineError {
35
+ retryAfterMs;
36
+ constructor(retryAfterMs = 5e3, message) {
37
+ super(message ?? `Rate limited. Retry after ${retryAfterMs}ms.`, 429, "RATE_LIMIT");
38
+ this.name = "RateLimitError";
39
+ this.retryAfterMs = retryAfterMs;
40
+ }
41
+ };
42
+ var ConfigError = class extends DeeplineError {
43
+ constructor(message) {
44
+ super(message, void 0, "CONFIG_ERROR");
45
+ this.name = "ConfigError";
46
+ }
47
+ };
48
+
49
+ // src/config.ts
50
+ var PROD_URL = "https://code.deepline.com";
51
+ var DEFAULT_TIMEOUT = 6e4;
52
+ var DEFAULT_MAX_RETRIES = 3;
53
+ function baseUrlSlug(baseUrl) {
54
+ let url;
55
+ try {
56
+ url = new URL(baseUrl);
57
+ } catch {
58
+ return "unknown";
59
+ }
60
+ const host = url.hostname || "unknown";
61
+ const port = url.port ? parseInt(url.port, 10) : null;
62
+ let slug = host.replace(/[^a-zA-Z0-9]/g, "-");
63
+ if (port && port !== 80 && port !== 443) {
64
+ slug = `${slug}-${port}`;
65
+ }
66
+ return slug.toLowerCase().replace(/^-+|-+$/g, "");
67
+ }
68
+ function parseEnvFile(filePath) {
69
+ if (!(0, import_node_fs.existsSync)(filePath)) return {};
70
+ const env = {};
71
+ const content = (0, import_node_fs.readFileSync)(filePath, "utf-8");
72
+ for (const line of content.split(/\r?\n/)) {
73
+ const trimmed = line.trim();
74
+ if (!trimmed || trimmed.startsWith("#")) continue;
75
+ const eqIndex = trimmed.indexOf("=");
76
+ if (eqIndex < 0) continue;
77
+ const key = trimmed.slice(0, eqIndex).trim();
78
+ let value = trimmed.slice(eqIndex + 1).trim();
79
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
80
+ value = value.slice(1, -1);
81
+ }
82
+ if (key && value) {
83
+ env[key] = value;
84
+ }
85
+ }
86
+ return env;
87
+ }
88
+ function loadCliEnv(baseUrl) {
89
+ const slug = baseUrlSlug(baseUrl);
90
+ const envPath = (0, import_node_path.join)((0, import_node_os.homedir)(), ".local", "deepline", slug, ".env");
91
+ return parseEnvFile(envPath);
92
+ }
93
+ function autoDetectBaseUrl() {
94
+ const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
95
+ if (envBase) return envBase;
96
+ const localEnvPath = (0, import_node_path.join)((0, import_node_os.homedir)(), ".local", "deepline", "localhost-3000", ".env");
97
+ if ((0, import_node_fs.existsSync)(localEnvPath)) {
98
+ return "http://localhost:3000";
99
+ }
100
+ return PROD_URL;
101
+ }
102
+ function resolveConfig(options) {
103
+ const baseUrl = options?.baseUrl?.trim() || autoDetectBaseUrl();
104
+ const cliEnv = loadCliEnv(baseUrl);
105
+ const apiKey = options?.apiKey?.trim() || process.env.DEEPLINE_API_KEY?.trim() || cliEnv.DEEPLINE_API_KEY || "";
106
+ if (!apiKey) {
107
+ throw new ConfigError(
108
+ `No API key found. Set DEEPLINE_API_KEY env var, pass apiKey option, or run: deepline auth register`
109
+ );
110
+ }
111
+ return {
112
+ apiKey,
113
+ baseUrl: baseUrl.replace(/\/$/, ""),
114
+ timeout: options?.timeout ?? DEFAULT_TIMEOUT,
115
+ maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
116
+ };
117
+ }
118
+
119
+ // src/cli/commands/auth.ts
120
+ var EXIT_OK = 0;
121
+ var EXIT_AUTH = 1;
122
+ var EXIT_SERVER = 2;
123
+ function envFilePath(baseUrl) {
124
+ const slug = baseUrlSlug(baseUrl);
125
+ return (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".local", "deepline", slug, ".env");
126
+ }
127
+ function saveEnvValues(values, baseUrl) {
128
+ const filePath = envFilePath(baseUrl);
129
+ const dir = (0, import_node_path2.dirname)(filePath);
130
+ if (!(0, import_node_fs2.existsSync)(dir)) {
131
+ (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
132
+ }
133
+ const existing = (0, import_node_fs2.existsSync)(filePath) ? parseEnvFile(filePath) : {};
134
+ const merged = { ...existing, ...values };
135
+ const lines = Object.entries(merged).filter(([, v]) => v !== "").map(([k, v]) => `${k}=${v}`);
136
+ (0, import_node_fs2.writeFileSync)(filePath, lines.join("\n") + "\n", "utf-8");
137
+ }
138
+ async function httpJson(method, url, apiKey, body) {
139
+ const headers = { "Content-Type": "application/json" };
140
+ if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
141
+ const response = await fetch(url, {
142
+ method,
143
+ headers,
144
+ body: body !== void 0 ? JSON.stringify(body) : void 0
145
+ });
146
+ let data;
147
+ try {
148
+ data = await response.json();
149
+ } catch {
150
+ data = {};
151
+ }
152
+ return { status: response.status, data };
153
+ }
154
+ function openInBrowser(url) {
155
+ const { execSync } = require("child_process");
156
+ const platform = process.platform;
157
+ try {
158
+ if (platform === "darwin") execSync(`open "${url}"`);
159
+ else if (platform === "win32") execSync(`start "" "${url}"`);
160
+ else execSync(`xdg-open "${url}"`);
161
+ } catch {
162
+ }
163
+ }
164
+ function sleep(ms) {
165
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
166
+ }
167
+ async function handleRegister(args) {
168
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
169
+ let orgName = "";
170
+ let agentName = "";
171
+ let noWait = false;
172
+ for (let i = 0; i < args.length; i++) {
173
+ if (args[i] === "--org-name" && args[i + 1]) orgName = args[++i];
174
+ else if (args[i] === "--agent-name" && args[i + 1]) agentName = args[++i];
175
+ else if (args[i] === "--no-wait") noWait = true;
176
+ }
177
+ if (!agentName) {
178
+ try {
179
+ agentName = (0, import_node_os3.hostname)() || "Deepline CLI (TS)";
180
+ } catch {
181
+ agentName = "Deepline CLI (TS)";
182
+ }
183
+ }
184
+ const payload = {};
185
+ if (orgName) payload.org_name = orgName;
186
+ if (agentName) payload.agent_name = agentName;
187
+ const { status, data } = await httpJson("POST", `${baseUrl}/api/v2/auth/cli/register`, null, payload);
188
+ if (status >= 400) {
189
+ console.error(`Auth register failed (status ${status}).`);
190
+ if (data.error) console.error(String(data.error));
191
+ return EXIT_SERVER;
192
+ }
193
+ const claimUrl = String(data.claim_url || "");
194
+ const claimToken = String(data.claim_token || "");
195
+ if (claimToken) {
196
+ saveEnvValues({ DEEPLINE_CLAIM_TOKEN: claimToken }, baseUrl);
197
+ }
198
+ if (claimUrl) {
199
+ console.log(" Opening approval page in your browser.");
200
+ console.log(` If it didn't open, cmd+click: ${claimUrl}`);
201
+ openInBrowser(claimUrl);
202
+ }
203
+ if (data.cli_message) {
204
+ console.log(String(data.cli_message));
205
+ }
206
+ if (noWait) return EXIT_OK;
207
+ if (!claimToken) {
208
+ console.error("Missing claim token from register response.");
209
+ return EXIT_SERVER;
210
+ }
211
+ while (true) {
212
+ const { status: s, data: statusData } = await httpJson(
213
+ "POST",
214
+ `${baseUrl}/api/v2/auth/cli/status`,
215
+ null,
216
+ { claim_token: claimToken, reveal: true }
217
+ );
218
+ if (s === 401 || s === 403) {
219
+ console.log("Status: unauthorized");
220
+ return EXIT_AUTH;
221
+ }
222
+ if (s >= 500 || s === 0 || s === 400) {
223
+ await sleep(2e3);
224
+ continue;
225
+ }
226
+ if (s >= 400) {
227
+ console.error(`Auth status error (status ${s}).`);
228
+ return EXIT_SERVER;
229
+ }
230
+ const state = String(statusData.status || "").toLowerCase();
231
+ if (state === "claimed") {
232
+ const apiKey = String(statusData.api_key || "");
233
+ if (apiKey) {
234
+ saveEnvValues({ DEEPLINE_API_KEY: apiKey, DEEPLINE_CLAIM_TOKEN: "" }, baseUrl);
235
+ console.log("");
236
+ console.log("DEEPLINE");
237
+ console.log("All set! Your CLI is connected.");
238
+ if (statusData.org_name) {
239
+ console.log(` Signed in with organization: ${statusData.org_name}`);
240
+ }
241
+ return EXIT_OK;
242
+ }
243
+ }
244
+ if (state === "expired") {
245
+ console.log("That approval link expired. Please run: deepline auth register");
246
+ return EXIT_AUTH;
247
+ }
248
+ await sleep(2e3);
249
+ }
250
+ }
251
+ async function handleStatus(args) {
252
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
253
+ const reveal = args.includes("--reveal");
254
+ try {
255
+ const { status: hStatus, data: hData } = await httpJson("GET", `${baseUrl}/api/v2/health`, null);
256
+ if (hStatus === 200) {
257
+ console.log(`Host: ${baseUrl}`);
258
+ console.log(`Host status: ${hData.status || "ok"}`);
259
+ console.log(`Host version: ${hData.version || "(unknown)"}`);
260
+ }
261
+ } catch {
262
+ console.log(`Host: ${baseUrl} (unreachable)`);
263
+ }
264
+ const env = loadCliEnv(baseUrl);
265
+ const apiKey = process.env.DEEPLINE_API_KEY?.trim() || env.DEEPLINE_API_KEY || "";
266
+ if (!apiKey) {
267
+ console.log("Status: not connected");
268
+ console.log("Run: deepline auth register");
269
+ return EXIT_OK;
270
+ }
271
+ const { status, data } = await httpJson("POST", `${baseUrl}/api/v2/auth/cli/status`, apiKey, {
272
+ api_key: apiKey,
273
+ reveal
274
+ });
275
+ if (status === 401 || status === 403) {
276
+ console.log("Status: unauthorized");
277
+ console.log("Run: deepline auth register");
278
+ return EXIT_AUTH;
279
+ }
280
+ if (status >= 400) {
281
+ console.error(`Auth status error (status ${status}).`);
282
+ return EXIT_SERVER;
283
+ }
284
+ console.log(`Status: ${data.status || "(unknown)"}`);
285
+ console.log(`Rate limit tier: ${data.rate_limit_tier || "(unknown)"}`);
286
+ if (data.org_name) console.log(`Workspace: ${data.org_name}`);
287
+ if (data.org_slug) console.log(`Workspace slug: ${data.org_slug}`);
288
+ if (data.org_id != null) console.log(`Org ID: ${data.org_id}`);
289
+ if (data.user_id != null) console.log(`User ID: ${data.user_id}`);
290
+ if (reveal) {
291
+ const apiKeyResp = String(data.api_key || apiKey);
292
+ if (apiKeyResp) {
293
+ saveEnvValues({ DEEPLINE_API_KEY: apiKeyResp, DEEPLINE_CLAIM_TOKEN: "" }, baseUrl);
294
+ console.log(`Saved API key to ${envFilePath(baseUrl)}`);
295
+ }
296
+ }
297
+ return EXIT_OK;
298
+ }
299
+ async function handleAuth(args) {
300
+ const subcommand = args[0];
301
+ const subArgs = args.slice(1);
302
+ switch (subcommand) {
303
+ case "register":
304
+ return handleRegister(subArgs);
305
+ case "status":
306
+ return handleStatus(subArgs);
307
+ default:
308
+ return handleStatus(args);
309
+ }
310
+ }
311
+
312
+ // src/http.ts
313
+ var SDK_VERSION = "0.1.0";
314
+ var HttpClient = class {
315
+ constructor(config) {
316
+ this.config = config;
317
+ }
318
+ config;
319
+ async request(path, options) {
320
+ const url = `${this.config.baseUrl}${path}`;
321
+ const method = options?.method ?? "GET";
322
+ const headers = {
323
+ "Authorization": `Bearer ${this.config.apiKey}`,
324
+ "User-Agent": `deepline-ts-sdk/${SDK_VERSION}`,
325
+ ...options?.headers
326
+ };
327
+ if (options?.body !== void 0) {
328
+ headers["Content-Type"] = "application/json";
329
+ }
330
+ let lastError = null;
331
+ for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
332
+ if (attempt > 0) {
333
+ const backoffMs = Math.min(1e3 * Math.pow(2, attempt - 1), 3e4);
334
+ await sleep2(backoffMs);
335
+ }
336
+ const controller = new AbortController();
337
+ const timeoutId = setTimeout(
338
+ () => controller.abort(),
339
+ options?.timeout ?? this.config.timeout
340
+ );
341
+ try {
342
+ const response = await fetch(url, {
343
+ method,
344
+ headers,
345
+ body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
346
+ signal: controller.signal
347
+ });
348
+ clearTimeout(timeoutId);
349
+ if (response.status === 401 || response.status === 403) {
350
+ throw new AuthError();
351
+ }
352
+ if (response.status === 429) {
353
+ const retryAfter = parseRetryAfter(response);
354
+ lastError = new RateLimitError(retryAfter);
355
+ if (attempt < this.config.maxRetries) continue;
356
+ throw lastError;
357
+ }
358
+ const body = await response.text();
359
+ let parsed;
360
+ try {
361
+ parsed = JSON.parse(body);
362
+ } catch {
363
+ parsed = body;
364
+ }
365
+ if (!response.ok) {
366
+ const msg = typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : `HTTP ${response.status}`;
367
+ throw new DeeplineError(msg, response.status, "API_ERROR", {
368
+ response: parsed
369
+ });
370
+ }
371
+ return parsed;
372
+ } catch (error) {
373
+ clearTimeout(timeoutId);
374
+ if (error instanceof AuthError || error instanceof DeeplineError) {
375
+ throw error;
376
+ }
377
+ if (error instanceof RateLimitError) {
378
+ lastError = error;
379
+ if (attempt < this.config.maxRetries) continue;
380
+ throw error;
381
+ }
382
+ lastError = error instanceof Error ? error : new Error(String(error));
383
+ if (attempt < this.config.maxRetries) continue;
384
+ }
385
+ }
386
+ throw lastError ?? new DeeplineError("Request failed after retries");
387
+ }
388
+ async get(path) {
389
+ return this.request(path, { method: "GET" });
390
+ }
391
+ async post(path, body) {
392
+ return this.request(path, { method: "POST", body });
393
+ }
394
+ };
395
+ function parseRetryAfter(response) {
396
+ const header = response.headers.get("retry-after");
397
+ if (header) {
398
+ const seconds = Number(header);
399
+ if (Number.isFinite(seconds) && seconds > 0) {
400
+ return seconds * 1e3;
401
+ }
402
+ }
403
+ return 5e3;
404
+ }
405
+ function sleep2(ms) {
406
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
407
+ }
408
+
409
+ // src/client.ts
410
+ var DeeplineClient = class {
411
+ http;
412
+ config;
413
+ constructor(options) {
414
+ this.config = resolveConfig(options);
415
+ this.http = new HttpClient(this.config);
416
+ }
417
+ get baseUrl() {
418
+ return this.config.baseUrl;
419
+ }
420
+ /** List available tools. */
421
+ async listTools() {
422
+ const res = await this.http.get("/api/v2/tools");
423
+ return res.tools;
424
+ }
425
+ /** Execute a single tool. */
426
+ async executeTool(toolId, input) {
427
+ const res = await this.http.post(
428
+ `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
429
+ { payload: input }
430
+ );
431
+ return res.result ?? res;
432
+ }
433
+ /**
434
+ * Submit a play for cloud execution.
435
+ * Returns a workflowId. Use pollPlay() to get status.
436
+ */
437
+ async submitPlay(code, csvPath, name) {
438
+ return this.http.post("/api/v2/plays/run", {
439
+ code,
440
+ csvPath,
441
+ name
442
+ });
443
+ }
444
+ /** Poll workflow status. */
445
+ async getPlayStatus(workflowId) {
446
+ return this.http.get(`/api/v2/plays/run/${workflowId}`);
447
+ }
448
+ /** Cancel a running play. */
449
+ async cancelPlay(workflowId) {
450
+ await this.http.request(`/api/v2/plays/run/${workflowId}`, { method: "DELETE" });
451
+ }
452
+ /**
453
+ * Run a play end-to-end: submit, poll until done, return result.
454
+ * onProgress is called on each poll with current status.
455
+ */
456
+ async runPlay(code, csvPath, name, options) {
457
+ const { workflowId } = await this.submitPlay(code, csvPath, name);
458
+ const pollInterval = options?.pollIntervalMs ?? 2e3;
459
+ const start = Date.now();
460
+ while (true) {
461
+ if (options?.signal?.aborted) {
462
+ await this.cancelPlay(workflowId);
463
+ return {
464
+ success: false,
465
+ workflowId,
466
+ logs: [],
467
+ durationMs: Date.now() - start,
468
+ error: "Cancelled by user"
469
+ };
470
+ }
471
+ const status = await this.getPlayStatus(workflowId);
472
+ options?.onProgress?.(status);
473
+ const terminal = ["COMPLETED", "FAILED", "CANCELLED", "TERMINATED", "TIMED_OUT"];
474
+ if (terminal.includes(status.temporalStatus)) {
475
+ return {
476
+ success: status.temporalStatus === "COMPLETED",
477
+ workflowId,
478
+ result: status.result,
479
+ logs: status.progress?.logs ?? [],
480
+ durationMs: Date.now() - start,
481
+ error: status.progress?.error ?? (status.temporalStatus !== "COMPLETED" ? status.temporalStatus : void 0)
482
+ };
483
+ }
484
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
485
+ }
486
+ }
487
+ /** Health check. */
488
+ async health() {
489
+ return this.http.get("/api/v2/health");
490
+ }
491
+ };
492
+
493
+ // src/cli/commands/tools.ts
494
+ async function handleTools(args) {
495
+ const subcommand = args[0];
496
+ const subArgs = args.slice(1);
497
+ switch (subcommand) {
498
+ case "list":
499
+ return listTools();
500
+ case "execute":
501
+ case "run":
502
+ return executeTool(subArgs);
503
+ default:
504
+ return listTools();
505
+ }
506
+ }
507
+ async function listTools() {
508
+ const dl = new DeeplineClient();
509
+ const tools = await dl.listTools();
510
+ console.log(`${tools.length} tools available:
511
+ `);
512
+ for (const tool of tools) {
513
+ const cats = tool.categories.length ? ` [${tool.categories.join(", ")}]` : "";
514
+ console.log(` ${tool.toolId}${cats}`);
515
+ console.log(` ${tool.description}`);
516
+ }
517
+ return 0;
518
+ }
519
+ async function executeTool(args) {
520
+ const toolId = args[0];
521
+ if (!toolId) {
522
+ console.error(`Usage: deepline tools execute <toolId> [--param key=value ...] [--json '{"k":"v"}']`);
523
+ return 1;
524
+ }
525
+ const params = {};
526
+ for (let i = 1; i < args.length; i++) {
527
+ const arg = args[i];
528
+ if ((arg === "--param" || arg === "-p") && args[i + 1]) {
529
+ const kv = args[++i];
530
+ const eqIdx = kv.indexOf("=");
531
+ if (eqIdx > 0) params[kv.slice(0, eqIdx)] = kv.slice(eqIdx + 1);
532
+ } else if (arg === "--json" && args[i + 1]) {
533
+ Object.assign(params, JSON.parse(args[++i]));
534
+ }
535
+ }
536
+ const dl = new DeeplineClient();
537
+ const result = await dl.executeTool(toolId, params);
538
+ console.log(JSON.stringify(result, null, 2));
539
+ return 0;
540
+ }
541
+
542
+ // src/cli/commands/play.ts
543
+ var import_node_fs3 = require("fs");
544
+ var import_node_path3 = require("path");
545
+ function camelToKebab(str) {
546
+ return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
547
+ }
548
+ function extractPlayName(code, filePath) {
549
+ const nameMatch = code.match(/export\s+default\s+async\s+function\s+(\w+)/);
550
+ if (nameMatch?.[1]) {
551
+ return camelToKebab(nameMatch[1]);
552
+ }
553
+ const base = (0, import_node_path3.basename)(filePath).replace(/\.(play\.)?(ts|js|mjs)$/, "");
554
+ return base.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
555
+ }
556
+ async function handlePlay(args) {
557
+ const playPath = args[0];
558
+ if (!playPath) {
559
+ console.error("Usage: deepline play <play-file.ts> --csv <file.csv>");
560
+ return 1;
561
+ }
562
+ let csvPath = "";
563
+ for (let i = 1; i < args.length; i++) {
564
+ const arg = args[i];
565
+ if ((arg === "--csv" || arg === "--input" || arg === "-i") && args[i + 1]) {
566
+ csvPath = (0, import_node_path3.resolve)(args[++i]);
567
+ }
568
+ }
569
+ const absPlayPath = (0, import_node_path3.resolve)(playPath);
570
+ let code;
571
+ try {
572
+ code = (0, import_node_fs3.readFileSync)(absPlayPath, "utf-8");
573
+ } catch {
574
+ console.error(`Cannot read play file: ${absPlayPath}`);
575
+ return 1;
576
+ }
577
+ const playName = extractPlayName(code, absPlayPath);
578
+ const fnMatch = code.match(
579
+ /export\s+default\s+(async\s+function\s*\w*\s*\([^)]*\)\s*\{[\s\S]*\})/
580
+ );
581
+ const playCode = fnMatch ? fnMatch[1] : code;
582
+ if (!csvPath) {
583
+ const csvArg = args.find((a) => a.endsWith(".csv"));
584
+ if (csvArg) csvPath = (0, import_node_path3.resolve)(csvArg);
585
+ }
586
+ if (!csvPath) {
587
+ console.error("CSV path required: --csv <file.csv>");
588
+ return 1;
589
+ }
590
+ const dl = new DeeplineClient();
591
+ const controller = new AbortController();
592
+ const onSigint = () => {
593
+ console.log("\nCancelling play...");
594
+ controller.abort();
595
+ };
596
+ process.on("SIGINT", onSigint);
597
+ let lastLogIndex = 0;
598
+ try {
599
+ console.log(`Submitting play: ${playPath} (${playName})`);
600
+ console.log(`CSV: ${csvPath}`);
601
+ console.log(`Server: ${dl.baseUrl}`);
602
+ console.log("");
603
+ const result = await dl.runPlay(playCode, csvPath, playName, {
604
+ signal: controller.signal,
605
+ pollIntervalMs: 1500,
606
+ onProgress: (status) => {
607
+ const logs = status.progress?.logs ?? [];
608
+ for (let i = lastLogIndex; i < logs.length; i++) {
609
+ console.log(logs[i]);
610
+ }
611
+ lastLogIndex = logs.length;
612
+ if (status.progress?.status) {
613
+ process.stdout.write(
614
+ `\r [${status.progress.status}]${status.progress.totalRows ? ` ${status.progress.totalRows} rows` : ""}`
615
+ );
616
+ }
617
+ }
618
+ });
619
+ console.log("");
620
+ if (result.success) {
621
+ console.log(`
622
+ Play completed in ${(result.durationMs / 1e3).toFixed(1)}s`);
623
+ if (result.result) {
624
+ const r = result.result;
625
+ if (r.rows && Array.isArray(r.rows)) {
626
+ console.log(` ${r.rows.length} rows output`);
627
+ }
628
+ if (r.stats) {
629
+ console.log(` Stats: ${JSON.stringify(r.stats)}`);
630
+ }
631
+ }
632
+ return 0;
633
+ } else {
634
+ console.error(`
635
+ Play failed: ${result.error}`);
636
+ return 1;
637
+ }
638
+ } finally {
639
+ process.removeListener("SIGINT", onSigint);
640
+ }
641
+ }
642
+
643
+ // src/cli/commands/enrich.ts
644
+ async function handleEnrich(args) {
645
+ console.log("Note: `enrich` runs via the play engine. Use `deepline play` directly.");
646
+ console.log("");
647
+ return handlePlay(args);
648
+ }
649
+
650
+ // src/cli/index.ts
651
+ var VERSION = "0.0.1";
652
+ async function main() {
653
+ const args = process.argv.slice(2);
654
+ const command = args[0];
655
+ const subArgs = args.slice(1);
656
+ let exitCode = 0;
657
+ try {
658
+ switch (command) {
659
+ case "auth":
660
+ exitCode = await handleAuth(subArgs);
661
+ break;
662
+ case "tools":
663
+ exitCode = await handleTools(subArgs);
664
+ break;
665
+ case "enrich":
666
+ exitCode = await handleEnrich(subArgs);
667
+ break;
668
+ case "play":
669
+ exitCode = await handlePlay(subArgs);
670
+ break;
671
+ case "health": {
672
+ const baseUrl = detectBaseUrl();
673
+ try {
674
+ const res = await fetch(`${baseUrl}/api/v2/health`);
675
+ const data = await res.json();
676
+ console.log(JSON.stringify(data, null, 2));
677
+ } catch (error) {
678
+ console.error(`Cannot reach ${baseUrl}: ${error instanceof Error ? error.message : error}`);
679
+ exitCode = 1;
680
+ }
681
+ break;
682
+ }
683
+ case "version":
684
+ case "--version":
685
+ case "-v":
686
+ console.log(`deepline ${VERSION}`);
687
+ break;
688
+ case "help":
689
+ case "--help":
690
+ case "-h":
691
+ case void 0:
692
+ printHelp();
693
+ break;
694
+ default:
695
+ console.error(`Unknown command: ${command}`);
696
+ printHelp();
697
+ exitCode = 1;
698
+ }
699
+ } catch (error) {
700
+ if (error instanceof Error) {
701
+ console.error(`Error: ${error.message}`);
702
+ } else {
703
+ console.error(`Error: ${error}`);
704
+ }
705
+ exitCode = 1;
706
+ }
707
+ process.exit(exitCode);
708
+ }
709
+ function detectBaseUrl() {
710
+ if (process.env.DEEPLINE_API_BASE_URL) {
711
+ return process.env.DEEPLINE_API_BASE_URL.replace(/\/$/, "");
712
+ }
713
+ return "https://code.deepline.com";
714
+ }
715
+ function printHelp() {
716
+ console.log(`
717
+ deepline \u2014 Deepline CLI (TypeScript SDK)
718
+
719
+ Usage:
720
+ deepline <command> [options]
721
+
722
+ Commands:
723
+ auth register Register this device (opens browser for approval)
724
+ auth status Show connection status
725
+ tools list List available tools
726
+ tools execute Execute a tool
727
+ tools find Find a tool by name
728
+ enrich Run CSV enrichment pipeline
729
+ play Run a play file
730
+ health Check server health
731
+ version Show version
732
+
733
+ Options:
734
+ --help, -h Show this help
735
+ --version, -v Show version
736
+
737
+ Examples:
738
+ deepline auth register
739
+ deepline tools list
740
+ deepline tools execute apollo_searchPeople --param domain=anthropic.com
741
+ deepline play enrich-leads.play.ts --input file=leads.csv
742
+ `.trim());
743
+ }
744
+ main();
745
+ //# sourceMappingURL=index.js.map