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.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-v2";
|
|
246
|
+
var SDK_VERSION = "0.1.30";
|
|
247
|
+
var SDK_API_CONTRACT = "2026-05-runs-v2-datasets";
|
|
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)) {
|
|
@@ -6241,23 +6394,17 @@ 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 } : {},
|
|
6403
|
+
...options?.exportedPath ? { local: { csv_path: options.exportedPath } } : {},
|
|
6256
6404
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
6257
6405
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
6258
|
-
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
|
|
6259
6406
|
...billing ? { billing } : {},
|
|
6260
|
-
next: buildRunNextCommands(status.runId
|
|
6407
|
+
next: buildRunNextCommands(status.runId)
|
|
6261
6408
|
};
|
|
6262
6409
|
}
|
|
6263
6410
|
function enrichPlayStatusWithDatasetStats(status) {
|
|
@@ -6295,12 +6442,20 @@ function formatDatasetStatsLines(datasetStats) {
|
|
|
6295
6442
|
}
|
|
6296
6443
|
function writePlayResult(status, jsonOutput, options) {
|
|
6297
6444
|
if (jsonOutput) {
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6445
|
+
const payload2 = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status, options);
|
|
6446
|
+
printCommandEnvelope({
|
|
6447
|
+
...payload2,
|
|
6448
|
+
render: {
|
|
6449
|
+
sections: [
|
|
6450
|
+
{
|
|
6451
|
+
title: "run result",
|
|
6452
|
+
lines: [
|
|
6453
|
+
`${status.status ?? "running"} ${status.runId ?? "unknown"}`
|
|
6454
|
+
]
|
|
6455
|
+
}
|
|
6456
|
+
]
|
|
6457
|
+
}
|
|
6458
|
+
}, { json: true });
|
|
6304
6459
|
return;
|
|
6305
6460
|
}
|
|
6306
6461
|
const result = status.result;
|
|
@@ -6317,22 +6472,8 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6317
6472
|
rowsInfo.columns,
|
|
6318
6473
|
extractDatasetExecutionStats(status)
|
|
6319
6474
|
) : null;
|
|
6320
|
-
|
|
6321
|
-
|
|
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
|
-
}
|
|
6475
|
+
if (options?.exportedPath) {
|
|
6476
|
+
lines.push(` exported CSV: file=${options.exportedPath}`);
|
|
6336
6477
|
}
|
|
6337
6478
|
for (const warning of warnings) {
|
|
6338
6479
|
lines.push(` warning: ${warning}`);
|
|
@@ -6345,29 +6486,184 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6345
6486
|
const renderedServerView = renderServerResultView(status.resultView);
|
|
6346
6487
|
if (result) {
|
|
6347
6488
|
lines.push(...formatReturnValue(result));
|
|
6489
|
+
lines.push(...collectDatasetHandleLines(result));
|
|
6348
6490
|
}
|
|
6349
6491
|
if (renderedServerView.lines.length > 0) {
|
|
6350
6492
|
lines.push(...renderedServerView.lines);
|
|
6351
6493
|
}
|
|
6352
6494
|
lines.push(...renderedServerView.actions);
|
|
6353
|
-
|
|
6495
|
+
const payload = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status, options);
|
|
6496
|
+
printCommandEnvelope({
|
|
6497
|
+
...payload,
|
|
6498
|
+
render: {
|
|
6499
|
+
sections: [{ title: "run result", lines }]
|
|
6500
|
+
}
|
|
6501
|
+
}, { json: jsonOutput, text: `${lines.join("\n")}
|
|
6502
|
+
` });
|
|
6503
|
+
}
|
|
6504
|
+
var RUN_EXPORT_PAGE_SIZE = 5e3;
|
|
6505
|
+
var PLAY_RUN_OUT_EXPORT_ATTEMPTS = 8;
|
|
6506
|
+
var PLAY_RUN_OUT_EXPORT_RETRY_DELAY_MS = 1e3;
|
|
6507
|
+
function shellSingleQuote(value) {
|
|
6508
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
6509
|
+
}
|
|
6510
|
+
function runExportRetryCommand(runId, outPath, datasetPath) {
|
|
6511
|
+
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(resolve8(outPath))}`;
|
|
6512
|
+
}
|
|
6513
|
+
function extractRunPlayName(status) {
|
|
6514
|
+
const run = status.run;
|
|
6515
|
+
const candidates = [
|
|
6516
|
+
status.playName,
|
|
6517
|
+
status.name,
|
|
6518
|
+
getRecordField(run, "playName"),
|
|
6519
|
+
getRecordField(run, "name")
|
|
6520
|
+
];
|
|
6521
|
+
for (const candidate of candidates) {
|
|
6522
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
6523
|
+
return candidate.trim();
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
return null;
|
|
6354
6527
|
}
|
|
6355
|
-
function
|
|
6528
|
+
function exportableSheetRow(row) {
|
|
6529
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
6530
|
+
return null;
|
|
6531
|
+
}
|
|
6532
|
+
const record = row;
|
|
6533
|
+
const data = record.data;
|
|
6534
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
6535
|
+
return data;
|
|
6536
|
+
}
|
|
6537
|
+
const fallback = { ...record };
|
|
6538
|
+
for (const key of [
|
|
6539
|
+
"key",
|
|
6540
|
+
"status",
|
|
6541
|
+
"cellMeta",
|
|
6542
|
+
"inputIndex",
|
|
6543
|
+
"runId",
|
|
6544
|
+
"error",
|
|
6545
|
+
"stage",
|
|
6546
|
+
"provider",
|
|
6547
|
+
"seq",
|
|
6548
|
+
"createdAt",
|
|
6549
|
+
"updatedAt"
|
|
6550
|
+
]) {
|
|
6551
|
+
delete fallback[key];
|
|
6552
|
+
}
|
|
6553
|
+
return fallback;
|
|
6554
|
+
}
|
|
6555
|
+
async function fetchBackingDatasetRows(input) {
|
|
6556
|
+
const playName = extractRunPlayName(input.status);
|
|
6557
|
+
const tableNamespace = input.rowsInfo.tableNamespace?.trim();
|
|
6558
|
+
if (!playName || !tableNamespace) {
|
|
6559
|
+
return null;
|
|
6560
|
+
}
|
|
6561
|
+
const sheetRows = [];
|
|
6562
|
+
let offset = 0;
|
|
6563
|
+
let expectedTotal = input.rowsInfo.totalRows;
|
|
6564
|
+
while (true) {
|
|
6565
|
+
const page = await input.client.runs.exportDatasetRows({
|
|
6566
|
+
playName,
|
|
6567
|
+
tableNamespace,
|
|
6568
|
+
runId: input.status.runId,
|
|
6569
|
+
limit: RUN_EXPORT_PAGE_SIZE,
|
|
6570
|
+
offset
|
|
6571
|
+
});
|
|
6572
|
+
sheetRows.push(...page.rows);
|
|
6573
|
+
const summaryTotal = page.summary?.stats?.total;
|
|
6574
|
+
if (typeof summaryTotal === "number" && Number.isFinite(summaryTotal)) {
|
|
6575
|
+
expectedTotal = Math.max(expectedTotal, Math.trunc(summaryTotal));
|
|
6576
|
+
}
|
|
6577
|
+
if (page.rows.length < RUN_EXPORT_PAGE_SIZE || sheetRows.length >= expectedTotal) {
|
|
6578
|
+
break;
|
|
6579
|
+
}
|
|
6580
|
+
offset += page.rows.length;
|
|
6581
|
+
}
|
|
6582
|
+
const rows = sheetRows.map(exportableSheetRow).filter((row) => Boolean(row));
|
|
6583
|
+
if (rows.length < input.rowsInfo.totalRows) {
|
|
6584
|
+
return null;
|
|
6585
|
+
}
|
|
6586
|
+
const columns = input.rowsInfo.columnsExplicit && input.rowsInfo.columns.length ? input.rowsInfo.columns : [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
6587
|
+
return {
|
|
6588
|
+
...input.rowsInfo,
|
|
6589
|
+
rows,
|
|
6590
|
+
columns,
|
|
6591
|
+
totalRows: rows.length,
|
|
6592
|
+
complete: true,
|
|
6593
|
+
source: `${input.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
|
|
6594
|
+
};
|
|
6595
|
+
}
|
|
6596
|
+
async function exportPlayStatusRows(client, status, outPath, options = {}) {
|
|
6356
6597
|
if (!outPath) {
|
|
6357
6598
|
return null;
|
|
6358
6599
|
}
|
|
6359
|
-
const
|
|
6600
|
+
const availableRows = collectSerializedDatasetRowsInfos(status);
|
|
6601
|
+
let rowsInfo = options.datasetPath ? availableRows.find((info) => info.source === options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
|
|
6602
|
+
if (!rowsInfo && options.datasetPath) {
|
|
6603
|
+
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
6604
|
+
throw new DeeplineError(
|
|
6605
|
+
`Run ${status.runId} did not return a dataset at ${options.datasetPath}.` + (available.length > 0 ? ` Available datasets: ${available.join(", ")}.` : ""),
|
|
6606
|
+
void 0,
|
|
6607
|
+
"RUN_EXPORT_DATASET_NOT_FOUND",
|
|
6608
|
+
{ runId: status.runId, dataset: options.datasetPath, available }
|
|
6609
|
+
);
|
|
6610
|
+
}
|
|
6611
|
+
if (!options.datasetPath && availableRows.length > 1) {
|
|
6612
|
+
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
6613
|
+
throw new DeeplineError(
|
|
6614
|
+
`Run ${status.runId} returned multiple datasets. Choose one with --dataset <path>: ${available.join(", ")}.`,
|
|
6615
|
+
void 0,
|
|
6616
|
+
"RUN_EXPORT_DATASET_REQUIRED",
|
|
6617
|
+
{ runId: status.runId, available }
|
|
6618
|
+
);
|
|
6619
|
+
}
|
|
6360
6620
|
if (!rowsInfo) {
|
|
6361
6621
|
throw new DeeplineError(
|
|
6362
6622
|
`Run ${status.runId} did not expose a row-shaped final output to export.`
|
|
6363
6623
|
);
|
|
6364
6624
|
}
|
|
6625
|
+
const attempts = Math.max(1, Math.trunc(options.attempts ?? 1));
|
|
6626
|
+
const retryDelayMs = Math.max(0, Math.trunc(options.retryDelayMs ?? 0));
|
|
6627
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
6628
|
+
if (rowsInfo.complete) {
|
|
6629
|
+
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6630
|
+
}
|
|
6631
|
+
const fetchedRowsInfo = await fetchBackingDatasetRows({
|
|
6632
|
+
client,
|
|
6633
|
+
status,
|
|
6634
|
+
rowsInfo
|
|
6635
|
+
});
|
|
6636
|
+
if (fetchedRowsInfo?.complete) {
|
|
6637
|
+
return {
|
|
6638
|
+
path: writeCanonicalRowsCsv(fetchedRowsInfo, outPath),
|
|
6639
|
+
rowsInfo: fetchedRowsInfo
|
|
6640
|
+
};
|
|
6641
|
+
}
|
|
6642
|
+
if (attempt < attempts && retryDelayMs > 0) {
|
|
6643
|
+
await sleep4(retryDelayMs);
|
|
6644
|
+
}
|
|
6645
|
+
}
|
|
6365
6646
|
if (!rowsInfo.complete) {
|
|
6647
|
+
const retryCommand = runExportRetryCommand(
|
|
6648
|
+
status.runId,
|
|
6649
|
+
outPath,
|
|
6650
|
+
options.datasetPath ?? rowsInfo.source
|
|
6651
|
+
);
|
|
6366
6652
|
throw new DeeplineError(
|
|
6367
|
-
`Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows};
|
|
6653
|
+
`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}`,
|
|
6654
|
+
void 0,
|
|
6655
|
+
"RUN_EXPORT_NOT_READY",
|
|
6656
|
+
{
|
|
6657
|
+
runId: status.runId,
|
|
6658
|
+
previewRowCount: rowsInfo.rows.length,
|
|
6659
|
+
totalRows: rowsInfo.totalRows,
|
|
6660
|
+
tableNamespace: rowsInfo.tableNamespace ?? null,
|
|
6661
|
+
dataset: options.datasetPath ?? rowsInfo.source,
|
|
6662
|
+
retry_command: retryCommand
|
|
6663
|
+
}
|
|
6368
6664
|
);
|
|
6369
6665
|
}
|
|
6370
|
-
return writeCanonicalRowsCsv(rowsInfo, outPath);
|
|
6666
|
+
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6371
6667
|
}
|
|
6372
6668
|
function renderServerResultView(value) {
|
|
6373
6669
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -6441,8 +6737,10 @@ function writeStartedPlayRun(input) {
|
|
|
6441
6737
|
dashboardUrl: input.dashboardUrl
|
|
6442
6738
|
};
|
|
6443
6739
|
if (input.jsonOutput) {
|
|
6444
|
-
|
|
6445
|
-
|
|
6740
|
+
printCommandEnvelope({
|
|
6741
|
+
...payload,
|
|
6742
|
+
render: { sections: [{ title: "play run", lines: [`Started ${input.playName}`, `run id: ${input.runId}`] }] }
|
|
6743
|
+
}, { json: true });
|
|
6446
6744
|
return;
|
|
6447
6745
|
}
|
|
6448
6746
|
const lines = [
|
|
@@ -6461,10 +6759,14 @@ function writeStartedPlayRun(input) {
|
|
|
6461
6759
|
input.progress.writeLine(output, process.stdout);
|
|
6462
6760
|
return;
|
|
6463
6761
|
}
|
|
6464
|
-
|
|
6762
|
+
printCommandEnvelope({
|
|
6763
|
+
...payload,
|
|
6764
|
+
render: { sections: [{ title: "play run", lines }] }
|
|
6765
|
+
}, { json: false, text: `${output}
|
|
6766
|
+
` });
|
|
6465
6767
|
}
|
|
6466
6768
|
function parsePlayRunOptions(args) {
|
|
6467
|
-
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 --
|
|
6769
|
+
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.";
|
|
6468
6770
|
let filePath = null;
|
|
6469
6771
|
let playName = null;
|
|
6470
6772
|
let input = null;
|
|
@@ -6534,6 +6836,11 @@ function parsePlayRunOptions(args) {
|
|
|
6534
6836
|
}
|
|
6535
6837
|
continue;
|
|
6536
6838
|
}
|
|
6839
|
+
if (arg === "--csv" || arg.startsWith("--csv=")) {
|
|
6840
|
+
throw new Error(
|
|
6841
|
+
`--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`
|
|
6842
|
+
);
|
|
6843
|
+
}
|
|
6537
6844
|
if (arg.startsWith("--")) {
|
|
6538
6845
|
const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
|
|
6539
6846
|
input ??= {};
|
|
@@ -6719,11 +7026,6 @@ async function handleFileBackedRun(options) {
|
|
|
6719
7026
|
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
6720
7027
|
compilerManifest.staticPipeline
|
|
6721
7028
|
);
|
|
6722
|
-
applyCsvShortcutInput({
|
|
6723
|
-
runtimeInput,
|
|
6724
|
-
bindings: fileInputBindings,
|
|
6725
|
-
fallbackInputPath: "file"
|
|
6726
|
-
});
|
|
6727
7029
|
const stagedFileInputs = await traceCliSpan(
|
|
6728
7030
|
"cli.play_stage_inputs",
|
|
6729
7031
|
{
|
|
@@ -6765,10 +7067,13 @@ async function handleFileBackedRun(options) {
|
|
|
6765
7067
|
progress
|
|
6766
7068
|
})
|
|
6767
7069
|
);
|
|
6768
|
-
const
|
|
7070
|
+
const exportResult = await traceCliSpan(
|
|
6769
7071
|
"cli.play_export_rows",
|
|
6770
7072
|
{ targetKind: "file", playName },
|
|
6771
|
-
() => exportPlayStatusRows(finalStatus, options.outPath
|
|
7073
|
+
() => exportPlayStatusRows(client, finalStatus, options.outPath, {
|
|
7074
|
+
attempts: PLAY_RUN_OUT_EXPORT_ATTEMPTS,
|
|
7075
|
+
retryDelayMs: PLAY_RUN_OUT_EXPORT_RETRY_DELAY_MS
|
|
7076
|
+
})
|
|
6772
7077
|
);
|
|
6773
7078
|
if (finalStatus.status === "completed") {
|
|
6774
7079
|
progress.complete();
|
|
@@ -6778,7 +7083,7 @@ async function handleFileBackedRun(options) {
|
|
|
6778
7083
|
traceCliSync(
|
|
6779
7084
|
"cli.play_write_result",
|
|
6780
7085
|
{ targetKind: "file", playName },
|
|
6781
|
-
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath })
|
|
7086
|
+
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath: exportResult?.path ?? null })
|
|
6782
7087
|
);
|
|
6783
7088
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
6784
7089
|
}
|
|
@@ -6868,10 +7173,6 @@ async function handleNamedRun(options) {
|
|
|
6868
7173
|
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
6869
7174
|
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
6870
7175
|
] : [];
|
|
6871
|
-
applyCsvShortcutInput({
|
|
6872
|
-
runtimeInput,
|
|
6873
|
-
bindings: fileInputBindings
|
|
6874
|
-
});
|
|
6875
7176
|
const stagedFileInputs = await traceCliSpan(
|
|
6876
7177
|
"cli.play_stage_inputs",
|
|
6877
7178
|
{
|
|
@@ -6910,10 +7211,13 @@ async function handleNamedRun(options) {
|
|
|
6910
7211
|
progress
|
|
6911
7212
|
})
|
|
6912
7213
|
);
|
|
6913
|
-
const
|
|
7214
|
+
const exportResult = await traceCliSpan(
|
|
6914
7215
|
"cli.play_export_rows",
|
|
6915
7216
|
{ targetKind: "name", playName },
|
|
6916
|
-
() => exportPlayStatusRows(finalStatus, options.outPath
|
|
7217
|
+
() => exportPlayStatusRows(client, finalStatus, options.outPath, {
|
|
7218
|
+
attempts: PLAY_RUN_OUT_EXPORT_ATTEMPTS,
|
|
7219
|
+
retryDelayMs: PLAY_RUN_OUT_EXPORT_RETRY_DELAY_MS
|
|
7220
|
+
})
|
|
6917
7221
|
);
|
|
6918
7222
|
if (finalStatus.status === "completed") {
|
|
6919
7223
|
progress.complete();
|
|
@@ -6923,7 +7227,7 @@ async function handleNamedRun(options) {
|
|
|
6923
7227
|
traceCliSync(
|
|
6924
7228
|
"cli.play_write_result",
|
|
6925
7229
|
{ targetKind: "name", playName },
|
|
6926
|
-
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath })
|
|
7230
|
+
() => writePlayResult(finalStatus, options.jsonOutput, { exportedPath: exportResult?.path ?? null })
|
|
6927
7231
|
);
|
|
6928
7232
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
6929
7233
|
}
|
|
@@ -6992,7 +7296,7 @@ function parseRunIdPositional(args, usage) {
|
|
|
6992
7296
|
}
|
|
6993
7297
|
continue;
|
|
6994
7298
|
}
|
|
6995
|
-
if ((arg === "--out" || arg === "--reason") && args[index + 1]) {
|
|
7299
|
+
if ((arg === "--out" || arg === "--reason" || arg === "--dataset") && args[index + 1]) {
|
|
6996
7300
|
index += 1;
|
|
6997
7301
|
continue;
|
|
6998
7302
|
}
|
|
@@ -7054,26 +7358,15 @@ async function handleRunsList(args) {
|
|
|
7054
7358
|
executionTime: run.executionTime,
|
|
7055
7359
|
playName: run.memo?.playName ?? playName
|
|
7056
7360
|
}));
|
|
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
|
-
}
|
|
7361
|
+
const lines = runs.length === 0 ? [`No runs found for ${playName}.`] : runs.map((run) => `${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
7362
|
+
printCommandEnvelope({
|
|
7363
|
+
runs,
|
|
7364
|
+
count: runs.length,
|
|
7365
|
+
next: {
|
|
7366
|
+
get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
|
|
7367
|
+
},
|
|
7368
|
+
render: { sections: [{ title: "runs", lines }] }
|
|
7369
|
+
}, { json: argsWantJson(args) });
|
|
7077
7370
|
return 0;
|
|
7078
7371
|
}
|
|
7079
7372
|
async function handleRunTail(args) {
|
|
@@ -7127,41 +7420,30 @@ async function handleRunLogs(args) {
|
|
|
7127
7420
|
const logs = status.progress?.logs ?? [];
|
|
7128
7421
|
if (outPath) {
|
|
7129
7422
|
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
|
-
}
|
|
7423
|
+
printCommandEnvelope({
|
|
7424
|
+
runId: status.runId,
|
|
7425
|
+
log_path: outPath,
|
|
7426
|
+
lineCount: logs.length,
|
|
7427
|
+
local: { log_path: outPath },
|
|
7428
|
+
render: { sections: [{ title: "run logs", lines: [`Wrote ${logs.length} log lines to ${outPath}`] }] }
|
|
7429
|
+
}, { json: argsWantJson(args) });
|
|
7142
7430
|
return 0;
|
|
7143
7431
|
}
|
|
7144
7432
|
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
|
-
}
|
|
7433
|
+
printCommandEnvelope({
|
|
7434
|
+
runId: status.runId,
|
|
7435
|
+
totalCount: logs.length,
|
|
7436
|
+
returnedCount: entries.length,
|
|
7437
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
7438
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
7439
|
+
truncated: logs.length > entries.length,
|
|
7440
|
+
hasMore: logs.length > entries.length,
|
|
7441
|
+
entries,
|
|
7442
|
+
next: {
|
|
7443
|
+
export: `deepline runs logs ${status.runId} --out run.log --json`
|
|
7444
|
+
},
|
|
7445
|
+
render: { sections: [{ title: "run logs", lines: entries }] }
|
|
7446
|
+
}, { json: argsWantJson(args), text: `${entries.join("\n")}${entries.length > 0 ? "\n" : ""}` });
|
|
7165
7447
|
return 0;
|
|
7166
7448
|
}
|
|
7167
7449
|
async function handleRunStop(args) {
|
|
@@ -7182,19 +7464,18 @@ async function handleRunStop(args) {
|
|
|
7182
7464
|
}
|
|
7183
7465
|
const client = new DeeplineClient();
|
|
7184
7466
|
const result = await client.runs.stop(runId, { reason });
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
`
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
}
|
|
7467
|
+
const lines = [
|
|
7468
|
+
`Stopped ${result.runId}`,
|
|
7469
|
+
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : []
|
|
7470
|
+
];
|
|
7471
|
+
printCommandEnvelope({
|
|
7472
|
+
...result,
|
|
7473
|
+
render: { sections: [{ title: "run stop", lines }] }
|
|
7474
|
+
}, { json: argsWantJson(args) });
|
|
7194
7475
|
return 0;
|
|
7195
7476
|
}
|
|
7196
7477
|
async function handleRunExport(args) {
|
|
7197
|
-
const usage = "Usage: deepline runs export <run-id> --out output.csv [--json]";
|
|
7478
|
+
const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--json]";
|
|
7198
7479
|
let runId;
|
|
7199
7480
|
try {
|
|
7200
7481
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -7203,10 +7484,15 @@ async function handleRunExport(args) {
|
|
|
7203
7484
|
return 1;
|
|
7204
7485
|
}
|
|
7205
7486
|
let outPath = null;
|
|
7487
|
+
let datasetPath = null;
|
|
7206
7488
|
for (let index = 0; index < args.length; index += 1) {
|
|
7207
7489
|
const arg = args[index];
|
|
7208
7490
|
if (arg === "--out" && args[index + 1]) {
|
|
7209
7491
|
outPath = resolve8(args[++index]);
|
|
7492
|
+
continue;
|
|
7493
|
+
}
|
|
7494
|
+
if (arg === "--dataset" && args[index + 1]) {
|
|
7495
|
+
datasetPath = args[++index];
|
|
7210
7496
|
}
|
|
7211
7497
|
}
|
|
7212
7498
|
if (!outPath) {
|
|
@@ -7215,21 +7501,18 @@ async function handleRunExport(args) {
|
|
|
7215
7501
|
}
|
|
7216
7502
|
const client = new DeeplineClient();
|
|
7217
7503
|
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
|
-
}
|
|
7504
|
+
const exportResult = await exportPlayStatusRows(client, status, outPath, {
|
|
7505
|
+
datasetPath
|
|
7506
|
+
});
|
|
7507
|
+
printCommandEnvelope({
|
|
7508
|
+
runId: status.runId,
|
|
7509
|
+
...datasetPath ? { dataset: datasetPath } : {},
|
|
7510
|
+
csv_path: exportResult?.path ?? null,
|
|
7511
|
+
rowCount: exportResult?.rowsInfo.totalRows ?? null,
|
|
7512
|
+
columns: exportResult?.rowsInfo.columns ?? [],
|
|
7513
|
+
local: { csv_path: exportResult?.path ?? null },
|
|
7514
|
+
render: { sections: [{ title: "run export", lines: [`Exported ${status.runId} to ${exportResult?.path ?? outPath}`] }] }
|
|
7515
|
+
}, { json: argsWantJson(args) });
|
|
7233
7516
|
return 0;
|
|
7234
7517
|
}
|
|
7235
7518
|
async function handlePlayGet(args) {
|
|
@@ -7677,8 +7960,7 @@ Notes:
|
|
|
7677
7960
|
Local files are bundled, preflighted, then run in Deepline cloud.
|
|
7678
7961
|
Named plays run the live saved revision.
|
|
7679
7962
|
Unknown --foo value and --foo.bar value flags are passed into play input.
|
|
7680
|
-
Example: --
|
|
7681
|
-
input.limit = 5.
|
|
7963
|
+
Example: --limit 5 becomes input.limit = 5.
|
|
7682
7964
|
File args accept local paths; the CLI stages files before submit.
|
|
7683
7965
|
--watch prints logs, previews, stats, and next commands.
|
|
7684
7966
|
--wait is accepted as a compatibility alias for --watch.
|
|
@@ -7711,8 +7993,8 @@ Idempotent execution:
|
|
|
7711
7993
|
Examples:
|
|
7712
7994
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
|
|
7713
7995
|
deepline plays run my.play.ts --input @input.json --wait --json
|
|
7996
|
+
deepline plays run enrich.play.ts --input '{"file":"leads.csv"}' --watch --out leads-enriched.csv
|
|
7714
7997
|
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
7998
|
deepline plays run cto-search.play.ts --limit 5 --watch
|
|
7717
7999
|
deepline runs get <run-id>
|
|
7718
8000
|
`
|
|
@@ -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];
|
|
@@ -7916,7 +8199,7 @@ Examples:
|
|
|
7916
8199
|
`
|
|
7917
8200
|
Concepts:
|
|
7918
8201
|
A run is one execution instance of a play. It has status, progress, logs,
|
|
7919
|
-
|
|
8202
|
+
returned result, dataset previews, recovery metadata, and optional dataset export.
|
|
7920
8203
|
tail reads the live stream. logs fetches persisted logs after the fact.
|
|
7921
8204
|
stop mutates cloud state by requesting cancellation.
|
|
7922
8205
|
|
|
@@ -7929,7 +8212,7 @@ Examples:
|
|
|
7929
8212
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
7930
8213
|
`
|
|
7931
8214
|
);
|
|
7932
|
-
runs.command("get <runId>").description("Get status, progress,
|
|
8215
|
+
runs.command("get <runId>").description("Get status, progress, result, errors, and recovery metadata for a play run.").addHelpText(
|
|
7933
8216
|
"after",
|
|
7934
8217
|
`
|
|
7935
8218
|
Notes:
|
|
@@ -8023,20 +8306,22 @@ Examples:
|
|
|
8023
8306
|
...options.json ? ["--json"] : []
|
|
8024
8307
|
]);
|
|
8025
8308
|
});
|
|
8026
|
-
runs.command("export <runId>").description("Export
|
|
8309
|
+
runs.command("export <runId>").description("Export a returned dataset handle for a play run to CSV.").addHelpText(
|
|
8027
8310
|
"after",
|
|
8028
8311
|
`
|
|
8029
8312
|
Notes:
|
|
8030
|
-
Writes
|
|
8031
|
-
first
|
|
8313
|
+
Writes a returned dataset handle to the requested local CSV path. Use runs get
|
|
8314
|
+
first to inspect dataset paths like result.rows or result.nested.contacts.
|
|
8032
8315
|
|
|
8033
8316
|
Examples:
|
|
8034
8317
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
8318
|
+
deepline runs export play/my-play/run/20260501t000000-000 --dataset result.rows --out output.csv
|
|
8035
8319
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --json
|
|
8036
8320
|
`
|
|
8037
|
-
).requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
8321
|
+
).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
8322
|
process.exitCode = await handleRunExport([
|
|
8039
8323
|
runId,
|
|
8324
|
+
...options.dataset ? ["--dataset", options.dataset] : [],
|
|
8040
8325
|
"--out",
|
|
8041
8326
|
options.out,
|
|
8042
8327
|
...options.json ? ["--json"] : []
|
|
@@ -8202,19 +8487,19 @@ function toListedTool(tool) {
|
|
|
8202
8487
|
async function listTools(args) {
|
|
8203
8488
|
const client = new DeeplineClient();
|
|
8204
8489
|
const items = (await client.listTools()).map(toListedTool);
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
`);
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
}
|
|
8490
|
+
const render = {
|
|
8491
|
+
sections: [
|
|
8492
|
+
{
|
|
8493
|
+
title: `${items.length} tools available:`,
|
|
8494
|
+
lines: items.flatMap((item) => {
|
|
8495
|
+
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8496
|
+
const listHint = item.listExtractorPaths?.length ? ` listExtractorPaths=${item.listExtractorPaths.join(",")}` : "";
|
|
8497
|
+
return [`${item.toolId}${cats}`, ` ${item.description}${listHint}`];
|
|
8498
|
+
})
|
|
8499
|
+
}
|
|
8500
|
+
]
|
|
8501
|
+
};
|
|
8502
|
+
printCommandEnvelope({ tools: items, count: items.length, render }, { json: argsWantJson(args) });
|
|
8218
8503
|
return 0;
|
|
8219
8504
|
}
|
|
8220
8505
|
async function searchTools(queryInput, options = {}) {
|
|
@@ -8232,21 +8517,27 @@ async function searchTools(queryInput, options = {}) {
|
|
|
8232
8517
|
includeSearchDebug: options.includeSearchDebug
|
|
8233
8518
|
});
|
|
8234
8519
|
const items = result.tools.map(toListedTool);
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8520
|
+
const envelope = {
|
|
8521
|
+
...result,
|
|
8522
|
+
tools: items,
|
|
8523
|
+
count: items.length,
|
|
8524
|
+
render: {
|
|
8525
|
+
sections: [
|
|
8526
|
+
{
|
|
8527
|
+
title: `${items.length} tools found:`,
|
|
8528
|
+
lines: items.flatMap((item) => {
|
|
8529
|
+
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8530
|
+
return [
|
|
8531
|
+
`${item.toolId}${cats}`,
|
|
8532
|
+
` ${item.description}`,
|
|
8533
|
+
...item.inputSchema ? [" inputSchema: yes"] : []
|
|
8534
|
+
];
|
|
8535
|
+
})
|
|
8536
|
+
}
|
|
8537
|
+
]
|
|
8248
8538
|
}
|
|
8249
|
-
}
|
|
8539
|
+
};
|
|
8540
|
+
printCommandEnvelope(envelope, { json: options.json || shouldEmitJson() });
|
|
8250
8541
|
return 0;
|
|
8251
8542
|
}
|
|
8252
8543
|
function playIdentifiers(play) {
|
|
@@ -8578,6 +8869,9 @@ function samplePayload(samples, key) {
|
|
|
8578
8869
|
if (!isRecord3(entry)) return void 0;
|
|
8579
8870
|
return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
|
|
8580
8871
|
}
|
|
8872
|
+
function commandEnvelopeFromRawResponse(rawResponse) {
|
|
8873
|
+
return isRecord3(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
|
|
8874
|
+
}
|
|
8581
8875
|
function isPlayTool(tool) {
|
|
8582
8876
|
const provider = typeof tool.provider === "string" ? tool.provider : "";
|
|
8583
8877
|
return provider === "deepline_native" && Boolean(recordField(tool, "playExpansion", "play_expansion"));
|
|
@@ -8742,6 +9036,53 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
8742
9036
|
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
9037
|
};
|
|
8744
9038
|
}
|
|
9039
|
+
function buildToolExecuteBaseEnvelope(input) {
|
|
9040
|
+
const envelope = commandEnvelopeFromRawResponse(input.rawResponse);
|
|
9041
|
+
const summaryEntries = Object.entries(input.summary);
|
|
9042
|
+
const output = input.listConversion ? {
|
|
9043
|
+
kind: "list",
|
|
9044
|
+
rowCount: input.listConversion.rows.length,
|
|
9045
|
+
columns: Object.keys(input.listConversion.rows[0] ?? {}),
|
|
9046
|
+
preview: input.listConversion.rows.slice(0, 5),
|
|
9047
|
+
listStrategy: input.listConversion.strategy,
|
|
9048
|
+
listSourcePath: input.listConversion.sourcePath
|
|
9049
|
+
} : {
|
|
9050
|
+
kind: summaryEntries.length > 0 ? "object" : "raw",
|
|
9051
|
+
summary: input.summary
|
|
9052
|
+
};
|
|
9053
|
+
const actions = input.listConversion ? [
|
|
9054
|
+
{
|
|
9055
|
+
label: "next",
|
|
9056
|
+
command: "move starter script into a project folder and expand it into a Deepline play"
|
|
9057
|
+
}
|
|
9058
|
+
] : [];
|
|
9059
|
+
return {
|
|
9060
|
+
...envelope,
|
|
9061
|
+
output,
|
|
9062
|
+
...summaryEntries.length > 0 ? { summary: input.summary } : {},
|
|
9063
|
+
next: input.listConversion ? {
|
|
9064
|
+
expandToPlay: "Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
|
|
9065
|
+
} : {},
|
|
9066
|
+
render: {
|
|
9067
|
+
sections: input.listConversion ? [
|
|
9068
|
+
{
|
|
9069
|
+
title: "output",
|
|
9070
|
+
lines: [
|
|
9071
|
+
`${input.listConversion.rows.length} row(s) extracted from ${input.listConversion.sourcePath ?? "auto-detected list"}`,
|
|
9072
|
+
`columns: ${JSON.stringify(Object.keys(input.listConversion.rows[0] ?? {}))}`,
|
|
9073
|
+
`preview: ${JSON.stringify(input.listConversion.rows.slice(0, 5))}`
|
|
9074
|
+
]
|
|
9075
|
+
}
|
|
9076
|
+
] : [
|
|
9077
|
+
{
|
|
9078
|
+
title: "result",
|
|
9079
|
+
lines: summaryEntries.length > 0 ? summaryEntries.map(([key, value]) => `${key}=${String(value)}`) : [JSON.stringify(input.rawResponse, null, 2)]
|
|
9080
|
+
}
|
|
9081
|
+
],
|
|
9082
|
+
actions
|
|
9083
|
+
}
|
|
9084
|
+
};
|
|
9085
|
+
}
|
|
8745
9086
|
async function executeTool(args) {
|
|
8746
9087
|
let parsed;
|
|
8747
9088
|
try {
|
|
@@ -8774,24 +9115,45 @@ async function executeTool(args) {
|
|
|
8774
9115
|
listExtractorPaths: metadata.listExtractorPaths ?? []
|
|
8775
9116
|
});
|
|
8776
9117
|
const summary = extractSummaryFields(rawResponse);
|
|
9118
|
+
const baseEnvelope = buildToolExecuteBaseEnvelope({
|
|
9119
|
+
toolId: parsed.toolId,
|
|
9120
|
+
rawResponse,
|
|
9121
|
+
listConversion,
|
|
9122
|
+
summary
|
|
9123
|
+
});
|
|
8777
9124
|
if (parsed.outputFormat === "json" || parsed.outputFormat === "auto" && shouldEmitJson()) {
|
|
8778
|
-
|
|
8779
|
-
`);
|
|
9125
|
+
printCommandEnvelope(baseEnvelope, { json: true });
|
|
8780
9126
|
return 0;
|
|
8781
9127
|
}
|
|
8782
9128
|
if (parsed.outputFormat === "json_file") {
|
|
8783
9129
|
const jsonPath = writeJsonOutputFile(rawResponse, `payload_${parsed.toolId}`);
|
|
8784
|
-
|
|
9130
|
+
printCommandEnvelope(
|
|
9131
|
+
{
|
|
9132
|
+
...baseEnvelope,
|
|
9133
|
+
local: {
|
|
9134
|
+
...isRecord3(baseEnvelope.local) ? baseEnvelope.local : {},
|
|
9135
|
+
payload_file: jsonPath
|
|
9136
|
+
}
|
|
9137
|
+
},
|
|
9138
|
+
{ json: true }
|
|
9139
|
+
);
|
|
8785
9140
|
return 0;
|
|
8786
9141
|
}
|
|
8787
9142
|
if (!listConversion) {
|
|
8788
9143
|
if (parsed.outputFormat === "csv" || parsed.outputFormat === "csv_file") {
|
|
8789
9144
|
const jsonPath = writeJsonOutputFile(rawResponse, `payload_${parsed.toolId}`);
|
|
8790
|
-
|
|
9145
|
+
printCommandEnvelope(
|
|
9146
|
+
{
|
|
9147
|
+
...baseEnvelope,
|
|
9148
|
+
local: {
|
|
9149
|
+
payload_file: jsonPath
|
|
9150
|
+
}
|
|
9151
|
+
},
|
|
9152
|
+
{ json: parsed.outputFormat === "csv_file" || shouldEmitJson() }
|
|
9153
|
+
);
|
|
8791
9154
|
return 0;
|
|
8792
9155
|
}
|
|
8793
|
-
|
|
8794
|
-
`);
|
|
9156
|
+
printCommandEnvelope(baseEnvelope, { json: false });
|
|
8795
9157
|
return 0;
|
|
8796
9158
|
}
|
|
8797
9159
|
const csv = writeCsvOutputFile(listConversion.rows, `${parsed.toolId}_output`);
|
|
@@ -8800,8 +9162,51 @@ async function executeTool(args) {
|
|
|
8800
9162
|
payload: parsed.params,
|
|
8801
9163
|
rows: listConversion.rows
|
|
8802
9164
|
});
|
|
9165
|
+
const materializedEnvelope = {
|
|
9166
|
+
...baseEnvelope,
|
|
9167
|
+
local: {
|
|
9168
|
+
extracted_csv: csv.path,
|
|
9169
|
+
extracted_csv_rows: csv.rowCount,
|
|
9170
|
+
extracted_csv_columns: csv.columns,
|
|
9171
|
+
preview: csv.preview,
|
|
9172
|
+
starter_script: seededScript.path,
|
|
9173
|
+
project_dir: seededScript.projectDir,
|
|
9174
|
+
copy_to_project: {
|
|
9175
|
+
macos_linux: seededScript.macCopyCommand,
|
|
9176
|
+
windows_powershell: seededScript.windowsCopyCommand
|
|
9177
|
+
}
|
|
9178
|
+
},
|
|
9179
|
+
render: {
|
|
9180
|
+
sections: [
|
|
9181
|
+
{
|
|
9182
|
+
title: `${csv.path} (${csv.rowCount} rows)`,
|
|
9183
|
+
lines: [
|
|
9184
|
+
...csv.columns.length > 0 ? [`columns: ${JSON.stringify(csv.columns)}`] : [],
|
|
9185
|
+
...Object.keys(summary).length > 0 ? [`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`] : [],
|
|
9186
|
+
`preview: ${JSON.stringify(csv.preview)}`,
|
|
9187
|
+
`starter script: ${seededScript.path}`
|
|
9188
|
+
]
|
|
9189
|
+
}
|
|
9190
|
+
],
|
|
9191
|
+
actions: [
|
|
9192
|
+
{
|
|
9193
|
+
label: "next",
|
|
9194
|
+
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."
|
|
9195
|
+
},
|
|
9196
|
+
{
|
|
9197
|
+
label: "macOS/Linux",
|
|
9198
|
+
command: seededScript.macCopyCommand
|
|
9199
|
+
},
|
|
9200
|
+
{
|
|
9201
|
+
label: "Windows PowerShell",
|
|
9202
|
+
command: seededScript.windowsCopyCommand
|
|
9203
|
+
}
|
|
9204
|
+
]
|
|
9205
|
+
}
|
|
9206
|
+
};
|
|
8803
9207
|
if (parsed.outputFormat === "csv_file") {
|
|
8804
|
-
|
|
9208
|
+
printCommandEnvelope({
|
|
9209
|
+
...materializedEnvelope,
|
|
8805
9210
|
extracted_csv: csv.path,
|
|
8806
9211
|
extracted_csv_rows: csv.rowCount,
|
|
8807
9212
|
extracted_csv_columns: csv.columns,
|
|
@@ -8815,28 +9220,24 @@ async function executeTool(args) {
|
|
|
8815
9220
|
windows_powershell: seededScript.windowsCopyCommand
|
|
8816
9221
|
},
|
|
8817
9222
|
summary
|
|
8818
|
-
})
|
|
8819
|
-
`);
|
|
9223
|
+
}, { json: true });
|
|
8820
9224
|
return 0;
|
|
8821
9225
|
}
|
|
8822
9226
|
if (parsed.noPreview) {
|
|
8823
|
-
|
|
9227
|
+
printCommandEnvelope(
|
|
9228
|
+
{
|
|
9229
|
+
...materializedEnvelope,
|
|
9230
|
+
local: {
|
|
9231
|
+
...materializedEnvelope.local ?? {},
|
|
9232
|
+
output_path: csv.path
|
|
9233
|
+
}
|
|
9234
|
+
},
|
|
9235
|
+
{ json: shouldEmitJson(), text: `${csv.path}
|
|
9236
|
+
` }
|
|
9237
|
+
);
|
|
8824
9238
|
return 0;
|
|
8825
9239
|
}
|
|
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}`);
|
|
9240
|
+
printCommandEnvelope(materializedEnvelope, { json: false });
|
|
8840
9241
|
return 0;
|
|
8841
9242
|
}
|
|
8842
9243
|
|
|
@@ -8910,23 +9311,36 @@ function runCommand(command, args) {
|
|
|
8910
9311
|
}
|
|
8911
9312
|
async function handleUpdate(options) {
|
|
8912
9313
|
const plan = resolveUpdatePlan();
|
|
9314
|
+
const render = {
|
|
9315
|
+
sections: [
|
|
9316
|
+
{
|
|
9317
|
+
title: "update",
|
|
9318
|
+
lines: plan.kind === "source" ? [
|
|
9319
|
+
"This Deepline CLI is running from SDK source, so it cannot safely update itself like an npm global.",
|
|
9320
|
+
`Update the backing checkout with: ${plan.manualCommand}`
|
|
9321
|
+
] : [`Updating Deepline SDK/CLI with: ${plan.manualCommand}`]
|
|
9322
|
+
}
|
|
9323
|
+
]
|
|
9324
|
+
};
|
|
8913
9325
|
if (options.json) {
|
|
8914
|
-
|
|
8915
|
-
`);
|
|
9326
|
+
printCommandEnvelope({ ...plan, render }, { json: true });
|
|
8916
9327
|
return 0;
|
|
8917
9328
|
}
|
|
8918
9329
|
if (options.printCommand) {
|
|
8919
|
-
|
|
8920
|
-
|
|
9330
|
+
printCommandEnvelope(
|
|
9331
|
+
{
|
|
9332
|
+
...plan,
|
|
9333
|
+
render: {
|
|
9334
|
+
sections: [{ title: "update command", lines: [plan.manualCommand] }]
|
|
9335
|
+
}
|
|
9336
|
+
},
|
|
9337
|
+
{ json: false, text: `${plan.manualCommand}
|
|
9338
|
+
` }
|
|
9339
|
+
);
|
|
8921
9340
|
return 0;
|
|
8922
9341
|
}
|
|
8923
9342
|
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
|
-
);
|
|
9343
|
+
printCommandEnvelope({ ...plan, render }, { json: false });
|
|
8930
9344
|
return 0;
|
|
8931
9345
|
}
|
|
8932
9346
|
process.stderr.write(`Updating Deepline SDK/CLI with: ${plan.manualCommand}
|
|
@@ -9239,7 +9653,7 @@ Exit codes:
|
|
|
9239
9653
|
registerDbCommands(program);
|
|
9240
9654
|
registerFeedbackCommands(program);
|
|
9241
9655
|
registerUpdateCommand(program);
|
|
9242
|
-
program.command("health").description("Check server health.").addHelpText(
|
|
9656
|
+
program.command("health").description("Check server health.").option("--json", "Force JSON output.").addHelpText(
|
|
9243
9657
|
"after",
|
|
9244
9658
|
`
|
|
9245
9659
|
Notes:
|