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.mjs
CHANGED
|
@@ -243,8 +243,8 @@ function saveProjectDeeplineEnvValues(baseUrl, values, startDir = projectEnvStar
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
// src/version.ts
|
|
246
|
-
var SDK_VERSION = "0.1.
|
|
247
|
-
var SDK_API_CONTRACT = "2026-05-runs-
|
|
246
|
+
var SDK_VERSION = "0.1.31";
|
|
247
|
+
var SDK_API_CONTRACT = "2026-05-runs-export-v3";
|
|
248
248
|
|
|
249
249
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
250
250
|
var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
|
|
@@ -653,6 +653,7 @@ var DeeplineClient = class {
|
|
|
653
653
|
list: (options2) => this.listRuns(options2),
|
|
654
654
|
tail: (runId, options2) => this.tailRun(runId, options2),
|
|
655
655
|
logs: (runId, options2) => this.getRunLogs(runId, options2),
|
|
656
|
+
exportDatasetRows: (input) => this.getPlaySheetRows(input),
|
|
656
657
|
stop: (runId, options2) => this.stopRun(runId, options2)
|
|
657
658
|
};
|
|
658
659
|
}
|
|
@@ -680,7 +681,7 @@ var DeeplineClient = class {
|
|
|
680
681
|
const target = play.reference || play.name;
|
|
681
682
|
if (options?.csvInput) {
|
|
682
683
|
const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
|
|
683
|
-
return `deepline plays run ${target}
|
|
684
|
+
return `deepline plays run ${target} --input '${JSON.stringify({ [inputField]: "leads.csv" })}' --watch`;
|
|
684
685
|
}
|
|
685
686
|
return `deepline plays run ${target} --input '{...}' --watch`;
|
|
686
687
|
}
|
|
@@ -1340,6 +1341,19 @@ var DeeplineClient = class {
|
|
|
1340
1341
|
entries
|
|
1341
1342
|
};
|
|
1342
1343
|
}
|
|
1344
|
+
async getPlaySheetRows(input) {
|
|
1345
|
+
const params = new URLSearchParams({
|
|
1346
|
+
tableNamespace: input.tableNamespace,
|
|
1347
|
+
limit: String(input.limit ?? 5e3),
|
|
1348
|
+
offset: String(input.offset ?? 0)
|
|
1349
|
+
});
|
|
1350
|
+
if (input.runId?.trim()) {
|
|
1351
|
+
params.set("runId", input.runId.trim());
|
|
1352
|
+
}
|
|
1353
|
+
return await this.http.get(
|
|
1354
|
+
`/api/v2/plays/${encodeURIComponent(input.playName)}/sheet?${params.toString()}`
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1343
1357
|
/**
|
|
1344
1358
|
* Stop a run by id using the public runs resource model.
|
|
1345
1359
|
*
|
|
@@ -1952,6 +1966,38 @@ function clip(value, maxLength) {
|
|
|
1952
1966
|
return value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
|
|
1953
1967
|
}
|
|
1954
1968
|
|
|
1969
|
+
// src/cli/command-envelope.ts
|
|
1970
|
+
function renderSections(render) {
|
|
1971
|
+
const lines = [];
|
|
1972
|
+
for (const section of render?.sections ?? []) {
|
|
1973
|
+
if (!section.title || section.lines.length === 0) continue;
|
|
1974
|
+
lines.push(section.title);
|
|
1975
|
+
lines.push(...section.lines.map((line) => ` ${line}`));
|
|
1976
|
+
}
|
|
1977
|
+
for (const action of render?.actions ?? []) {
|
|
1978
|
+
if (!action.label || !action.command) continue;
|
|
1979
|
+
lines.push(`${action.label}: ${action.command}`);
|
|
1980
|
+
}
|
|
1981
|
+
return lines;
|
|
1982
|
+
}
|
|
1983
|
+
function renderCommandEnvelopeText(envelope) {
|
|
1984
|
+
const lines = renderSections(envelope.render);
|
|
1985
|
+
if (lines.length > 0) {
|
|
1986
|
+
return `${lines.join("\n")}
|
|
1987
|
+
`;
|
|
1988
|
+
}
|
|
1989
|
+
return `${JSON.stringify(envelope, null, 2)}
|
|
1990
|
+
`;
|
|
1991
|
+
}
|
|
1992
|
+
function printCommandEnvelope(envelope, options = {}) {
|
|
1993
|
+
const jsonOutput = typeof options.json === "boolean" ? options.json : shouldEmitJson();
|
|
1994
|
+
if (jsonOutput) {
|
|
1995
|
+
printJson(envelope);
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
process.stdout.write(options.text ?? renderCommandEnvelopeText(envelope));
|
|
1999
|
+
}
|
|
2000
|
+
|
|
1955
2001
|
// src/cli/commands/auth.ts
|
|
1956
2002
|
var EXIT_OK = 0;
|
|
1957
2003
|
var EXIT_AUTH = 1;
|
|
@@ -2200,6 +2246,7 @@ async function handleStatus(args) {
|
|
|
2200
2246
|
const reveal = args.includes("--reveal");
|
|
2201
2247
|
const jsonOutput = argsWantJson(args);
|
|
2202
2248
|
let hostStatusPayload = null;
|
|
2249
|
+
const hostLines = [];
|
|
2203
2250
|
try {
|
|
2204
2251
|
const { status: hStatus, data: hData } = await httpJson("GET", `${baseUrl}/api/v2/health`, null);
|
|
2205
2252
|
if (hStatus === 200) {
|
|
@@ -2208,11 +2255,9 @@ async function handleStatus(args) {
|
|
|
2208
2255
|
hostStatus: hData.status || "ok",
|
|
2209
2256
|
hostVersion: hData.version || "(unknown)"
|
|
2210
2257
|
};
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
console.log(`Host version: ${hData.version || "(unknown)"}`);
|
|
2215
|
-
}
|
|
2258
|
+
hostLines.push(`Host: ${baseUrl}`);
|
|
2259
|
+
hostLines.push(`Host status: ${hData.status || "ok"}`);
|
|
2260
|
+
hostLines.push(`Host version: ${hData.version || "(unknown)"}`);
|
|
2216
2261
|
}
|
|
2217
2262
|
} catch {
|
|
2218
2263
|
hostStatusPayload = {
|
|
@@ -2220,40 +2265,34 @@ async function handleStatus(args) {
|
|
|
2220
2265
|
hostStatus: "unreachable",
|
|
2221
2266
|
hostVersion: null
|
|
2222
2267
|
};
|
|
2223
|
-
|
|
2224
|
-
console.log(`Host: ${baseUrl} (unreachable)`);
|
|
2225
|
-
}
|
|
2268
|
+
hostLines.push(`Host: ${baseUrl} (unreachable)`);
|
|
2226
2269
|
}
|
|
2227
2270
|
const env = loadCliEnv(baseUrl);
|
|
2228
2271
|
const apiKey = process.env.DEEPLINE_API_KEY?.trim() || env.DEEPLINE_API_KEY || "";
|
|
2229
2272
|
if (!apiKey) {
|
|
2230
2273
|
if (env.DEEPLINE_CLAIM_TOKEN?.trim()) {
|
|
2231
|
-
|
|
2232
|
-
process.stdout.write(`${JSON.stringify({
|
|
2233
|
-
...hostStatusPayload ?? { host: baseUrl },
|
|
2234
|
-
status: "pending",
|
|
2235
|
-
connected: false,
|
|
2236
|
-
next: "deepline auth wait"
|
|
2237
|
-
})}
|
|
2238
|
-
`);
|
|
2239
|
-
return EXIT_OK;
|
|
2240
|
-
}
|
|
2241
|
-
console.log("Status: pending");
|
|
2242
|
-
console.log("Run: deepline auth wait");
|
|
2243
|
-
return EXIT_OK;
|
|
2244
|
-
}
|
|
2245
|
-
if (jsonOutput) {
|
|
2246
|
-
process.stdout.write(`${JSON.stringify({
|
|
2274
|
+
printCommandEnvelope({
|
|
2247
2275
|
...hostStatusPayload ?? { host: baseUrl },
|
|
2248
|
-
status: "
|
|
2276
|
+
status: "pending",
|
|
2249
2277
|
connected: false,
|
|
2250
|
-
next: "deepline auth
|
|
2251
|
-
|
|
2252
|
-
|
|
2278
|
+
next: "deepline auth wait",
|
|
2279
|
+
render: {
|
|
2280
|
+
sections: [{ title: "auth status", lines: [...hostLines, "Status: pending"] }],
|
|
2281
|
+
actions: [{ label: "Run", command: "deepline auth wait" }]
|
|
2282
|
+
}
|
|
2283
|
+
}, { json: jsonOutput });
|
|
2253
2284
|
return EXIT_OK;
|
|
2254
2285
|
}
|
|
2255
|
-
|
|
2256
|
-
|
|
2286
|
+
printCommandEnvelope({
|
|
2287
|
+
...hostStatusPayload ?? { host: baseUrl },
|
|
2288
|
+
status: "not connected",
|
|
2289
|
+
connected: false,
|
|
2290
|
+
next: "deepline auth register",
|
|
2291
|
+
render: {
|
|
2292
|
+
sections: [{ title: "auth status", lines: [...hostLines, "Status: not connected"] }],
|
|
2293
|
+
actions: [{ label: "Run", command: "deepline auth register" }]
|
|
2294
|
+
}
|
|
2295
|
+
}, { json: jsonOutput });
|
|
2257
2296
|
return EXIT_OK;
|
|
2258
2297
|
}
|
|
2259
2298
|
const { status, data } = await httpJson("POST", `${baseUrl}/api/v2/auth/cli/status`, apiKey, {
|
|
@@ -2261,18 +2300,16 @@ async function handleStatus(args) {
|
|
|
2261
2300
|
reveal
|
|
2262
2301
|
});
|
|
2263
2302
|
if (status === 401 || status === 403) {
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
}
|
|
2274
|
-
console.log("Status: unauthorized");
|
|
2275
|
-
console.log("Run: deepline auth register");
|
|
2303
|
+
printCommandEnvelope({
|
|
2304
|
+
...hostStatusPayload ?? { host: baseUrl },
|
|
2305
|
+
status: "unauthorized",
|
|
2306
|
+
connected: false,
|
|
2307
|
+
next: "deepline auth register",
|
|
2308
|
+
render: {
|
|
2309
|
+
sections: [{ title: "auth status", lines: [...hostLines, "Status: unauthorized"] }],
|
|
2310
|
+
actions: [{ label: "Run", command: "deepline auth register" }]
|
|
2311
|
+
}
|
|
2312
|
+
}, { json: jsonOutput });
|
|
2276
2313
|
return EXIT_AUTH;
|
|
2277
2314
|
}
|
|
2278
2315
|
if (status >= 400) {
|
|
@@ -2294,23 +2331,7 @@ async function handleStatus(args) {
|
|
|
2294
2331
|
},
|
|
2295
2332
|
examples: Array.isArray(data.examples) ? data.examples : []
|
|
2296
2333
|
};
|
|
2297
|
-
|
|
2298
|
-
process.stdout.write(`${JSON.stringify(payload)}
|
|
2299
|
-
`);
|
|
2300
|
-
} else {
|
|
2301
|
-
console.log(`Status: ${payload.status}`);
|
|
2302
|
-
console.log(`Rate limit tier: ${payload.rateLimitTier}`);
|
|
2303
|
-
if (payload.workspace.name) console.log(`Workspace: ${payload.workspace.name}`);
|
|
2304
|
-
if (payload.workspace.slug) console.log(`Workspace slug: ${payload.workspace.slug}`);
|
|
2305
|
-
if (payload.workspace.id != null) console.log(`Org ID: ${payload.workspace.id}`);
|
|
2306
|
-
if (payload.user.id != null) console.log(`User ID: ${payload.user.id}`);
|
|
2307
|
-
if (payload.examples.length > 0) {
|
|
2308
|
-
console.log("Examples:");
|
|
2309
|
-
for (const example of payload.examples.slice(0, 3)) {
|
|
2310
|
-
console.log(` ${String(example)}`);
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2334
|
+
let savedApiKeyPath = null;
|
|
2314
2335
|
if (reveal) {
|
|
2315
2336
|
const apiKeyResp = String(data.api_key || apiKey);
|
|
2316
2337
|
if (apiKeyResp) {
|
|
@@ -2319,9 +2340,31 @@ async function handleStatus(args) {
|
|
|
2319
2340
|
DEEPLINE_API_KEY: apiKeyResp,
|
|
2320
2341
|
DEEPLINE_CLAIM_TOKEN: ""
|
|
2321
2342
|
}, baseUrl);
|
|
2322
|
-
|
|
2343
|
+
savedApiKeyPath = envFilePath(baseUrl);
|
|
2323
2344
|
}
|
|
2324
2345
|
}
|
|
2346
|
+
printCommandEnvelope({
|
|
2347
|
+
...payload,
|
|
2348
|
+
...savedApiKeyPath ? { saved_api_key_path: savedApiKeyPath } : {},
|
|
2349
|
+
render: {
|
|
2350
|
+
sections: [
|
|
2351
|
+
{
|
|
2352
|
+
title: "auth status",
|
|
2353
|
+
lines: [
|
|
2354
|
+
...hostLines,
|
|
2355
|
+
`Status: ${payload.status}`,
|
|
2356
|
+
`Rate limit tier: ${payload.rateLimitTier}`,
|
|
2357
|
+
...payload.workspace.name ? [`Workspace: ${payload.workspace.name}`] : [],
|
|
2358
|
+
...payload.workspace.slug ? [`Workspace slug: ${payload.workspace.slug}`] : [],
|
|
2359
|
+
...payload.workspace.id != null ? [`Org ID: ${payload.workspace.id}`] : [],
|
|
2360
|
+
...payload.user.id != null ? [`User ID: ${payload.user.id}`] : [],
|
|
2361
|
+
...payload.examples.length > 0 ? ["Examples:", ...payload.examples.slice(0, 3).map((example) => ` ${String(example)}`)] : [],
|
|
2362
|
+
...savedApiKeyPath ? [`Saved API key to ${savedApiKeyPath}`] : []
|
|
2363
|
+
]
|
|
2364
|
+
}
|
|
2365
|
+
]
|
|
2366
|
+
}
|
|
2367
|
+
}, { json: jsonOutput });
|
|
2325
2368
|
return EXIT_OK;
|
|
2326
2369
|
}
|
|
2327
2370
|
function registerAuthCommands(program) {
|
|
@@ -2404,19 +2447,18 @@ import { stringify as stringify2 } from "csv-stringify/sync";
|
|
|
2404
2447
|
function humanize(value) {
|
|
2405
2448
|
return String(value || "").split("_").filter(Boolean).map((token) => token[0]?.toUpperCase() + token.slice(1)).join(" ") || "Unknown";
|
|
2406
2449
|
}
|
|
2407
|
-
function
|
|
2450
|
+
function recentUsageLines(entries) {
|
|
2408
2451
|
if (entries.length === 0) {
|
|
2409
|
-
|
|
2410
|
-
return;
|
|
2452
|
+
return ["Recent activity: none yet"];
|
|
2411
2453
|
}
|
|
2412
|
-
|
|
2454
|
+
const lines = ["Recent activity:"];
|
|
2413
2455
|
for (const entry of entries) {
|
|
2414
2456
|
const op = `${humanize(entry.provider)} ${humanize(entry.operation)}`.trim();
|
|
2415
2457
|
const charge = entry.billing_mode === "no_bill" ? "free" : `${entry.credits ?? 0} cr`;
|
|
2416
2458
|
const status = entry.status || "completed";
|
|
2417
|
-
|
|
2418
|
-
`);
|
|
2459
|
+
lines.push(`${op} | ${charge} | ${status} | ${entry.created_at || "unknown"}`);
|
|
2419
2460
|
}
|
|
2461
|
+
return lines;
|
|
2420
2462
|
}
|
|
2421
2463
|
function summarizeLedgerRows(summary, rows) {
|
|
2422
2464
|
const netDelta = rows.reduce((sum, row) => {
|
|
@@ -2469,49 +2511,49 @@ function defaultLedgerExportPath() {
|
|
|
2469
2511
|
async function handleBalance(options) {
|
|
2470
2512
|
const { http } = getAuthedHttpClient();
|
|
2471
2513
|
const payload = await http.get("/api/v2/billing/balance");
|
|
2472
|
-
if (shouldEmitJson(options.json)) return printJson(payload);
|
|
2473
2514
|
const status = String(payload.balance_status || "");
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2515
|
+
const lines = status === "no_billing" ? [
|
|
2516
|
+
"Balance: 0 credits",
|
|
2517
|
+
"Billing: No billing account or payment method set up for this workspace."
|
|
2518
|
+
] : [`Balance: ${payload.balance ?? "(unknown)"} credits`];
|
|
2519
|
+
printCommandEnvelope({
|
|
2520
|
+
...payload,
|
|
2521
|
+
render: { sections: [{ title: "billing balance", lines }] }
|
|
2522
|
+
}, { json: options.json });
|
|
2523
|
+
return;
|
|
2481
2524
|
}
|
|
2482
2525
|
async function handleUsage(options) {
|
|
2483
2526
|
const { http } = getAuthedHttpClient();
|
|
2484
2527
|
const params = new URLSearchParams();
|
|
2485
2528
|
if (options.limit) params.set("recent_limit", options.limit);
|
|
2486
2529
|
if (options.offset) params.set("recent_offset", options.offset);
|
|
2487
|
-
const suffix = params.
|
|
2530
|
+
const suffix = Array.from(params).length > 0 ? `?${params.toString()}` : "";
|
|
2488
2531
|
const payload = await http.get(`/api/v2/billing/usage${suffix}`);
|
|
2489
|
-
if (shouldEmitJson(options.json)) return printJson(payload);
|
|
2490
2532
|
const usage = payload.usage ?? {};
|
|
2491
2533
|
const quota = payload.quota ?? {};
|
|
2492
2534
|
const recent = payload.recent ?? {};
|
|
2493
|
-
|
|
2494
|
-
`)
|
|
2495
|
-
|
|
2496
|
-
`)
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2535
|
+
const lines = [
|
|
2536
|
+
`Balance: ${payload.balance ?? "(unknown)"}`,
|
|
2537
|
+
`Last 30 days spent: ${usage.month_spent_credits ?? "(unknown)"}`,
|
|
2538
|
+
`Monthly limit: ${quota.enabled ? quota.monthly_credits_limit ?? "(unknown)" : "off"}`,
|
|
2539
|
+
...recentUsageLines(Array.isArray(recent.entries) ? recent.entries : [])
|
|
2540
|
+
];
|
|
2541
|
+
printCommandEnvelope({
|
|
2542
|
+
...payload,
|
|
2543
|
+
render: { sections: [{ title: "billing usage", lines }] }
|
|
2544
|
+
}, { json: options.json });
|
|
2502
2545
|
}
|
|
2503
2546
|
async function handleLimit(options) {
|
|
2504
2547
|
const { http } = getAuthedHttpClient();
|
|
2505
2548
|
const payload = await http.get("/api/v2/billing/limit");
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
}
|
|
2514
|
-
process.stdout.write("Monthly limit: off\n");
|
|
2549
|
+
const lines = payload.enabled ? [
|
|
2550
|
+
`Monthly limit: ${payload.monthly_credits_limit ?? "(unknown)"}`,
|
|
2551
|
+
`Remaining before cap: ${payload.remaining_credits ?? "(unknown)"}`
|
|
2552
|
+
] : ["Monthly limit: off"];
|
|
2553
|
+
printCommandEnvelope({
|
|
2554
|
+
...payload,
|
|
2555
|
+
render: { sections: [{ title: "billing limit", lines }] }
|
|
2556
|
+
}, { json: options.json });
|
|
2515
2557
|
}
|
|
2516
2558
|
async function handleSetLimit(credits, options) {
|
|
2517
2559
|
const { http } = getAuthedHttpClient();
|
|
@@ -2519,15 +2561,20 @@ async function handleSetLimit(credits, options) {
|
|
|
2519
2561
|
method: "PUT",
|
|
2520
2562
|
body: { monthly_credits_limit: Number.parseInt(credits, 10) }
|
|
2521
2563
|
});
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2564
|
+
printCommandEnvelope({
|
|
2565
|
+
...payload,
|
|
2566
|
+
render: {
|
|
2567
|
+
sections: [{ title: "billing limit", lines: [`Monthly billing limit set to ${credits} credits.`] }]
|
|
2568
|
+
}
|
|
2569
|
+
}, { json: options.json });
|
|
2525
2570
|
}
|
|
2526
2571
|
async function handleLimitOff(options) {
|
|
2527
2572
|
const { http } = getAuthedHttpClient();
|
|
2528
2573
|
const payload = await http.request("/api/v2/billing/limit", { method: "DELETE" });
|
|
2529
|
-
|
|
2530
|
-
|
|
2574
|
+
printCommandEnvelope({
|
|
2575
|
+
...payload,
|
|
2576
|
+
render: { sections: [{ title: "billing limit", lines: ["Monthly billing limit is now off."] }] }
|
|
2577
|
+
}, { json: options.json });
|
|
2531
2578
|
}
|
|
2532
2579
|
async function handleHistory(options) {
|
|
2533
2580
|
const { http } = getAuthedHttpClient();
|
|
@@ -2546,13 +2593,20 @@ async function handleHistory(options) {
|
|
|
2546
2593
|
};
|
|
2547
2594
|
});
|
|
2548
2595
|
const outputPath = await writeCsvRowsFile(`billing-history-${options.time}`, rows);
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2596
|
+
printCommandEnvelope({
|
|
2597
|
+
output_path: outputPath,
|
|
2598
|
+
row_count: rows.length,
|
|
2599
|
+
time_window: options.time,
|
|
2600
|
+
render: {
|
|
2601
|
+
sections: [
|
|
2602
|
+
{
|
|
2603
|
+
title: "billing history",
|
|
2604
|
+
lines: [`Billing history written to ${outputPath}`, `${rows.length} row(s) exported.`]
|
|
2605
|
+
}
|
|
2606
|
+
]
|
|
2607
|
+
},
|
|
2608
|
+
local: { output_path: outputPath }
|
|
2609
|
+
}, { json: options.json });
|
|
2556
2610
|
}
|
|
2557
2611
|
async function handleLedgerExportAll(options) {
|
|
2558
2612
|
const { http } = getAuthedHttpClient();
|
|
@@ -2584,20 +2638,25 @@ async function handleLedgerExportAll(options) {
|
|
|
2584
2638
|
if (nextCursor === cursor) break;
|
|
2585
2639
|
cursor = nextCursor;
|
|
2586
2640
|
}
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2641
|
+
printCommandEnvelope({
|
|
2642
|
+
output_path: outputPath,
|
|
2643
|
+
row_count: summary.row_count,
|
|
2644
|
+
net_delta_credits: summary.net_delta_credits,
|
|
2645
|
+
scope: "current_auth_context",
|
|
2646
|
+
render: {
|
|
2647
|
+
sections: [
|
|
2648
|
+
{
|
|
2649
|
+
title: "billing ledger",
|
|
2650
|
+
lines: [
|
|
2651
|
+
`Billing ledger written to ${outputPath}`,
|
|
2652
|
+
`${summary.row_count} row(s) exported for the current auth context.`,
|
|
2653
|
+
`Net ledger delta: ${summary.net_delta_credits} Deepline Credits`
|
|
2654
|
+
]
|
|
2655
|
+
}
|
|
2656
|
+
]
|
|
2657
|
+
},
|
|
2658
|
+
local: { output_path: outputPath }
|
|
2659
|
+
}, { json: options.json });
|
|
2601
2660
|
}
|
|
2602
2661
|
async function handleCheckout(options) {
|
|
2603
2662
|
const { http } = getAuthedHttpClient();
|
|
@@ -2606,20 +2665,22 @@ async function handleCheckout(options) {
|
|
|
2606
2665
|
...options.credits ? { credits: Number.parseInt(options.credits, 10) } : {},
|
|
2607
2666
|
...options.discountCode ? { discountCode: options.discountCode } : {}
|
|
2608
2667
|
});
|
|
2609
|
-
if (shouldEmitJson(options.json)) return printJson(payload);
|
|
2610
2668
|
const url = String(payload.url || payload.checkout_url || "");
|
|
2611
|
-
if (!options.noOpen && url) openInBrowser(url);
|
|
2612
|
-
|
|
2613
|
-
|
|
2669
|
+
if (!options.json && !options.noOpen && url) openInBrowser(url);
|
|
2670
|
+
printCommandEnvelope({
|
|
2671
|
+
...payload,
|
|
2672
|
+
render: { sections: [{ title: "billing checkout", lines: [url || "Checkout session created."] }] }
|
|
2673
|
+
}, { json: options.json });
|
|
2614
2674
|
}
|
|
2615
2675
|
async function handleRedeemCode(code, options) {
|
|
2616
2676
|
const { http } = getAuthedHttpClient();
|
|
2617
2677
|
const payload = await http.post("/api/v2/billing/checkout/verify", { code });
|
|
2618
|
-
if (shouldEmitJson(options.json)) return printJson(payload);
|
|
2619
2678
|
const url = String(payload.url || "");
|
|
2620
|
-
if (!options.noOpen && url) openInBrowser(url);
|
|
2621
|
-
|
|
2622
|
-
|
|
2679
|
+
if (!options.json && !options.noOpen && url) openInBrowser(url);
|
|
2680
|
+
printCommandEnvelope({
|
|
2681
|
+
...payload,
|
|
2682
|
+
render: { sections: [{ title: "billing code", lines: [url || "Code redeemed."] }] }
|
|
2683
|
+
}, { json: options.json });
|
|
2623
2684
|
}
|
|
2624
2685
|
function registerBillingCommands(program) {
|
|
2625
2686
|
const billing = program.command("billing").description("Inspect balance, usage, limits, and checkout flows.").addHelpText(
|
|
@@ -2821,6 +2882,25 @@ function isRecord2(value) {
|
|
|
2821
2882
|
function isSerializedDataset(value) {
|
|
2822
2883
|
return isRecord2(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
|
|
2823
2884
|
}
|
|
2885
|
+
function pathParts(path) {
|
|
2886
|
+
return path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
2887
|
+
}
|
|
2888
|
+
function valueAtPath(root, path) {
|
|
2889
|
+
let cursor = root;
|
|
2890
|
+
for (const part of pathParts(path)) {
|
|
2891
|
+
if (!isRecord2(cursor)) {
|
|
2892
|
+
return void 0;
|
|
2893
|
+
}
|
|
2894
|
+
cursor = cursor[part];
|
|
2895
|
+
}
|
|
2896
|
+
return cursor;
|
|
2897
|
+
}
|
|
2898
|
+
function totalRowsForDataset(result, datasetPath) {
|
|
2899
|
+
const metadata = isRecord2(result._metadata) ? result._metadata : null;
|
|
2900
|
+
const parentPath = datasetPath.split(".").slice(0, -1).join(".");
|
|
2901
|
+
const parent = parentPath ? valueAtPath({ result }, parentPath) : result;
|
|
2902
|
+
return metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count ?? (isRecord2(parent) ? parent.totalRows ?? parent.rowCount ?? parent.count : void 0) ?? result.totalRows ?? result.rowCount ?? result.count;
|
|
2903
|
+
}
|
|
2824
2904
|
function rowArray(value) {
|
|
2825
2905
|
if (!Array.isArray(value)) {
|
|
2826
2906
|
return null;
|
|
@@ -2852,11 +2932,81 @@ function inferColumns(rows) {
|
|
|
2852
2932
|
}
|
|
2853
2933
|
return columns;
|
|
2854
2934
|
}
|
|
2855
|
-
function
|
|
2935
|
+
function canonicalRowsInfoFromCandidate(input) {
|
|
2936
|
+
const candidate = input;
|
|
2937
|
+
if (isSerializedDataset(candidate.value)) {
|
|
2938
|
+
const rawRows = rowArray(candidate.value.preview) ?? [];
|
|
2939
|
+
const totalRows2 = readNumber(candidate.value.count) ?? rawRows.length;
|
|
2940
|
+
const hasExplicitColumns = Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string");
|
|
2941
|
+
const rawColumns = hasExplicitColumns ? candidate.value.columns : inferColumns(rawRows);
|
|
2942
|
+
const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
|
|
2943
|
+
rows: rawRows,
|
|
2944
|
+
columns: rawColumns
|
|
2945
|
+
});
|
|
2946
|
+
return {
|
|
2947
|
+
rows: rows2,
|
|
2948
|
+
totalRows: totalRows2,
|
|
2949
|
+
columns,
|
|
2950
|
+
columnsExplicit: hasExplicitColumns,
|
|
2951
|
+
complete: rows2.length === totalRows2,
|
|
2952
|
+
source: candidate.source,
|
|
2953
|
+
datasetId: typeof candidate.value.datasetId === "string" ? candidate.value.datasetId : null,
|
|
2954
|
+
tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2957
|
+
if (candidate.serializedOnly) {
|
|
2958
|
+
return null;
|
|
2959
|
+
}
|
|
2960
|
+
const rows = rowArray(candidate.value);
|
|
2961
|
+
if (!rows) {
|
|
2962
|
+
return null;
|
|
2963
|
+
}
|
|
2964
|
+
const totalRows = readNumber(candidate.total) ?? rows.length;
|
|
2965
|
+
const sanitized = sanitizeCsvProjectionInfo({
|
|
2966
|
+
rows,
|
|
2967
|
+
columns: inferColumns(rows)
|
|
2968
|
+
});
|
|
2969
|
+
return {
|
|
2970
|
+
rows: sanitized.rows,
|
|
2971
|
+
totalRows,
|
|
2972
|
+
columns: sanitized.columns,
|
|
2973
|
+
complete: rows.length === totalRows,
|
|
2974
|
+
source: candidate.source
|
|
2975
|
+
};
|
|
2976
|
+
}
|
|
2977
|
+
function collectDatasetCandidates(input) {
|
|
2978
|
+
if (input.depth && input.depth > 16) {
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
if (isSerializedDataset(input.value)) {
|
|
2982
|
+
input.output.push({
|
|
2983
|
+
source: input.path,
|
|
2984
|
+
value: input.value,
|
|
2985
|
+
total: input.total
|
|
2986
|
+
});
|
|
2987
|
+
return;
|
|
2988
|
+
}
|
|
2989
|
+
if (!isRecord2(input.value)) {
|
|
2990
|
+
return;
|
|
2991
|
+
}
|
|
2992
|
+
for (const [key, child] of Object.entries(input.value)) {
|
|
2993
|
+
if (key === "preview" || key === "access") {
|
|
2994
|
+
continue;
|
|
2995
|
+
}
|
|
2996
|
+
collectDatasetCandidates({
|
|
2997
|
+
value: child,
|
|
2998
|
+
path: `${input.path}.${key}`,
|
|
2999
|
+
total: totalRowsForDataset(input.value, `${input.path}.${key}`),
|
|
3000
|
+
output: input.output,
|
|
3001
|
+
depth: (input.depth ?? 0) + 1
|
|
3002
|
+
});
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
function collectCanonicalRowsInfos(statusOrResult) {
|
|
2856
3006
|
const root = isRecord2(statusOrResult) ? statusOrResult : null;
|
|
2857
3007
|
const result = isRecord2(root?.result) ? root.result : root;
|
|
2858
3008
|
if (!result) {
|
|
2859
|
-
return
|
|
3009
|
+
return [];
|
|
2860
3010
|
}
|
|
2861
3011
|
const metadata = isRecord2(result._metadata) ? result._metadata : null;
|
|
2862
3012
|
const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
|
|
@@ -2876,41 +3026,57 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2876
3026
|
{ source: "result.output.results", value: result.output.results, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count }
|
|
2877
3027
|
);
|
|
2878
3028
|
}
|
|
3029
|
+
collectDatasetCandidates({
|
|
3030
|
+
value: result,
|
|
3031
|
+
path: "result",
|
|
3032
|
+
total: totalFromMetadata,
|
|
3033
|
+
output: candidates
|
|
3034
|
+
});
|
|
3035
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3036
|
+
const infos = [];
|
|
2879
3037
|
for (const candidate of candidates) {
|
|
2880
|
-
if (
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
});
|
|
2888
|
-
return {
|
|
2889
|
-
rows: rows2,
|
|
2890
|
-
totalRows: totalRows2,
|
|
2891
|
-
columns,
|
|
2892
|
-
complete: rows2.length === totalRows2,
|
|
2893
|
-
source: candidate.source
|
|
2894
|
-
};
|
|
3038
|
+
if (seen.has(candidate.source)) {
|
|
3039
|
+
continue;
|
|
3040
|
+
}
|
|
3041
|
+
seen.add(candidate.source);
|
|
3042
|
+
const info = canonicalRowsInfoFromCandidate(candidate);
|
|
3043
|
+
if (info) {
|
|
3044
|
+
infos.push(info);
|
|
2895
3045
|
}
|
|
2896
|
-
|
|
2897
|
-
|
|
3046
|
+
}
|
|
3047
|
+
return infos;
|
|
3048
|
+
}
|
|
3049
|
+
function collectSerializedDatasetRowsInfos(statusOrResult) {
|
|
3050
|
+
const root = isRecord2(statusOrResult) ? statusOrResult : null;
|
|
3051
|
+
const result = isRecord2(root?.result) ? root.result : root;
|
|
3052
|
+
if (!result) {
|
|
3053
|
+
return [];
|
|
3054
|
+
}
|
|
3055
|
+
const candidates = [];
|
|
3056
|
+
collectDatasetCandidates({
|
|
3057
|
+
value: result,
|
|
3058
|
+
path: "result",
|
|
3059
|
+
output: candidates
|
|
3060
|
+
});
|
|
3061
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3062
|
+
const infos = [];
|
|
3063
|
+
for (const candidate of candidates) {
|
|
3064
|
+
if (seen.has(candidate.source)) {
|
|
2898
3065
|
continue;
|
|
2899
3066
|
}
|
|
2900
|
-
|
|
2901
|
-
const
|
|
2902
|
-
|
|
2903
|
-
|
|
3067
|
+
seen.add(candidate.source);
|
|
3068
|
+
const info = canonicalRowsInfoFromCandidate({
|
|
3069
|
+
...candidate,
|
|
3070
|
+
serializedOnly: true
|
|
2904
3071
|
});
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
columns: sanitized.columns,
|
|
2909
|
-
complete: rows.length === totalRows,
|
|
2910
|
-
source: candidate.source
|
|
2911
|
-
};
|
|
3072
|
+
if (info) {
|
|
3073
|
+
infos.push(info);
|
|
3074
|
+
}
|
|
2912
3075
|
}
|
|
2913
|
-
return
|
|
3076
|
+
return infos;
|
|
3077
|
+
}
|
|
3078
|
+
function extractCanonicalRowsInfo(statusOrResult) {
|
|
3079
|
+
return collectCanonicalRowsInfos(statusOrResult)[0] ?? null;
|
|
2914
3080
|
}
|
|
2915
3081
|
function percentText(numerator, denominator) {
|
|
2916
3082
|
return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
|
|
@@ -3237,7 +3403,7 @@ function formatCell(value) {
|
|
|
3237
3403
|
const text = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
3238
3404
|
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
|
|
3239
3405
|
}
|
|
3240
|
-
function
|
|
3406
|
+
function tableLines(result) {
|
|
3241
3407
|
const rows = result.rows.filter(
|
|
3242
3408
|
(row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)
|
|
3243
3409
|
);
|
|
@@ -3245,17 +3411,17 @@ function printTable(result) {
|
|
|
3245
3411
|
const businessColumns = responseColumns.filter((column) => !column.startsWith("_"));
|
|
3246
3412
|
const columns = businessColumns.length > 0 ? businessColumns : responseColumns;
|
|
3247
3413
|
const hiddenColumns = responseColumns.filter((column) => !columns.includes(column));
|
|
3248
|
-
|
|
3414
|
+
const lines = [
|
|
3249
3415
|
`${result.command} returned ${result.row_count_returned} row(s)` + (result.truncated ? " (truncated)" : "")
|
|
3250
|
-
|
|
3416
|
+
];
|
|
3251
3417
|
if (hiddenColumns.length > 0) {
|
|
3252
|
-
|
|
3418
|
+
lines.push(
|
|
3253
3419
|
`Showing ${columns.length}/${responseColumns.length} columns; hidden metadata: ${hiddenColumns.join(", ")}`
|
|
3254
3420
|
);
|
|
3255
|
-
|
|
3421
|
+
lines.push("Use --json or select metadata columns explicitly when you need run ids/errors/stages.");
|
|
3256
3422
|
}
|
|
3257
3423
|
if (rows.length === 0) {
|
|
3258
|
-
return;
|
|
3424
|
+
return lines;
|
|
3259
3425
|
}
|
|
3260
3426
|
const widths = columns.map(
|
|
3261
3427
|
(column) => Math.min(
|
|
@@ -3266,13 +3432,14 @@ function printTable(result) {
|
|
|
3266
3432
|
)
|
|
3267
3433
|
)
|
|
3268
3434
|
);
|
|
3269
|
-
|
|
3270
|
-
|
|
3435
|
+
lines.push(columns.map((column, index) => column.padEnd(widths[index])).join(" "));
|
|
3436
|
+
lines.push(widths.map((width) => "-".repeat(width)).join(" "));
|
|
3271
3437
|
for (const row of rows) {
|
|
3272
|
-
|
|
3438
|
+
lines.push(
|
|
3273
3439
|
columns.map((column, index) => formatCell(row[column]).padEnd(widths[index])).join(" ")
|
|
3274
3440
|
);
|
|
3275
3441
|
}
|
|
3442
|
+
return lines;
|
|
3276
3443
|
}
|
|
3277
3444
|
async function handleDbQuery(args) {
|
|
3278
3445
|
const sqlIndex = args.indexOf("--sql");
|
|
@@ -3286,18 +3453,18 @@ async function handleDbQuery(args) {
|
|
|
3286
3453
|
const jsonOutput = argsWantJson(args);
|
|
3287
3454
|
const client = new DeeplineClient();
|
|
3288
3455
|
const result = await client.queryCustomerDb({ sql, maxRows });
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
}
|
|
3300
|
-
);
|
|
3456
|
+
const toolCommand = `deepline tools execute query_customer_db --payload ${JSON.stringify({
|
|
3457
|
+
sql,
|
|
3458
|
+
...maxRows ? { max_rows: maxRows } : {}
|
|
3459
|
+
})} --json`;
|
|
3460
|
+
printCommandEnvelope({
|
|
3461
|
+
...result,
|
|
3462
|
+
next: { toolEquivalent: toolCommand },
|
|
3463
|
+
render: {
|
|
3464
|
+
sections: [{ title: "customer db query", lines: tableLines(result) }],
|
|
3465
|
+
actions: [{ label: "Tool equivalent", command: toolCommand }]
|
|
3466
|
+
}
|
|
3467
|
+
}, { json: jsonOutput });
|
|
3301
3468
|
return 0;
|
|
3302
3469
|
}
|
|
3303
3470
|
function registerDbCommands(program) {
|
|
@@ -3345,11 +3512,12 @@ async function handleFeedback(text, options) {
|
|
|
3345
3512
|
...options.command ? { command: options.command } : {},
|
|
3346
3513
|
...options.payload ? { payload: options.payload } : {}
|
|
3347
3514
|
});
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3515
|
+
printCommandEnvelope({
|
|
3516
|
+
...response,
|
|
3517
|
+
render: {
|
|
3518
|
+
sections: [{ title: "feedback", lines: ["Feedback submitted. Thank you."] }]
|
|
3519
|
+
}
|
|
3520
|
+
}, { json: options.json });
|
|
3353
3521
|
}
|
|
3354
3522
|
function registerFeedbackCommands(program) {
|
|
3355
3523
|
const feedback = program.command("feedback").description("Submit CLI feedback to Deepline.").addHelpText(
|
|
@@ -3381,37 +3549,37 @@ Examples:
|
|
|
3381
3549
|
async function fetchOrganizations(http, apiKey) {
|
|
3382
3550
|
return http.post("/api/v2/auth/cli/organizations", { api_key: apiKey });
|
|
3383
3551
|
}
|
|
3384
|
-
function
|
|
3385
|
-
|
|
3552
|
+
function orgListLines(orgs) {
|
|
3553
|
+
return orgs.map((org, index) => {
|
|
3386
3554
|
const current = org.is_current ? " (current)" : "";
|
|
3387
3555
|
const role = org.role ? ` [${org.role}]` : "";
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
}
|
|
3556
|
+
return `${index + 1}. ${org.name}${role}${current}`;
|
|
3557
|
+
});
|
|
3391
3558
|
}
|
|
3392
3559
|
async function handleOrgList(options) {
|
|
3393
3560
|
const config = resolveConfig();
|
|
3394
3561
|
const http = new HttpClient(config);
|
|
3395
3562
|
const payload = await fetchOrganizations(http, config.apiKey);
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3563
|
+
printCommandEnvelope({
|
|
3564
|
+
...payload,
|
|
3565
|
+
render: {
|
|
3566
|
+
sections: [{ title: "Your organizations:", lines: orgListLines(payload.organizations) }]
|
|
3567
|
+
}
|
|
3568
|
+
}, { json: options.json });
|
|
3402
3569
|
}
|
|
3403
3570
|
async function handleOrgSwitch(selection, options) {
|
|
3404
3571
|
const config = resolveConfig();
|
|
3405
3572
|
const http = new HttpClient(config);
|
|
3406
3573
|
const payload = await fetchOrganizations(http, config.apiKey);
|
|
3407
3574
|
if (!selection && !options.orgId) {
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3575
|
+
printCommandEnvelope({
|
|
3576
|
+
...payload,
|
|
3577
|
+
next: { switch: "deepline org switch <number>" },
|
|
3578
|
+
render: {
|
|
3579
|
+
sections: [{ title: "Your organizations:", lines: orgListLines(payload.organizations) }],
|
|
3580
|
+
actions: [{ label: "Run", command: "deepline org switch <number>" }]
|
|
3581
|
+
}
|
|
3582
|
+
}, { json: options.json });
|
|
3415
3583
|
return;
|
|
3416
3584
|
}
|
|
3417
3585
|
let target = payload.organizations.find((org) => org.org_id === options.orgId);
|
|
@@ -3427,12 +3595,12 @@ async function handleOrgSwitch(selection, options) {
|
|
|
3427
3595
|
throw new Error("Could not resolve the selected organization.");
|
|
3428
3596
|
}
|
|
3429
3597
|
if (target.is_current) {
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3598
|
+
printCommandEnvelope({
|
|
3599
|
+
ok: true,
|
|
3600
|
+
unchanged: true,
|
|
3601
|
+
organization: target,
|
|
3602
|
+
render: { sections: [{ title: "org switch", lines: [`Already on ${target.name}.`] }] }
|
|
3603
|
+
}, { json: options.json });
|
|
3436
3604
|
return;
|
|
3437
3605
|
}
|
|
3438
3606
|
const switched = await http.post(
|
|
@@ -3444,14 +3612,24 @@ async function handleOrgSwitch(selection, options) {
|
|
|
3444
3612
|
DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
|
|
3445
3613
|
DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
|
|
3446
3614
|
});
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3615
|
+
const { api_key: _apiKey, ...publicSwitched } = switched;
|
|
3616
|
+
printCommandEnvelope({
|
|
3617
|
+
ok: true,
|
|
3618
|
+
host_env_path: hostEnvFilePath(config.baseUrl),
|
|
3619
|
+
...publicSwitched,
|
|
3620
|
+
api_key_saved: true,
|
|
3621
|
+
render: {
|
|
3622
|
+
sections: [
|
|
3623
|
+
{
|
|
3624
|
+
title: "org switch",
|
|
3625
|
+
lines: [
|
|
3626
|
+
`Switched to ${switched.org_name}.`,
|
|
3627
|
+
`Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
|
|
3628
|
+
]
|
|
3629
|
+
}
|
|
3630
|
+
]
|
|
3631
|
+
}
|
|
3632
|
+
}, { json: options.json });
|
|
3455
3633
|
}
|
|
3456
3634
|
function registerOrgCommands(program) {
|
|
3457
3635
|
const org = program.command("org").description("List and switch organizations.").addHelpText(
|
|
@@ -5134,6 +5312,9 @@ function traceCliSync(phase, fields, run) {
|
|
|
5134
5312
|
throw error;
|
|
5135
5313
|
}
|
|
5136
5314
|
}
|
|
5315
|
+
function sleep4(ms) {
|
|
5316
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
5317
|
+
}
|
|
5137
5318
|
function parseReferencedPlayTarget(target) {
|
|
5138
5319
|
const trimmed = target.trim();
|
|
5139
5320
|
const slashIndex = trimmed.indexOf("/");
|
|
@@ -5358,15 +5539,6 @@ function fileInputBindingsFromStaticPipeline(staticPipeline) {
|
|
|
5358
5539
|
);
|
|
5359
5540
|
return inputField ? [{ inputPath: inputField }] : [];
|
|
5360
5541
|
}
|
|
5361
|
-
function applyCsvShortcutInput(input) {
|
|
5362
|
-
const csvValue = getDottedInputValue(input.runtimeInput, "csv");
|
|
5363
|
-
if (csvValue == null || csvValue === "") return;
|
|
5364
|
-
const candidate = input.bindings.find((binding) => binding.inputPath !== "csv")?.inputPath ?? input.fallbackInputPath ?? null;
|
|
5365
|
-
if (!candidate || candidate === "csv") return;
|
|
5366
|
-
const existing = getDottedInputValue(input.runtimeInput, candidate);
|
|
5367
|
-
if (existing != null && existing !== "") return;
|
|
5368
|
-
setDottedInputValue(input.runtimeInput, candidate, csvValue);
|
|
5369
|
-
}
|
|
5370
5542
|
function isLocalFilePathValue(value) {
|
|
5371
5543
|
if (typeof value !== "string" || !value.trim()) return false;
|
|
5372
5544
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
@@ -6034,25 +6206,38 @@ function formatReturnValue(result) {
|
|
|
6034
6206
|
}
|
|
6035
6207
|
return lines;
|
|
6036
6208
|
}
|
|
6037
|
-
function
|
|
6038
|
-
|
|
6039
|
-
|
|
6209
|
+
function isDatasetHandle(value) {
|
|
6210
|
+
return Boolean(
|
|
6211
|
+
value && typeof value === "object" && !Array.isArray(value) && value.kind === "dataset"
|
|
6212
|
+
);
|
|
6213
|
+
}
|
|
6214
|
+
function collectDatasetHandleLines(value, path = "result") {
|
|
6215
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6216
|
+
return [];
|
|
6040
6217
|
}
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
}
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
}
|
|
6218
|
+
if (isDatasetHandle(value)) {
|
|
6219
|
+
const record = value;
|
|
6220
|
+
const count = typeof record.count === "number" ? record.count : typeof record.rowCount === "number" ? record.rowCount : null;
|
|
6221
|
+
const preview = Array.isArray(record.preview) ? record.preview : [];
|
|
6222
|
+
const lines2 = [
|
|
6223
|
+
` dataset ${typeof record.path === "string" ? record.path : path}: rows=${count === null ? "-" : formatInteger(count)} preview=${formatInteger(preview.length)}`
|
|
6224
|
+
];
|
|
6225
|
+
if (typeof record.queryDatasetCommand === "string") {
|
|
6226
|
+
lines2.push(` query dataset: ${record.queryDatasetCommand}`);
|
|
6227
|
+
}
|
|
6228
|
+
if (typeof record.slowExportAsCsvCommand === "string") {
|
|
6229
|
+
lines2.push(` export CSV: ${record.slowExportAsCsvCommand}`);
|
|
6230
|
+
}
|
|
6231
|
+
return lines2;
|
|
6232
|
+
}
|
|
6233
|
+
const lines = [];
|
|
6234
|
+
for (const [key, child] of Object.entries(value)) {
|
|
6235
|
+
if (key === "preview" || key === "access") {
|
|
6236
|
+
continue;
|
|
6237
|
+
}
|
|
6238
|
+
lines.push(...collectDatasetHandleLines(child, `${path}.${key}`));
|
|
6239
|
+
}
|
|
6240
|
+
return lines;
|
|
6056
6241
|
}
|
|
6057
6242
|
function buildRunWarnings(status, rowsInfo) {
|
|
6058
6243
|
if (status.status === "completed" && rowsInfo?.totalRows === 0) {
|
|
@@ -6065,19 +6250,12 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
6065
6250
|
}
|
|
6066
6251
|
return [];
|
|
6067
6252
|
}
|
|
6068
|
-
function buildRunNextCommands(runId
|
|
6069
|
-
|
|
6253
|
+
function buildRunNextCommands(runId) {
|
|
6254
|
+
return {
|
|
6070
6255
|
get: `deepline runs get ${runId} --json`,
|
|
6071
6256
|
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
6072
6257
|
logs: `deepline runs logs ${runId} --out run.log --json`
|
|
6073
6258
|
};
|
|
6074
|
-
if (!rowsInfo || rowsInfo.complete) {
|
|
6075
|
-
commands.exportCsv = buildRunExportCommand(runId);
|
|
6076
|
-
}
|
|
6077
|
-
return commands;
|
|
6078
|
-
}
|
|
6079
|
-
function buildRunExportCommand(runId) {
|
|
6080
|
-
return `deepline runs export ${runId} --out output.csv`;
|
|
6081
6259
|
}
|
|
6082
6260
|
var RUN_LOG_PREVIEW_LIMIT = 20;
|
|
6083
6261
|
function getRecordField(value, key) {
|
|
@@ -6132,31 +6310,6 @@ function normalizeProgressForEnvelope(status, rowsInfo) {
|
|
|
6132
6310
|
wait: status.wait ?? null
|
|
6133
6311
|
};
|
|
6134
6312
|
}
|
|
6135
|
-
function normalizeOutputsForEnvelope(rowsInfo, runId, exportedPath) {
|
|
6136
|
-
if (!rowsInfo) {
|
|
6137
|
-
return exportedPath ? [{ name: "output", kind: "file", path: exportedPath }] : [];
|
|
6138
|
-
}
|
|
6139
|
-
const isPartial = !rowsInfo.complete;
|
|
6140
|
-
return [
|
|
6141
|
-
{
|
|
6142
|
-
name: "rows",
|
|
6143
|
-
kind: "dataset",
|
|
6144
|
-
rowCount: rowsInfo.totalRows,
|
|
6145
|
-
columns: rowsInfo.columns,
|
|
6146
|
-
preview: rowsInfo.rows.slice(0, 5),
|
|
6147
|
-
previewRowCount: Math.min(rowsInfo.rows.length, 5),
|
|
6148
|
-
previewLimit: 5,
|
|
6149
|
-
...isPartial ? {
|
|
6150
|
-
isPartial: true,
|
|
6151
|
-
previewCount: rowsInfo.rows.length,
|
|
6152
|
-
totalCount: rowsInfo.totalRows
|
|
6153
|
-
} : { isPartial: false },
|
|
6154
|
-
complete: rowsInfo.complete,
|
|
6155
|
-
source: rowsInfo.source,
|
|
6156
|
-
...exportedPath ? { csv_path: exportedPath } : {}
|
|
6157
|
-
}
|
|
6158
|
-
];
|
|
6159
|
-
}
|
|
6160
6313
|
function normalizeStepsForEnvelope(status) {
|
|
6161
6314
|
const directSteps = getRecordField(status, "steps");
|
|
6162
6315
|
if (Array.isArray(directSteps)) {
|
|
@@ -6218,7 +6371,7 @@ function stripProviderSpendFromBilling(value) {
|
|
|
6218
6371
|
}
|
|
6219
6372
|
return next;
|
|
6220
6373
|
}
|
|
6221
|
-
function compactPlayStatus(status
|
|
6374
|
+
function compactPlayStatus(status) {
|
|
6222
6375
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
6223
6376
|
const result = status && typeof status === "object" ? status.result : null;
|
|
6224
6377
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
@@ -6241,23 +6394,16 @@ function compactPlayStatus(status, options) {
|
|
|
6241
6394
|
status: status.status,
|
|
6242
6395
|
run: normalizeRunStatusForEnvelope(status),
|
|
6243
6396
|
progress: normalizeProgressForEnvelope(status, rowsInfo),
|
|
6244
|
-
outputs: normalizeOutputsForEnvelope(
|
|
6245
|
-
rowsInfo,
|
|
6246
|
-
status.runId,
|
|
6247
|
-
options?.exportedPath
|
|
6248
|
-
),
|
|
6249
6397
|
steps: normalizeStepsForEnvelope(status),
|
|
6250
6398
|
errors: normalizeErrorsForEnvelope(status, error),
|
|
6251
6399
|
logs: normalizeLogsForEnvelope(status),
|
|
6252
6400
|
...error ? { error } : {},
|
|
6253
6401
|
...warnings.length > 0 ? { warnings } : {},
|
|
6254
|
-
output: buildOutputSummary(rowsInfo, status.runId, options?.exportedPath) ?? result ?? null,
|
|
6255
6402
|
...result !== void 0 ? { result } : {},
|
|
6256
6403
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
6257
6404
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
6258
|
-
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
|
|
6259
6405
|
...billing ? { billing } : {},
|
|
6260
|
-
next: buildRunNextCommands(status.runId
|
|
6406
|
+
next: buildRunNextCommands(status.runId)
|
|
6261
6407
|
};
|
|
6262
6408
|
}
|
|
6263
6409
|
function enrichPlayStatusWithDatasetStats(status) {
|
|
@@ -6295,12 +6441,20 @@ function formatDatasetStatsLines(datasetStats) {
|
|
|
6295
6441
|
}
|
|
6296
6442
|
function writePlayResult(status, jsonOutput, options) {
|
|
6297
6443
|
if (jsonOutput) {
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6444
|
+
const payload2 = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status);
|
|
6445
|
+
printCommandEnvelope({
|
|
6446
|
+
...payload2,
|
|
6447
|
+
render: {
|
|
6448
|
+
sections: [
|
|
6449
|
+
{
|
|
6450
|
+
title: "run result",
|
|
6451
|
+
lines: [
|
|
6452
|
+
`${status.status ?? "running"} ${status.runId ?? "unknown"}`
|
|
6453
|
+
]
|
|
6454
|
+
}
|
|
6455
|
+
]
|
|
6456
|
+
}
|
|
6457
|
+
}, { json: true });
|
|
6304
6458
|
return;
|
|
6305
6459
|
}
|
|
6306
6460
|
const result = status.result;
|
|
@@ -6317,23 +6471,6 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6317
6471
|
rowsInfo.columns,
|
|
6318
6472
|
extractDatasetExecutionStats(status)
|
|
6319
6473
|
) : null;
|
|
6320
|
-
const outputSummary = buildOutputSummary(
|
|
6321
|
-
rowsInfo,
|
|
6322
|
-
runId,
|
|
6323
|
-
options?.exportedPath
|
|
6324
|
-
);
|
|
6325
|
-
if (outputSummary) {
|
|
6326
|
-
const columns = Array.isArray(outputSummary.columns) ? outputSummary.columns.length : 0;
|
|
6327
|
-
const path = typeof outputSummary.csv_path === "string" ? ` file=${outputSummary.csv_path}` : "";
|
|
6328
|
-
lines.push(
|
|
6329
|
-
` output: rows=${formatInteger(outputSummary.rowCount)} columns=${formatInteger(columns)}${path}`
|
|
6330
|
-
);
|
|
6331
|
-
if (outputSummary.isPartial === true) {
|
|
6332
|
-
lines.push(
|
|
6333
|
-
` partial output: showing ${formatInteger(outputSummary.previewCount)} preview row(s) of ${formatInteger(outputSummary.totalCount)}`
|
|
6334
|
-
);
|
|
6335
|
-
}
|
|
6336
|
-
}
|
|
6337
6474
|
for (const warning of warnings) {
|
|
6338
6475
|
lines.push(` warning: ${warning}`);
|
|
6339
6476
|
}
|
|
@@ -6345,29 +6482,213 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6345
6482
|
const renderedServerView = renderServerResultView(status.resultView);
|
|
6346
6483
|
if (result) {
|
|
6347
6484
|
lines.push(...formatReturnValue(result));
|
|
6485
|
+
lines.push(...collectDatasetHandleLines(result));
|
|
6348
6486
|
}
|
|
6349
6487
|
if (renderedServerView.lines.length > 0) {
|
|
6350
6488
|
lines.push(...renderedServerView.lines);
|
|
6351
6489
|
}
|
|
6352
6490
|
lines.push(...renderedServerView.actions);
|
|
6353
|
-
|
|
6491
|
+
const payload = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status);
|
|
6492
|
+
printCommandEnvelope({
|
|
6493
|
+
...payload,
|
|
6494
|
+
render: {
|
|
6495
|
+
sections: [{ title: "run result", lines }]
|
|
6496
|
+
}
|
|
6497
|
+
}, { json: jsonOutput, text: `${lines.join("\n")}
|
|
6498
|
+
` });
|
|
6499
|
+
}
|
|
6500
|
+
var RUN_EXPORT_PAGE_SIZE = 5e3;
|
|
6501
|
+
function shellSingleQuote(value) {
|
|
6502
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
6503
|
+
}
|
|
6504
|
+
function runExportRetryCommand(runId, outPath, datasetPath) {
|
|
6505
|
+
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(resolve8(outPath))}`;
|
|
6506
|
+
}
|
|
6507
|
+
function extractRunPlayName(status) {
|
|
6508
|
+
const run = status.run;
|
|
6509
|
+
const candidates = [
|
|
6510
|
+
status.playName,
|
|
6511
|
+
status.name,
|
|
6512
|
+
getRecordField(run, "playName"),
|
|
6513
|
+
getRecordField(run, "name")
|
|
6514
|
+
];
|
|
6515
|
+
for (const candidate of candidates) {
|
|
6516
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
6517
|
+
return candidate.trim();
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6520
|
+
return null;
|
|
6521
|
+
}
|
|
6522
|
+
function exportableSheetRow(row) {
|
|
6523
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
6524
|
+
return null;
|
|
6525
|
+
}
|
|
6526
|
+
const record = row;
|
|
6527
|
+
const data = record.data;
|
|
6528
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
6529
|
+
return data;
|
|
6530
|
+
}
|
|
6531
|
+
const fallback = { ...record };
|
|
6532
|
+
for (const key of [
|
|
6533
|
+
"key",
|
|
6534
|
+
"status",
|
|
6535
|
+
"cellMeta",
|
|
6536
|
+
"inputIndex",
|
|
6537
|
+
"runId",
|
|
6538
|
+
"error",
|
|
6539
|
+
"stage",
|
|
6540
|
+
"provider",
|
|
6541
|
+
"seq",
|
|
6542
|
+
"createdAt",
|
|
6543
|
+
"updatedAt"
|
|
6544
|
+
]) {
|
|
6545
|
+
delete fallback[key];
|
|
6546
|
+
}
|
|
6547
|
+
return fallback;
|
|
6548
|
+
}
|
|
6549
|
+
function mergeExportColumns(preferredColumns, rows) {
|
|
6550
|
+
const columns = [];
|
|
6551
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6552
|
+
for (const column of preferredColumns) {
|
|
6553
|
+
if (!column || seen.has(column)) {
|
|
6554
|
+
continue;
|
|
6555
|
+
}
|
|
6556
|
+
seen.add(column);
|
|
6557
|
+
columns.push(column);
|
|
6558
|
+
}
|
|
6559
|
+
for (const row of rows) {
|
|
6560
|
+
for (const column of Object.keys(row)) {
|
|
6561
|
+
if (seen.has(column)) {
|
|
6562
|
+
continue;
|
|
6563
|
+
}
|
|
6564
|
+
seen.add(column);
|
|
6565
|
+
columns.push(column);
|
|
6566
|
+
}
|
|
6567
|
+
}
|
|
6568
|
+
return columns;
|
|
6354
6569
|
}
|
|
6355
|
-
function
|
|
6570
|
+
async function fetchBackingDatasetRows(input) {
|
|
6571
|
+
const playName = extractRunPlayName(input.status);
|
|
6572
|
+
const tableNamespace = input.rowsInfo.tableNamespace?.trim();
|
|
6573
|
+
if (!playName || !tableNamespace) {
|
|
6574
|
+
return null;
|
|
6575
|
+
}
|
|
6576
|
+
const sheetRows = [];
|
|
6577
|
+
let offset = 0;
|
|
6578
|
+
let expectedTotal = input.rowsInfo.totalRows;
|
|
6579
|
+
while (true) {
|
|
6580
|
+
const page = await input.client.runs.exportDatasetRows({
|
|
6581
|
+
playName,
|
|
6582
|
+
tableNamespace,
|
|
6583
|
+
runId: input.status.runId,
|
|
6584
|
+
limit: RUN_EXPORT_PAGE_SIZE,
|
|
6585
|
+
offset
|
|
6586
|
+
});
|
|
6587
|
+
sheetRows.push(...page.rows);
|
|
6588
|
+
const summaryTotal = page.summary?.stats?.total;
|
|
6589
|
+
if (typeof summaryTotal === "number" && Number.isFinite(summaryTotal)) {
|
|
6590
|
+
expectedTotal = Math.max(expectedTotal, Math.trunc(summaryTotal));
|
|
6591
|
+
}
|
|
6592
|
+
if (page.rows.length < RUN_EXPORT_PAGE_SIZE || sheetRows.length >= expectedTotal) {
|
|
6593
|
+
break;
|
|
6594
|
+
}
|
|
6595
|
+
offset += page.rows.length;
|
|
6596
|
+
}
|
|
6597
|
+
const rows = sheetRows.map(exportableSheetRow).filter((row) => Boolean(row));
|
|
6598
|
+
if (rows.length < input.rowsInfo.totalRows) {
|
|
6599
|
+
return null;
|
|
6600
|
+
}
|
|
6601
|
+
const columns = mergeExportColumns(
|
|
6602
|
+
input.rowsInfo.columnsExplicit ? input.rowsInfo.columns : [],
|
|
6603
|
+
rows
|
|
6604
|
+
);
|
|
6605
|
+
return {
|
|
6606
|
+
...input.rowsInfo,
|
|
6607
|
+
rows,
|
|
6608
|
+
columns,
|
|
6609
|
+
totalRows: rows.length,
|
|
6610
|
+
complete: true,
|
|
6611
|
+
source: `${input.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
|
|
6612
|
+
};
|
|
6613
|
+
}
|
|
6614
|
+
async function exportPlayStatusRows(client, status, outPath, options = {}) {
|
|
6356
6615
|
if (!outPath) {
|
|
6357
6616
|
return null;
|
|
6358
6617
|
}
|
|
6359
|
-
const
|
|
6618
|
+
const availableRows = collectSerializedDatasetRowsInfos(status);
|
|
6619
|
+
let rowsInfo = options.datasetPath ? availableRows.find((info) => info.source === options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
|
|
6620
|
+
if (!rowsInfo && options.datasetPath) {
|
|
6621
|
+
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
6622
|
+
throw new DeeplineError(
|
|
6623
|
+
`Run ${status.runId} did not return a dataset at ${options.datasetPath}.` + (available.length > 0 ? ` Available datasets: ${available.join(", ")}.` : ""),
|
|
6624
|
+
void 0,
|
|
6625
|
+
"RUN_EXPORT_DATASET_NOT_FOUND",
|
|
6626
|
+
{ runId: status.runId, dataset: options.datasetPath, available }
|
|
6627
|
+
);
|
|
6628
|
+
}
|
|
6629
|
+
if (!options.datasetPath && availableRows.length > 1) {
|
|
6630
|
+
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
6631
|
+
throw new DeeplineError(
|
|
6632
|
+
`Run ${status.runId} returned multiple datasets. Choose one with --dataset <path>: ${available.join(", ")}.`,
|
|
6633
|
+
void 0,
|
|
6634
|
+
"RUN_EXPORT_DATASET_REQUIRED",
|
|
6635
|
+
{ runId: status.runId, available }
|
|
6636
|
+
);
|
|
6637
|
+
}
|
|
6360
6638
|
if (!rowsInfo) {
|
|
6361
6639
|
throw new DeeplineError(
|
|
6362
6640
|
`Run ${status.runId} did not expose a row-shaped final output to export.`
|
|
6363
6641
|
);
|
|
6364
6642
|
}
|
|
6643
|
+
const attempts = Math.max(1, Math.trunc(options.attempts ?? 1));
|
|
6644
|
+
const retryDelayMs = Math.max(0, Math.trunc(options.retryDelayMs ?? 0));
|
|
6645
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
6646
|
+
let fetchedRowsInfo = null;
|
|
6647
|
+
try {
|
|
6648
|
+
fetchedRowsInfo = await fetchBackingDatasetRows({
|
|
6649
|
+
client,
|
|
6650
|
+
status,
|
|
6651
|
+
rowsInfo
|
|
6652
|
+
});
|
|
6653
|
+
} catch (error) {
|
|
6654
|
+
if (!rowsInfo.complete) {
|
|
6655
|
+
throw error;
|
|
6656
|
+
}
|
|
6657
|
+
}
|
|
6658
|
+
if (fetchedRowsInfo?.complete) {
|
|
6659
|
+
return {
|
|
6660
|
+
path: writeCanonicalRowsCsv(fetchedRowsInfo, outPath),
|
|
6661
|
+
rowsInfo: fetchedRowsInfo
|
|
6662
|
+
};
|
|
6663
|
+
}
|
|
6664
|
+
if (rowsInfo.complete) {
|
|
6665
|
+
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6666
|
+
}
|
|
6667
|
+
if (attempt < attempts && retryDelayMs > 0) {
|
|
6668
|
+
await sleep4(retryDelayMs);
|
|
6669
|
+
}
|
|
6670
|
+
}
|
|
6365
6671
|
if (!rowsInfo.complete) {
|
|
6672
|
+
const retryCommand = runExportRetryCommand(
|
|
6673
|
+
status.runId,
|
|
6674
|
+
outPath,
|
|
6675
|
+
options.datasetPath ?? rowsInfo.source
|
|
6676
|
+
);
|
|
6366
6677
|
throw new DeeplineError(
|
|
6367
|
-
`Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows};
|
|
6678
|
+
`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}`,
|
|
6679
|
+
void 0,
|
|
6680
|
+
"RUN_EXPORT_NOT_READY",
|
|
6681
|
+
{
|
|
6682
|
+
runId: status.runId,
|
|
6683
|
+
previewRowCount: rowsInfo.rows.length,
|
|
6684
|
+
totalRows: rowsInfo.totalRows,
|
|
6685
|
+
tableNamespace: rowsInfo.tableNamespace ?? null,
|
|
6686
|
+
dataset: options.datasetPath ?? rowsInfo.source,
|
|
6687
|
+
retry_command: retryCommand
|
|
6688
|
+
}
|
|
6368
6689
|
);
|
|
6369
6690
|
}
|
|
6370
|
-
return writeCanonicalRowsCsv(rowsInfo, outPath);
|
|
6691
|
+
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6371
6692
|
}
|
|
6372
6693
|
function renderServerResultView(value) {
|
|
6373
6694
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -6441,8 +6762,10 @@ function writeStartedPlayRun(input) {
|
|
|
6441
6762
|
dashboardUrl: input.dashboardUrl
|
|
6442
6763
|
};
|
|
6443
6764
|
if (input.jsonOutput) {
|
|
6444
|
-
|
|
6445
|
-
|
|
6765
|
+
printCommandEnvelope({
|
|
6766
|
+
...payload,
|
|
6767
|
+
render: { sections: [{ title: "play run", lines: [`Started ${input.playName}`, `run id: ${input.runId}`] }] }
|
|
6768
|
+
}, { json: true });
|
|
6446
6769
|
return;
|
|
6447
6770
|
}
|
|
6448
6771
|
const lines = [
|
|
@@ -6461,10 +6784,14 @@ function writeStartedPlayRun(input) {
|
|
|
6461
6784
|
input.progress.writeLine(output, process.stdout);
|
|
6462
6785
|
return;
|
|
6463
6786
|
}
|
|
6464
|
-
|
|
6787
|
+
printCommandEnvelope({
|
|
6788
|
+
...payload,
|
|
6789
|
+
render: { sections: [{ title: "play run", lines }] }
|
|
6790
|
+
}, { json: false, text: `${output}
|
|
6791
|
+
` });
|
|
6465
6792
|
}
|
|
6466
6793
|
function parsePlayRunOptions(args) {
|
|
6467
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--
|
|
6794
|
+
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.";
|
|
6468
6795
|
let filePath = null;
|
|
6469
6796
|
let playName = null;
|
|
6470
6797
|
let input = null;
|
|
@@ -6475,7 +6802,6 @@ function parsePlayRunOptions(args) {
|
|
|
6475
6802
|
const emitLogs = !jsonOutput || args.includes("--logs");
|
|
6476
6803
|
const force = args.includes("--force");
|
|
6477
6804
|
const noOpen = args.includes("--no-open");
|
|
6478
|
-
let outPath = null;
|
|
6479
6805
|
let waitTimeoutMs = null;
|
|
6480
6806
|
for (let index = 0; index < args.length; index += 1) {
|
|
6481
6807
|
const arg = args[index];
|
|
@@ -6503,9 +6829,10 @@ function parsePlayRunOptions(args) {
|
|
|
6503
6829
|
revisionSelector = "latest";
|
|
6504
6830
|
continue;
|
|
6505
6831
|
}
|
|
6506
|
-
if (arg === "--out"
|
|
6507
|
-
|
|
6508
|
-
|
|
6832
|
+
if (arg === "--out" || arg.startsWith("--out=")) {
|
|
6833
|
+
throw new Error(
|
|
6834
|
+
"--out is not a plays run flag. Run the play first, then export rows with: deepline runs export <run-id> --out output.csv"
|
|
6835
|
+
);
|
|
6509
6836
|
}
|
|
6510
6837
|
if (arg === "--poll-interval-ms" || arg === "--interval-ms") {
|
|
6511
6838
|
throw new Error(
|
|
@@ -6534,6 +6861,11 @@ function parsePlayRunOptions(args) {
|
|
|
6534
6861
|
}
|
|
6535
6862
|
continue;
|
|
6536
6863
|
}
|
|
6864
|
+
if (arg === "--csv" || arg.startsWith("--csv=")) {
|
|
6865
|
+
throw new Error(
|
|
6866
|
+
`--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`
|
|
6867
|
+
);
|
|
6868
|
+
}
|
|
6537
6869
|
if (arg.startsWith("--")) {
|
|
6538
6870
|
const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
|
|
6539
6871
|
input ??= {};
|
|
@@ -6570,11 +6902,6 @@ function parsePlayRunOptions(args) {
|
|
|
6570
6902
|
"--live, --latest, and --revision-id only apply to named plays."
|
|
6571
6903
|
);
|
|
6572
6904
|
}
|
|
6573
|
-
if (outPath && !watch) {
|
|
6574
|
-
throw new Error(
|
|
6575
|
-
"--out requires --watch so the CLI can export the completed run output."
|
|
6576
|
-
);
|
|
6577
|
-
}
|
|
6578
6905
|
return {
|
|
6579
6906
|
target: filePath ? { kind: "file", path: filePath } : { kind: "name", name: playName },
|
|
6580
6907
|
input,
|
|
@@ -6585,7 +6912,6 @@ function parsePlayRunOptions(args) {
|
|
|
6585
6912
|
jsonOutput,
|
|
6586
6913
|
waitTimeoutMs,
|
|
6587
6914
|
force,
|
|
6588
|
-
outPath,
|
|
6589
6915
|
noOpen
|
|
6590
6916
|
};
|
|
6591
6917
|
}
|
|
@@ -6719,11 +7045,6 @@ async function handleFileBackedRun(options) {
|
|
|
6719
7045
|
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
6720
7046
|
compilerManifest.staticPipeline
|
|
6721
7047
|
);
|
|
6722
|
-
applyCsvShortcutInput({
|
|
6723
|
-
runtimeInput,
|
|
6724
|
-
bindings: fileInputBindings,
|
|
6725
|
-
fallbackInputPath: "file"
|
|
6726
|
-
});
|
|
6727
7048
|
const stagedFileInputs = await traceCliSpan(
|
|
6728
7049
|
"cli.play_stage_inputs",
|
|
6729
7050
|
{
|
|
@@ -6765,11 +7086,6 @@ async function handleFileBackedRun(options) {
|
|
|
6765
7086
|
progress
|
|
6766
7087
|
})
|
|
6767
7088
|
);
|
|
6768
|
-
const exportedPath = traceCliSync(
|
|
6769
|
-
"cli.play_export_rows",
|
|
6770
|
-
{ targetKind: "file", playName },
|
|
6771
|
-
() => exportPlayStatusRows(finalStatus, options.outPath)
|
|
6772
|
-
);
|
|
6773
7089
|
if (finalStatus.status === "completed") {
|
|
6774
7090
|
progress.complete();
|
|
6775
7091
|
} else {
|
|
@@ -6778,7 +7094,7 @@ async function handleFileBackedRun(options) {
|
|
|
6778
7094
|
traceCliSync(
|
|
6779
7095
|
"cli.play_write_result",
|
|
6780
7096
|
{ targetKind: "file", playName },
|
|
6781
|
-
() => writePlayResult(finalStatus, options.jsonOutput
|
|
7097
|
+
() => writePlayResult(finalStatus, options.jsonOutput)
|
|
6782
7098
|
);
|
|
6783
7099
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
6784
7100
|
}
|
|
@@ -6868,10 +7184,6 @@ async function handleNamedRun(options) {
|
|
|
6868
7184
|
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
6869
7185
|
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
6870
7186
|
] : [];
|
|
6871
|
-
applyCsvShortcutInput({
|
|
6872
|
-
runtimeInput,
|
|
6873
|
-
bindings: fileInputBindings
|
|
6874
|
-
});
|
|
6875
7187
|
const stagedFileInputs = await traceCliSpan(
|
|
6876
7188
|
"cli.play_stage_inputs",
|
|
6877
7189
|
{
|
|
@@ -6910,11 +7222,6 @@ async function handleNamedRun(options) {
|
|
|
6910
7222
|
progress
|
|
6911
7223
|
})
|
|
6912
7224
|
);
|
|
6913
|
-
const exportedPath = traceCliSync(
|
|
6914
|
-
"cli.play_export_rows",
|
|
6915
|
-
{ targetKind: "name", playName },
|
|
6916
|
-
() => exportPlayStatusRows(finalStatus, options.outPath)
|
|
6917
|
-
);
|
|
6918
7225
|
if (finalStatus.status === "completed") {
|
|
6919
7226
|
progress.complete();
|
|
6920
7227
|
} else {
|
|
@@ -6923,7 +7230,7 @@ async function handleNamedRun(options) {
|
|
|
6923
7230
|
traceCliSync(
|
|
6924
7231
|
"cli.play_write_result",
|
|
6925
7232
|
{ targetKind: "name", playName },
|
|
6926
|
-
() => writePlayResult(finalStatus, options.jsonOutput
|
|
7233
|
+
() => writePlayResult(finalStatus, options.jsonOutput)
|
|
6927
7234
|
);
|
|
6928
7235
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
6929
7236
|
}
|
|
@@ -6992,7 +7299,7 @@ function parseRunIdPositional(args, usage) {
|
|
|
6992
7299
|
}
|
|
6993
7300
|
continue;
|
|
6994
7301
|
}
|
|
6995
|
-
if ((arg === "--out" || arg === "--reason") && args[index + 1]) {
|
|
7302
|
+
if ((arg === "--out" || arg === "--reason" || arg === "--dataset") && args[index + 1]) {
|
|
6996
7303
|
index += 1;
|
|
6997
7304
|
continue;
|
|
6998
7305
|
}
|
|
@@ -7054,26 +7361,15 @@ async function handleRunsList(args) {
|
|
|
7054
7361
|
executionTime: run.executionTime,
|
|
7055
7362
|
playName: run.memo?.playName ?? playName
|
|
7056
7363
|
}));
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
`
|
|
7067
|
-
);
|
|
7068
|
-
} else {
|
|
7069
|
-
if (runs.length === 0) {
|
|
7070
|
-
console.log(`No runs found for ${playName}.`);
|
|
7071
|
-
} else {
|
|
7072
|
-
for (const run of runs) {
|
|
7073
|
-
console.log(`${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
7074
|
-
}
|
|
7075
|
-
}
|
|
7076
|
-
}
|
|
7364
|
+
const lines = runs.length === 0 ? [`No runs found for ${playName}.`] : runs.map((run) => `${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
7365
|
+
printCommandEnvelope({
|
|
7366
|
+
runs,
|
|
7367
|
+
count: runs.length,
|
|
7368
|
+
next: {
|
|
7369
|
+
get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
|
|
7370
|
+
},
|
|
7371
|
+
render: { sections: [{ title: "runs", lines }] }
|
|
7372
|
+
}, { json: argsWantJson(args) });
|
|
7077
7373
|
return 0;
|
|
7078
7374
|
}
|
|
7079
7375
|
async function handleRunTail(args) {
|
|
@@ -7127,41 +7423,30 @@ async function handleRunLogs(args) {
|
|
|
7127
7423
|
const logs = status.progress?.logs ?? [];
|
|
7128
7424
|
if (outPath) {
|
|
7129
7425
|
writeFileSync5(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
`
|
|
7138
|
-
);
|
|
7139
|
-
} else {
|
|
7140
|
-
console.log(`Wrote ${logs.length} log lines to ${outPath}`);
|
|
7141
|
-
}
|
|
7426
|
+
printCommandEnvelope({
|
|
7427
|
+
runId: status.runId,
|
|
7428
|
+
log_path: outPath,
|
|
7429
|
+
lineCount: logs.length,
|
|
7430
|
+
local: { log_path: outPath },
|
|
7431
|
+
render: { sections: [{ title: "run logs", lines: [`Wrote ${logs.length} log lines to ${outPath}`] }] }
|
|
7432
|
+
}, { json: argsWantJson(args) });
|
|
7142
7433
|
return 0;
|
|
7143
7434
|
}
|
|
7144
7435
|
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
})}
|
|
7160
|
-
`
|
|
7161
|
-
);
|
|
7162
|
-
} else {
|
|
7163
|
-
process.stdout.write(`${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`);
|
|
7164
|
-
}
|
|
7436
|
+
printCommandEnvelope({
|
|
7437
|
+
runId: status.runId,
|
|
7438
|
+
totalCount: logs.length,
|
|
7439
|
+
returnedCount: entries.length,
|
|
7440
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
7441
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
7442
|
+
truncated: logs.length > entries.length,
|
|
7443
|
+
hasMore: logs.length > entries.length,
|
|
7444
|
+
entries,
|
|
7445
|
+
next: {
|
|
7446
|
+
export: `deepline runs logs ${status.runId} --out run.log --json`
|
|
7447
|
+
},
|
|
7448
|
+
render: { sections: [{ title: "run logs", lines: entries }] }
|
|
7449
|
+
}, { json: argsWantJson(args), text: `${entries.join("\n")}${entries.length > 0 ? "\n" : ""}` });
|
|
7165
7450
|
return 0;
|
|
7166
7451
|
}
|
|
7167
7452
|
async function handleRunStop(args) {
|
|
@@ -7182,19 +7467,18 @@ async function handleRunStop(args) {
|
|
|
7182
7467
|
}
|
|
7183
7468
|
const client = new DeeplineClient();
|
|
7184
7469
|
const result = await client.runs.stop(runId, { reason });
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
`
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
}
|
|
7470
|
+
const lines = [
|
|
7471
|
+
`Stopped ${result.runId}`,
|
|
7472
|
+
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : []
|
|
7473
|
+
];
|
|
7474
|
+
printCommandEnvelope({
|
|
7475
|
+
...result,
|
|
7476
|
+
render: { sections: [{ title: "run stop", lines }] }
|
|
7477
|
+
}, { json: argsWantJson(args) });
|
|
7194
7478
|
return 0;
|
|
7195
7479
|
}
|
|
7196
7480
|
async function handleRunExport(args) {
|
|
7197
|
-
const usage = "Usage: deepline runs export <run-id> --out output.csv [--json]";
|
|
7481
|
+
const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--json]";
|
|
7198
7482
|
let runId;
|
|
7199
7483
|
try {
|
|
7200
7484
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -7203,10 +7487,15 @@ async function handleRunExport(args) {
|
|
|
7203
7487
|
return 1;
|
|
7204
7488
|
}
|
|
7205
7489
|
let outPath = null;
|
|
7490
|
+
let datasetPath = null;
|
|
7206
7491
|
for (let index = 0; index < args.length; index += 1) {
|
|
7207
7492
|
const arg = args[index];
|
|
7208
7493
|
if (arg === "--out" && args[index + 1]) {
|
|
7209
7494
|
outPath = resolve8(args[++index]);
|
|
7495
|
+
continue;
|
|
7496
|
+
}
|
|
7497
|
+
if (arg === "--dataset" && args[index + 1]) {
|
|
7498
|
+
datasetPath = args[++index];
|
|
7210
7499
|
}
|
|
7211
7500
|
}
|
|
7212
7501
|
if (!outPath) {
|
|
@@ -7215,21 +7504,18 @@ async function handleRunExport(args) {
|
|
|
7215
7504
|
}
|
|
7216
7505
|
const client = new DeeplineClient();
|
|
7217
7506
|
const status = await client.getPlayStatus(runId);
|
|
7218
|
-
const
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
`
|
|
7229
|
-
|
|
7230
|
-
} else {
|
|
7231
|
-
console.log(`Exported ${status.runId} to ${exportedPath}`);
|
|
7232
|
-
}
|
|
7507
|
+
const exportResult = await exportPlayStatusRows(client, status, outPath, {
|
|
7508
|
+
datasetPath
|
|
7509
|
+
});
|
|
7510
|
+
printCommandEnvelope({
|
|
7511
|
+
runId: status.runId,
|
|
7512
|
+
...datasetPath ? { dataset: datasetPath } : {},
|
|
7513
|
+
csv_path: exportResult?.path ?? null,
|
|
7514
|
+
rowCount: exportResult?.rowsInfo.totalRows ?? null,
|
|
7515
|
+
columns: exportResult?.rowsInfo.columns ?? [],
|
|
7516
|
+
local: { csv_path: exportResult?.path ?? null },
|
|
7517
|
+
render: { sections: [{ title: "run export", lines: [`Exported ${status.runId} to ${exportResult?.path ?? outPath}`] }] }
|
|
7518
|
+
}, { json: argsWantJson(args) });
|
|
7233
7519
|
return 0;
|
|
7234
7520
|
}
|
|
7235
7521
|
async function handlePlayGet(args) {
|
|
@@ -7677,8 +7963,7 @@ Notes:
|
|
|
7677
7963
|
Local files are bundled, preflighted, then run in Deepline cloud.
|
|
7678
7964
|
Named plays run the live saved revision.
|
|
7679
7965
|
Unknown --foo value and --foo.bar value flags are passed into play input.
|
|
7680
|
-
Example: --
|
|
7681
|
-
input.limit = 5.
|
|
7966
|
+
Example: --limit 5 becomes input.limit = 5.
|
|
7682
7967
|
File args accept local paths; the CLI stages files before submit.
|
|
7683
7968
|
--watch prints logs, previews, stats, and next commands.
|
|
7684
7969
|
--wait is accepted as a compatibility alias for --watch.
|
|
@@ -7712,16 +7997,13 @@ Examples:
|
|
|
7712
7997
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
|
|
7713
7998
|
deepline plays run my.play.ts --input @input.json --wait --json
|
|
7714
7999
|
deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
|
|
7715
|
-
deepline plays run enrich.play.ts --csv leads.csv --watch --out leads-enriched.csv
|
|
7716
8000
|
deepline plays run cto-search.play.ts --limit 5 --watch
|
|
8001
|
+
deepline runs export <run-id> --out output.csv
|
|
7717
8002
|
deepline runs get <run-id>
|
|
7718
8003
|
`
|
|
7719
8004
|
).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(
|
|
7720
8005
|
"--revision-id <id>",
|
|
7721
8006
|
"Run a specific saved revision instead of the live revision"
|
|
7722
|
-
).option(
|
|
7723
|
-
"--out <path>",
|
|
7724
|
-
"Write the completed row output to CSV; requires --watch"
|
|
7725
8007
|
).option("--watch", "Stream logs until completion").option("--wait", "Alias for --watch; stream logs until completion").option(
|
|
7726
8008
|
"--logs",
|
|
7727
8009
|
"When output is non-interactive, stream play logs to stderr while waiting"
|
|
@@ -7730,8 +8012,9 @@ Examples:
|
|
|
7730
8012
|
`
|
|
7731
8013
|
Pass-through input flags:
|
|
7732
8014
|
Unknown flags are accepted intentionally and become play input fields. Use
|
|
7733
|
-
this for play-specific inputs like --
|
|
7734
|
-
--
|
|
8015
|
+
this for play-specific inputs like --limit 5 or --filters.title "GTM Engineer".
|
|
8016
|
+
For CSV file inputs, prefer --input '{"file":"leads.csv"}' so the field name
|
|
8017
|
+
matches the play's ctx.csv(input.file) contract.
|
|
7735
8018
|
`
|
|
7736
8019
|
).action(async (target, options, command) => {
|
|
7737
8020
|
const passthroughArgs = [...command.args];
|
|
@@ -7752,7 +8035,6 @@ Pass-through input flags:
|
|
|
7752
8035
|
...options.live ? ["--live"] : [],
|
|
7753
8036
|
...options.latest ? ["--latest"] : [],
|
|
7754
8037
|
...options.revisionId ? ["--revision-id", options.revisionId] : [],
|
|
7755
|
-
...options.out ? ["--out", options.out] : [],
|
|
7756
8038
|
...options.watch || options.wait ? ["--watch"] : [],
|
|
7757
8039
|
...options.logs ? ["--logs"] : [],
|
|
7758
8040
|
...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
|
|
@@ -7916,7 +8198,7 @@ Examples:
|
|
|
7916
8198
|
`
|
|
7917
8199
|
Concepts:
|
|
7918
8200
|
A run is one execution instance of a play. It has status, progress, logs,
|
|
7919
|
-
|
|
8201
|
+
returned result, dataset previews, recovery metadata, and optional dataset export.
|
|
7920
8202
|
tail reads the live stream. logs fetches persisted logs after the fact.
|
|
7921
8203
|
stop mutates cloud state by requesting cancellation.
|
|
7922
8204
|
|
|
@@ -7929,7 +8211,7 @@ Examples:
|
|
|
7929
8211
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
7930
8212
|
`
|
|
7931
8213
|
);
|
|
7932
|
-
runs.command("get <runId>").description("Get status, progress,
|
|
8214
|
+
runs.command("get <runId>").description("Get status, progress, result, errors, and recovery metadata for a play run.").addHelpText(
|
|
7933
8215
|
"after",
|
|
7934
8216
|
`
|
|
7935
8217
|
Notes:
|
|
@@ -8023,20 +8305,22 @@ Examples:
|
|
|
8023
8305
|
...options.json ? ["--json"] : []
|
|
8024
8306
|
]);
|
|
8025
8307
|
});
|
|
8026
|
-
runs.command("export <runId>").description("Export
|
|
8308
|
+
runs.command("export <runId>").description("Export a returned dataset handle for a play run to CSV.").addHelpText(
|
|
8027
8309
|
"after",
|
|
8028
8310
|
`
|
|
8029
8311
|
Notes:
|
|
8030
|
-
Writes
|
|
8031
|
-
first
|
|
8312
|
+
Writes a returned dataset handle to the requested local CSV path. Use runs get
|
|
8313
|
+
first to inspect dataset paths like result.rows or result.nested.contacts.
|
|
8032
8314
|
|
|
8033
8315
|
Examples:
|
|
8034
8316
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
8317
|
+
deepline runs export play/my-play/run/20260501t000000-000 --dataset result.rows --out output.csv
|
|
8035
8318
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --json
|
|
8036
8319
|
`
|
|
8037
|
-
).requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
8320
|
+
).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) => {
|
|
8038
8321
|
process.exitCode = await handleRunExport([
|
|
8039
8322
|
runId,
|
|
8323
|
+
...options.dataset ? ["--dataset", options.dataset] : [],
|
|
8040
8324
|
"--out",
|
|
8041
8325
|
options.out,
|
|
8042
8326
|
...options.json ? ["--json"] : []
|
|
@@ -8202,19 +8486,19 @@ function toListedTool(tool) {
|
|
|
8202
8486
|
async function listTools(args) {
|
|
8203
8487
|
const client = new DeeplineClient();
|
|
8204
8488
|
const items = (await client.listTools()).map(toListedTool);
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
`);
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
}
|
|
8489
|
+
const render = {
|
|
8490
|
+
sections: [
|
|
8491
|
+
{
|
|
8492
|
+
title: `${items.length} tools available:`,
|
|
8493
|
+
lines: items.flatMap((item) => {
|
|
8494
|
+
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8495
|
+
const listHint = item.listExtractorPaths?.length ? ` listExtractorPaths=${item.listExtractorPaths.join(",")}` : "";
|
|
8496
|
+
return [`${item.toolId}${cats}`, ` ${item.description}${listHint}`];
|
|
8497
|
+
})
|
|
8498
|
+
}
|
|
8499
|
+
]
|
|
8500
|
+
};
|
|
8501
|
+
printCommandEnvelope({ tools: items, count: items.length, render }, { json: argsWantJson(args) });
|
|
8218
8502
|
return 0;
|
|
8219
8503
|
}
|
|
8220
8504
|
async function searchTools(queryInput, options = {}) {
|
|
@@ -8232,21 +8516,27 @@ async function searchTools(queryInput, options = {}) {
|
|
|
8232
8516
|
includeSearchDebug: options.includeSearchDebug
|
|
8233
8517
|
});
|
|
8234
8518
|
const items = result.tools.map(toListedTool);
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8519
|
+
const envelope = {
|
|
8520
|
+
...result,
|
|
8521
|
+
tools: items,
|
|
8522
|
+
count: items.length,
|
|
8523
|
+
render: {
|
|
8524
|
+
sections: [
|
|
8525
|
+
{
|
|
8526
|
+
title: `${items.length} tools found:`,
|
|
8527
|
+
lines: items.flatMap((item) => {
|
|
8528
|
+
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8529
|
+
return [
|
|
8530
|
+
`${item.toolId}${cats}`,
|
|
8531
|
+
` ${item.description}`,
|
|
8532
|
+
...item.inputSchema ? [" inputSchema: yes"] : []
|
|
8533
|
+
];
|
|
8534
|
+
})
|
|
8535
|
+
}
|
|
8536
|
+
]
|
|
8248
8537
|
}
|
|
8249
|
-
}
|
|
8538
|
+
};
|
|
8539
|
+
printCommandEnvelope(envelope, { json: options.json || shouldEmitJson() });
|
|
8250
8540
|
return 0;
|
|
8251
8541
|
}
|
|
8252
8542
|
function playIdentifiers(play) {
|
|
@@ -8578,6 +8868,9 @@ function samplePayload(samples, key) {
|
|
|
8578
8868
|
if (!isRecord3(entry)) return void 0;
|
|
8579
8869
|
return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
|
|
8580
8870
|
}
|
|
8871
|
+
function commandEnvelopeFromRawResponse(rawResponse) {
|
|
8872
|
+
return isRecord3(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
|
|
8873
|
+
}
|
|
8581
8874
|
function isPlayTool(tool) {
|
|
8582
8875
|
const provider = typeof tool.provider === "string" ? tool.provider : "";
|
|
8583
8876
|
return provider === "deepline_native" && Boolean(recordField(tool, "playExpansion", "play_expansion"));
|
|
@@ -8742,6 +9035,53 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
8742
9035
|
windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
|
|
8743
9036
|
};
|
|
8744
9037
|
}
|
|
9038
|
+
function buildToolExecuteBaseEnvelope(input) {
|
|
9039
|
+
const envelope = commandEnvelopeFromRawResponse(input.rawResponse);
|
|
9040
|
+
const summaryEntries = Object.entries(input.summary);
|
|
9041
|
+
const output = input.listConversion ? {
|
|
9042
|
+
kind: "list",
|
|
9043
|
+
rowCount: input.listConversion.rows.length,
|
|
9044
|
+
columns: Object.keys(input.listConversion.rows[0] ?? {}),
|
|
9045
|
+
preview: input.listConversion.rows.slice(0, 5),
|
|
9046
|
+
listStrategy: input.listConversion.strategy,
|
|
9047
|
+
listSourcePath: input.listConversion.sourcePath
|
|
9048
|
+
} : {
|
|
9049
|
+
kind: summaryEntries.length > 0 ? "object" : "raw",
|
|
9050
|
+
summary: input.summary
|
|
9051
|
+
};
|
|
9052
|
+
const actions = input.listConversion ? [
|
|
9053
|
+
{
|
|
9054
|
+
label: "next",
|
|
9055
|
+
command: "move starter script into a project folder and expand it into a Deepline play"
|
|
9056
|
+
}
|
|
9057
|
+
] : [];
|
|
9058
|
+
return {
|
|
9059
|
+
...envelope,
|
|
9060
|
+
output,
|
|
9061
|
+
...summaryEntries.length > 0 ? { summary: input.summary } : {},
|
|
9062
|
+
next: input.listConversion ? {
|
|
9063
|
+
expandToPlay: "Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
|
|
9064
|
+
} : {},
|
|
9065
|
+
render: {
|
|
9066
|
+
sections: input.listConversion ? [
|
|
9067
|
+
{
|
|
9068
|
+
title: "output",
|
|
9069
|
+
lines: [
|
|
9070
|
+
`${input.listConversion.rows.length} row(s) extracted from ${input.listConversion.sourcePath ?? "auto-detected list"}`,
|
|
9071
|
+
`columns: ${JSON.stringify(Object.keys(input.listConversion.rows[0] ?? {}))}`,
|
|
9072
|
+
`preview: ${JSON.stringify(input.listConversion.rows.slice(0, 5))}`
|
|
9073
|
+
]
|
|
9074
|
+
}
|
|
9075
|
+
] : [
|
|
9076
|
+
{
|
|
9077
|
+
title: "result",
|
|
9078
|
+
lines: summaryEntries.length > 0 ? summaryEntries.map(([key, value]) => `${key}=${String(value)}`) : [JSON.stringify(input.rawResponse, null, 2)]
|
|
9079
|
+
}
|
|
9080
|
+
],
|
|
9081
|
+
actions
|
|
9082
|
+
}
|
|
9083
|
+
};
|
|
9084
|
+
}
|
|
8745
9085
|
async function executeTool(args) {
|
|
8746
9086
|
let parsed;
|
|
8747
9087
|
try {
|
|
@@ -8774,24 +9114,45 @@ async function executeTool(args) {
|
|
|
8774
9114
|
listExtractorPaths: metadata.listExtractorPaths ?? []
|
|
8775
9115
|
});
|
|
8776
9116
|
const summary = extractSummaryFields(rawResponse);
|
|
9117
|
+
const baseEnvelope = buildToolExecuteBaseEnvelope({
|
|
9118
|
+
toolId: parsed.toolId,
|
|
9119
|
+
rawResponse,
|
|
9120
|
+
listConversion,
|
|
9121
|
+
summary
|
|
9122
|
+
});
|
|
8777
9123
|
if (parsed.outputFormat === "json" || parsed.outputFormat === "auto" && shouldEmitJson()) {
|
|
8778
|
-
|
|
8779
|
-
`);
|
|
9124
|
+
printCommandEnvelope(baseEnvelope, { json: true });
|
|
8780
9125
|
return 0;
|
|
8781
9126
|
}
|
|
8782
9127
|
if (parsed.outputFormat === "json_file") {
|
|
8783
9128
|
const jsonPath = writeJsonOutputFile(rawResponse, `payload_${parsed.toolId}`);
|
|
8784
|
-
|
|
9129
|
+
printCommandEnvelope(
|
|
9130
|
+
{
|
|
9131
|
+
...baseEnvelope,
|
|
9132
|
+
local: {
|
|
9133
|
+
...isRecord3(baseEnvelope.local) ? baseEnvelope.local : {},
|
|
9134
|
+
payload_file: jsonPath
|
|
9135
|
+
}
|
|
9136
|
+
},
|
|
9137
|
+
{ json: true }
|
|
9138
|
+
);
|
|
8785
9139
|
return 0;
|
|
8786
9140
|
}
|
|
8787
9141
|
if (!listConversion) {
|
|
8788
9142
|
if (parsed.outputFormat === "csv" || parsed.outputFormat === "csv_file") {
|
|
8789
9143
|
const jsonPath = writeJsonOutputFile(rawResponse, `payload_${parsed.toolId}`);
|
|
8790
|
-
|
|
9144
|
+
printCommandEnvelope(
|
|
9145
|
+
{
|
|
9146
|
+
...baseEnvelope,
|
|
9147
|
+
local: {
|
|
9148
|
+
payload_file: jsonPath
|
|
9149
|
+
}
|
|
9150
|
+
},
|
|
9151
|
+
{ json: parsed.outputFormat === "csv_file" || shouldEmitJson() }
|
|
9152
|
+
);
|
|
8791
9153
|
return 0;
|
|
8792
9154
|
}
|
|
8793
|
-
|
|
8794
|
-
`);
|
|
9155
|
+
printCommandEnvelope(baseEnvelope, { json: false });
|
|
8795
9156
|
return 0;
|
|
8796
9157
|
}
|
|
8797
9158
|
const csv = writeCsvOutputFile(listConversion.rows, `${parsed.toolId}_output`);
|
|
@@ -8800,8 +9161,51 @@ async function executeTool(args) {
|
|
|
8800
9161
|
payload: parsed.params,
|
|
8801
9162
|
rows: listConversion.rows
|
|
8802
9163
|
});
|
|
9164
|
+
const materializedEnvelope = {
|
|
9165
|
+
...baseEnvelope,
|
|
9166
|
+
local: {
|
|
9167
|
+
extracted_csv: csv.path,
|
|
9168
|
+
extracted_csv_rows: csv.rowCount,
|
|
9169
|
+
extracted_csv_columns: csv.columns,
|
|
9170
|
+
preview: csv.preview,
|
|
9171
|
+
starter_script: seededScript.path,
|
|
9172
|
+
project_dir: seededScript.projectDir,
|
|
9173
|
+
copy_to_project: {
|
|
9174
|
+
macos_linux: seededScript.macCopyCommand,
|
|
9175
|
+
windows_powershell: seededScript.windowsCopyCommand
|
|
9176
|
+
}
|
|
9177
|
+
},
|
|
9178
|
+
render: {
|
|
9179
|
+
sections: [
|
|
9180
|
+
{
|
|
9181
|
+
title: `${csv.path} (${csv.rowCount} rows)`,
|
|
9182
|
+
lines: [
|
|
9183
|
+
...csv.columns.length > 0 ? [`columns: ${JSON.stringify(csv.columns)}`] : [],
|
|
9184
|
+
...Object.keys(summary).length > 0 ? [`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`] : [],
|
|
9185
|
+
`preview: ${JSON.stringify(csv.preview)}`,
|
|
9186
|
+
`starter script: ${seededScript.path}`
|
|
9187
|
+
]
|
|
9188
|
+
}
|
|
9189
|
+
],
|
|
9190
|
+
actions: [
|
|
9191
|
+
{
|
|
9192
|
+
label: "next",
|
|
9193
|
+
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."
|
|
9194
|
+
},
|
|
9195
|
+
{
|
|
9196
|
+
label: "macOS/Linux",
|
|
9197
|
+
command: seededScript.macCopyCommand
|
|
9198
|
+
},
|
|
9199
|
+
{
|
|
9200
|
+
label: "Windows PowerShell",
|
|
9201
|
+
command: seededScript.windowsCopyCommand
|
|
9202
|
+
}
|
|
9203
|
+
]
|
|
9204
|
+
}
|
|
9205
|
+
};
|
|
8803
9206
|
if (parsed.outputFormat === "csv_file") {
|
|
8804
|
-
|
|
9207
|
+
printCommandEnvelope({
|
|
9208
|
+
...materializedEnvelope,
|
|
8805
9209
|
extracted_csv: csv.path,
|
|
8806
9210
|
extracted_csv_rows: csv.rowCount,
|
|
8807
9211
|
extracted_csv_columns: csv.columns,
|
|
@@ -8815,28 +9219,24 @@ async function executeTool(args) {
|
|
|
8815
9219
|
windows_powershell: seededScript.windowsCopyCommand
|
|
8816
9220
|
},
|
|
8817
9221
|
summary
|
|
8818
|
-
})
|
|
8819
|
-
`);
|
|
9222
|
+
}, { json: true });
|
|
8820
9223
|
return 0;
|
|
8821
9224
|
}
|
|
8822
9225
|
if (parsed.noPreview) {
|
|
8823
|
-
|
|
9226
|
+
printCommandEnvelope(
|
|
9227
|
+
{
|
|
9228
|
+
...materializedEnvelope,
|
|
9229
|
+
local: {
|
|
9230
|
+
...materializedEnvelope.local ?? {},
|
|
9231
|
+
output_path: csv.path
|
|
9232
|
+
}
|
|
9233
|
+
},
|
|
9234
|
+
{ json: shouldEmitJson(), text: `${csv.path}
|
|
9235
|
+
` }
|
|
9236
|
+
);
|
|
8824
9237
|
return 0;
|
|
8825
9238
|
}
|
|
8826
|
-
|
|
8827
|
-
if (csv.columns.length > 0) {
|
|
8828
|
-
console.log(`columns: ${JSON.stringify(csv.columns)}`);
|
|
8829
|
-
}
|
|
8830
|
-
if (Object.keys(summary).length > 0) {
|
|
8831
|
-
console.log(`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`);
|
|
8832
|
-
}
|
|
8833
|
-
console.log(`preview: ${JSON.stringify(csv.preview)}`);
|
|
8834
|
-
console.log(`starter script: ${seededScript.path}`);
|
|
8835
|
-
console.log(
|
|
8836
|
-
"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."
|
|
8837
|
-
);
|
|
8838
|
-
console.log(`macOS/Linux: ${seededScript.macCopyCommand}`);
|
|
8839
|
-
console.log(`Windows PowerShell: ${seededScript.windowsCopyCommand}`);
|
|
9239
|
+
printCommandEnvelope(materializedEnvelope, { json: false });
|
|
8840
9240
|
return 0;
|
|
8841
9241
|
}
|
|
8842
9242
|
|
|
@@ -8910,23 +9310,36 @@ function runCommand(command, args) {
|
|
|
8910
9310
|
}
|
|
8911
9311
|
async function handleUpdate(options) {
|
|
8912
9312
|
const plan = resolveUpdatePlan();
|
|
9313
|
+
const render = {
|
|
9314
|
+
sections: [
|
|
9315
|
+
{
|
|
9316
|
+
title: "update",
|
|
9317
|
+
lines: plan.kind === "source" ? [
|
|
9318
|
+
"This Deepline CLI is running from SDK source, so it cannot safely update itself like an npm global.",
|
|
9319
|
+
`Update the backing checkout with: ${plan.manualCommand}`
|
|
9320
|
+
] : [`Updating Deepline SDK/CLI with: ${plan.manualCommand}`]
|
|
9321
|
+
}
|
|
9322
|
+
]
|
|
9323
|
+
};
|
|
8913
9324
|
if (options.json) {
|
|
8914
|
-
|
|
8915
|
-
`);
|
|
9325
|
+
printCommandEnvelope({ ...plan, render }, { json: true });
|
|
8916
9326
|
return 0;
|
|
8917
9327
|
}
|
|
8918
9328
|
if (options.printCommand) {
|
|
8919
|
-
|
|
8920
|
-
|
|
9329
|
+
printCommandEnvelope(
|
|
9330
|
+
{
|
|
9331
|
+
...plan,
|
|
9332
|
+
render: {
|
|
9333
|
+
sections: [{ title: "update command", lines: [plan.manualCommand] }]
|
|
9334
|
+
}
|
|
9335
|
+
},
|
|
9336
|
+
{ json: false, text: `${plan.manualCommand}
|
|
9337
|
+
` }
|
|
9338
|
+
);
|
|
8921
9339
|
return 0;
|
|
8922
9340
|
}
|
|
8923
9341
|
if (plan.kind === "source") {
|
|
8924
|
-
|
|
8925
|
-
`This Deepline CLI is running from SDK source, so it cannot safely update itself like an npm global.
|
|
8926
|
-
Update the backing checkout with:
|
|
8927
|
-
${plan.manualCommand}
|
|
8928
|
-
`
|
|
8929
|
-
);
|
|
9342
|
+
printCommandEnvelope({ ...plan, render }, { json: false });
|
|
8930
9343
|
return 0;
|
|
8931
9344
|
}
|
|
8932
9345
|
process.stderr.write(`Updating Deepline SDK/CLI with: ${plan.manualCommand}
|
|
@@ -9239,7 +9652,7 @@ Exit codes:
|
|
|
9239
9652
|
registerDbCommands(program);
|
|
9240
9653
|
registerFeedbackCommands(program);
|
|
9241
9654
|
registerUpdateCommand(program);
|
|
9242
|
-
program.command("health").description("Check server health.").addHelpText(
|
|
9655
|
+
program.command("health").description("Check server health.").option("--json", "Force JSON output.").addHelpText(
|
|
9243
9656
|
"after",
|
|
9244
9657
|
`
|
|
9245
9658
|
Notes:
|