deepline 0.1.28 → 0.1.30
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 +900 -486
- package/dist/cli/index.mjs +900 -486
- package/dist/index.d.mts +42 -2
- package/dist/index.d.ts +42 -2
- package/dist/index.js +17 -3
- package/dist/index.mjs +17 -3
- package/dist/repo/apps/play-runner-workers/src/runtime/dataset-handles.ts +62 -2
- package/dist/repo/sdk/src/client.ts +54 -1
- package/dist/repo/sdk/src/play.ts +5 -0
- 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-v2";
|
|
269
|
+
var SDK_VERSION = "0.1.30";
|
|
270
|
+
var SDK_API_CONTRACT = "2026-05-runs-v2-datasets";
|
|
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)) {
|
|
@@ -6254,23 +6407,17 @@ 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 } : {},
|
|
6416
|
+
...options?.exportedPath ? { local: { csv_path: options.exportedPath } } : {},
|
|
6269
6417
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
6270
6418
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
6271
|
-
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
|
|
6272
6419
|
...billing ? { billing } : {},
|
|
6273
|
-
next: buildRunNextCommands(status.runId
|
|
6420
|
+
next: buildRunNextCommands(status.runId)
|
|
6274
6421
|
};
|
|
6275
6422
|
}
|
|
6276
6423
|
function enrichPlayStatusWithDatasetStats(status) {
|
|
@@ -6308,12 +6455,20 @@ function formatDatasetStatsLines(datasetStats) {
|
|
|
6308
6455
|
}
|
|
6309
6456
|
function writePlayResult(status, jsonOutput, options) {
|
|
6310
6457
|
if (jsonOutput) {
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6458
|
+
const payload2 = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status, options);
|
|
6459
|
+
printCommandEnvelope({
|
|
6460
|
+
...payload2,
|
|
6461
|
+
render: {
|
|
6462
|
+
sections: [
|
|
6463
|
+
{
|
|
6464
|
+
title: "run result",
|
|
6465
|
+
lines: [
|
|
6466
|
+
`${status.status ?? "running"} ${status.runId ?? "unknown"}`
|
|
6467
|
+
]
|
|
6468
|
+
}
|
|
6469
|
+
]
|
|
6470
|
+
}
|
|
6471
|
+
}, { json: true });
|
|
6317
6472
|
return;
|
|
6318
6473
|
}
|
|
6319
6474
|
const result = status.result;
|
|
@@ -6330,22 +6485,8 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6330
6485
|
rowsInfo.columns,
|
|
6331
6486
|
extractDatasetExecutionStats(status)
|
|
6332
6487
|
) : null;
|
|
6333
|
-
|
|
6334
|
-
|
|
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
|
-
}
|
|
6488
|
+
if (options?.exportedPath) {
|
|
6489
|
+
lines.push(` exported CSV: file=${options.exportedPath}`);
|
|
6349
6490
|
}
|
|
6350
6491
|
for (const warning of warnings) {
|
|
6351
6492
|
lines.push(` warning: ${warning}`);
|
|
@@ -6358,29 +6499,184 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6358
6499
|
const renderedServerView = renderServerResultView(status.resultView);
|
|
6359
6500
|
if (result) {
|
|
6360
6501
|
lines.push(...formatReturnValue(result));
|
|
6502
|
+
lines.push(...collectDatasetHandleLines(result));
|
|
6361
6503
|
}
|
|
6362
6504
|
if (renderedServerView.lines.length > 0) {
|
|
6363
6505
|
lines.push(...renderedServerView.lines);
|
|
6364
6506
|
}
|
|
6365
6507
|
lines.push(...renderedServerView.actions);
|
|
6366
|
-
|
|
6508
|
+
const payload = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status, options);
|
|
6509
|
+
printCommandEnvelope({
|
|
6510
|
+
...payload,
|
|
6511
|
+
render: {
|
|
6512
|
+
sections: [{ title: "run result", lines }]
|
|
6513
|
+
}
|
|
6514
|
+
}, { json: jsonOutput, text: `${lines.join("\n")}
|
|
6515
|
+
` });
|
|
6516
|
+
}
|
|
6517
|
+
var RUN_EXPORT_PAGE_SIZE = 5e3;
|
|
6518
|
+
var PLAY_RUN_OUT_EXPORT_ATTEMPTS = 8;
|
|
6519
|
+
var PLAY_RUN_OUT_EXPORT_RETRY_DELAY_MS = 1e3;
|
|
6520
|
+
function shellSingleQuote(value) {
|
|
6521
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
6522
|
+
}
|
|
6523
|
+
function runExportRetryCommand(runId, outPath, datasetPath) {
|
|
6524
|
+
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote((0, import_node_path9.resolve)(outPath))}`;
|
|
6525
|
+
}
|
|
6526
|
+
function extractRunPlayName(status) {
|
|
6527
|
+
const run = status.run;
|
|
6528
|
+
const candidates = [
|
|
6529
|
+
status.playName,
|
|
6530
|
+
status.name,
|
|
6531
|
+
getRecordField(run, "playName"),
|
|
6532
|
+
getRecordField(run, "name")
|
|
6533
|
+
];
|
|
6534
|
+
for (const candidate of candidates) {
|
|
6535
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
6536
|
+
return candidate.trim();
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
return null;
|
|
6367
6540
|
}
|
|
6368
|
-
function
|
|
6541
|
+
function exportableSheetRow(row) {
|
|
6542
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
6543
|
+
return null;
|
|
6544
|
+
}
|
|
6545
|
+
const record = row;
|
|
6546
|
+
const data = record.data;
|
|
6547
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
6548
|
+
return data;
|
|
6549
|
+
}
|
|
6550
|
+
const fallback = { ...record };
|
|
6551
|
+
for (const key of [
|
|
6552
|
+
"key",
|
|
6553
|
+
"status",
|
|
6554
|
+
"cellMeta",
|
|
6555
|
+
"inputIndex",
|
|
6556
|
+
"runId",
|
|
6557
|
+
"error",
|
|
6558
|
+
"stage",
|
|
6559
|
+
"provider",
|
|
6560
|
+
"seq",
|
|
6561
|
+
"createdAt",
|
|
6562
|
+
"updatedAt"
|
|
6563
|
+
]) {
|
|
6564
|
+
delete fallback[key];
|
|
6565
|
+
}
|
|
6566
|
+
return fallback;
|
|
6567
|
+
}
|
|
6568
|
+
async function fetchBackingDatasetRows(input) {
|
|
6569
|
+
const playName = extractRunPlayName(input.status);
|
|
6570
|
+
const tableNamespace = input.rowsInfo.tableNamespace?.trim();
|
|
6571
|
+
if (!playName || !tableNamespace) {
|
|
6572
|
+
return null;
|
|
6573
|
+
}
|
|
6574
|
+
const sheetRows = [];
|
|
6575
|
+
let offset = 0;
|
|
6576
|
+
let expectedTotal = input.rowsInfo.totalRows;
|
|
6577
|
+
while (true) {
|
|
6578
|
+
const page = await input.client.runs.exportDatasetRows({
|
|
6579
|
+
playName,
|
|
6580
|
+
tableNamespace,
|
|
6581
|
+
runId: input.status.runId,
|
|
6582
|
+
limit: RUN_EXPORT_PAGE_SIZE,
|
|
6583
|
+
offset
|
|
6584
|
+
});
|
|
6585
|
+
sheetRows.push(...page.rows);
|
|
6586
|
+
const summaryTotal = page.summary?.stats?.total;
|
|
6587
|
+
if (typeof summaryTotal === "number" && Number.isFinite(summaryTotal)) {
|
|
6588
|
+
expectedTotal = Math.max(expectedTotal, Math.trunc(summaryTotal));
|
|
6589
|
+
}
|
|
6590
|
+
if (page.rows.length < RUN_EXPORT_PAGE_SIZE || sheetRows.length >= expectedTotal) {
|
|
6591
|
+
break;
|
|
6592
|
+
}
|
|
6593
|
+
offset += page.rows.length;
|
|
6594
|
+
}
|
|
6595
|
+
const rows = sheetRows.map(exportableSheetRow).filter((row) => Boolean(row));
|
|
6596
|
+
if (rows.length < input.rowsInfo.totalRows) {
|
|
6597
|
+
return null;
|
|
6598
|
+
}
|
|
6599
|
+
const columns = input.rowsInfo.columnsExplicit && input.rowsInfo.columns.length ? input.rowsInfo.columns : [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
6600
|
+
return {
|
|
6601
|
+
...input.rowsInfo,
|
|
6602
|
+
rows,
|
|
6603
|
+
columns,
|
|
6604
|
+
totalRows: rows.length,
|
|
6605
|
+
complete: true,
|
|
6606
|
+
source: `${input.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
|
|
6607
|
+
};
|
|
6608
|
+
}
|
|
6609
|
+
async function exportPlayStatusRows(client, status, outPath, options = {}) {
|
|
6369
6610
|
if (!outPath) {
|
|
6370
6611
|
return null;
|
|
6371
6612
|
}
|
|
6372
|
-
const
|
|
6613
|
+
const availableRows = collectSerializedDatasetRowsInfos(status);
|
|
6614
|
+
let rowsInfo = options.datasetPath ? availableRows.find((info) => info.source === options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
|
|
6615
|
+
if (!rowsInfo && options.datasetPath) {
|
|
6616
|
+
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
6617
|
+
throw new DeeplineError(
|
|
6618
|
+
`Run ${status.runId} did not return a dataset at ${options.datasetPath}.` + (available.length > 0 ? ` Available datasets: ${available.join(", ")}.` : ""),
|
|
6619
|
+
void 0,
|
|
6620
|
+
"RUN_EXPORT_DATASET_NOT_FOUND",
|
|
6621
|
+
{ runId: status.runId, dataset: options.datasetPath, available }
|
|
6622
|
+
);
|
|
6623
|
+
}
|
|
6624
|
+
if (!options.datasetPath && availableRows.length > 1) {
|
|
6625
|
+
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
6626
|
+
throw new DeeplineError(
|
|
6627
|
+
`Run ${status.runId} returned multiple datasets. Choose one with --dataset <path>: ${available.join(", ")}.`,
|
|
6628
|
+
void 0,
|
|
6629
|
+
"RUN_EXPORT_DATASET_REQUIRED",
|
|
6630
|
+
{ runId: status.runId, available }
|
|
6631
|
+
);
|
|
6632
|
+
}
|
|
6373
6633
|
if (!rowsInfo) {
|
|
6374
6634
|
throw new DeeplineError(
|
|
6375
6635
|
`Run ${status.runId} did not expose a row-shaped final output to export.`
|
|
6376
6636
|
);
|
|
6377
6637
|
}
|
|
6638
|
+
const attempts = Math.max(1, Math.trunc(options.attempts ?? 1));
|
|
6639
|
+
const retryDelayMs = Math.max(0, Math.trunc(options.retryDelayMs ?? 0));
|
|
6640
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
6641
|
+
if (rowsInfo.complete) {
|
|
6642
|
+
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6643
|
+
}
|
|
6644
|
+
const fetchedRowsInfo = await fetchBackingDatasetRows({
|
|
6645
|
+
client,
|
|
6646
|
+
status,
|
|
6647
|
+
rowsInfo
|
|
6648
|
+
});
|
|
6649
|
+
if (fetchedRowsInfo?.complete) {
|
|
6650
|
+
return {
|
|
6651
|
+
path: writeCanonicalRowsCsv(fetchedRowsInfo, outPath),
|
|
6652
|
+
rowsInfo: fetchedRowsInfo
|
|
6653
|
+
};
|
|
6654
|
+
}
|
|
6655
|
+
if (attempt < attempts && retryDelayMs > 0) {
|
|
6656
|
+
await sleep4(retryDelayMs);
|
|
6657
|
+
}
|
|
6658
|
+
}
|
|
6378
6659
|
if (!rowsInfo.complete) {
|
|
6660
|
+
const retryCommand = runExportRetryCommand(
|
|
6661
|
+
status.runId,
|
|
6662
|
+
outPath,
|
|
6663
|
+
options.datasetPath ?? rowsInfo.source
|
|
6664
|
+
);
|
|
6379
6665
|
throw new DeeplineError(
|
|
6380
|
-
`Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows};
|
|
6666
|
+
`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}`,
|
|
6667
|
+
void 0,
|
|
6668
|
+
"RUN_EXPORT_NOT_READY",
|
|
6669
|
+
{
|
|
6670
|
+
runId: status.runId,
|
|
6671
|
+
previewRowCount: rowsInfo.rows.length,
|
|
6672
|
+
totalRows: rowsInfo.totalRows,
|
|
6673
|
+
tableNamespace: rowsInfo.tableNamespace ?? null,
|
|
6674
|
+
dataset: options.datasetPath ?? rowsInfo.source,
|
|
6675
|
+
retry_command: retryCommand
|
|
6676
|
+
}
|
|
6381
6677
|
);
|
|
6382
6678
|
}
|
|
6383
|
-
return writeCanonicalRowsCsv(rowsInfo, outPath);
|
|
6679
|
+
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6384
6680
|
}
|
|
6385
6681
|
function renderServerResultView(value) {
|
|
6386
6682
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -6454,8 +6750,10 @@ function writeStartedPlayRun(input) {
|
|
|
6454
6750
|
dashboardUrl: input.dashboardUrl
|
|
6455
6751
|
};
|
|
6456
6752
|
if (input.jsonOutput) {
|
|
6457
|
-
|
|
6458
|
-
|
|
6753
|
+
printCommandEnvelope({
|
|
6754
|
+
...payload,
|
|
6755
|
+
render: { sections: [{ title: "play run", lines: [`Started ${input.playName}`, `run id: ${input.runId}`] }] }
|
|
6756
|
+
}, { json: true });
|
|
6459
6757
|
return;
|
|
6460
6758
|
}
|
|
6461
6759
|
const lines = [
|
|
@@ -6474,10 +6772,14 @@ function writeStartedPlayRun(input) {
|
|
|
6474
6772
|
input.progress.writeLine(output, process.stdout);
|
|
6475
6773
|
return;
|
|
6476
6774
|
}
|
|
6477
|
-
|
|
6775
|
+
printCommandEnvelope({
|
|
6776
|
+
...payload,
|
|
6777
|
+
render: { sections: [{ title: "play run", lines }] }
|
|
6778
|
+
}, { json: false, text: `${output}
|
|
6779
|
+
` });
|
|
6478
6780
|
}
|
|
6479
6781
|
function parsePlayRunOptions(args) {
|
|
6480
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--<input> value]\n Unknown --<input> value flags, such as --
|
|
6782
|
+
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--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
6783
|
let filePath = null;
|
|
6482
6784
|
let playName = null;
|
|
6483
6785
|
let input = null;
|
|
@@ -6547,6 +6849,11 @@ function parsePlayRunOptions(args) {
|
|
|
6547
6849
|
}
|
|
6548
6850
|
continue;
|
|
6549
6851
|
}
|
|
6852
|
+
if (arg === "--csv" || arg.startsWith("--csv=")) {
|
|
6853
|
+
throw new Error(
|
|
6854
|
+
`--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`
|
|
6855
|
+
);
|
|
6856
|
+
}
|
|
6550
6857
|
if (arg.startsWith("--")) {
|
|
6551
6858
|
const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
|
|
6552
6859
|
input ??= {};
|
|
@@ -6732,11 +7039,6 @@ async function handleFileBackedRun(options) {
|
|
|
6732
7039
|
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
6733
7040
|
compilerManifest.staticPipeline
|
|
6734
7041
|
);
|
|
6735
|
-
applyCsvShortcutInput({
|
|
6736
|
-
runtimeInput,
|
|
6737
|
-
bindings: fileInputBindings,
|
|
6738
|
-
fallbackInputPath: "file"
|
|
6739
|
-
});
|
|
6740
7042
|
const stagedFileInputs = await traceCliSpan(
|
|
6741
7043
|
"cli.play_stage_inputs",
|
|
6742
7044
|
{
|
|
@@ -6778,10 +7080,13 @@ async function handleFileBackedRun(options) {
|
|
|
6778
7080
|
progress
|
|
6779
7081
|
})
|
|
6780
7082
|
);
|
|
6781
|
-
const
|
|
7083
|
+
const exportResult = await traceCliSpan(
|
|
6782
7084
|
"cli.play_export_rows",
|
|
6783
7085
|
{ targetKind: "file", playName },
|
|
6784
|
-
() => exportPlayStatusRows(finalStatus, options.outPath
|
|
7086
|
+
() => exportPlayStatusRows(client, finalStatus, options.outPath, {
|
|
7087
|
+
attempts: PLAY_RUN_OUT_EXPORT_ATTEMPTS,
|
|
7088
|
+
retryDelayMs: PLAY_RUN_OUT_EXPORT_RETRY_DELAY_MS
|
|
7089
|
+
})
|
|
6785
7090
|
);
|
|
6786
7091
|
if (finalStatus.status === "completed") {
|
|
6787
7092
|
progress.complete();
|
|
@@ -6791,7 +7096,7 @@ async function handleFileBackedRun(options) {
|
|
|
6791
7096
|
traceCliSync(
|
|
6792
7097
|
"cli.play_write_result",
|
|
6793
7098
|
{ targetKind: "file", playName },
|
|
6794
|
-
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath })
|
|
7099
|
+
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath: exportResult?.path ?? null })
|
|
6795
7100
|
);
|
|
6796
7101
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
6797
7102
|
}
|
|
@@ -6881,10 +7186,6 @@ async function handleNamedRun(options) {
|
|
|
6881
7186
|
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
6882
7187
|
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
6883
7188
|
] : [];
|
|
6884
|
-
applyCsvShortcutInput({
|
|
6885
|
-
runtimeInput,
|
|
6886
|
-
bindings: fileInputBindings
|
|
6887
|
-
});
|
|
6888
7189
|
const stagedFileInputs = await traceCliSpan(
|
|
6889
7190
|
"cli.play_stage_inputs",
|
|
6890
7191
|
{
|
|
@@ -6923,10 +7224,13 @@ async function handleNamedRun(options) {
|
|
|
6923
7224
|
progress
|
|
6924
7225
|
})
|
|
6925
7226
|
);
|
|
6926
|
-
const
|
|
7227
|
+
const exportResult = await traceCliSpan(
|
|
6927
7228
|
"cli.play_export_rows",
|
|
6928
7229
|
{ targetKind: "name", playName },
|
|
6929
|
-
() => exportPlayStatusRows(finalStatus, options.outPath
|
|
7230
|
+
() => exportPlayStatusRows(client, finalStatus, options.outPath, {
|
|
7231
|
+
attempts: PLAY_RUN_OUT_EXPORT_ATTEMPTS,
|
|
7232
|
+
retryDelayMs: PLAY_RUN_OUT_EXPORT_RETRY_DELAY_MS
|
|
7233
|
+
})
|
|
6930
7234
|
);
|
|
6931
7235
|
if (finalStatus.status === "completed") {
|
|
6932
7236
|
progress.complete();
|
|
@@ -6936,7 +7240,7 @@ async function handleNamedRun(options) {
|
|
|
6936
7240
|
traceCliSync(
|
|
6937
7241
|
"cli.play_write_result",
|
|
6938
7242
|
{ targetKind: "name", playName },
|
|
6939
|
-
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath })
|
|
7243
|
+
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath: exportResult?.path ?? null })
|
|
6940
7244
|
);
|
|
6941
7245
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
6942
7246
|
}
|
|
@@ -7005,7 +7309,7 @@ function parseRunIdPositional(args, usage) {
|
|
|
7005
7309
|
}
|
|
7006
7310
|
continue;
|
|
7007
7311
|
}
|
|
7008
|
-
if ((arg === "--out" || arg === "--reason") && args[index + 1]) {
|
|
7312
|
+
if ((arg === "--out" || arg === "--reason" || arg === "--dataset") && args[index + 1]) {
|
|
7009
7313
|
index += 1;
|
|
7010
7314
|
continue;
|
|
7011
7315
|
}
|
|
@@ -7067,26 +7371,15 @@ async function handleRunsList(args) {
|
|
|
7067
7371
|
executionTime: run.executionTime,
|
|
7068
7372
|
playName: run.memo?.playName ?? playName
|
|
7069
7373
|
}));
|
|
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
|
-
}
|
|
7374
|
+
const lines = runs.length === 0 ? [`No runs found for ${playName}.`] : runs.map((run) => `${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
7375
|
+
printCommandEnvelope({
|
|
7376
|
+
runs,
|
|
7377
|
+
count: runs.length,
|
|
7378
|
+
next: {
|
|
7379
|
+
get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
|
|
7380
|
+
},
|
|
7381
|
+
render: { sections: [{ title: "runs", lines }] }
|
|
7382
|
+
}, { json: argsWantJson(args) });
|
|
7090
7383
|
return 0;
|
|
7091
7384
|
}
|
|
7092
7385
|
async function handleRunTail(args) {
|
|
@@ -7140,41 +7433,30 @@ async function handleRunLogs(args) {
|
|
|
7140
7433
|
const logs = status.progress?.logs ?? [];
|
|
7141
7434
|
if (outPath) {
|
|
7142
7435
|
(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
|
-
}
|
|
7436
|
+
printCommandEnvelope({
|
|
7437
|
+
runId: status.runId,
|
|
7438
|
+
log_path: outPath,
|
|
7439
|
+
lineCount: logs.length,
|
|
7440
|
+
local: { log_path: outPath },
|
|
7441
|
+
render: { sections: [{ title: "run logs", lines: [`Wrote ${logs.length} log lines to ${outPath}`] }] }
|
|
7442
|
+
}, { json: argsWantJson(args) });
|
|
7155
7443
|
return 0;
|
|
7156
7444
|
}
|
|
7157
7445
|
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
|
-
}
|
|
7446
|
+
printCommandEnvelope({
|
|
7447
|
+
runId: status.runId,
|
|
7448
|
+
totalCount: logs.length,
|
|
7449
|
+
returnedCount: entries.length,
|
|
7450
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
7451
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
7452
|
+
truncated: logs.length > entries.length,
|
|
7453
|
+
hasMore: logs.length > entries.length,
|
|
7454
|
+
entries,
|
|
7455
|
+
next: {
|
|
7456
|
+
export: `deepline runs logs ${status.runId} --out run.log --json`
|
|
7457
|
+
},
|
|
7458
|
+
render: { sections: [{ title: "run logs", lines: entries }] }
|
|
7459
|
+
}, { json: argsWantJson(args), text: `${entries.join("\n")}${entries.length > 0 ? "\n" : ""}` });
|
|
7178
7460
|
return 0;
|
|
7179
7461
|
}
|
|
7180
7462
|
async function handleRunStop(args) {
|
|
@@ -7195,19 +7477,18 @@ async function handleRunStop(args) {
|
|
|
7195
7477
|
}
|
|
7196
7478
|
const client = new DeeplineClient();
|
|
7197
7479
|
const result = await client.runs.stop(runId, { reason });
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
`
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
}
|
|
7480
|
+
const lines = [
|
|
7481
|
+
`Stopped ${result.runId}`,
|
|
7482
|
+
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : []
|
|
7483
|
+
];
|
|
7484
|
+
printCommandEnvelope({
|
|
7485
|
+
...result,
|
|
7486
|
+
render: { sections: [{ title: "run stop", lines }] }
|
|
7487
|
+
}, { json: argsWantJson(args) });
|
|
7207
7488
|
return 0;
|
|
7208
7489
|
}
|
|
7209
7490
|
async function handleRunExport(args) {
|
|
7210
|
-
const usage = "Usage: deepline runs export <run-id> --out output.csv [--json]";
|
|
7491
|
+
const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--json]";
|
|
7211
7492
|
let runId;
|
|
7212
7493
|
try {
|
|
7213
7494
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -7216,10 +7497,15 @@ async function handleRunExport(args) {
|
|
|
7216
7497
|
return 1;
|
|
7217
7498
|
}
|
|
7218
7499
|
let outPath = null;
|
|
7500
|
+
let datasetPath = null;
|
|
7219
7501
|
for (let index = 0; index < args.length; index += 1) {
|
|
7220
7502
|
const arg = args[index];
|
|
7221
7503
|
if (arg === "--out" && args[index + 1]) {
|
|
7222
7504
|
outPath = (0, import_node_path9.resolve)(args[++index]);
|
|
7505
|
+
continue;
|
|
7506
|
+
}
|
|
7507
|
+
if (arg === "--dataset" && args[index + 1]) {
|
|
7508
|
+
datasetPath = args[++index];
|
|
7223
7509
|
}
|
|
7224
7510
|
}
|
|
7225
7511
|
if (!outPath) {
|
|
@@ -7228,21 +7514,18 @@ async function handleRunExport(args) {
|
|
|
7228
7514
|
}
|
|
7229
7515
|
const client = new DeeplineClient();
|
|
7230
7516
|
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
|
-
}
|
|
7517
|
+
const exportResult = await exportPlayStatusRows(client, status, outPath, {
|
|
7518
|
+
datasetPath
|
|
7519
|
+
});
|
|
7520
|
+
printCommandEnvelope({
|
|
7521
|
+
runId: status.runId,
|
|
7522
|
+
...datasetPath ? { dataset: datasetPath } : {},
|
|
7523
|
+
csv_path: exportResult?.path ?? null,
|
|
7524
|
+
rowCount: exportResult?.rowsInfo.totalRows ?? null,
|
|
7525
|
+
columns: exportResult?.rowsInfo.columns ?? [],
|
|
7526
|
+
local: { csv_path: exportResult?.path ?? null },
|
|
7527
|
+
render: { sections: [{ title: "run export", lines: [`Exported ${status.runId} to ${exportResult?.path ?? outPath}`] }] }
|
|
7528
|
+
}, { json: argsWantJson(args) });
|
|
7246
7529
|
return 0;
|
|
7247
7530
|
}
|
|
7248
7531
|
async function handlePlayGet(args) {
|
|
@@ -7690,8 +7973,7 @@ Notes:
|
|
|
7690
7973
|
Local files are bundled, preflighted, then run in Deepline cloud.
|
|
7691
7974
|
Named plays run the live saved revision.
|
|
7692
7975
|
Unknown --foo value and --foo.bar value flags are passed into play input.
|
|
7693
|
-
Example: --
|
|
7694
|
-
input.limit = 5.
|
|
7976
|
+
Example: --limit 5 becomes input.limit = 5.
|
|
7695
7977
|
File args accept local paths; the CLI stages files before submit.
|
|
7696
7978
|
--watch prints logs, previews, stats, and next commands.
|
|
7697
7979
|
--wait is accepted as a compatibility alias for --watch.
|
|
@@ -7724,8 +8006,8 @@ Idempotent execution:
|
|
|
7724
8006
|
Examples:
|
|
7725
8007
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
|
|
7726
8008
|
deepline plays run my.play.ts --input @input.json --wait --json
|
|
8009
|
+
deepline plays run enrich.play.ts --input '{"file":"leads.csv"}' --watch --out leads-enriched.csv
|
|
7727
8010
|
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
8011
|
deepline plays run cto-search.play.ts --limit 5 --watch
|
|
7730
8012
|
deepline runs get <run-id>
|
|
7731
8013
|
`
|
|
@@ -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];
|
|
@@ -7929,7 +8212,7 @@ Examples:
|
|
|
7929
8212
|
`
|
|
7930
8213
|
Concepts:
|
|
7931
8214
|
A run is one execution instance of a play. It has status, progress, logs,
|
|
7932
|
-
|
|
8215
|
+
returned result, dataset previews, recovery metadata, and optional dataset export.
|
|
7933
8216
|
tail reads the live stream. logs fetches persisted logs after the fact.
|
|
7934
8217
|
stop mutates cloud state by requesting cancellation.
|
|
7935
8218
|
|
|
@@ -7942,7 +8225,7 @@ Examples:
|
|
|
7942
8225
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
7943
8226
|
`
|
|
7944
8227
|
);
|
|
7945
|
-
runs.command("get <runId>").description("Get status, progress,
|
|
8228
|
+
runs.command("get <runId>").description("Get status, progress, result, errors, and recovery metadata for a play run.").addHelpText(
|
|
7946
8229
|
"after",
|
|
7947
8230
|
`
|
|
7948
8231
|
Notes:
|
|
@@ -8036,20 +8319,22 @@ Examples:
|
|
|
8036
8319
|
...options.json ? ["--json"] : []
|
|
8037
8320
|
]);
|
|
8038
8321
|
});
|
|
8039
|
-
runs.command("export <runId>").description("Export
|
|
8322
|
+
runs.command("export <runId>").description("Export a returned dataset handle for a play run to CSV.").addHelpText(
|
|
8040
8323
|
"after",
|
|
8041
8324
|
`
|
|
8042
8325
|
Notes:
|
|
8043
|
-
Writes
|
|
8044
|
-
first
|
|
8326
|
+
Writes a returned dataset handle to the requested local CSV path. Use runs get
|
|
8327
|
+
first to inspect dataset paths like result.rows or result.nested.contacts.
|
|
8045
8328
|
|
|
8046
8329
|
Examples:
|
|
8047
8330
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
8331
|
+
deepline runs export play/my-play/run/20260501t000000-000 --dataset result.rows --out output.csv
|
|
8048
8332
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --json
|
|
8049
8333
|
`
|
|
8050
|
-
).requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
8334
|
+
).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
8335
|
process.exitCode = await handleRunExport([
|
|
8052
8336
|
runId,
|
|
8337
|
+
...options.dataset ? ["--dataset", options.dataset] : [],
|
|
8053
8338
|
"--out",
|
|
8054
8339
|
options.out,
|
|
8055
8340
|
...options.json ? ["--json"] : []
|
|
@@ -8215,19 +8500,19 @@ function toListedTool(tool) {
|
|
|
8215
8500
|
async function listTools(args) {
|
|
8216
8501
|
const client = new DeeplineClient();
|
|
8217
8502
|
const items = (await client.listTools()).map(toListedTool);
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
`);
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
}
|
|
8503
|
+
const render = {
|
|
8504
|
+
sections: [
|
|
8505
|
+
{
|
|
8506
|
+
title: `${items.length} tools available:`,
|
|
8507
|
+
lines: items.flatMap((item) => {
|
|
8508
|
+
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8509
|
+
const listHint = item.listExtractorPaths?.length ? ` listExtractorPaths=${item.listExtractorPaths.join(",")}` : "";
|
|
8510
|
+
return [`${item.toolId}${cats}`, ` ${item.description}${listHint}`];
|
|
8511
|
+
})
|
|
8512
|
+
}
|
|
8513
|
+
]
|
|
8514
|
+
};
|
|
8515
|
+
printCommandEnvelope({ tools: items, count: items.length, render }, { json: argsWantJson(args) });
|
|
8231
8516
|
return 0;
|
|
8232
8517
|
}
|
|
8233
8518
|
async function searchTools(queryInput, options = {}) {
|
|
@@ -8245,21 +8530,27 @@ async function searchTools(queryInput, options = {}) {
|
|
|
8245
8530
|
includeSearchDebug: options.includeSearchDebug
|
|
8246
8531
|
});
|
|
8247
8532
|
const items = result.tools.map(toListedTool);
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8533
|
+
const envelope = {
|
|
8534
|
+
...result,
|
|
8535
|
+
tools: items,
|
|
8536
|
+
count: items.length,
|
|
8537
|
+
render: {
|
|
8538
|
+
sections: [
|
|
8539
|
+
{
|
|
8540
|
+
title: `${items.length} tools found:`,
|
|
8541
|
+
lines: items.flatMap((item) => {
|
|
8542
|
+
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8543
|
+
return [
|
|
8544
|
+
`${item.toolId}${cats}`,
|
|
8545
|
+
` ${item.description}`,
|
|
8546
|
+
...item.inputSchema ? [" inputSchema: yes"] : []
|
|
8547
|
+
];
|
|
8548
|
+
})
|
|
8549
|
+
}
|
|
8550
|
+
]
|
|
8261
8551
|
}
|
|
8262
|
-
}
|
|
8552
|
+
};
|
|
8553
|
+
printCommandEnvelope(envelope, { json: options.json || shouldEmitJson() });
|
|
8263
8554
|
return 0;
|
|
8264
8555
|
}
|
|
8265
8556
|
function playIdentifiers(play) {
|
|
@@ -8591,6 +8882,9 @@ function samplePayload(samples, key) {
|
|
|
8591
8882
|
if (!isRecord3(entry)) return void 0;
|
|
8592
8883
|
return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
|
|
8593
8884
|
}
|
|
8885
|
+
function commandEnvelopeFromRawResponse(rawResponse) {
|
|
8886
|
+
return isRecord3(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
|
|
8887
|
+
}
|
|
8594
8888
|
function isPlayTool(tool) {
|
|
8595
8889
|
const provider = typeof tool.provider === "string" ? tool.provider : "";
|
|
8596
8890
|
return provider === "deepline_native" && Boolean(recordField(tool, "playExpansion", "play_expansion"));
|
|
@@ -8755,6 +9049,53 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
8755
9049
|
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
9050
|
};
|
|
8757
9051
|
}
|
|
9052
|
+
function buildToolExecuteBaseEnvelope(input) {
|
|
9053
|
+
const envelope = commandEnvelopeFromRawResponse(input.rawResponse);
|
|
9054
|
+
const summaryEntries = Object.entries(input.summary);
|
|
9055
|
+
const output = input.listConversion ? {
|
|
9056
|
+
kind: "list",
|
|
9057
|
+
rowCount: input.listConversion.rows.length,
|
|
9058
|
+
columns: Object.keys(input.listConversion.rows[0] ?? {}),
|
|
9059
|
+
preview: input.listConversion.rows.slice(0, 5),
|
|
9060
|
+
listStrategy: input.listConversion.strategy,
|
|
9061
|
+
listSourcePath: input.listConversion.sourcePath
|
|
9062
|
+
} : {
|
|
9063
|
+
kind: summaryEntries.length > 0 ? "object" : "raw",
|
|
9064
|
+
summary: input.summary
|
|
9065
|
+
};
|
|
9066
|
+
const actions = input.listConversion ? [
|
|
9067
|
+
{
|
|
9068
|
+
label: "next",
|
|
9069
|
+
command: "move starter script into a project folder and expand it into a Deepline play"
|
|
9070
|
+
}
|
|
9071
|
+
] : [];
|
|
9072
|
+
return {
|
|
9073
|
+
...envelope,
|
|
9074
|
+
output,
|
|
9075
|
+
...summaryEntries.length > 0 ? { summary: input.summary } : {},
|
|
9076
|
+
next: input.listConversion ? {
|
|
9077
|
+
expandToPlay: "Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
|
|
9078
|
+
} : {},
|
|
9079
|
+
render: {
|
|
9080
|
+
sections: input.listConversion ? [
|
|
9081
|
+
{
|
|
9082
|
+
title: "output",
|
|
9083
|
+
lines: [
|
|
9084
|
+
`${input.listConversion.rows.length} row(s) extracted from ${input.listConversion.sourcePath ?? "auto-detected list"}`,
|
|
9085
|
+
`columns: ${JSON.stringify(Object.keys(input.listConversion.rows[0] ?? {}))}`,
|
|
9086
|
+
`preview: ${JSON.stringify(input.listConversion.rows.slice(0, 5))}`
|
|
9087
|
+
]
|
|
9088
|
+
}
|
|
9089
|
+
] : [
|
|
9090
|
+
{
|
|
9091
|
+
title: "result",
|
|
9092
|
+
lines: summaryEntries.length > 0 ? summaryEntries.map(([key, value]) => `${key}=${String(value)}`) : [JSON.stringify(input.rawResponse, null, 2)]
|
|
9093
|
+
}
|
|
9094
|
+
],
|
|
9095
|
+
actions
|
|
9096
|
+
}
|
|
9097
|
+
};
|
|
9098
|
+
}
|
|
8758
9099
|
async function executeTool(args) {
|
|
8759
9100
|
let parsed;
|
|
8760
9101
|
try {
|
|
@@ -8787,24 +9128,45 @@ async function executeTool(args) {
|
|
|
8787
9128
|
listExtractorPaths: metadata.listExtractorPaths ?? []
|
|
8788
9129
|
});
|
|
8789
9130
|
const summary = extractSummaryFields(rawResponse);
|
|
9131
|
+
const baseEnvelope = buildToolExecuteBaseEnvelope({
|
|
9132
|
+
toolId: parsed.toolId,
|
|
9133
|
+
rawResponse,
|
|
9134
|
+
listConversion,
|
|
9135
|
+
summary
|
|
9136
|
+
});
|
|
8790
9137
|
if (parsed.outputFormat === "json" || parsed.outputFormat === "auto" && shouldEmitJson()) {
|
|
8791
|
-
|
|
8792
|
-
`);
|
|
9138
|
+
printCommandEnvelope(baseEnvelope, { json: true });
|
|
8793
9139
|
return 0;
|
|
8794
9140
|
}
|
|
8795
9141
|
if (parsed.outputFormat === "json_file") {
|
|
8796
9142
|
const jsonPath = writeJsonOutputFile(rawResponse, `payload_${parsed.toolId}`);
|
|
8797
|
-
|
|
9143
|
+
printCommandEnvelope(
|
|
9144
|
+
{
|
|
9145
|
+
...baseEnvelope,
|
|
9146
|
+
local: {
|
|
9147
|
+
...isRecord3(baseEnvelope.local) ? baseEnvelope.local : {},
|
|
9148
|
+
payload_file: jsonPath
|
|
9149
|
+
}
|
|
9150
|
+
},
|
|
9151
|
+
{ json: true }
|
|
9152
|
+
);
|
|
8798
9153
|
return 0;
|
|
8799
9154
|
}
|
|
8800
9155
|
if (!listConversion) {
|
|
8801
9156
|
if (parsed.outputFormat === "csv" || parsed.outputFormat === "csv_file") {
|
|
8802
9157
|
const jsonPath = writeJsonOutputFile(rawResponse, `payload_${parsed.toolId}`);
|
|
8803
|
-
|
|
9158
|
+
printCommandEnvelope(
|
|
9159
|
+
{
|
|
9160
|
+
...baseEnvelope,
|
|
9161
|
+
local: {
|
|
9162
|
+
payload_file: jsonPath
|
|
9163
|
+
}
|
|
9164
|
+
},
|
|
9165
|
+
{ json: parsed.outputFormat === "csv_file" || shouldEmitJson() }
|
|
9166
|
+
);
|
|
8804
9167
|
return 0;
|
|
8805
9168
|
}
|
|
8806
|
-
|
|
8807
|
-
`);
|
|
9169
|
+
printCommandEnvelope(baseEnvelope, { json: false });
|
|
8808
9170
|
return 0;
|
|
8809
9171
|
}
|
|
8810
9172
|
const csv = writeCsvOutputFile(listConversion.rows, `${parsed.toolId}_output`);
|
|
@@ -8813,8 +9175,51 @@ async function executeTool(args) {
|
|
|
8813
9175
|
payload: parsed.params,
|
|
8814
9176
|
rows: listConversion.rows
|
|
8815
9177
|
});
|
|
9178
|
+
const materializedEnvelope = {
|
|
9179
|
+
...baseEnvelope,
|
|
9180
|
+
local: {
|
|
9181
|
+
extracted_csv: csv.path,
|
|
9182
|
+
extracted_csv_rows: csv.rowCount,
|
|
9183
|
+
extracted_csv_columns: csv.columns,
|
|
9184
|
+
preview: csv.preview,
|
|
9185
|
+
starter_script: seededScript.path,
|
|
9186
|
+
project_dir: seededScript.projectDir,
|
|
9187
|
+
copy_to_project: {
|
|
9188
|
+
macos_linux: seededScript.macCopyCommand,
|
|
9189
|
+
windows_powershell: seededScript.windowsCopyCommand
|
|
9190
|
+
}
|
|
9191
|
+
},
|
|
9192
|
+
render: {
|
|
9193
|
+
sections: [
|
|
9194
|
+
{
|
|
9195
|
+
title: `${csv.path} (${csv.rowCount} rows)`,
|
|
9196
|
+
lines: [
|
|
9197
|
+
...csv.columns.length > 0 ? [`columns: ${JSON.stringify(csv.columns)}`] : [],
|
|
9198
|
+
...Object.keys(summary).length > 0 ? [`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`] : [],
|
|
9199
|
+
`preview: ${JSON.stringify(csv.preview)}`,
|
|
9200
|
+
`starter script: ${seededScript.path}`
|
|
9201
|
+
]
|
|
9202
|
+
}
|
|
9203
|
+
],
|
|
9204
|
+
actions: [
|
|
9205
|
+
{
|
|
9206
|
+
label: "next",
|
|
9207
|
+
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."
|
|
9208
|
+
},
|
|
9209
|
+
{
|
|
9210
|
+
label: "macOS/Linux",
|
|
9211
|
+
command: seededScript.macCopyCommand
|
|
9212
|
+
},
|
|
9213
|
+
{
|
|
9214
|
+
label: "Windows PowerShell",
|
|
9215
|
+
command: seededScript.windowsCopyCommand
|
|
9216
|
+
}
|
|
9217
|
+
]
|
|
9218
|
+
}
|
|
9219
|
+
};
|
|
8816
9220
|
if (parsed.outputFormat === "csv_file") {
|
|
8817
|
-
|
|
9221
|
+
printCommandEnvelope({
|
|
9222
|
+
...materializedEnvelope,
|
|
8818
9223
|
extracted_csv: csv.path,
|
|
8819
9224
|
extracted_csv_rows: csv.rowCount,
|
|
8820
9225
|
extracted_csv_columns: csv.columns,
|
|
@@ -8828,28 +9233,24 @@ async function executeTool(args) {
|
|
|
8828
9233
|
windows_powershell: seededScript.windowsCopyCommand
|
|
8829
9234
|
},
|
|
8830
9235
|
summary
|
|
8831
|
-
})
|
|
8832
|
-
`);
|
|
9236
|
+
}, { json: true });
|
|
8833
9237
|
return 0;
|
|
8834
9238
|
}
|
|
8835
9239
|
if (parsed.noPreview) {
|
|
8836
|
-
|
|
9240
|
+
printCommandEnvelope(
|
|
9241
|
+
{
|
|
9242
|
+
...materializedEnvelope,
|
|
9243
|
+
local: {
|
|
9244
|
+
...materializedEnvelope.local ?? {},
|
|
9245
|
+
output_path: csv.path
|
|
9246
|
+
}
|
|
9247
|
+
},
|
|
9248
|
+
{ json: shouldEmitJson(), text: `${csv.path}
|
|
9249
|
+
` }
|
|
9250
|
+
);
|
|
8837
9251
|
return 0;
|
|
8838
9252
|
}
|
|
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}`);
|
|
9253
|
+
printCommandEnvelope(materializedEnvelope, { json: false });
|
|
8853
9254
|
return 0;
|
|
8854
9255
|
}
|
|
8855
9256
|
|
|
@@ -8923,23 +9324,36 @@ function runCommand(command, args) {
|
|
|
8923
9324
|
}
|
|
8924
9325
|
async function handleUpdate(options) {
|
|
8925
9326
|
const plan = resolveUpdatePlan();
|
|
9327
|
+
const render = {
|
|
9328
|
+
sections: [
|
|
9329
|
+
{
|
|
9330
|
+
title: "update",
|
|
9331
|
+
lines: plan.kind === "source" ? [
|
|
9332
|
+
"This Deepline CLI is running from SDK source, so it cannot safely update itself like an npm global.",
|
|
9333
|
+
`Update the backing checkout with: ${plan.manualCommand}`
|
|
9334
|
+
] : [`Updating Deepline SDK/CLI with: ${plan.manualCommand}`]
|
|
9335
|
+
}
|
|
9336
|
+
]
|
|
9337
|
+
};
|
|
8926
9338
|
if (options.json) {
|
|
8927
|
-
|
|
8928
|
-
`);
|
|
9339
|
+
printCommandEnvelope({ ...plan, render }, { json: true });
|
|
8929
9340
|
return 0;
|
|
8930
9341
|
}
|
|
8931
9342
|
if (options.printCommand) {
|
|
8932
|
-
|
|
8933
|
-
|
|
9343
|
+
printCommandEnvelope(
|
|
9344
|
+
{
|
|
9345
|
+
...plan,
|
|
9346
|
+
render: {
|
|
9347
|
+
sections: [{ title: "update command", lines: [plan.manualCommand] }]
|
|
9348
|
+
}
|
|
9349
|
+
},
|
|
9350
|
+
{ json: false, text: `${plan.manualCommand}
|
|
9351
|
+
` }
|
|
9352
|
+
);
|
|
8934
9353
|
return 0;
|
|
8935
9354
|
}
|
|
8936
9355
|
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
|
-
);
|
|
9356
|
+
printCommandEnvelope({ ...plan, render }, { json: false });
|
|
8943
9357
|
return 0;
|
|
8944
9358
|
}
|
|
8945
9359
|
process.stderr.write(`Updating Deepline SDK/CLI with: ${plan.manualCommand}
|
|
@@ -9252,7 +9666,7 @@ Exit codes:
|
|
|
9252
9666
|
registerDbCommands(program);
|
|
9253
9667
|
registerFeedbackCommands(program);
|
|
9254
9668
|
registerUpdateCommand(program);
|
|
9255
|
-
program.command("health").description("Check server health.").addHelpText(
|
|
9669
|
+
program.command("health").description("Check server health.").option("--json", "Force JSON output.").addHelpText(
|
|
9256
9670
|
"after",
|
|
9257
9671
|
`
|
|
9258
9672
|
Notes:
|