deepline 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/cli/index.js +921 -508
- package/dist/cli/index.mjs +921 -508
- package/dist/index.d.mts +39 -2
- package/dist/index.d.ts +39 -2
- package/dist/index.js +17 -3
- package/dist/index.mjs +17 -3
- package/dist/repo/sdk/src/client.ts +54 -1
- package/dist/repo/sdk/src/version.ts +2 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -266,8 +266,8 @@ function saveProjectDeeplineEnvValues(baseUrl, values, startDir = projectEnvStar
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
// src/version.ts
|
|
269
|
-
var SDK_VERSION = "0.1.
|
|
270
|
-
var SDK_API_CONTRACT = "2026-05-runs-
|
|
269
|
+
var SDK_VERSION = "0.1.31";
|
|
270
|
+
var SDK_API_CONTRACT = "2026-05-runs-export-v3";
|
|
271
271
|
|
|
272
272
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
273
273
|
var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
|
|
@@ -676,6 +676,7 @@ var DeeplineClient = class {
|
|
|
676
676
|
list: (options2) => this.listRuns(options2),
|
|
677
677
|
tail: (runId, options2) => this.tailRun(runId, options2),
|
|
678
678
|
logs: (runId, options2) => this.getRunLogs(runId, options2),
|
|
679
|
+
exportDatasetRows: (input) => this.getPlaySheetRows(input),
|
|
679
680
|
stop: (runId, options2) => this.stopRun(runId, options2)
|
|
680
681
|
};
|
|
681
682
|
}
|
|
@@ -703,7 +704,7 @@ var DeeplineClient = class {
|
|
|
703
704
|
const target = play.reference || play.name;
|
|
704
705
|
if (options?.csvInput) {
|
|
705
706
|
const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
|
|
706
|
-
return `deepline plays run ${target}
|
|
707
|
+
return `deepline plays run ${target} --input '${JSON.stringify({ [inputField]: "leads.csv" })}' --watch`;
|
|
707
708
|
}
|
|
708
709
|
return `deepline plays run ${target} --input '{...}' --watch`;
|
|
709
710
|
}
|
|
@@ -1363,6 +1364,19 @@ var DeeplineClient = class {
|
|
|
1363
1364
|
entries
|
|
1364
1365
|
};
|
|
1365
1366
|
}
|
|
1367
|
+
async getPlaySheetRows(input) {
|
|
1368
|
+
const params = new URLSearchParams({
|
|
1369
|
+
tableNamespace: input.tableNamespace,
|
|
1370
|
+
limit: String(input.limit ?? 5e3),
|
|
1371
|
+
offset: String(input.offset ?? 0)
|
|
1372
|
+
});
|
|
1373
|
+
if (input.runId?.trim()) {
|
|
1374
|
+
params.set("runId", input.runId.trim());
|
|
1375
|
+
}
|
|
1376
|
+
return await this.http.get(
|
|
1377
|
+
`/api/v2/plays/${encodeURIComponent(input.playName)}/sheet?${params.toString()}`
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1366
1380
|
/**
|
|
1367
1381
|
* Stop a run by id using the public runs resource model.
|
|
1368
1382
|
*
|
|
@@ -1970,6 +1984,38 @@ function clip(value, maxLength) {
|
|
|
1970
1984
|
return value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
|
|
1971
1985
|
}
|
|
1972
1986
|
|
|
1987
|
+
// src/cli/command-envelope.ts
|
|
1988
|
+
function renderSections(render) {
|
|
1989
|
+
const lines = [];
|
|
1990
|
+
for (const section of render?.sections ?? []) {
|
|
1991
|
+
if (!section.title || section.lines.length === 0) continue;
|
|
1992
|
+
lines.push(section.title);
|
|
1993
|
+
lines.push(...section.lines.map((line) => ` ${line}`));
|
|
1994
|
+
}
|
|
1995
|
+
for (const action of render?.actions ?? []) {
|
|
1996
|
+
if (!action.label || !action.command) continue;
|
|
1997
|
+
lines.push(`${action.label}: ${action.command}`);
|
|
1998
|
+
}
|
|
1999
|
+
return lines;
|
|
2000
|
+
}
|
|
2001
|
+
function renderCommandEnvelopeText(envelope) {
|
|
2002
|
+
const lines = renderSections(envelope.render);
|
|
2003
|
+
if (lines.length > 0) {
|
|
2004
|
+
return `${lines.join("\n")}
|
|
2005
|
+
`;
|
|
2006
|
+
}
|
|
2007
|
+
return `${JSON.stringify(envelope, null, 2)}
|
|
2008
|
+
`;
|
|
2009
|
+
}
|
|
2010
|
+
function printCommandEnvelope(envelope, options = {}) {
|
|
2011
|
+
const jsonOutput = typeof options.json === "boolean" ? options.json : shouldEmitJson();
|
|
2012
|
+
if (jsonOutput) {
|
|
2013
|
+
printJson(envelope);
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
process.stdout.write(options.text ?? renderCommandEnvelopeText(envelope));
|
|
2017
|
+
}
|
|
2018
|
+
|
|
1973
2019
|
// src/cli/commands/auth.ts
|
|
1974
2020
|
var EXIT_OK = 0;
|
|
1975
2021
|
var EXIT_AUTH = 1;
|
|
@@ -2218,6 +2264,7 @@ async function handleStatus(args) {
|
|
|
2218
2264
|
const reveal = args.includes("--reveal");
|
|
2219
2265
|
const jsonOutput = argsWantJson(args);
|
|
2220
2266
|
let hostStatusPayload = null;
|
|
2267
|
+
const hostLines = [];
|
|
2221
2268
|
try {
|
|
2222
2269
|
const { status: hStatus, data: hData } = await httpJson("GET", `${baseUrl}/api/v2/health`, null);
|
|
2223
2270
|
if (hStatus === 200) {
|
|
@@ -2226,11 +2273,9 @@ async function handleStatus(args) {
|
|
|
2226
2273
|
hostStatus: hData.status || "ok",
|
|
2227
2274
|
hostVersion: hData.version || "(unknown)"
|
|
2228
2275
|
};
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
console.log(`Host version: ${hData.version || "(unknown)"}`);
|
|
2233
|
-
}
|
|
2276
|
+
hostLines.push(`Host: ${baseUrl}`);
|
|
2277
|
+
hostLines.push(`Host status: ${hData.status || "ok"}`);
|
|
2278
|
+
hostLines.push(`Host version: ${hData.version || "(unknown)"}`);
|
|
2234
2279
|
}
|
|
2235
2280
|
} catch {
|
|
2236
2281
|
hostStatusPayload = {
|
|
@@ -2238,40 +2283,34 @@ async function handleStatus(args) {
|
|
|
2238
2283
|
hostStatus: "unreachable",
|
|
2239
2284
|
hostVersion: null
|
|
2240
2285
|
};
|
|
2241
|
-
|
|
2242
|
-
console.log(`Host: ${baseUrl} (unreachable)`);
|
|
2243
|
-
}
|
|
2286
|
+
hostLines.push(`Host: ${baseUrl} (unreachable)`);
|
|
2244
2287
|
}
|
|
2245
2288
|
const env = loadCliEnv(baseUrl);
|
|
2246
2289
|
const apiKey = process.env.DEEPLINE_API_KEY?.trim() || env.DEEPLINE_API_KEY || "";
|
|
2247
2290
|
if (!apiKey) {
|
|
2248
2291
|
if (env.DEEPLINE_CLAIM_TOKEN?.trim()) {
|
|
2249
|
-
|
|
2250
|
-
process.stdout.write(`${JSON.stringify({
|
|
2251
|
-
...hostStatusPayload ?? { host: baseUrl },
|
|
2252
|
-
status: "pending",
|
|
2253
|
-
connected: false,
|
|
2254
|
-
next: "deepline auth wait"
|
|
2255
|
-
})}
|
|
2256
|
-
`);
|
|
2257
|
-
return EXIT_OK;
|
|
2258
|
-
}
|
|
2259
|
-
console.log("Status: pending");
|
|
2260
|
-
console.log("Run: deepline auth wait");
|
|
2261
|
-
return EXIT_OK;
|
|
2262
|
-
}
|
|
2263
|
-
if (jsonOutput) {
|
|
2264
|
-
process.stdout.write(`${JSON.stringify({
|
|
2292
|
+
printCommandEnvelope({
|
|
2265
2293
|
...hostStatusPayload ?? { host: baseUrl },
|
|
2266
|
-
status: "
|
|
2294
|
+
status: "pending",
|
|
2267
2295
|
connected: false,
|
|
2268
|
-
next: "deepline auth
|
|
2269
|
-
|
|
2270
|
-
|
|
2296
|
+
next: "deepline auth wait",
|
|
2297
|
+
render: {
|
|
2298
|
+
sections: [{ title: "auth status", lines: [...hostLines, "Status: pending"] }],
|
|
2299
|
+
actions: [{ label: "Run", command: "deepline auth wait" }]
|
|
2300
|
+
}
|
|
2301
|
+
}, { json: jsonOutput });
|
|
2271
2302
|
return EXIT_OK;
|
|
2272
2303
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2304
|
+
printCommandEnvelope({
|
|
2305
|
+
...hostStatusPayload ?? { host: baseUrl },
|
|
2306
|
+
status: "not connected",
|
|
2307
|
+
connected: false,
|
|
2308
|
+
next: "deepline auth register",
|
|
2309
|
+
render: {
|
|
2310
|
+
sections: [{ title: "auth status", lines: [...hostLines, "Status: not connected"] }],
|
|
2311
|
+
actions: [{ label: "Run", command: "deepline auth register" }]
|
|
2312
|
+
}
|
|
2313
|
+
}, { json: jsonOutput });
|
|
2275
2314
|
return EXIT_OK;
|
|
2276
2315
|
}
|
|
2277
2316
|
const { status, data } = await httpJson("POST", `${baseUrl}/api/v2/auth/cli/status`, apiKey, {
|
|
@@ -2279,18 +2318,16 @@ async function handleStatus(args) {
|
|
|
2279
2318
|
reveal
|
|
2280
2319
|
});
|
|
2281
2320
|
if (status === 401 || status === 403) {
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
}
|
|
2292
|
-
console.log("Status: unauthorized");
|
|
2293
|
-
console.log("Run: deepline auth register");
|
|
2321
|
+
printCommandEnvelope({
|
|
2322
|
+
...hostStatusPayload ?? { host: baseUrl },
|
|
2323
|
+
status: "unauthorized",
|
|
2324
|
+
connected: false,
|
|
2325
|
+
next: "deepline auth register",
|
|
2326
|
+
render: {
|
|
2327
|
+
sections: [{ title: "auth status", lines: [...hostLines, "Status: unauthorized"] }],
|
|
2328
|
+
actions: [{ label: "Run", command: "deepline auth register" }]
|
|
2329
|
+
}
|
|
2330
|
+
}, { json: jsonOutput });
|
|
2294
2331
|
return EXIT_AUTH;
|
|
2295
2332
|
}
|
|
2296
2333
|
if (status >= 400) {
|
|
@@ -2312,23 +2349,7 @@ async function handleStatus(args) {
|
|
|
2312
2349
|
},
|
|
2313
2350
|
examples: Array.isArray(data.examples) ? data.examples : []
|
|
2314
2351
|
};
|
|
2315
|
-
|
|
2316
|
-
process.stdout.write(`${JSON.stringify(payload)}
|
|
2317
|
-
`);
|
|
2318
|
-
} else {
|
|
2319
|
-
console.log(`Status: ${payload.status}`);
|
|
2320
|
-
console.log(`Rate limit tier: ${payload.rateLimitTier}`);
|
|
2321
|
-
if (payload.workspace.name) console.log(`Workspace: ${payload.workspace.name}`);
|
|
2322
|
-
if (payload.workspace.slug) console.log(`Workspace slug: ${payload.workspace.slug}`);
|
|
2323
|
-
if (payload.workspace.id != null) console.log(`Org ID: ${payload.workspace.id}`);
|
|
2324
|
-
if (payload.user.id != null) console.log(`User ID: ${payload.user.id}`);
|
|
2325
|
-
if (payload.examples.length > 0) {
|
|
2326
|
-
console.log("Examples:");
|
|
2327
|
-
for (const example of payload.examples.slice(0, 3)) {
|
|
2328
|
-
console.log(` ${String(example)}`);
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2352
|
+
let savedApiKeyPath = null;
|
|
2332
2353
|
if (reveal) {
|
|
2333
2354
|
const apiKeyResp = String(data.api_key || apiKey);
|
|
2334
2355
|
if (apiKeyResp) {
|
|
@@ -2337,9 +2358,31 @@ async function handleStatus(args) {
|
|
|
2337
2358
|
DEEPLINE_API_KEY: apiKeyResp,
|
|
2338
2359
|
DEEPLINE_CLAIM_TOKEN: ""
|
|
2339
2360
|
}, baseUrl);
|
|
2340
|
-
|
|
2361
|
+
savedApiKeyPath = envFilePath(baseUrl);
|
|
2341
2362
|
}
|
|
2342
2363
|
}
|
|
2364
|
+
printCommandEnvelope({
|
|
2365
|
+
...payload,
|
|
2366
|
+
...savedApiKeyPath ? { saved_api_key_path: savedApiKeyPath } : {},
|
|
2367
|
+
render: {
|
|
2368
|
+
sections: [
|
|
2369
|
+
{
|
|
2370
|
+
title: "auth status",
|
|
2371
|
+
lines: [
|
|
2372
|
+
...hostLines,
|
|
2373
|
+
`Status: ${payload.status}`,
|
|
2374
|
+
`Rate limit tier: ${payload.rateLimitTier}`,
|
|
2375
|
+
...payload.workspace.name ? [`Workspace: ${payload.workspace.name}`] : [],
|
|
2376
|
+
...payload.workspace.slug ? [`Workspace slug: ${payload.workspace.slug}`] : [],
|
|
2377
|
+
...payload.workspace.id != null ? [`Org ID: ${payload.workspace.id}`] : [],
|
|
2378
|
+
...payload.user.id != null ? [`User ID: ${payload.user.id}`] : [],
|
|
2379
|
+
...payload.examples.length > 0 ? ["Examples:", ...payload.examples.slice(0, 3).map((example) => ` ${String(example)}`)] : [],
|
|
2380
|
+
...savedApiKeyPath ? [`Saved API key to ${savedApiKeyPath}`] : []
|
|
2381
|
+
]
|
|
2382
|
+
}
|
|
2383
|
+
]
|
|
2384
|
+
}
|
|
2385
|
+
}, { json: jsonOutput });
|
|
2343
2386
|
return EXIT_OK;
|
|
2344
2387
|
}
|
|
2345
2388
|
function registerAuthCommands(program) {
|
|
@@ -2422,19 +2465,18 @@ var import_sync3 = require("csv-stringify/sync");
|
|
|
2422
2465
|
function humanize(value) {
|
|
2423
2466
|
return String(value || "").split("_").filter(Boolean).map((token) => token[0]?.toUpperCase() + token.slice(1)).join(" ") || "Unknown";
|
|
2424
2467
|
}
|
|
2425
|
-
function
|
|
2468
|
+
function recentUsageLines(entries) {
|
|
2426
2469
|
if (entries.length === 0) {
|
|
2427
|
-
|
|
2428
|
-
return;
|
|
2470
|
+
return ["Recent activity: none yet"];
|
|
2429
2471
|
}
|
|
2430
|
-
|
|
2472
|
+
const lines = ["Recent activity:"];
|
|
2431
2473
|
for (const entry of entries) {
|
|
2432
2474
|
const op = `${humanize(entry.provider)} ${humanize(entry.operation)}`.trim();
|
|
2433
2475
|
const charge = entry.billing_mode === "no_bill" ? "free" : `${entry.credits ?? 0} cr`;
|
|
2434
2476
|
const status = entry.status || "completed";
|
|
2435
|
-
|
|
2436
|
-
`);
|
|
2477
|
+
lines.push(`${op} | ${charge} | ${status} | ${entry.created_at || "unknown"}`);
|
|
2437
2478
|
}
|
|
2479
|
+
return lines;
|
|
2438
2480
|
}
|
|
2439
2481
|
function summarizeLedgerRows(summary, rows) {
|
|
2440
2482
|
const netDelta = rows.reduce((sum, row) => {
|
|
@@ -2487,49 +2529,49 @@ function defaultLedgerExportPath() {
|
|
|
2487
2529
|
async function handleBalance(options) {
|
|
2488
2530
|
const { http } = getAuthedHttpClient();
|
|
2489
2531
|
const payload = await http.get("/api/v2/billing/balance");
|
|
2490
|
-
if (shouldEmitJson(options.json)) return printJson(payload);
|
|
2491
2532
|
const status = String(payload.balance_status || "");
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2533
|
+
const lines = status === "no_billing" ? [
|
|
2534
|
+
"Balance: 0 credits",
|
|
2535
|
+
"Billing: No billing account or payment method set up for this workspace."
|
|
2536
|
+
] : [`Balance: ${payload.balance ?? "(unknown)"} credits`];
|
|
2537
|
+
printCommandEnvelope({
|
|
2538
|
+
...payload,
|
|
2539
|
+
render: { sections: [{ title: "billing balance", lines }] }
|
|
2540
|
+
}, { json: options.json });
|
|
2541
|
+
return;
|
|
2499
2542
|
}
|
|
2500
2543
|
async function handleUsage(options) {
|
|
2501
2544
|
const { http } = getAuthedHttpClient();
|
|
2502
2545
|
const params = new URLSearchParams();
|
|
2503
2546
|
if (options.limit) params.set("recent_limit", options.limit);
|
|
2504
2547
|
if (options.offset) params.set("recent_offset", options.offset);
|
|
2505
|
-
const suffix = params.
|
|
2548
|
+
const suffix = Array.from(params).length > 0 ? `?${params.toString()}` : "";
|
|
2506
2549
|
const payload = await http.get(`/api/v2/billing/usage${suffix}`);
|
|
2507
|
-
if (shouldEmitJson(options.json)) return printJson(payload);
|
|
2508
2550
|
const usage = payload.usage ?? {};
|
|
2509
2551
|
const quota = payload.quota ?? {};
|
|
2510
2552
|
const recent = payload.recent ?? {};
|
|
2511
|
-
|
|
2512
|
-
`)
|
|
2513
|
-
|
|
2514
|
-
`)
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2553
|
+
const lines = [
|
|
2554
|
+
`Balance: ${payload.balance ?? "(unknown)"}`,
|
|
2555
|
+
`Last 30 days spent: ${usage.month_spent_credits ?? "(unknown)"}`,
|
|
2556
|
+
`Monthly limit: ${quota.enabled ? quota.monthly_credits_limit ?? "(unknown)" : "off"}`,
|
|
2557
|
+
...recentUsageLines(Array.isArray(recent.entries) ? recent.entries : [])
|
|
2558
|
+
];
|
|
2559
|
+
printCommandEnvelope({
|
|
2560
|
+
...payload,
|
|
2561
|
+
render: { sections: [{ title: "billing usage", lines }] }
|
|
2562
|
+
}, { json: options.json });
|
|
2520
2563
|
}
|
|
2521
2564
|
async function handleLimit(options) {
|
|
2522
2565
|
const { http } = getAuthedHttpClient();
|
|
2523
2566
|
const payload = await http.get("/api/v2/billing/limit");
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
}
|
|
2532
|
-
process.stdout.write("Monthly limit: off\n");
|
|
2567
|
+
const lines = payload.enabled ? [
|
|
2568
|
+
`Monthly limit: ${payload.monthly_credits_limit ?? "(unknown)"}`,
|
|
2569
|
+
`Remaining before cap: ${payload.remaining_credits ?? "(unknown)"}`
|
|
2570
|
+
] : ["Monthly limit: off"];
|
|
2571
|
+
printCommandEnvelope({
|
|
2572
|
+
...payload,
|
|
2573
|
+
render: { sections: [{ title: "billing limit", lines }] }
|
|
2574
|
+
}, { json: options.json });
|
|
2533
2575
|
}
|
|
2534
2576
|
async function handleSetLimit(credits, options) {
|
|
2535
2577
|
const { http } = getAuthedHttpClient();
|
|
@@ -2537,15 +2579,20 @@ async function handleSetLimit(credits, options) {
|
|
|
2537
2579
|
method: "PUT",
|
|
2538
2580
|
body: { monthly_credits_limit: Number.parseInt(credits, 10) }
|
|
2539
2581
|
});
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2582
|
+
printCommandEnvelope({
|
|
2583
|
+
...payload,
|
|
2584
|
+
render: {
|
|
2585
|
+
sections: [{ title: "billing limit", lines: [`Monthly billing limit set to ${credits} credits.`] }]
|
|
2586
|
+
}
|
|
2587
|
+
}, { json: options.json });
|
|
2543
2588
|
}
|
|
2544
2589
|
async function handleLimitOff(options) {
|
|
2545
2590
|
const { http } = getAuthedHttpClient();
|
|
2546
2591
|
const payload = await http.request("/api/v2/billing/limit", { method: "DELETE" });
|
|
2547
|
-
|
|
2548
|
-
|
|
2592
|
+
printCommandEnvelope({
|
|
2593
|
+
...payload,
|
|
2594
|
+
render: { sections: [{ title: "billing limit", lines: ["Monthly billing limit is now off."] }] }
|
|
2595
|
+
}, { json: options.json });
|
|
2549
2596
|
}
|
|
2550
2597
|
async function handleHistory(options) {
|
|
2551
2598
|
const { http } = getAuthedHttpClient();
|
|
@@ -2564,13 +2611,20 @@ async function handleHistory(options) {
|
|
|
2564
2611
|
};
|
|
2565
2612
|
});
|
|
2566
2613
|
const outputPath = await writeCsvRowsFile(`billing-history-${options.time}`, rows);
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2614
|
+
printCommandEnvelope({
|
|
2615
|
+
output_path: outputPath,
|
|
2616
|
+
row_count: rows.length,
|
|
2617
|
+
time_window: options.time,
|
|
2618
|
+
render: {
|
|
2619
|
+
sections: [
|
|
2620
|
+
{
|
|
2621
|
+
title: "billing history",
|
|
2622
|
+
lines: [`Billing history written to ${outputPath}`, `${rows.length} row(s) exported.`]
|
|
2623
|
+
}
|
|
2624
|
+
]
|
|
2625
|
+
},
|
|
2626
|
+
local: { output_path: outputPath }
|
|
2627
|
+
}, { json: options.json });
|
|
2574
2628
|
}
|
|
2575
2629
|
async function handleLedgerExportAll(options) {
|
|
2576
2630
|
const { http } = getAuthedHttpClient();
|
|
@@ -2602,20 +2656,25 @@ async function handleLedgerExportAll(options) {
|
|
|
2602
2656
|
if (nextCursor === cursor) break;
|
|
2603
2657
|
cursor = nextCursor;
|
|
2604
2658
|
}
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2659
|
+
printCommandEnvelope({
|
|
2660
|
+
output_path: outputPath,
|
|
2661
|
+
row_count: summary.row_count,
|
|
2662
|
+
net_delta_credits: summary.net_delta_credits,
|
|
2663
|
+
scope: "current_auth_context",
|
|
2664
|
+
render: {
|
|
2665
|
+
sections: [
|
|
2666
|
+
{
|
|
2667
|
+
title: "billing ledger",
|
|
2668
|
+
lines: [
|
|
2669
|
+
`Billing ledger written to ${outputPath}`,
|
|
2670
|
+
`${summary.row_count} row(s) exported for the current auth context.`,
|
|
2671
|
+
`Net ledger delta: ${summary.net_delta_credits} Deepline Credits`
|
|
2672
|
+
]
|
|
2673
|
+
}
|
|
2674
|
+
]
|
|
2675
|
+
},
|
|
2676
|
+
local: { output_path: outputPath }
|
|
2677
|
+
}, { json: options.json });
|
|
2619
2678
|
}
|
|
2620
2679
|
async function handleCheckout(options) {
|
|
2621
2680
|
const { http } = getAuthedHttpClient();
|
|
@@ -2624,20 +2683,22 @@ async function handleCheckout(options) {
|
|
|
2624
2683
|
...options.credits ? { credits: Number.parseInt(options.credits, 10) } : {},
|
|
2625
2684
|
...options.discountCode ? { discountCode: options.discountCode } : {}
|
|
2626
2685
|
});
|
|
2627
|
-
if (shouldEmitJson(options.json)) return printJson(payload);
|
|
2628
2686
|
const url = String(payload.url || payload.checkout_url || "");
|
|
2629
|
-
if (!options.noOpen && url) openInBrowser(url);
|
|
2630
|
-
|
|
2631
|
-
|
|
2687
|
+
if (!options.json && !options.noOpen && url) openInBrowser(url);
|
|
2688
|
+
printCommandEnvelope({
|
|
2689
|
+
...payload,
|
|
2690
|
+
render: { sections: [{ title: "billing checkout", lines: [url || "Checkout session created."] }] }
|
|
2691
|
+
}, { json: options.json });
|
|
2632
2692
|
}
|
|
2633
2693
|
async function handleRedeemCode(code, options) {
|
|
2634
2694
|
const { http } = getAuthedHttpClient();
|
|
2635
2695
|
const payload = await http.post("/api/v2/billing/checkout/verify", { code });
|
|
2636
|
-
if (shouldEmitJson(options.json)) return printJson(payload);
|
|
2637
2696
|
const url = String(payload.url || "");
|
|
2638
|
-
if (!options.noOpen && url) openInBrowser(url);
|
|
2639
|
-
|
|
2640
|
-
|
|
2697
|
+
if (!options.json && !options.noOpen && url) openInBrowser(url);
|
|
2698
|
+
printCommandEnvelope({
|
|
2699
|
+
...payload,
|
|
2700
|
+
render: { sections: [{ title: "billing code", lines: [url || "Code redeemed."] }] }
|
|
2701
|
+
}, { json: options.json });
|
|
2641
2702
|
}
|
|
2642
2703
|
function registerBillingCommands(program) {
|
|
2643
2704
|
const billing = program.command("billing").description("Inspect balance, usage, limits, and checkout flows.").addHelpText(
|
|
@@ -2839,6 +2900,25 @@ function isRecord2(value) {
|
|
|
2839
2900
|
function isSerializedDataset(value) {
|
|
2840
2901
|
return isRecord2(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
|
|
2841
2902
|
}
|
|
2903
|
+
function pathParts(path) {
|
|
2904
|
+
return path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
2905
|
+
}
|
|
2906
|
+
function valueAtPath(root, path) {
|
|
2907
|
+
let cursor = root;
|
|
2908
|
+
for (const part of pathParts(path)) {
|
|
2909
|
+
if (!isRecord2(cursor)) {
|
|
2910
|
+
return void 0;
|
|
2911
|
+
}
|
|
2912
|
+
cursor = cursor[part];
|
|
2913
|
+
}
|
|
2914
|
+
return cursor;
|
|
2915
|
+
}
|
|
2916
|
+
function totalRowsForDataset(result, datasetPath) {
|
|
2917
|
+
const metadata = isRecord2(result._metadata) ? result._metadata : null;
|
|
2918
|
+
const parentPath = datasetPath.split(".").slice(0, -1).join(".");
|
|
2919
|
+
const parent = parentPath ? valueAtPath({ result }, parentPath) : result;
|
|
2920
|
+
return metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count ?? (isRecord2(parent) ? parent.totalRows ?? parent.rowCount ?? parent.count : void 0) ?? result.totalRows ?? result.rowCount ?? result.count;
|
|
2921
|
+
}
|
|
2842
2922
|
function rowArray(value) {
|
|
2843
2923
|
if (!Array.isArray(value)) {
|
|
2844
2924
|
return null;
|
|
@@ -2870,11 +2950,81 @@ function inferColumns(rows) {
|
|
|
2870
2950
|
}
|
|
2871
2951
|
return columns;
|
|
2872
2952
|
}
|
|
2873
|
-
function
|
|
2953
|
+
function canonicalRowsInfoFromCandidate(input) {
|
|
2954
|
+
const candidate = input;
|
|
2955
|
+
if (isSerializedDataset(candidate.value)) {
|
|
2956
|
+
const rawRows = rowArray(candidate.value.preview) ?? [];
|
|
2957
|
+
const totalRows2 = readNumber(candidate.value.count) ?? rawRows.length;
|
|
2958
|
+
const hasExplicitColumns = Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string");
|
|
2959
|
+
const rawColumns = hasExplicitColumns ? candidate.value.columns : inferColumns(rawRows);
|
|
2960
|
+
const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
|
|
2961
|
+
rows: rawRows,
|
|
2962
|
+
columns: rawColumns
|
|
2963
|
+
});
|
|
2964
|
+
return {
|
|
2965
|
+
rows: rows2,
|
|
2966
|
+
totalRows: totalRows2,
|
|
2967
|
+
columns,
|
|
2968
|
+
columnsExplicit: hasExplicitColumns,
|
|
2969
|
+
complete: rows2.length === totalRows2,
|
|
2970
|
+
source: candidate.source,
|
|
2971
|
+
datasetId: typeof candidate.value.datasetId === "string" ? candidate.value.datasetId : null,
|
|
2972
|
+
tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
|
|
2973
|
+
};
|
|
2974
|
+
}
|
|
2975
|
+
if (candidate.serializedOnly) {
|
|
2976
|
+
return null;
|
|
2977
|
+
}
|
|
2978
|
+
const rows = rowArray(candidate.value);
|
|
2979
|
+
if (!rows) {
|
|
2980
|
+
return null;
|
|
2981
|
+
}
|
|
2982
|
+
const totalRows = readNumber(candidate.total) ?? rows.length;
|
|
2983
|
+
const sanitized = sanitizeCsvProjectionInfo({
|
|
2984
|
+
rows,
|
|
2985
|
+
columns: inferColumns(rows)
|
|
2986
|
+
});
|
|
2987
|
+
return {
|
|
2988
|
+
rows: sanitized.rows,
|
|
2989
|
+
totalRows,
|
|
2990
|
+
columns: sanitized.columns,
|
|
2991
|
+
complete: rows.length === totalRows,
|
|
2992
|
+
source: candidate.source
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
function collectDatasetCandidates(input) {
|
|
2996
|
+
if (input.depth && input.depth > 16) {
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
if (isSerializedDataset(input.value)) {
|
|
3000
|
+
input.output.push({
|
|
3001
|
+
source: input.path,
|
|
3002
|
+
value: input.value,
|
|
3003
|
+
total: input.total
|
|
3004
|
+
});
|
|
3005
|
+
return;
|
|
3006
|
+
}
|
|
3007
|
+
if (!isRecord2(input.value)) {
|
|
3008
|
+
return;
|
|
3009
|
+
}
|
|
3010
|
+
for (const [key, child] of Object.entries(input.value)) {
|
|
3011
|
+
if (key === "preview" || key === "access") {
|
|
3012
|
+
continue;
|
|
3013
|
+
}
|
|
3014
|
+
collectDatasetCandidates({
|
|
3015
|
+
value: child,
|
|
3016
|
+
path: `${input.path}.${key}`,
|
|
3017
|
+
total: totalRowsForDataset(input.value, `${input.path}.${key}`),
|
|
3018
|
+
output: input.output,
|
|
3019
|
+
depth: (input.depth ?? 0) + 1
|
|
3020
|
+
});
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
function collectCanonicalRowsInfos(statusOrResult) {
|
|
2874
3024
|
const root = isRecord2(statusOrResult) ? statusOrResult : null;
|
|
2875
3025
|
const result = isRecord2(root?.result) ? root.result : root;
|
|
2876
3026
|
if (!result) {
|
|
2877
|
-
return
|
|
3027
|
+
return [];
|
|
2878
3028
|
}
|
|
2879
3029
|
const metadata = isRecord2(result._metadata) ? result._metadata : null;
|
|
2880
3030
|
const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
|
|
@@ -2894,41 +3044,57 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2894
3044
|
{ source: "result.output.results", value: result.output.results, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count }
|
|
2895
3045
|
);
|
|
2896
3046
|
}
|
|
3047
|
+
collectDatasetCandidates({
|
|
3048
|
+
value: result,
|
|
3049
|
+
path: "result",
|
|
3050
|
+
total: totalFromMetadata,
|
|
3051
|
+
output: candidates
|
|
3052
|
+
});
|
|
3053
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3054
|
+
const infos = [];
|
|
2897
3055
|
for (const candidate of candidates) {
|
|
2898
|
-
if (
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
});
|
|
2906
|
-
return {
|
|
2907
|
-
rows: rows2,
|
|
2908
|
-
totalRows: totalRows2,
|
|
2909
|
-
columns,
|
|
2910
|
-
complete: rows2.length === totalRows2,
|
|
2911
|
-
source: candidate.source
|
|
2912
|
-
};
|
|
3056
|
+
if (seen.has(candidate.source)) {
|
|
3057
|
+
continue;
|
|
3058
|
+
}
|
|
3059
|
+
seen.add(candidate.source);
|
|
3060
|
+
const info = canonicalRowsInfoFromCandidate(candidate);
|
|
3061
|
+
if (info) {
|
|
3062
|
+
infos.push(info);
|
|
2913
3063
|
}
|
|
2914
|
-
|
|
2915
|
-
|
|
3064
|
+
}
|
|
3065
|
+
return infos;
|
|
3066
|
+
}
|
|
3067
|
+
function collectSerializedDatasetRowsInfos(statusOrResult) {
|
|
3068
|
+
const root = isRecord2(statusOrResult) ? statusOrResult : null;
|
|
3069
|
+
const result = isRecord2(root?.result) ? root.result : root;
|
|
3070
|
+
if (!result) {
|
|
3071
|
+
return [];
|
|
3072
|
+
}
|
|
3073
|
+
const candidates = [];
|
|
3074
|
+
collectDatasetCandidates({
|
|
3075
|
+
value: result,
|
|
3076
|
+
path: "result",
|
|
3077
|
+
output: candidates
|
|
3078
|
+
});
|
|
3079
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3080
|
+
const infos = [];
|
|
3081
|
+
for (const candidate of candidates) {
|
|
3082
|
+
if (seen.has(candidate.source)) {
|
|
2916
3083
|
continue;
|
|
2917
3084
|
}
|
|
2918
|
-
|
|
2919
|
-
const
|
|
2920
|
-
|
|
2921
|
-
|
|
3085
|
+
seen.add(candidate.source);
|
|
3086
|
+
const info = canonicalRowsInfoFromCandidate({
|
|
3087
|
+
...candidate,
|
|
3088
|
+
serializedOnly: true
|
|
2922
3089
|
});
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
columns: sanitized.columns,
|
|
2927
|
-
complete: rows.length === totalRows,
|
|
2928
|
-
source: candidate.source
|
|
2929
|
-
};
|
|
3090
|
+
if (info) {
|
|
3091
|
+
infos.push(info);
|
|
3092
|
+
}
|
|
2930
3093
|
}
|
|
2931
|
-
return
|
|
3094
|
+
return infos;
|
|
3095
|
+
}
|
|
3096
|
+
function extractCanonicalRowsInfo(statusOrResult) {
|
|
3097
|
+
return collectCanonicalRowsInfos(statusOrResult)[0] ?? null;
|
|
2932
3098
|
}
|
|
2933
3099
|
function percentText(numerator, denominator) {
|
|
2934
3100
|
return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
|
|
@@ -3255,7 +3421,7 @@ function formatCell(value) {
|
|
|
3255
3421
|
const text = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
3256
3422
|
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
|
|
3257
3423
|
}
|
|
3258
|
-
function
|
|
3424
|
+
function tableLines(result) {
|
|
3259
3425
|
const rows = result.rows.filter(
|
|
3260
3426
|
(row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)
|
|
3261
3427
|
);
|
|
@@ -3263,17 +3429,17 @@ function printTable(result) {
|
|
|
3263
3429
|
const businessColumns = responseColumns.filter((column) => !column.startsWith("_"));
|
|
3264
3430
|
const columns = businessColumns.length > 0 ? businessColumns : responseColumns;
|
|
3265
3431
|
const hiddenColumns = responseColumns.filter((column) => !columns.includes(column));
|
|
3266
|
-
|
|
3432
|
+
const lines = [
|
|
3267
3433
|
`${result.command} returned ${result.row_count_returned} row(s)` + (result.truncated ? " (truncated)" : "")
|
|
3268
|
-
|
|
3434
|
+
];
|
|
3269
3435
|
if (hiddenColumns.length > 0) {
|
|
3270
|
-
|
|
3436
|
+
lines.push(
|
|
3271
3437
|
`Showing ${columns.length}/${responseColumns.length} columns; hidden metadata: ${hiddenColumns.join(", ")}`
|
|
3272
3438
|
);
|
|
3273
|
-
|
|
3439
|
+
lines.push("Use --json or select metadata columns explicitly when you need run ids/errors/stages.");
|
|
3274
3440
|
}
|
|
3275
3441
|
if (rows.length === 0) {
|
|
3276
|
-
return;
|
|
3442
|
+
return lines;
|
|
3277
3443
|
}
|
|
3278
3444
|
const widths = columns.map(
|
|
3279
3445
|
(column) => Math.min(
|
|
@@ -3284,13 +3450,14 @@ function printTable(result) {
|
|
|
3284
3450
|
)
|
|
3285
3451
|
)
|
|
3286
3452
|
);
|
|
3287
|
-
|
|
3288
|
-
|
|
3453
|
+
lines.push(columns.map((column, index) => column.padEnd(widths[index])).join(" "));
|
|
3454
|
+
lines.push(widths.map((width) => "-".repeat(width)).join(" "));
|
|
3289
3455
|
for (const row of rows) {
|
|
3290
|
-
|
|
3456
|
+
lines.push(
|
|
3291
3457
|
columns.map((column, index) => formatCell(row[column]).padEnd(widths[index])).join(" ")
|
|
3292
3458
|
);
|
|
3293
3459
|
}
|
|
3460
|
+
return lines;
|
|
3294
3461
|
}
|
|
3295
3462
|
async function handleDbQuery(args) {
|
|
3296
3463
|
const sqlIndex = args.indexOf("--sql");
|
|
@@ -3304,18 +3471,18 @@ async function handleDbQuery(args) {
|
|
|
3304
3471
|
const jsonOutput = argsWantJson(args);
|
|
3305
3472
|
const client = new DeeplineClient();
|
|
3306
3473
|
const result = await client.queryCustomerDb({ sql, maxRows });
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
}
|
|
3318
|
-
);
|
|
3474
|
+
const toolCommand = `deepline tools execute query_customer_db --payload ${JSON.stringify({
|
|
3475
|
+
sql,
|
|
3476
|
+
...maxRows ? { max_rows: maxRows } : {}
|
|
3477
|
+
})} --json`;
|
|
3478
|
+
printCommandEnvelope({
|
|
3479
|
+
...result,
|
|
3480
|
+
next: { toolEquivalent: toolCommand },
|
|
3481
|
+
render: {
|
|
3482
|
+
sections: [{ title: "customer db query", lines: tableLines(result) }],
|
|
3483
|
+
actions: [{ label: "Tool equivalent", command: toolCommand }]
|
|
3484
|
+
}
|
|
3485
|
+
}, { json: jsonOutput });
|
|
3319
3486
|
return 0;
|
|
3320
3487
|
}
|
|
3321
3488
|
function registerDbCommands(program) {
|
|
@@ -3363,11 +3530,12 @@ async function handleFeedback(text, options) {
|
|
|
3363
3530
|
...options.command ? { command: options.command } : {},
|
|
3364
3531
|
...options.payload ? { payload: options.payload } : {}
|
|
3365
3532
|
});
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3533
|
+
printCommandEnvelope({
|
|
3534
|
+
...response,
|
|
3535
|
+
render: {
|
|
3536
|
+
sections: [{ title: "feedback", lines: ["Feedback submitted. Thank you."] }]
|
|
3537
|
+
}
|
|
3538
|
+
}, { json: options.json });
|
|
3371
3539
|
}
|
|
3372
3540
|
function registerFeedbackCommands(program) {
|
|
3373
3541
|
const feedback = program.command("feedback").description("Submit CLI feedback to Deepline.").addHelpText(
|
|
@@ -3399,37 +3567,37 @@ Examples:
|
|
|
3399
3567
|
async function fetchOrganizations(http, apiKey) {
|
|
3400
3568
|
return http.post("/api/v2/auth/cli/organizations", { api_key: apiKey });
|
|
3401
3569
|
}
|
|
3402
|
-
function
|
|
3403
|
-
|
|
3570
|
+
function orgListLines(orgs) {
|
|
3571
|
+
return orgs.map((org, index) => {
|
|
3404
3572
|
const current = org.is_current ? " (current)" : "";
|
|
3405
3573
|
const role = org.role ? ` [${org.role}]` : "";
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
}
|
|
3574
|
+
return `${index + 1}. ${org.name}${role}${current}`;
|
|
3575
|
+
});
|
|
3409
3576
|
}
|
|
3410
3577
|
async function handleOrgList(options) {
|
|
3411
3578
|
const config = resolveConfig();
|
|
3412
3579
|
const http = new HttpClient(config);
|
|
3413
3580
|
const payload = await fetchOrganizations(http, config.apiKey);
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3581
|
+
printCommandEnvelope({
|
|
3582
|
+
...payload,
|
|
3583
|
+
render: {
|
|
3584
|
+
sections: [{ title: "Your organizations:", lines: orgListLines(payload.organizations) }]
|
|
3585
|
+
}
|
|
3586
|
+
}, { json: options.json });
|
|
3420
3587
|
}
|
|
3421
3588
|
async function handleOrgSwitch(selection, options) {
|
|
3422
3589
|
const config = resolveConfig();
|
|
3423
3590
|
const http = new HttpClient(config);
|
|
3424
3591
|
const payload = await fetchOrganizations(http, config.apiKey);
|
|
3425
3592
|
if (!selection && !options.orgId) {
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3593
|
+
printCommandEnvelope({
|
|
3594
|
+
...payload,
|
|
3595
|
+
next: { switch: "deepline org switch <number>" },
|
|
3596
|
+
render: {
|
|
3597
|
+
sections: [{ title: "Your organizations:", lines: orgListLines(payload.organizations) }],
|
|
3598
|
+
actions: [{ label: "Run", command: "deepline org switch <number>" }]
|
|
3599
|
+
}
|
|
3600
|
+
}, { json: options.json });
|
|
3433
3601
|
return;
|
|
3434
3602
|
}
|
|
3435
3603
|
let target = payload.organizations.find((org) => org.org_id === options.orgId);
|
|
@@ -3445,12 +3613,12 @@ async function handleOrgSwitch(selection, options) {
|
|
|
3445
3613
|
throw new Error("Could not resolve the selected organization.");
|
|
3446
3614
|
}
|
|
3447
3615
|
if (target.is_current) {
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3616
|
+
printCommandEnvelope({
|
|
3617
|
+
ok: true,
|
|
3618
|
+
unchanged: true,
|
|
3619
|
+
organization: target,
|
|
3620
|
+
render: { sections: [{ title: "org switch", lines: [`Already on ${target.name}.`] }] }
|
|
3621
|
+
}, { json: options.json });
|
|
3454
3622
|
return;
|
|
3455
3623
|
}
|
|
3456
3624
|
const switched = await http.post(
|
|
@@ -3462,14 +3630,24 @@ async function handleOrgSwitch(selection, options) {
|
|
|
3462
3630
|
DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
|
|
3463
3631
|
DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
|
|
3464
3632
|
});
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3633
|
+
const { api_key: _apiKey, ...publicSwitched } = switched;
|
|
3634
|
+
printCommandEnvelope({
|
|
3635
|
+
ok: true,
|
|
3636
|
+
host_env_path: hostEnvFilePath(config.baseUrl),
|
|
3637
|
+
...publicSwitched,
|
|
3638
|
+
api_key_saved: true,
|
|
3639
|
+
render: {
|
|
3640
|
+
sections: [
|
|
3641
|
+
{
|
|
3642
|
+
title: "org switch",
|
|
3643
|
+
lines: [
|
|
3644
|
+
`Switched to ${switched.org_name}.`,
|
|
3645
|
+
`Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
|
|
3646
|
+
]
|
|
3647
|
+
}
|
|
3648
|
+
]
|
|
3649
|
+
}
|
|
3650
|
+
}, { json: options.json });
|
|
3473
3651
|
}
|
|
3474
3652
|
function registerOrgCommands(program) {
|
|
3475
3653
|
const org = program.command("org").description("List and switch organizations.").addHelpText(
|
|
@@ -5147,6 +5325,9 @@ function traceCliSync(phase, fields, run) {
|
|
|
5147
5325
|
throw error;
|
|
5148
5326
|
}
|
|
5149
5327
|
}
|
|
5328
|
+
function sleep4(ms) {
|
|
5329
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
5330
|
+
}
|
|
5150
5331
|
function parseReferencedPlayTarget(target) {
|
|
5151
5332
|
const trimmed = target.trim();
|
|
5152
5333
|
const slashIndex = trimmed.indexOf("/");
|
|
@@ -5371,15 +5552,6 @@ function fileInputBindingsFromStaticPipeline(staticPipeline) {
|
|
|
5371
5552
|
);
|
|
5372
5553
|
return inputField ? [{ inputPath: inputField }] : [];
|
|
5373
5554
|
}
|
|
5374
|
-
function applyCsvShortcutInput(input) {
|
|
5375
|
-
const csvValue = getDottedInputValue(input.runtimeInput, "csv");
|
|
5376
|
-
if (csvValue == null || csvValue === "") return;
|
|
5377
|
-
const candidate = input.bindings.find((binding) => binding.inputPath !== "csv")?.inputPath ?? input.fallbackInputPath ?? null;
|
|
5378
|
-
if (!candidate || candidate === "csv") return;
|
|
5379
|
-
const existing = getDottedInputValue(input.runtimeInput, candidate);
|
|
5380
|
-
if (existing != null && existing !== "") return;
|
|
5381
|
-
setDottedInputValue(input.runtimeInput, candidate, csvValue);
|
|
5382
|
-
}
|
|
5383
5555
|
function isLocalFilePathValue(value) {
|
|
5384
5556
|
if (typeof value !== "string" || !value.trim()) return false;
|
|
5385
5557
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
@@ -6047,25 +6219,38 @@ function formatReturnValue(result) {
|
|
|
6047
6219
|
}
|
|
6048
6220
|
return lines;
|
|
6049
6221
|
}
|
|
6050
|
-
function
|
|
6051
|
-
|
|
6052
|
-
|
|
6222
|
+
function isDatasetHandle(value) {
|
|
6223
|
+
return Boolean(
|
|
6224
|
+
value && typeof value === "object" && !Array.isArray(value) && value.kind === "dataset"
|
|
6225
|
+
);
|
|
6226
|
+
}
|
|
6227
|
+
function collectDatasetHandleLines(value, path = "result") {
|
|
6228
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6229
|
+
return [];
|
|
6053
6230
|
}
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
}
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
}
|
|
6231
|
+
if (isDatasetHandle(value)) {
|
|
6232
|
+
const record = value;
|
|
6233
|
+
const count = typeof record.count === "number" ? record.count : typeof record.rowCount === "number" ? record.rowCount : null;
|
|
6234
|
+
const preview = Array.isArray(record.preview) ? record.preview : [];
|
|
6235
|
+
const lines2 = [
|
|
6236
|
+
` dataset ${typeof record.path === "string" ? record.path : path}: rows=${count === null ? "-" : formatInteger(count)} preview=${formatInteger(preview.length)}`
|
|
6237
|
+
];
|
|
6238
|
+
if (typeof record.queryDatasetCommand === "string") {
|
|
6239
|
+
lines2.push(` query dataset: ${record.queryDatasetCommand}`);
|
|
6240
|
+
}
|
|
6241
|
+
if (typeof record.slowExportAsCsvCommand === "string") {
|
|
6242
|
+
lines2.push(` export CSV: ${record.slowExportAsCsvCommand}`);
|
|
6243
|
+
}
|
|
6244
|
+
return lines2;
|
|
6245
|
+
}
|
|
6246
|
+
const lines = [];
|
|
6247
|
+
for (const [key, child] of Object.entries(value)) {
|
|
6248
|
+
if (key === "preview" || key === "access") {
|
|
6249
|
+
continue;
|
|
6250
|
+
}
|
|
6251
|
+
lines.push(...collectDatasetHandleLines(child, `${path}.${key}`));
|
|
6252
|
+
}
|
|
6253
|
+
return lines;
|
|
6069
6254
|
}
|
|
6070
6255
|
function buildRunWarnings(status, rowsInfo) {
|
|
6071
6256
|
if (status.status === "completed" && rowsInfo?.totalRows === 0) {
|
|
@@ -6078,19 +6263,12 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
6078
6263
|
}
|
|
6079
6264
|
return [];
|
|
6080
6265
|
}
|
|
6081
|
-
function buildRunNextCommands(runId
|
|
6082
|
-
|
|
6266
|
+
function buildRunNextCommands(runId) {
|
|
6267
|
+
return {
|
|
6083
6268
|
get: `deepline runs get ${runId} --json`,
|
|
6084
6269
|
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
6085
6270
|
logs: `deepline runs logs ${runId} --out run.log --json`
|
|
6086
6271
|
};
|
|
6087
|
-
if (!rowsInfo || rowsInfo.complete) {
|
|
6088
|
-
commands.exportCsv = buildRunExportCommand(runId);
|
|
6089
|
-
}
|
|
6090
|
-
return commands;
|
|
6091
|
-
}
|
|
6092
|
-
function buildRunExportCommand(runId) {
|
|
6093
|
-
return `deepline runs export ${runId} --out output.csv`;
|
|
6094
6272
|
}
|
|
6095
6273
|
var RUN_LOG_PREVIEW_LIMIT = 20;
|
|
6096
6274
|
function getRecordField(value, key) {
|
|
@@ -6145,31 +6323,6 @@ function normalizeProgressForEnvelope(status, rowsInfo) {
|
|
|
6145
6323
|
wait: status.wait ?? null
|
|
6146
6324
|
};
|
|
6147
6325
|
}
|
|
6148
|
-
function normalizeOutputsForEnvelope(rowsInfo, runId, exportedPath) {
|
|
6149
|
-
if (!rowsInfo) {
|
|
6150
|
-
return exportedPath ? [{ name: "output", kind: "file", path: exportedPath }] : [];
|
|
6151
|
-
}
|
|
6152
|
-
const isPartial = !rowsInfo.complete;
|
|
6153
|
-
return [
|
|
6154
|
-
{
|
|
6155
|
-
name: "rows",
|
|
6156
|
-
kind: "dataset",
|
|
6157
|
-
rowCount: rowsInfo.totalRows,
|
|
6158
|
-
columns: rowsInfo.columns,
|
|
6159
|
-
preview: rowsInfo.rows.slice(0, 5),
|
|
6160
|
-
previewRowCount: Math.min(rowsInfo.rows.length, 5),
|
|
6161
|
-
previewLimit: 5,
|
|
6162
|
-
...isPartial ? {
|
|
6163
|
-
isPartial: true,
|
|
6164
|
-
previewCount: rowsInfo.rows.length,
|
|
6165
|
-
totalCount: rowsInfo.totalRows
|
|
6166
|
-
} : { isPartial: false },
|
|
6167
|
-
complete: rowsInfo.complete,
|
|
6168
|
-
source: rowsInfo.source,
|
|
6169
|
-
...exportedPath ? { csv_path: exportedPath } : {}
|
|
6170
|
-
}
|
|
6171
|
-
];
|
|
6172
|
-
}
|
|
6173
6326
|
function normalizeStepsForEnvelope(status) {
|
|
6174
6327
|
const directSteps = getRecordField(status, "steps");
|
|
6175
6328
|
if (Array.isArray(directSteps)) {
|
|
@@ -6231,7 +6384,7 @@ function stripProviderSpendFromBilling(value) {
|
|
|
6231
6384
|
}
|
|
6232
6385
|
return next;
|
|
6233
6386
|
}
|
|
6234
|
-
function compactPlayStatus(status
|
|
6387
|
+
function compactPlayStatus(status) {
|
|
6235
6388
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
6236
6389
|
const result = status && typeof status === "object" ? status.result : null;
|
|
6237
6390
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
@@ -6254,23 +6407,16 @@ function compactPlayStatus(status, options) {
|
|
|
6254
6407
|
status: status.status,
|
|
6255
6408
|
run: normalizeRunStatusForEnvelope(status),
|
|
6256
6409
|
progress: normalizeProgressForEnvelope(status, rowsInfo),
|
|
6257
|
-
outputs: normalizeOutputsForEnvelope(
|
|
6258
|
-
rowsInfo,
|
|
6259
|
-
status.runId,
|
|
6260
|
-
options?.exportedPath
|
|
6261
|
-
),
|
|
6262
6410
|
steps: normalizeStepsForEnvelope(status),
|
|
6263
6411
|
errors: normalizeErrorsForEnvelope(status, error),
|
|
6264
6412
|
logs: normalizeLogsForEnvelope(status),
|
|
6265
6413
|
...error ? { error } : {},
|
|
6266
6414
|
...warnings.length > 0 ? { warnings } : {},
|
|
6267
|
-
output: buildOutputSummary(rowsInfo, status.runId, options?.exportedPath) ?? result ?? null,
|
|
6268
6415
|
...result !== void 0 ? { result } : {},
|
|
6269
6416
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
6270
6417
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
6271
|
-
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
|
|
6272
6418
|
...billing ? { billing } : {},
|
|
6273
|
-
next: buildRunNextCommands(status.runId
|
|
6419
|
+
next: buildRunNextCommands(status.runId)
|
|
6274
6420
|
};
|
|
6275
6421
|
}
|
|
6276
6422
|
function enrichPlayStatusWithDatasetStats(status) {
|
|
@@ -6308,12 +6454,20 @@ function formatDatasetStatsLines(datasetStats) {
|
|
|
6308
6454
|
}
|
|
6309
6455
|
function writePlayResult(status, jsonOutput, options) {
|
|
6310
6456
|
if (jsonOutput) {
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6457
|
+
const payload2 = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status);
|
|
6458
|
+
printCommandEnvelope({
|
|
6459
|
+
...payload2,
|
|
6460
|
+
render: {
|
|
6461
|
+
sections: [
|
|
6462
|
+
{
|
|
6463
|
+
title: "run result",
|
|
6464
|
+
lines: [
|
|
6465
|
+
`${status.status ?? "running"} ${status.runId ?? "unknown"}`
|
|
6466
|
+
]
|
|
6467
|
+
}
|
|
6468
|
+
]
|
|
6469
|
+
}
|
|
6470
|
+
}, { json: true });
|
|
6317
6471
|
return;
|
|
6318
6472
|
}
|
|
6319
6473
|
const result = status.result;
|
|
@@ -6330,23 +6484,6 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6330
6484
|
rowsInfo.columns,
|
|
6331
6485
|
extractDatasetExecutionStats(status)
|
|
6332
6486
|
) : null;
|
|
6333
|
-
const outputSummary = buildOutputSummary(
|
|
6334
|
-
rowsInfo,
|
|
6335
|
-
runId,
|
|
6336
|
-
options?.exportedPath
|
|
6337
|
-
);
|
|
6338
|
-
if (outputSummary) {
|
|
6339
|
-
const columns = Array.isArray(outputSummary.columns) ? outputSummary.columns.length : 0;
|
|
6340
|
-
const path = typeof outputSummary.csv_path === "string" ? ` file=${outputSummary.csv_path}` : "";
|
|
6341
|
-
lines.push(
|
|
6342
|
-
` output: rows=${formatInteger(outputSummary.rowCount)} columns=${formatInteger(columns)}${path}`
|
|
6343
|
-
);
|
|
6344
|
-
if (outputSummary.isPartial === true) {
|
|
6345
|
-
lines.push(
|
|
6346
|
-
` partial output: showing ${formatInteger(outputSummary.previewCount)} preview row(s) of ${formatInteger(outputSummary.totalCount)}`
|
|
6347
|
-
);
|
|
6348
|
-
}
|
|
6349
|
-
}
|
|
6350
6487
|
for (const warning of warnings) {
|
|
6351
6488
|
lines.push(` warning: ${warning}`);
|
|
6352
6489
|
}
|
|
@@ -6358,29 +6495,213 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6358
6495
|
const renderedServerView = renderServerResultView(status.resultView);
|
|
6359
6496
|
if (result) {
|
|
6360
6497
|
lines.push(...formatReturnValue(result));
|
|
6498
|
+
lines.push(...collectDatasetHandleLines(result));
|
|
6361
6499
|
}
|
|
6362
6500
|
if (renderedServerView.lines.length > 0) {
|
|
6363
6501
|
lines.push(...renderedServerView.lines);
|
|
6364
6502
|
}
|
|
6365
6503
|
lines.push(...renderedServerView.actions);
|
|
6366
|
-
|
|
6504
|
+
const payload = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status);
|
|
6505
|
+
printCommandEnvelope({
|
|
6506
|
+
...payload,
|
|
6507
|
+
render: {
|
|
6508
|
+
sections: [{ title: "run result", lines }]
|
|
6509
|
+
}
|
|
6510
|
+
}, { json: jsonOutput, text: `${lines.join("\n")}
|
|
6511
|
+
` });
|
|
6512
|
+
}
|
|
6513
|
+
var RUN_EXPORT_PAGE_SIZE = 5e3;
|
|
6514
|
+
function shellSingleQuote(value) {
|
|
6515
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
6516
|
+
}
|
|
6517
|
+
function runExportRetryCommand(runId, outPath, datasetPath) {
|
|
6518
|
+
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote((0, import_node_path9.resolve)(outPath))}`;
|
|
6519
|
+
}
|
|
6520
|
+
function extractRunPlayName(status) {
|
|
6521
|
+
const run = status.run;
|
|
6522
|
+
const candidates = [
|
|
6523
|
+
status.playName,
|
|
6524
|
+
status.name,
|
|
6525
|
+
getRecordField(run, "playName"),
|
|
6526
|
+
getRecordField(run, "name")
|
|
6527
|
+
];
|
|
6528
|
+
for (const candidate of candidates) {
|
|
6529
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
6530
|
+
return candidate.trim();
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
return null;
|
|
6534
|
+
}
|
|
6535
|
+
function exportableSheetRow(row) {
|
|
6536
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
6537
|
+
return null;
|
|
6538
|
+
}
|
|
6539
|
+
const record = row;
|
|
6540
|
+
const data = record.data;
|
|
6541
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
6542
|
+
return data;
|
|
6543
|
+
}
|
|
6544
|
+
const fallback = { ...record };
|
|
6545
|
+
for (const key of [
|
|
6546
|
+
"key",
|
|
6547
|
+
"status",
|
|
6548
|
+
"cellMeta",
|
|
6549
|
+
"inputIndex",
|
|
6550
|
+
"runId",
|
|
6551
|
+
"error",
|
|
6552
|
+
"stage",
|
|
6553
|
+
"provider",
|
|
6554
|
+
"seq",
|
|
6555
|
+
"createdAt",
|
|
6556
|
+
"updatedAt"
|
|
6557
|
+
]) {
|
|
6558
|
+
delete fallback[key];
|
|
6559
|
+
}
|
|
6560
|
+
return fallback;
|
|
6561
|
+
}
|
|
6562
|
+
function mergeExportColumns(preferredColumns, rows) {
|
|
6563
|
+
const columns = [];
|
|
6564
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6565
|
+
for (const column of preferredColumns) {
|
|
6566
|
+
if (!column || seen.has(column)) {
|
|
6567
|
+
continue;
|
|
6568
|
+
}
|
|
6569
|
+
seen.add(column);
|
|
6570
|
+
columns.push(column);
|
|
6571
|
+
}
|
|
6572
|
+
for (const row of rows) {
|
|
6573
|
+
for (const column of Object.keys(row)) {
|
|
6574
|
+
if (seen.has(column)) {
|
|
6575
|
+
continue;
|
|
6576
|
+
}
|
|
6577
|
+
seen.add(column);
|
|
6578
|
+
columns.push(column);
|
|
6579
|
+
}
|
|
6580
|
+
}
|
|
6581
|
+
return columns;
|
|
6367
6582
|
}
|
|
6368
|
-
function
|
|
6583
|
+
async function fetchBackingDatasetRows(input) {
|
|
6584
|
+
const playName = extractRunPlayName(input.status);
|
|
6585
|
+
const tableNamespace = input.rowsInfo.tableNamespace?.trim();
|
|
6586
|
+
if (!playName || !tableNamespace) {
|
|
6587
|
+
return null;
|
|
6588
|
+
}
|
|
6589
|
+
const sheetRows = [];
|
|
6590
|
+
let offset = 0;
|
|
6591
|
+
let expectedTotal = input.rowsInfo.totalRows;
|
|
6592
|
+
while (true) {
|
|
6593
|
+
const page = await input.client.runs.exportDatasetRows({
|
|
6594
|
+
playName,
|
|
6595
|
+
tableNamespace,
|
|
6596
|
+
runId: input.status.runId,
|
|
6597
|
+
limit: RUN_EXPORT_PAGE_SIZE,
|
|
6598
|
+
offset
|
|
6599
|
+
});
|
|
6600
|
+
sheetRows.push(...page.rows);
|
|
6601
|
+
const summaryTotal = page.summary?.stats?.total;
|
|
6602
|
+
if (typeof summaryTotal === "number" && Number.isFinite(summaryTotal)) {
|
|
6603
|
+
expectedTotal = Math.max(expectedTotal, Math.trunc(summaryTotal));
|
|
6604
|
+
}
|
|
6605
|
+
if (page.rows.length < RUN_EXPORT_PAGE_SIZE || sheetRows.length >= expectedTotal) {
|
|
6606
|
+
break;
|
|
6607
|
+
}
|
|
6608
|
+
offset += page.rows.length;
|
|
6609
|
+
}
|
|
6610
|
+
const rows = sheetRows.map(exportableSheetRow).filter((row) => Boolean(row));
|
|
6611
|
+
if (rows.length < input.rowsInfo.totalRows) {
|
|
6612
|
+
return null;
|
|
6613
|
+
}
|
|
6614
|
+
const columns = mergeExportColumns(
|
|
6615
|
+
input.rowsInfo.columnsExplicit ? input.rowsInfo.columns : [],
|
|
6616
|
+
rows
|
|
6617
|
+
);
|
|
6618
|
+
return {
|
|
6619
|
+
...input.rowsInfo,
|
|
6620
|
+
rows,
|
|
6621
|
+
columns,
|
|
6622
|
+
totalRows: rows.length,
|
|
6623
|
+
complete: true,
|
|
6624
|
+
source: `${input.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
|
|
6625
|
+
};
|
|
6626
|
+
}
|
|
6627
|
+
async function exportPlayStatusRows(client, status, outPath, options = {}) {
|
|
6369
6628
|
if (!outPath) {
|
|
6370
6629
|
return null;
|
|
6371
6630
|
}
|
|
6372
|
-
const
|
|
6631
|
+
const availableRows = collectSerializedDatasetRowsInfos(status);
|
|
6632
|
+
let rowsInfo = options.datasetPath ? availableRows.find((info) => info.source === options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
|
|
6633
|
+
if (!rowsInfo && options.datasetPath) {
|
|
6634
|
+
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
6635
|
+
throw new DeeplineError(
|
|
6636
|
+
`Run ${status.runId} did not return a dataset at ${options.datasetPath}.` + (available.length > 0 ? ` Available datasets: ${available.join(", ")}.` : ""),
|
|
6637
|
+
void 0,
|
|
6638
|
+
"RUN_EXPORT_DATASET_NOT_FOUND",
|
|
6639
|
+
{ runId: status.runId, dataset: options.datasetPath, available }
|
|
6640
|
+
);
|
|
6641
|
+
}
|
|
6642
|
+
if (!options.datasetPath && availableRows.length > 1) {
|
|
6643
|
+
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
6644
|
+
throw new DeeplineError(
|
|
6645
|
+
`Run ${status.runId} returned multiple datasets. Choose one with --dataset <path>: ${available.join(", ")}.`,
|
|
6646
|
+
void 0,
|
|
6647
|
+
"RUN_EXPORT_DATASET_REQUIRED",
|
|
6648
|
+
{ runId: status.runId, available }
|
|
6649
|
+
);
|
|
6650
|
+
}
|
|
6373
6651
|
if (!rowsInfo) {
|
|
6374
6652
|
throw new DeeplineError(
|
|
6375
6653
|
`Run ${status.runId} did not expose a row-shaped final output to export.`
|
|
6376
6654
|
);
|
|
6377
6655
|
}
|
|
6656
|
+
const attempts = Math.max(1, Math.trunc(options.attempts ?? 1));
|
|
6657
|
+
const retryDelayMs = Math.max(0, Math.trunc(options.retryDelayMs ?? 0));
|
|
6658
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
6659
|
+
let fetchedRowsInfo = null;
|
|
6660
|
+
try {
|
|
6661
|
+
fetchedRowsInfo = await fetchBackingDatasetRows({
|
|
6662
|
+
client,
|
|
6663
|
+
status,
|
|
6664
|
+
rowsInfo
|
|
6665
|
+
});
|
|
6666
|
+
} catch (error) {
|
|
6667
|
+
if (!rowsInfo.complete) {
|
|
6668
|
+
throw error;
|
|
6669
|
+
}
|
|
6670
|
+
}
|
|
6671
|
+
if (fetchedRowsInfo?.complete) {
|
|
6672
|
+
return {
|
|
6673
|
+
path: writeCanonicalRowsCsv(fetchedRowsInfo, outPath),
|
|
6674
|
+
rowsInfo: fetchedRowsInfo
|
|
6675
|
+
};
|
|
6676
|
+
}
|
|
6677
|
+
if (rowsInfo.complete) {
|
|
6678
|
+
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6679
|
+
}
|
|
6680
|
+
if (attempt < attempts && retryDelayMs > 0) {
|
|
6681
|
+
await sleep4(retryDelayMs);
|
|
6682
|
+
}
|
|
6683
|
+
}
|
|
6378
6684
|
if (!rowsInfo.complete) {
|
|
6685
|
+
const retryCommand = runExportRetryCommand(
|
|
6686
|
+
status.runId,
|
|
6687
|
+
outPath,
|
|
6688
|
+
options.datasetPath ?? rowsInfo.source
|
|
6689
|
+
);
|
|
6379
6690
|
throw new DeeplineError(
|
|
6380
|
-
`Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows};
|
|
6691
|
+
`Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; the backing dataset was not ready to export yet. Retry with: ${retryCommand}`,
|
|
6692
|
+
void 0,
|
|
6693
|
+
"RUN_EXPORT_NOT_READY",
|
|
6694
|
+
{
|
|
6695
|
+
runId: status.runId,
|
|
6696
|
+
previewRowCount: rowsInfo.rows.length,
|
|
6697
|
+
totalRows: rowsInfo.totalRows,
|
|
6698
|
+
tableNamespace: rowsInfo.tableNamespace ?? null,
|
|
6699
|
+
dataset: options.datasetPath ?? rowsInfo.source,
|
|
6700
|
+
retry_command: retryCommand
|
|
6701
|
+
}
|
|
6381
6702
|
);
|
|
6382
6703
|
}
|
|
6383
|
-
return writeCanonicalRowsCsv(rowsInfo, outPath);
|
|
6704
|
+
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6384
6705
|
}
|
|
6385
6706
|
function renderServerResultView(value) {
|
|
6386
6707
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -6454,8 +6775,10 @@ function writeStartedPlayRun(input) {
|
|
|
6454
6775
|
dashboardUrl: input.dashboardUrl
|
|
6455
6776
|
};
|
|
6456
6777
|
if (input.jsonOutput) {
|
|
6457
|
-
|
|
6458
|
-
|
|
6778
|
+
printCommandEnvelope({
|
|
6779
|
+
...payload,
|
|
6780
|
+
render: { sections: [{ title: "play run", lines: [`Started ${input.playName}`, `run id: ${input.runId}`] }] }
|
|
6781
|
+
}, { json: true });
|
|
6459
6782
|
return;
|
|
6460
6783
|
}
|
|
6461
6784
|
const lines = [
|
|
@@ -6474,10 +6797,14 @@ function writeStartedPlayRun(input) {
|
|
|
6474
6797
|
input.progress.writeLine(output, process.stdout);
|
|
6475
6798
|
return;
|
|
6476
6799
|
}
|
|
6477
|
-
|
|
6800
|
+
printCommandEnvelope({
|
|
6801
|
+
...payload,
|
|
6802
|
+
render: { sections: [{ title: "play run", lines }] }
|
|
6803
|
+
}, { json: false, text: `${output}
|
|
6804
|
+
` });
|
|
6478
6805
|
}
|
|
6479
6806
|
function parsePlayRunOptions(args) {
|
|
6480
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--
|
|
6807
|
+
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.map guidance.";
|
|
6481
6808
|
let filePath = null;
|
|
6482
6809
|
let playName = null;
|
|
6483
6810
|
let input = null;
|
|
@@ -6488,7 +6815,6 @@ function parsePlayRunOptions(args) {
|
|
|
6488
6815
|
const emitLogs = !jsonOutput || args.includes("--logs");
|
|
6489
6816
|
const force = args.includes("--force");
|
|
6490
6817
|
const noOpen = args.includes("--no-open");
|
|
6491
|
-
let outPath = null;
|
|
6492
6818
|
let waitTimeoutMs = null;
|
|
6493
6819
|
for (let index = 0; index < args.length; index += 1) {
|
|
6494
6820
|
const arg = args[index];
|
|
@@ -6516,9 +6842,10 @@ function parsePlayRunOptions(args) {
|
|
|
6516
6842
|
revisionSelector = "latest";
|
|
6517
6843
|
continue;
|
|
6518
6844
|
}
|
|
6519
|
-
if (arg === "--out"
|
|
6520
|
-
|
|
6521
|
-
|
|
6845
|
+
if (arg === "--out" || arg.startsWith("--out=")) {
|
|
6846
|
+
throw new Error(
|
|
6847
|
+
"--out is not a plays run flag. Run the play first, then export rows with: deepline runs export <run-id> --out output.csv"
|
|
6848
|
+
);
|
|
6522
6849
|
}
|
|
6523
6850
|
if (arg === "--poll-interval-ms" || arg === "--interval-ms") {
|
|
6524
6851
|
throw new Error(
|
|
@@ -6547,6 +6874,11 @@ function parsePlayRunOptions(args) {
|
|
|
6547
6874
|
}
|
|
6548
6875
|
continue;
|
|
6549
6876
|
}
|
|
6877
|
+
if (arg === "--csv" || arg.startsWith("--csv=")) {
|
|
6878
|
+
throw new Error(
|
|
6879
|
+
`--csv is not a plays run flag. Pass CSV paths through --input, for example: deepline plays run my.play.ts --input '{"file":"leads.csv"}' --watch`
|
|
6880
|
+
);
|
|
6881
|
+
}
|
|
6550
6882
|
if (arg.startsWith("--")) {
|
|
6551
6883
|
const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
|
|
6552
6884
|
input ??= {};
|
|
@@ -6583,11 +6915,6 @@ function parsePlayRunOptions(args) {
|
|
|
6583
6915
|
"--live, --latest, and --revision-id only apply to named plays."
|
|
6584
6916
|
);
|
|
6585
6917
|
}
|
|
6586
|
-
if (outPath && !watch) {
|
|
6587
|
-
throw new Error(
|
|
6588
|
-
"--out requires --watch so the CLI can export the completed run output."
|
|
6589
|
-
);
|
|
6590
|
-
}
|
|
6591
6918
|
return {
|
|
6592
6919
|
target: filePath ? { kind: "file", path: filePath } : { kind: "name", name: playName },
|
|
6593
6920
|
input,
|
|
@@ -6598,7 +6925,6 @@ function parsePlayRunOptions(args) {
|
|
|
6598
6925
|
jsonOutput,
|
|
6599
6926
|
waitTimeoutMs,
|
|
6600
6927
|
force,
|
|
6601
|
-
outPath,
|
|
6602
6928
|
noOpen
|
|
6603
6929
|
};
|
|
6604
6930
|
}
|
|
@@ -6732,11 +7058,6 @@ async function handleFileBackedRun(options) {
|
|
|
6732
7058
|
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
6733
7059
|
compilerManifest.staticPipeline
|
|
6734
7060
|
);
|
|
6735
|
-
applyCsvShortcutInput({
|
|
6736
|
-
runtimeInput,
|
|
6737
|
-
bindings: fileInputBindings,
|
|
6738
|
-
fallbackInputPath: "file"
|
|
6739
|
-
});
|
|
6740
7061
|
const stagedFileInputs = await traceCliSpan(
|
|
6741
7062
|
"cli.play_stage_inputs",
|
|
6742
7063
|
{
|
|
@@ -6778,11 +7099,6 @@ async function handleFileBackedRun(options) {
|
|
|
6778
7099
|
progress
|
|
6779
7100
|
})
|
|
6780
7101
|
);
|
|
6781
|
-
const exportedPath = traceCliSync(
|
|
6782
|
-
"cli.play_export_rows",
|
|
6783
|
-
{ targetKind: "file", playName },
|
|
6784
|
-
() => exportPlayStatusRows(finalStatus, options.outPath)
|
|
6785
|
-
);
|
|
6786
7102
|
if (finalStatus.status === "completed") {
|
|
6787
7103
|
progress.complete();
|
|
6788
7104
|
} else {
|
|
@@ -6791,7 +7107,7 @@ async function handleFileBackedRun(options) {
|
|
|
6791
7107
|
traceCliSync(
|
|
6792
7108
|
"cli.play_write_result",
|
|
6793
7109
|
{ targetKind: "file", playName },
|
|
6794
|
-
() => writePlayResult(finalStatus, options.jsonOutput
|
|
7110
|
+
() => writePlayResult(finalStatus, options.jsonOutput)
|
|
6795
7111
|
);
|
|
6796
7112
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
6797
7113
|
}
|
|
@@ -6881,10 +7197,6 @@ async function handleNamedRun(options) {
|
|
|
6881
7197
|
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
6882
7198
|
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
6883
7199
|
] : [];
|
|
6884
|
-
applyCsvShortcutInput({
|
|
6885
|
-
runtimeInput,
|
|
6886
|
-
bindings: fileInputBindings
|
|
6887
|
-
});
|
|
6888
7200
|
const stagedFileInputs = await traceCliSpan(
|
|
6889
7201
|
"cli.play_stage_inputs",
|
|
6890
7202
|
{
|
|
@@ -6923,11 +7235,6 @@ async function handleNamedRun(options) {
|
|
|
6923
7235
|
progress
|
|
6924
7236
|
})
|
|
6925
7237
|
);
|
|
6926
|
-
const exportedPath = traceCliSync(
|
|
6927
|
-
"cli.play_export_rows",
|
|
6928
|
-
{ targetKind: "name", playName },
|
|
6929
|
-
() => exportPlayStatusRows(finalStatus, options.outPath)
|
|
6930
|
-
);
|
|
6931
7238
|
if (finalStatus.status === "completed") {
|
|
6932
7239
|
progress.complete();
|
|
6933
7240
|
} else {
|
|
@@ -6936,7 +7243,7 @@ async function handleNamedRun(options) {
|
|
|
6936
7243
|
traceCliSync(
|
|
6937
7244
|
"cli.play_write_result",
|
|
6938
7245
|
{ targetKind: "name", playName },
|
|
6939
|
-
() => writePlayResult(finalStatus, options.jsonOutput
|
|
7246
|
+
() => writePlayResult(finalStatus, options.jsonOutput)
|
|
6940
7247
|
);
|
|
6941
7248
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
6942
7249
|
}
|
|
@@ -7005,7 +7312,7 @@ function parseRunIdPositional(args, usage) {
|
|
|
7005
7312
|
}
|
|
7006
7313
|
continue;
|
|
7007
7314
|
}
|
|
7008
|
-
if ((arg === "--out" || arg === "--reason") && args[index + 1]) {
|
|
7315
|
+
if ((arg === "--out" || arg === "--reason" || arg === "--dataset") && args[index + 1]) {
|
|
7009
7316
|
index += 1;
|
|
7010
7317
|
continue;
|
|
7011
7318
|
}
|
|
@@ -7067,26 +7374,15 @@ async function handleRunsList(args) {
|
|
|
7067
7374
|
executionTime: run.executionTime,
|
|
7068
7375
|
playName: run.memo?.playName ?? playName
|
|
7069
7376
|
}));
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
`
|
|
7080
|
-
);
|
|
7081
|
-
} else {
|
|
7082
|
-
if (runs.length === 0) {
|
|
7083
|
-
console.log(`No runs found for ${playName}.`);
|
|
7084
|
-
} else {
|
|
7085
|
-
for (const run of runs) {
|
|
7086
|
-
console.log(`${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
7087
|
-
}
|
|
7088
|
-
}
|
|
7089
|
-
}
|
|
7377
|
+
const lines = runs.length === 0 ? [`No runs found for ${playName}.`] : runs.map((run) => `${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
7378
|
+
printCommandEnvelope({
|
|
7379
|
+
runs,
|
|
7380
|
+
count: runs.length,
|
|
7381
|
+
next: {
|
|
7382
|
+
get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
|
|
7383
|
+
},
|
|
7384
|
+
render: { sections: [{ title: "runs", lines }] }
|
|
7385
|
+
}, { json: argsWantJson(args) });
|
|
7090
7386
|
return 0;
|
|
7091
7387
|
}
|
|
7092
7388
|
async function handleRunTail(args) {
|
|
@@ -7140,41 +7436,30 @@ async function handleRunLogs(args) {
|
|
|
7140
7436
|
const logs = status.progress?.logs ?? [];
|
|
7141
7437
|
if (outPath) {
|
|
7142
7438
|
(0, import_node_fs7.writeFileSync)(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
`
|
|
7151
|
-
);
|
|
7152
|
-
} else {
|
|
7153
|
-
console.log(`Wrote ${logs.length} log lines to ${outPath}`);
|
|
7154
|
-
}
|
|
7439
|
+
printCommandEnvelope({
|
|
7440
|
+
runId: status.runId,
|
|
7441
|
+
log_path: outPath,
|
|
7442
|
+
lineCount: logs.length,
|
|
7443
|
+
local: { log_path: outPath },
|
|
7444
|
+
render: { sections: [{ title: "run logs", lines: [`Wrote ${logs.length} log lines to ${outPath}`] }] }
|
|
7445
|
+
}, { json: argsWantJson(args) });
|
|
7155
7446
|
return 0;
|
|
7156
7447
|
}
|
|
7157
7448
|
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
})}
|
|
7173
|
-
`
|
|
7174
|
-
);
|
|
7175
|
-
} else {
|
|
7176
|
-
process.stdout.write(`${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`);
|
|
7177
|
-
}
|
|
7449
|
+
printCommandEnvelope({
|
|
7450
|
+
runId: status.runId,
|
|
7451
|
+
totalCount: logs.length,
|
|
7452
|
+
returnedCount: entries.length,
|
|
7453
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
7454
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
7455
|
+
truncated: logs.length > entries.length,
|
|
7456
|
+
hasMore: logs.length > entries.length,
|
|
7457
|
+
entries,
|
|
7458
|
+
next: {
|
|
7459
|
+
export: `deepline runs logs ${status.runId} --out run.log --json`
|
|
7460
|
+
},
|
|
7461
|
+
render: { sections: [{ title: "run logs", lines: entries }] }
|
|
7462
|
+
}, { json: argsWantJson(args), text: `${entries.join("\n")}${entries.length > 0 ? "\n" : ""}` });
|
|
7178
7463
|
return 0;
|
|
7179
7464
|
}
|
|
7180
7465
|
async function handleRunStop(args) {
|
|
@@ -7195,19 +7480,18 @@ async function handleRunStop(args) {
|
|
|
7195
7480
|
}
|
|
7196
7481
|
const client = new DeeplineClient();
|
|
7197
7482
|
const result = await client.runs.stop(runId, { reason });
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
`
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
}
|
|
7483
|
+
const lines = [
|
|
7484
|
+
`Stopped ${result.runId}`,
|
|
7485
|
+
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : []
|
|
7486
|
+
];
|
|
7487
|
+
printCommandEnvelope({
|
|
7488
|
+
...result,
|
|
7489
|
+
render: { sections: [{ title: "run stop", lines }] }
|
|
7490
|
+
}, { json: argsWantJson(args) });
|
|
7207
7491
|
return 0;
|
|
7208
7492
|
}
|
|
7209
7493
|
async function handleRunExport(args) {
|
|
7210
|
-
const usage = "Usage: deepline runs export <run-id> --out output.csv [--json]";
|
|
7494
|
+
const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--json]";
|
|
7211
7495
|
let runId;
|
|
7212
7496
|
try {
|
|
7213
7497
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -7216,10 +7500,15 @@ async function handleRunExport(args) {
|
|
|
7216
7500
|
return 1;
|
|
7217
7501
|
}
|
|
7218
7502
|
let outPath = null;
|
|
7503
|
+
let datasetPath = null;
|
|
7219
7504
|
for (let index = 0; index < args.length; index += 1) {
|
|
7220
7505
|
const arg = args[index];
|
|
7221
7506
|
if (arg === "--out" && args[index + 1]) {
|
|
7222
7507
|
outPath = (0, import_node_path9.resolve)(args[++index]);
|
|
7508
|
+
continue;
|
|
7509
|
+
}
|
|
7510
|
+
if (arg === "--dataset" && args[index + 1]) {
|
|
7511
|
+
datasetPath = args[++index];
|
|
7223
7512
|
}
|
|
7224
7513
|
}
|
|
7225
7514
|
if (!outPath) {
|
|
@@ -7228,21 +7517,18 @@ async function handleRunExport(args) {
|
|
|
7228
7517
|
}
|
|
7229
7518
|
const client = new DeeplineClient();
|
|
7230
7519
|
const status = await client.getPlayStatus(runId);
|
|
7231
|
-
const
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
`
|
|
7242
|
-
|
|
7243
|
-
} else {
|
|
7244
|
-
console.log(`Exported ${status.runId} to ${exportedPath}`);
|
|
7245
|
-
}
|
|
7520
|
+
const exportResult = await exportPlayStatusRows(client, status, outPath, {
|
|
7521
|
+
datasetPath
|
|
7522
|
+
});
|
|
7523
|
+
printCommandEnvelope({
|
|
7524
|
+
runId: status.runId,
|
|
7525
|
+
...datasetPath ? { dataset: datasetPath } : {},
|
|
7526
|
+
csv_path: exportResult?.path ?? null,
|
|
7527
|
+
rowCount: exportResult?.rowsInfo.totalRows ?? null,
|
|
7528
|
+
columns: exportResult?.rowsInfo.columns ?? [],
|
|
7529
|
+
local: { csv_path: exportResult?.path ?? null },
|
|
7530
|
+
render: { sections: [{ title: "run export", lines: [`Exported ${status.runId} to ${exportResult?.path ?? outPath}`] }] }
|
|
7531
|
+
}, { json: argsWantJson(args) });
|
|
7246
7532
|
return 0;
|
|
7247
7533
|
}
|
|
7248
7534
|
async function handlePlayGet(args) {
|
|
@@ -7690,8 +7976,7 @@ Notes:
|
|
|
7690
7976
|
Local files are bundled, preflighted, then run in Deepline cloud.
|
|
7691
7977
|
Named plays run the live saved revision.
|
|
7692
7978
|
Unknown --foo value and --foo.bar value flags are passed into play input.
|
|
7693
|
-
Example: --
|
|
7694
|
-
input.limit = 5.
|
|
7979
|
+
Example: --limit 5 becomes input.limit = 5.
|
|
7695
7980
|
File args accept local paths; the CLI stages files before submit.
|
|
7696
7981
|
--watch prints logs, previews, stats, and next commands.
|
|
7697
7982
|
--wait is accepted as a compatibility alias for --watch.
|
|
@@ -7725,16 +8010,13 @@ Examples:
|
|
|
7725
8010
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
|
|
7726
8011
|
deepline plays run my.play.ts --input @input.json --wait --json
|
|
7727
8012
|
deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
|
|
7728
|
-
deepline plays run enrich.play.ts --csv leads.csv --watch --out leads-enriched.csv
|
|
7729
8013
|
deepline plays run cto-search.play.ts --limit 5 --watch
|
|
8014
|
+
deepline runs export <run-id> --out output.csv
|
|
7730
8015
|
deepline runs get <run-id>
|
|
7731
8016
|
`
|
|
7732
8017
|
).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(
|
|
7733
8018
|
"--revision-id <id>",
|
|
7734
8019
|
"Run a specific saved revision instead of the live revision"
|
|
7735
|
-
).option(
|
|
7736
|
-
"--out <path>",
|
|
7737
|
-
"Write the completed row output to CSV; requires --watch"
|
|
7738
8020
|
).option("--watch", "Stream logs until completion").option("--wait", "Alias for --watch; stream logs until completion").option(
|
|
7739
8021
|
"--logs",
|
|
7740
8022
|
"When output is non-interactive, stream play logs to stderr while waiting"
|
|
@@ -7743,8 +8025,9 @@ Examples:
|
|
|
7743
8025
|
`
|
|
7744
8026
|
Pass-through input flags:
|
|
7745
8027
|
Unknown flags are accepted intentionally and become play input fields. Use
|
|
7746
|
-
this for play-specific inputs like --
|
|
7747
|
-
--
|
|
8028
|
+
this for play-specific inputs like --limit 5 or --filters.title "GTM Engineer".
|
|
8029
|
+
For CSV file inputs, prefer --input '{"file":"leads.csv"}' so the field name
|
|
8030
|
+
matches the play's ctx.csv(input.file) contract.
|
|
7748
8031
|
`
|
|
7749
8032
|
).action(async (target, options, command) => {
|
|
7750
8033
|
const passthroughArgs = [...command.args];
|
|
@@ -7765,7 +8048,6 @@ Pass-through input flags:
|
|
|
7765
8048
|
...options.live ? ["--live"] : [],
|
|
7766
8049
|
...options.latest ? ["--latest"] : [],
|
|
7767
8050
|
...options.revisionId ? ["--revision-id", options.revisionId] : [],
|
|
7768
|
-
...options.out ? ["--out", options.out] : [],
|
|
7769
8051
|
...options.watch || options.wait ? ["--watch"] : [],
|
|
7770
8052
|
...options.logs ? ["--logs"] : [],
|
|
7771
8053
|
...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
|
|
@@ -7929,7 +8211,7 @@ Examples:
|
|
|
7929
8211
|
`
|
|
7930
8212
|
Concepts:
|
|
7931
8213
|
A run is one execution instance of a play. It has status, progress, logs,
|
|
7932
|
-
|
|
8214
|
+
returned result, dataset previews, recovery metadata, and optional dataset export.
|
|
7933
8215
|
tail reads the live stream. logs fetches persisted logs after the fact.
|
|
7934
8216
|
stop mutates cloud state by requesting cancellation.
|
|
7935
8217
|
|
|
@@ -7942,7 +8224,7 @@ Examples:
|
|
|
7942
8224
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
7943
8225
|
`
|
|
7944
8226
|
);
|
|
7945
|
-
runs.command("get <runId>").description("Get status, progress,
|
|
8227
|
+
runs.command("get <runId>").description("Get status, progress, result, errors, and recovery metadata for a play run.").addHelpText(
|
|
7946
8228
|
"after",
|
|
7947
8229
|
`
|
|
7948
8230
|
Notes:
|
|
@@ -8036,20 +8318,22 @@ Examples:
|
|
|
8036
8318
|
...options.json ? ["--json"] : []
|
|
8037
8319
|
]);
|
|
8038
8320
|
});
|
|
8039
|
-
runs.command("export <runId>").description("Export
|
|
8321
|
+
runs.command("export <runId>").description("Export a returned dataset handle for a play run to CSV.").addHelpText(
|
|
8040
8322
|
"after",
|
|
8041
8323
|
`
|
|
8042
8324
|
Notes:
|
|
8043
|
-
Writes
|
|
8044
|
-
first
|
|
8325
|
+
Writes a returned dataset handle to the requested local CSV path. Use runs get
|
|
8326
|
+
first to inspect dataset paths like result.rows or result.nested.contacts.
|
|
8045
8327
|
|
|
8046
8328
|
Examples:
|
|
8047
8329
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
8330
|
+
deepline runs export play/my-play/run/20260501t000000-000 --dataset result.rows --out output.csv
|
|
8048
8331
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --json
|
|
8049
8332
|
`
|
|
8050
|
-
).requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
8333
|
+
).requiredOption("--out <path>", "Output CSV path").option("--dataset <path>", "Returned dataset handle path, such as result.rows").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
8051
8334
|
process.exitCode = await handleRunExport([
|
|
8052
8335
|
runId,
|
|
8336
|
+
...options.dataset ? ["--dataset", options.dataset] : [],
|
|
8053
8337
|
"--out",
|
|
8054
8338
|
options.out,
|
|
8055
8339
|
...options.json ? ["--json"] : []
|
|
@@ -8215,19 +8499,19 @@ function toListedTool(tool) {
|
|
|
8215
8499
|
async function listTools(args) {
|
|
8216
8500
|
const client = new DeeplineClient();
|
|
8217
8501
|
const items = (await client.listTools()).map(toListedTool);
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
`);
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
}
|
|
8502
|
+
const render = {
|
|
8503
|
+
sections: [
|
|
8504
|
+
{
|
|
8505
|
+
title: `${items.length} tools available:`,
|
|
8506
|
+
lines: items.flatMap((item) => {
|
|
8507
|
+
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8508
|
+
const listHint = item.listExtractorPaths?.length ? ` listExtractorPaths=${item.listExtractorPaths.join(",")}` : "";
|
|
8509
|
+
return [`${item.toolId}${cats}`, ` ${item.description}${listHint}`];
|
|
8510
|
+
})
|
|
8511
|
+
}
|
|
8512
|
+
]
|
|
8513
|
+
};
|
|
8514
|
+
printCommandEnvelope({ tools: items, count: items.length, render }, { json: argsWantJson(args) });
|
|
8231
8515
|
return 0;
|
|
8232
8516
|
}
|
|
8233
8517
|
async function searchTools(queryInput, options = {}) {
|
|
@@ -8245,21 +8529,27 @@ async function searchTools(queryInput, options = {}) {
|
|
|
8245
8529
|
includeSearchDebug: options.includeSearchDebug
|
|
8246
8530
|
});
|
|
8247
8531
|
const items = result.tools.map(toListedTool);
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8532
|
+
const envelope = {
|
|
8533
|
+
...result,
|
|
8534
|
+
tools: items,
|
|
8535
|
+
count: items.length,
|
|
8536
|
+
render: {
|
|
8537
|
+
sections: [
|
|
8538
|
+
{
|
|
8539
|
+
title: `${items.length} tools found:`,
|
|
8540
|
+
lines: items.flatMap((item) => {
|
|
8541
|
+
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8542
|
+
return [
|
|
8543
|
+
`${item.toolId}${cats}`,
|
|
8544
|
+
` ${item.description}`,
|
|
8545
|
+
...item.inputSchema ? [" inputSchema: yes"] : []
|
|
8546
|
+
];
|
|
8547
|
+
})
|
|
8548
|
+
}
|
|
8549
|
+
]
|
|
8261
8550
|
}
|
|
8262
|
-
}
|
|
8551
|
+
};
|
|
8552
|
+
printCommandEnvelope(envelope, { json: options.json || shouldEmitJson() });
|
|
8263
8553
|
return 0;
|
|
8264
8554
|
}
|
|
8265
8555
|
function playIdentifiers(play) {
|
|
@@ -8591,6 +8881,9 @@ function samplePayload(samples, key) {
|
|
|
8591
8881
|
if (!isRecord3(entry)) return void 0;
|
|
8592
8882
|
return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
|
|
8593
8883
|
}
|
|
8884
|
+
function commandEnvelopeFromRawResponse(rawResponse) {
|
|
8885
|
+
return isRecord3(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
|
|
8886
|
+
}
|
|
8594
8887
|
function isPlayTool(tool) {
|
|
8595
8888
|
const provider = typeof tool.provider === "string" ? tool.provider : "";
|
|
8596
8889
|
return provider === "deepline_native" && Boolean(recordField(tool, "playExpansion", "play_expansion"));
|
|
@@ -8755,6 +9048,53 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
8755
9048
|
windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
|
|
8756
9049
|
};
|
|
8757
9050
|
}
|
|
9051
|
+
function buildToolExecuteBaseEnvelope(input) {
|
|
9052
|
+
const envelope = commandEnvelopeFromRawResponse(input.rawResponse);
|
|
9053
|
+
const summaryEntries = Object.entries(input.summary);
|
|
9054
|
+
const output = input.listConversion ? {
|
|
9055
|
+
kind: "list",
|
|
9056
|
+
rowCount: input.listConversion.rows.length,
|
|
9057
|
+
columns: Object.keys(input.listConversion.rows[0] ?? {}),
|
|
9058
|
+
preview: input.listConversion.rows.slice(0, 5),
|
|
9059
|
+
listStrategy: input.listConversion.strategy,
|
|
9060
|
+
listSourcePath: input.listConversion.sourcePath
|
|
9061
|
+
} : {
|
|
9062
|
+
kind: summaryEntries.length > 0 ? "object" : "raw",
|
|
9063
|
+
summary: input.summary
|
|
9064
|
+
};
|
|
9065
|
+
const actions = input.listConversion ? [
|
|
9066
|
+
{
|
|
9067
|
+
label: "next",
|
|
9068
|
+
command: "move starter script into a project folder and expand it into a Deepline play"
|
|
9069
|
+
}
|
|
9070
|
+
] : [];
|
|
9071
|
+
return {
|
|
9072
|
+
...envelope,
|
|
9073
|
+
output,
|
|
9074
|
+
...summaryEntries.length > 0 ? { summary: input.summary } : {},
|
|
9075
|
+
next: input.listConversion ? {
|
|
9076
|
+
expandToPlay: "Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
|
|
9077
|
+
} : {},
|
|
9078
|
+
render: {
|
|
9079
|
+
sections: input.listConversion ? [
|
|
9080
|
+
{
|
|
9081
|
+
title: "output",
|
|
9082
|
+
lines: [
|
|
9083
|
+
`${input.listConversion.rows.length} row(s) extracted from ${input.listConversion.sourcePath ?? "auto-detected list"}`,
|
|
9084
|
+
`columns: ${JSON.stringify(Object.keys(input.listConversion.rows[0] ?? {}))}`,
|
|
9085
|
+
`preview: ${JSON.stringify(input.listConversion.rows.slice(0, 5))}`
|
|
9086
|
+
]
|
|
9087
|
+
}
|
|
9088
|
+
] : [
|
|
9089
|
+
{
|
|
9090
|
+
title: "result",
|
|
9091
|
+
lines: summaryEntries.length > 0 ? summaryEntries.map(([key, value]) => `${key}=${String(value)}`) : [JSON.stringify(input.rawResponse, null, 2)]
|
|
9092
|
+
}
|
|
9093
|
+
],
|
|
9094
|
+
actions
|
|
9095
|
+
}
|
|
9096
|
+
};
|
|
9097
|
+
}
|
|
8758
9098
|
async function executeTool(args) {
|
|
8759
9099
|
let parsed;
|
|
8760
9100
|
try {
|
|
@@ -8787,24 +9127,45 @@ async function executeTool(args) {
|
|
|
8787
9127
|
listExtractorPaths: metadata.listExtractorPaths ?? []
|
|
8788
9128
|
});
|
|
8789
9129
|
const summary = extractSummaryFields(rawResponse);
|
|
9130
|
+
const baseEnvelope = buildToolExecuteBaseEnvelope({
|
|
9131
|
+
toolId: parsed.toolId,
|
|
9132
|
+
rawResponse,
|
|
9133
|
+
listConversion,
|
|
9134
|
+
summary
|
|
9135
|
+
});
|
|
8790
9136
|
if (parsed.outputFormat === "json" || parsed.outputFormat === "auto" && shouldEmitJson()) {
|
|
8791
|
-
|
|
8792
|
-
`);
|
|
9137
|
+
printCommandEnvelope(baseEnvelope, { json: true });
|
|
8793
9138
|
return 0;
|
|
8794
9139
|
}
|
|
8795
9140
|
if (parsed.outputFormat === "json_file") {
|
|
8796
9141
|
const jsonPath = writeJsonOutputFile(rawResponse, `payload_${parsed.toolId}`);
|
|
8797
|
-
|
|
9142
|
+
printCommandEnvelope(
|
|
9143
|
+
{
|
|
9144
|
+
...baseEnvelope,
|
|
9145
|
+
local: {
|
|
9146
|
+
...isRecord3(baseEnvelope.local) ? baseEnvelope.local : {},
|
|
9147
|
+
payload_file: jsonPath
|
|
9148
|
+
}
|
|
9149
|
+
},
|
|
9150
|
+
{ json: true }
|
|
9151
|
+
);
|
|
8798
9152
|
return 0;
|
|
8799
9153
|
}
|
|
8800
9154
|
if (!listConversion) {
|
|
8801
9155
|
if (parsed.outputFormat === "csv" || parsed.outputFormat === "csv_file") {
|
|
8802
9156
|
const jsonPath = writeJsonOutputFile(rawResponse, `payload_${parsed.toolId}`);
|
|
8803
|
-
|
|
9157
|
+
printCommandEnvelope(
|
|
9158
|
+
{
|
|
9159
|
+
...baseEnvelope,
|
|
9160
|
+
local: {
|
|
9161
|
+
payload_file: jsonPath
|
|
9162
|
+
}
|
|
9163
|
+
},
|
|
9164
|
+
{ json: parsed.outputFormat === "csv_file" || shouldEmitJson() }
|
|
9165
|
+
);
|
|
8804
9166
|
return 0;
|
|
8805
9167
|
}
|
|
8806
|
-
|
|
8807
|
-
`);
|
|
9168
|
+
printCommandEnvelope(baseEnvelope, { json: false });
|
|
8808
9169
|
return 0;
|
|
8809
9170
|
}
|
|
8810
9171
|
const csv = writeCsvOutputFile(listConversion.rows, `${parsed.toolId}_output`);
|
|
@@ -8813,8 +9174,51 @@ async function executeTool(args) {
|
|
|
8813
9174
|
payload: parsed.params,
|
|
8814
9175
|
rows: listConversion.rows
|
|
8815
9176
|
});
|
|
9177
|
+
const materializedEnvelope = {
|
|
9178
|
+
...baseEnvelope,
|
|
9179
|
+
local: {
|
|
9180
|
+
extracted_csv: csv.path,
|
|
9181
|
+
extracted_csv_rows: csv.rowCount,
|
|
9182
|
+
extracted_csv_columns: csv.columns,
|
|
9183
|
+
preview: csv.preview,
|
|
9184
|
+
starter_script: seededScript.path,
|
|
9185
|
+
project_dir: seededScript.projectDir,
|
|
9186
|
+
copy_to_project: {
|
|
9187
|
+
macos_linux: seededScript.macCopyCommand,
|
|
9188
|
+
windows_powershell: seededScript.windowsCopyCommand
|
|
9189
|
+
}
|
|
9190
|
+
},
|
|
9191
|
+
render: {
|
|
9192
|
+
sections: [
|
|
9193
|
+
{
|
|
9194
|
+
title: `${csv.path} (${csv.rowCount} rows)`,
|
|
9195
|
+
lines: [
|
|
9196
|
+
...csv.columns.length > 0 ? [`columns: ${JSON.stringify(csv.columns)}`] : [],
|
|
9197
|
+
...Object.keys(summary).length > 0 ? [`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`] : [],
|
|
9198
|
+
`preview: ${JSON.stringify(csv.preview)}`,
|
|
9199
|
+
`starter script: ${seededScript.path}`
|
|
9200
|
+
]
|
|
9201
|
+
}
|
|
9202
|
+
],
|
|
9203
|
+
actions: [
|
|
9204
|
+
{
|
|
9205
|
+
label: "next",
|
|
9206
|
+
command: "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."
|
|
9207
|
+
},
|
|
9208
|
+
{
|
|
9209
|
+
label: "macOS/Linux",
|
|
9210
|
+
command: seededScript.macCopyCommand
|
|
9211
|
+
},
|
|
9212
|
+
{
|
|
9213
|
+
label: "Windows PowerShell",
|
|
9214
|
+
command: seededScript.windowsCopyCommand
|
|
9215
|
+
}
|
|
9216
|
+
]
|
|
9217
|
+
}
|
|
9218
|
+
};
|
|
8816
9219
|
if (parsed.outputFormat === "csv_file") {
|
|
8817
|
-
|
|
9220
|
+
printCommandEnvelope({
|
|
9221
|
+
...materializedEnvelope,
|
|
8818
9222
|
extracted_csv: csv.path,
|
|
8819
9223
|
extracted_csv_rows: csv.rowCount,
|
|
8820
9224
|
extracted_csv_columns: csv.columns,
|
|
@@ -8828,28 +9232,24 @@ async function executeTool(args) {
|
|
|
8828
9232
|
windows_powershell: seededScript.windowsCopyCommand
|
|
8829
9233
|
},
|
|
8830
9234
|
summary
|
|
8831
|
-
})
|
|
8832
|
-
`);
|
|
9235
|
+
}, { json: true });
|
|
8833
9236
|
return 0;
|
|
8834
9237
|
}
|
|
8835
9238
|
if (parsed.noPreview) {
|
|
8836
|
-
|
|
9239
|
+
printCommandEnvelope(
|
|
9240
|
+
{
|
|
9241
|
+
...materializedEnvelope,
|
|
9242
|
+
local: {
|
|
9243
|
+
...materializedEnvelope.local ?? {},
|
|
9244
|
+
output_path: csv.path
|
|
9245
|
+
}
|
|
9246
|
+
},
|
|
9247
|
+
{ json: shouldEmitJson(), text: `${csv.path}
|
|
9248
|
+
` }
|
|
9249
|
+
);
|
|
8837
9250
|
return 0;
|
|
8838
9251
|
}
|
|
8839
|
-
|
|
8840
|
-
if (csv.columns.length > 0) {
|
|
8841
|
-
console.log(`columns: ${JSON.stringify(csv.columns)}`);
|
|
8842
|
-
}
|
|
8843
|
-
if (Object.keys(summary).length > 0) {
|
|
8844
|
-
console.log(`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`);
|
|
8845
|
-
}
|
|
8846
|
-
console.log(`preview: ${JSON.stringify(csv.preview)}`);
|
|
8847
|
-
console.log(`starter script: ${seededScript.path}`);
|
|
8848
|
-
console.log(
|
|
8849
|
-
"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."
|
|
8850
|
-
);
|
|
8851
|
-
console.log(`macOS/Linux: ${seededScript.macCopyCommand}`);
|
|
8852
|
-
console.log(`Windows PowerShell: ${seededScript.windowsCopyCommand}`);
|
|
9252
|
+
printCommandEnvelope(materializedEnvelope, { json: false });
|
|
8853
9253
|
return 0;
|
|
8854
9254
|
}
|
|
8855
9255
|
|
|
@@ -8923,23 +9323,36 @@ function runCommand(command, args) {
|
|
|
8923
9323
|
}
|
|
8924
9324
|
async function handleUpdate(options) {
|
|
8925
9325
|
const plan = resolveUpdatePlan();
|
|
9326
|
+
const render = {
|
|
9327
|
+
sections: [
|
|
9328
|
+
{
|
|
9329
|
+
title: "update",
|
|
9330
|
+
lines: plan.kind === "source" ? [
|
|
9331
|
+
"This Deepline CLI is running from SDK source, so it cannot safely update itself like an npm global.",
|
|
9332
|
+
`Update the backing checkout with: ${plan.manualCommand}`
|
|
9333
|
+
] : [`Updating Deepline SDK/CLI with: ${plan.manualCommand}`]
|
|
9334
|
+
}
|
|
9335
|
+
]
|
|
9336
|
+
};
|
|
8926
9337
|
if (options.json) {
|
|
8927
|
-
|
|
8928
|
-
`);
|
|
9338
|
+
printCommandEnvelope({ ...plan, render }, { json: true });
|
|
8929
9339
|
return 0;
|
|
8930
9340
|
}
|
|
8931
9341
|
if (options.printCommand) {
|
|
8932
|
-
|
|
8933
|
-
|
|
9342
|
+
printCommandEnvelope(
|
|
9343
|
+
{
|
|
9344
|
+
...plan,
|
|
9345
|
+
render: {
|
|
9346
|
+
sections: [{ title: "update command", lines: [plan.manualCommand] }]
|
|
9347
|
+
}
|
|
9348
|
+
},
|
|
9349
|
+
{ json: false, text: `${plan.manualCommand}
|
|
9350
|
+
` }
|
|
9351
|
+
);
|
|
8934
9352
|
return 0;
|
|
8935
9353
|
}
|
|
8936
9354
|
if (plan.kind === "source") {
|
|
8937
|
-
|
|
8938
|
-
`This Deepline CLI is running from SDK source, so it cannot safely update itself like an npm global.
|
|
8939
|
-
Update the backing checkout with:
|
|
8940
|
-
${plan.manualCommand}
|
|
8941
|
-
`
|
|
8942
|
-
);
|
|
9355
|
+
printCommandEnvelope({ ...plan, render }, { json: false });
|
|
8943
9356
|
return 0;
|
|
8944
9357
|
}
|
|
8945
9358
|
process.stderr.write(`Updating Deepline SDK/CLI with: ${plan.manualCommand}
|
|
@@ -9252,7 +9665,7 @@ Exit codes:
|
|
|
9252
9665
|
registerDbCommands(program);
|
|
9253
9666
|
registerFeedbackCommands(program);
|
|
9254
9667
|
registerUpdateCommand(program);
|
|
9255
|
-
program.command("health").description("Check server health.").addHelpText(
|
|
9668
|
+
program.command("health").description("Check server health.").option("--json", "Force JSON output.").addHelpText(
|
|
9256
9669
|
"after",
|
|
9257
9670
|
`
|
|
9258
9671
|
Notes:
|