deepline 0.1.33 → 0.1.35
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/dist/cli/index.js +959 -207
- package/dist/cli/index.mjs +892 -140
- package/dist/index.d.mts +127 -63
- package/dist/index.d.ts +127 -63
- package/dist/index.js +516 -45
- package/dist/index.mjs +516 -45
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +212 -1
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +129 -2
- package/dist/repo/apps/play-runner-workers/src/entry.ts +147 -64
- package/dist/repo/apps/play-runner-workers/src/workflow-retry.ts +50 -0
- package/dist/repo/sdk/src/client.ts +46 -33
- package/dist/repo/sdk/src/http.ts +44 -4
- package/dist/repo/sdk/src/index.ts +8 -5
- package/dist/repo/sdk/src/play.ts +124 -45
- package/dist/repo/sdk/src/plays/harness-stub.ts +2 -2
- package/dist/repo/sdk/src/tool-output.ts +26 -7
- package/dist/repo/sdk/src/types.ts +45 -11
- package/dist/repo/sdk/src/version.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/run-failure.ts +49 -0
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +138 -35
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -193,8 +193,8 @@ function resolveConfig(options) {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
// src/version.ts
|
|
196
|
-
var SDK_VERSION = "0.1.
|
|
197
|
-
var SDK_API_CONTRACT = "2026-05-
|
|
196
|
+
var SDK_VERSION = "0.1.35";
|
|
197
|
+
var SDK_API_CONTRACT = "2026-05-v2-tool-result-contract";
|
|
198
198
|
|
|
199
199
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
200
200
|
var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
|
|
@@ -276,7 +276,7 @@ var HttpClient = class {
|
|
|
276
276
|
const response = await fetch(candidateUrl, {
|
|
277
277
|
method,
|
|
278
278
|
headers,
|
|
279
|
-
body: options?.formData !== void 0 ? options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
279
|
+
body: options?.formData !== void 0 ? typeof options.formData === "function" ? options.formData() : options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
280
280
|
signal: controller.signal
|
|
281
281
|
});
|
|
282
282
|
clearTimeout(timeoutId);
|
|
@@ -359,10 +359,13 @@ var HttpClient = class {
|
|
|
359
359
|
throw new AuthError();
|
|
360
360
|
}
|
|
361
361
|
if (!response.ok) {
|
|
362
|
+
const body = await response.text();
|
|
363
|
+
const parsed = parseResponseBody(body);
|
|
362
364
|
throw new DeeplineError(
|
|
363
|
-
|
|
365
|
+
apiErrorMessage(parsed, response.status),
|
|
364
366
|
response.status,
|
|
365
|
-
"API_ERROR"
|
|
367
|
+
"API_ERROR",
|
|
368
|
+
{ response: parsed }
|
|
366
369
|
);
|
|
367
370
|
}
|
|
368
371
|
if (!response.body) {
|
|
@@ -412,6 +415,26 @@ var HttpClient = class {
|
|
|
412
415
|
return this.request(path, { method: "DELETE" });
|
|
413
416
|
}
|
|
414
417
|
};
|
|
418
|
+
function parseResponseBody(body) {
|
|
419
|
+
try {
|
|
420
|
+
return JSON.parse(body);
|
|
421
|
+
} catch {
|
|
422
|
+
return body;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function apiErrorMessage(parsed, status) {
|
|
426
|
+
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
427
|
+
if (typeof errorValue === "string") {
|
|
428
|
+
return errorValue;
|
|
429
|
+
}
|
|
430
|
+
if (errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string") {
|
|
431
|
+
return errorValue.message;
|
|
432
|
+
}
|
|
433
|
+
if (typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string") {
|
|
434
|
+
return parsed.message;
|
|
435
|
+
}
|
|
436
|
+
return `HTTP ${status}`;
|
|
437
|
+
}
|
|
415
438
|
function parseRetryAfter(response) {
|
|
416
439
|
const header = response.headers.get("retry-after");
|
|
417
440
|
if (header) {
|
|
@@ -475,15 +498,17 @@ function decodeSseFrame(frame) {
|
|
|
475
498
|
return parsed;
|
|
476
499
|
}
|
|
477
500
|
function sleep(ms) {
|
|
478
|
-
return new Promise((
|
|
501
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
479
502
|
}
|
|
480
503
|
|
|
481
504
|
// src/client.ts
|
|
482
505
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
483
506
|
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
507
|
+
var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
|
|
508
|
+
var V2_EXECUTE_RESPONSE_CONTRACT = "v2-tool-execution-result";
|
|
484
509
|
var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
|
|
485
510
|
function sleep2(ms) {
|
|
486
|
-
return new Promise((
|
|
511
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
487
512
|
}
|
|
488
513
|
function isTransientCompileManifestError(error) {
|
|
489
514
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
@@ -749,13 +774,16 @@ var DeeplineClient = class {
|
|
|
749
774
|
/**
|
|
750
775
|
* Execute a tool and return the standard execution envelope.
|
|
751
776
|
*
|
|
752
|
-
* The `
|
|
753
|
-
* contains provider
|
|
777
|
+
* The `toolExecutionResult.toolOutput.raw` field contains the raw tool output.
|
|
778
|
+
* `toolExecutionResult.toolOutput.meta` contains tool/provider metadata.
|
|
754
779
|
* Top-level fields such as `status`, `job_id`, and `billing` describe the
|
|
755
|
-
* Deepline execution.
|
|
780
|
+
* Deepline execution envelope.
|
|
756
781
|
*/
|
|
757
782
|
async executeTool(toolId, input, options) {
|
|
758
|
-
const headers =
|
|
783
|
+
const headers = {
|
|
784
|
+
[EXECUTE_RESPONSE_CONTRACT_HEADER]: V2_EXECUTE_RESPONSE_CONTRACT,
|
|
785
|
+
...options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : {}
|
|
786
|
+
};
|
|
759
787
|
return this.http.post(
|
|
760
788
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
761
789
|
{ payload: input },
|
|
@@ -1053,34 +1081,37 @@ var DeeplineClient = class {
|
|
|
1053
1081
|
* ```
|
|
1054
1082
|
*/
|
|
1055
1083
|
async stagePlayFiles(files) {
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
"metadata",
|
|
1059
|
-
JSON.stringify({
|
|
1060
|
-
files: files.map((file, index) => ({
|
|
1061
|
-
index,
|
|
1062
|
-
logicalPath: file.logicalPath,
|
|
1063
|
-
contentHash: file.contentHash,
|
|
1064
|
-
contentType: file.contentType,
|
|
1065
|
-
bytes: file.bytes
|
|
1066
|
-
}))
|
|
1067
|
-
})
|
|
1068
|
-
);
|
|
1069
|
-
for (const [index, file] of files.entries()) {
|
|
1070
|
-
const bytes = decodeBase64Bytes(file.contentBase64);
|
|
1071
|
-
const body = bytes.buffer.slice(
|
|
1072
|
-
bytes.byteOffset,
|
|
1073
|
-
bytes.byteOffset + bytes.byteLength
|
|
1074
|
-
);
|
|
1084
|
+
const buildFormData = () => {
|
|
1085
|
+
const formData = new FormData();
|
|
1075
1086
|
formData.set(
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1087
|
+
"metadata",
|
|
1088
|
+
JSON.stringify({
|
|
1089
|
+
files: files.map((file, index) => ({
|
|
1090
|
+
index,
|
|
1091
|
+
logicalPath: file.logicalPath,
|
|
1092
|
+
contentHash: file.contentHash,
|
|
1093
|
+
contentType: file.contentType,
|
|
1094
|
+
bytes: file.bytes
|
|
1095
|
+
}))
|
|
1096
|
+
})
|
|
1079
1097
|
);
|
|
1080
|
-
|
|
1098
|
+
for (const [index, file] of files.entries()) {
|
|
1099
|
+
const bytes = decodeBase64Bytes(file.contentBase64);
|
|
1100
|
+
const body = bytes.buffer.slice(
|
|
1101
|
+
bytes.byteOffset,
|
|
1102
|
+
bytes.byteOffset + bytes.byteLength
|
|
1103
|
+
);
|
|
1104
|
+
formData.set(
|
|
1105
|
+
`file:${index}`,
|
|
1106
|
+
new Blob([body], { type: file.contentType }),
|
|
1107
|
+
file.logicalPath
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
return formData;
|
|
1111
|
+
};
|
|
1081
1112
|
const response = await this.http.postFormData(
|
|
1082
1113
|
"/api/v2/plays/files/stage",
|
|
1083
|
-
|
|
1114
|
+
buildFormData
|
|
1084
1115
|
);
|
|
1085
1116
|
return response.files;
|
|
1086
1117
|
}
|
|
@@ -1874,6 +1905,103 @@ function csvStringFromRows(rows, columns) {
|
|
|
1874
1905
|
...columns?.length ? { columns } : {}
|
|
1875
1906
|
});
|
|
1876
1907
|
}
|
|
1908
|
+
function parseMaybeJsonObject(value) {
|
|
1909
|
+
if (typeof value !== "string") {
|
|
1910
|
+
return value;
|
|
1911
|
+
}
|
|
1912
|
+
const trimmed = value.trim();
|
|
1913
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
|
1914
|
+
return value;
|
|
1915
|
+
}
|
|
1916
|
+
try {
|
|
1917
|
+
return JSON.parse(trimmed);
|
|
1918
|
+
} catch {
|
|
1919
|
+
return value;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
function flattenObjectColumns(row) {
|
|
1923
|
+
const flattened = {};
|
|
1924
|
+
for (const [key, rawValue] of Object.entries(row)) {
|
|
1925
|
+
const value = parseMaybeJsonObject(rawValue);
|
|
1926
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1927
|
+
for (const [nestedKey, nestedValue] of Object.entries(
|
|
1928
|
+
value
|
|
1929
|
+
)) {
|
|
1930
|
+
flattened[`${key}.${nestedKey}`] = nestedValue && typeof nestedValue === "object" ? JSON.stringify(nestedValue) : nestedValue;
|
|
1931
|
+
}
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
flattened[key] = Array.isArray(value) ? JSON.stringify(value) : value;
|
|
1935
|
+
}
|
|
1936
|
+
return flattened;
|
|
1937
|
+
}
|
|
1938
|
+
function recordRows(value) {
|
|
1939
|
+
return value.filter(
|
|
1940
|
+
(row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
function dataExportRows(rows) {
|
|
1944
|
+
return rows.map((row) => flattenObjectColumns(row));
|
|
1945
|
+
}
|
|
1946
|
+
function dataExportColumns(rows, preferredColumns = []) {
|
|
1947
|
+
const discoveredColumns = [
|
|
1948
|
+
...new Set(rows.flatMap((row) => Object.keys(row)))
|
|
1949
|
+
];
|
|
1950
|
+
if (rows.length === 0) {
|
|
1951
|
+
return [...new Set(preferredColumns.filter(Boolean))];
|
|
1952
|
+
}
|
|
1953
|
+
const discovered = new Set(discoveredColumns);
|
|
1954
|
+
const columns = [];
|
|
1955
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1956
|
+
for (const column of preferredColumns) {
|
|
1957
|
+
if (!column) {
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
const expandedColumns = discovered.has(column) ? [column] : discoveredColumns.filter(
|
|
1961
|
+
(discoveredColumn) => discoveredColumn.startsWith(`${column}.`)
|
|
1962
|
+
);
|
|
1963
|
+
for (const expandedColumn of expandedColumns) {
|
|
1964
|
+
if (seen.has(expandedColumn)) {
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1967
|
+
seen.add(expandedColumn);
|
|
1968
|
+
columns.push(expandedColumn);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
for (const column of discoveredColumns) {
|
|
1972
|
+
if (seen.has(column)) {
|
|
1973
|
+
continue;
|
|
1974
|
+
}
|
|
1975
|
+
seen.add(column);
|
|
1976
|
+
columns.push(column);
|
|
1977
|
+
}
|
|
1978
|
+
return columns;
|
|
1979
|
+
}
|
|
1980
|
+
function dataExportCsvString(rows, preferredColumns = []) {
|
|
1981
|
+
const flattenedRows = dataExportRows(rows);
|
|
1982
|
+
return csvStringFromRows(
|
|
1983
|
+
flattenedRows,
|
|
1984
|
+
dataExportColumns(flattenedRows, preferredColumns)
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1987
|
+
function markdownCell(value) {
|
|
1988
|
+
if (value === null || value === void 0) {
|
|
1989
|
+
return "";
|
|
1990
|
+
}
|
|
1991
|
+
const text = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
1992
|
+
return text.replace(/\|/g, "\\|").replace(/\r?\n/g, "<br>");
|
|
1993
|
+
}
|
|
1994
|
+
function markdownTableFromRows(rows, preferredColumns = []) {
|
|
1995
|
+
const flattenedRows = dataExportRows(rows);
|
|
1996
|
+
const columns = dataExportColumns(flattenedRows, preferredColumns);
|
|
1997
|
+
const header = `| ${columns.map(markdownCell).join(" | ")} |`;
|
|
1998
|
+
const separator = `| ${columns.map(() => "---").join(" | ")} |`;
|
|
1999
|
+
const body = flattenedRows.map(
|
|
2000
|
+
(row) => `| ${columns.map((column) => markdownCell(row[column])).join(" | ")} |`
|
|
2001
|
+
);
|
|
2002
|
+
return `${[header, separator, ...body].join("\n")}
|
|
2003
|
+
`;
|
|
2004
|
+
}
|
|
1877
2005
|
function printJson(value) {
|
|
1878
2006
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
1879
2007
|
`);
|
|
@@ -2050,7 +2178,7 @@ function buildCandidateUrls2(url) {
|
|
|
2050
2178
|
}
|
|
2051
2179
|
}
|
|
2052
2180
|
function sleep3(ms) {
|
|
2053
|
-
return new Promise((
|
|
2181
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
2054
2182
|
}
|
|
2055
2183
|
function printDeeplineLogo() {
|
|
2056
2184
|
if (process.stdout.isTTY && (process.stdout.columns ?? 80) >= 70) {
|
|
@@ -3250,10 +3378,12 @@ function writeCanonicalRowsCsv(rowsInfo, outPath) {
|
|
|
3250
3378
|
rows: rowsInfo.rows,
|
|
3251
3379
|
columns: rowsInfo.columns
|
|
3252
3380
|
});
|
|
3381
|
+
const rows = dataExportRows(sanitized.rows);
|
|
3382
|
+
const columns = dataExportColumns(rows, sanitized.columns);
|
|
3253
3383
|
const resolved = resolve4(outPath);
|
|
3254
3384
|
writeFileSync4(
|
|
3255
3385
|
resolved,
|
|
3256
|
-
csvStringFromRows(
|
|
3386
|
+
csvStringFromRows(rows, columns),
|
|
3257
3387
|
"utf-8"
|
|
3258
3388
|
);
|
|
3259
3389
|
return resolved;
|
|
@@ -3374,6 +3504,14 @@ Examples:
|
|
|
3374
3504
|
}
|
|
3375
3505
|
|
|
3376
3506
|
// src/cli/commands/db.ts
|
|
3507
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
3508
|
+
import { resolve as resolve5 } from "path";
|
|
3509
|
+
var CUSTOMER_DB_QUERY_FORMATS = /* @__PURE__ */ new Set([
|
|
3510
|
+
"table",
|
|
3511
|
+
"json",
|
|
3512
|
+
"csv",
|
|
3513
|
+
"markdown"
|
|
3514
|
+
]);
|
|
3377
3515
|
function parsePositiveInteger(value, flagName) {
|
|
3378
3516
|
const parsed = Number.parseInt(value, 10);
|
|
3379
3517
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
@@ -3387,10 +3525,8 @@ function formatCell(value) {
|
|
|
3387
3525
|
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
|
|
3388
3526
|
}
|
|
3389
3527
|
function tableLines(result) {
|
|
3390
|
-
const rows = result
|
|
3391
|
-
|
|
3392
|
-
);
|
|
3393
|
-
const responseColumns = result.columns.length > 0 ? result.columns.map((column) => column.name) : [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
3528
|
+
const rows = dataExportRows(customerDbRows(result));
|
|
3529
|
+
const responseColumns = dataExportColumns(rows, customerDbColumnNames(result));
|
|
3394
3530
|
const businessColumns = responseColumns.filter((column) => !column.startsWith("_"));
|
|
3395
3531
|
const columns = businessColumns.length > 0 ? businessColumns : responseColumns;
|
|
3396
3532
|
const hiddenColumns = responseColumns.filter((column) => !columns.includes(column));
|
|
@@ -3424,22 +3560,146 @@ function tableLines(result) {
|
|
|
3424
3560
|
}
|
|
3425
3561
|
return lines;
|
|
3426
3562
|
}
|
|
3563
|
+
function customerDbRows(result) {
|
|
3564
|
+
return recordRows(result.rows);
|
|
3565
|
+
}
|
|
3566
|
+
function customerDbColumnNames(result) {
|
|
3567
|
+
return result.columns.map((column) => column.name).filter(Boolean);
|
|
3568
|
+
}
|
|
3569
|
+
function writeCustomerDbCsv(result, outPath) {
|
|
3570
|
+
const resolved = resolve5(outPath);
|
|
3571
|
+
writeFileSync5(
|
|
3572
|
+
resolved,
|
|
3573
|
+
dataExportCsvString(customerDbRows(result), customerDbColumnNames(result)),
|
|
3574
|
+
"utf-8"
|
|
3575
|
+
);
|
|
3576
|
+
return resolved;
|
|
3577
|
+
}
|
|
3578
|
+
function dbQueryExportEnvelope(input) {
|
|
3579
|
+
const destination = input.outPath ?? "stdout";
|
|
3580
|
+
return {
|
|
3581
|
+
command: input.result.command,
|
|
3582
|
+
format: input.format,
|
|
3583
|
+
row_count: input.result.row_count,
|
|
3584
|
+
row_count_returned: input.result.row_count_returned,
|
|
3585
|
+
truncated: input.result.truncated,
|
|
3586
|
+
...input.outPath ? { file: input.outPath, local: { file: input.outPath } } : {},
|
|
3587
|
+
next: { toolEquivalent: input.toolCommand },
|
|
3588
|
+
render: {
|
|
3589
|
+
sections: [
|
|
3590
|
+
{
|
|
3591
|
+
title: "customer db export",
|
|
3592
|
+
lines: [
|
|
3593
|
+
`Rendered ${input.result.row_count_returned} row(s) as ${input.format} to ${destination}`
|
|
3594
|
+
]
|
|
3595
|
+
}
|
|
3596
|
+
],
|
|
3597
|
+
actions: [{ label: "Tool equivalent", command: input.toolCommand }]
|
|
3598
|
+
}
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3427
3601
|
async function handleDbQuery(args) {
|
|
3428
3602
|
const sqlIndex = args.indexOf("--sql");
|
|
3429
3603
|
const sql = sqlIndex >= 0 ? args[sqlIndex + 1]?.trim() : "";
|
|
3430
3604
|
if (!sql) {
|
|
3431
|
-
console.error(
|
|
3605
|
+
console.error(
|
|
3606
|
+
'Usage: deepline db query --sql "select * from table limit 20" [--max-rows N] [--json]'
|
|
3607
|
+
);
|
|
3432
3608
|
return 1;
|
|
3433
3609
|
}
|
|
3434
3610
|
const maxRowsIndex = args.indexOf("--max-rows");
|
|
3435
3611
|
const maxRows = maxRowsIndex >= 0 && args[maxRowsIndex + 1] ? parsePositiveInteger(args[maxRowsIndex + 1], "--max-rows") : void 0;
|
|
3612
|
+
const formatIndex = args.indexOf("--format");
|
|
3613
|
+
const format = formatIndex >= 0 ? args[formatIndex + 1]?.trim().toLowerCase() : "";
|
|
3614
|
+
if (format && !CUSTOMER_DB_QUERY_FORMATS.has(format)) {
|
|
3615
|
+
console.error(
|
|
3616
|
+
'Usage: deepline db query --sql "select * from table limit 20" [--format table|json|csv|markdown] [--out path]'
|
|
3617
|
+
);
|
|
3618
|
+
return 1;
|
|
3619
|
+
}
|
|
3620
|
+
const outIndex = args.indexOf("--out");
|
|
3621
|
+
const outPath = outIndex >= 0 ? args[outIndex + 1]?.trim() : "";
|
|
3622
|
+
if (outIndex >= 0 && !outPath) {
|
|
3623
|
+
console.error("--out requires a path.");
|
|
3624
|
+
return 1;
|
|
3625
|
+
}
|
|
3436
3626
|
const jsonOutput = argsWantJson(args);
|
|
3627
|
+
const explicitJsonOutput = args.includes("--json");
|
|
3437
3628
|
const client = new DeeplineClient();
|
|
3438
3629
|
const result = await client.queryCustomerDb({ sql, maxRows });
|
|
3439
3630
|
const toolCommand = `deepline tools execute query_customer_db --payload ${JSON.stringify({
|
|
3440
3631
|
sql,
|
|
3441
3632
|
...maxRows ? { max_rows: maxRows } : {}
|
|
3442
3633
|
})} --json`;
|
|
3634
|
+
if (format === "csv") {
|
|
3635
|
+
if (outPath) {
|
|
3636
|
+
const exportedPath = writeCustomerDbCsv(result, outPath);
|
|
3637
|
+
printCommandEnvelope(
|
|
3638
|
+
dbQueryExportEnvelope({
|
|
3639
|
+
result,
|
|
3640
|
+
format,
|
|
3641
|
+
outPath: exportedPath,
|
|
3642
|
+
toolCommand
|
|
3643
|
+
}),
|
|
3644
|
+
{
|
|
3645
|
+
json: jsonOutput,
|
|
3646
|
+
text: `Exported ${result.row_count_returned} row(s) to ${exportedPath}
|
|
3647
|
+
`
|
|
3648
|
+
}
|
|
3649
|
+
);
|
|
3650
|
+
return 0;
|
|
3651
|
+
}
|
|
3652
|
+
printCommandEnvelope(
|
|
3653
|
+
dbQueryExportEnvelope({
|
|
3654
|
+
result,
|
|
3655
|
+
format,
|
|
3656
|
+
outPath: null,
|
|
3657
|
+
toolCommand
|
|
3658
|
+
}),
|
|
3659
|
+
{
|
|
3660
|
+
json: explicitJsonOutput,
|
|
3661
|
+
text: dataExportCsvString(customerDbRows(result), customerDbColumnNames(result))
|
|
3662
|
+
}
|
|
3663
|
+
);
|
|
3664
|
+
return 0;
|
|
3665
|
+
}
|
|
3666
|
+
if (format === "markdown") {
|
|
3667
|
+
const content = markdownTableFromRows(
|
|
3668
|
+
customerDbRows(result),
|
|
3669
|
+
customerDbColumnNames(result)
|
|
3670
|
+
);
|
|
3671
|
+
if (outPath) {
|
|
3672
|
+
const exportedPath = resolve5(outPath);
|
|
3673
|
+
writeFileSync5(exportedPath, content, "utf-8");
|
|
3674
|
+
printCommandEnvelope(
|
|
3675
|
+
dbQueryExportEnvelope({
|
|
3676
|
+
result,
|
|
3677
|
+
format,
|
|
3678
|
+
outPath: exportedPath,
|
|
3679
|
+
toolCommand
|
|
3680
|
+
}),
|
|
3681
|
+
{
|
|
3682
|
+
json: jsonOutput,
|
|
3683
|
+
text: `Exported ${result.row_count_returned} row(s) to ${exportedPath}
|
|
3684
|
+
`
|
|
3685
|
+
}
|
|
3686
|
+
);
|
|
3687
|
+
return 0;
|
|
3688
|
+
}
|
|
3689
|
+
printCommandEnvelope(
|
|
3690
|
+
dbQueryExportEnvelope({
|
|
3691
|
+
result,
|
|
3692
|
+
format,
|
|
3693
|
+
outPath: null,
|
|
3694
|
+
toolCommand
|
|
3695
|
+
}),
|
|
3696
|
+
{
|
|
3697
|
+
json: explicitJsonOutput,
|
|
3698
|
+
text: content
|
|
3699
|
+
}
|
|
3700
|
+
);
|
|
3701
|
+
return 0;
|
|
3702
|
+
}
|
|
3443
3703
|
printCommandEnvelope({
|
|
3444
3704
|
...result,
|
|
3445
3705
|
next: { toolEquivalent: toolCommand },
|
|
@@ -3447,7 +3707,7 @@ async function handleDbQuery(args) {
|
|
|
3447
3707
|
sections: [{ title: "customer db query", lines: tableLines(result) }],
|
|
3448
3708
|
actions: [{ label: "Tool equivalent", command: toolCommand }]
|
|
3449
3709
|
}
|
|
3450
|
-
}, { json: jsonOutput });
|
|
3710
|
+
}, { json: jsonOutput || format === "json" });
|
|
3451
3711
|
return 0;
|
|
3452
3712
|
}
|
|
3453
3713
|
function registerDbCommands(program) {
|
|
@@ -3457,11 +3717,14 @@ function registerDbCommands(program) {
|
|
|
3457
3717
|
Notes:
|
|
3458
3718
|
Runs SQL against the active workspace customer database through Deepline APIs.
|
|
3459
3719
|
Results are bounded by the server and --max-rows. Use --json for stable output.
|
|
3720
|
+
Use --format csv or --format markdown for agent-readable exports and display tables.
|
|
3460
3721
|
|
|
3461
3722
|
Examples:
|
|
3462
3723
|
deepline db query --sql "select * from companies limit 20"
|
|
3463
3724
|
deepline db query --sql "select domain, name from companies limit 20" --json
|
|
3464
3725
|
deepline db query --sql "select * from contacts" --max-rows 100 --json
|
|
3726
|
+
deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
|
|
3727
|
+
deepline db query --sql "select domain, name from companies limit 20" --format markdown
|
|
3465
3728
|
`
|
|
3466
3729
|
);
|
|
3467
3730
|
db.command("query").alias("psql").description("Run SQL against the tenant customer database.").addHelpText(
|
|
@@ -3470,17 +3733,23 @@ Examples:
|
|
|
3470
3733
|
Notes:
|
|
3471
3734
|
Requires --sql. Output is a compact table in a terminal and raw JSON with
|
|
3472
3735
|
--json or when stdout is piped. The active auth workspace determines scope.
|
|
3736
|
+
--format csv and --format markdown are explicit data/display formats and can
|
|
3737
|
+
be written directly with --out.
|
|
3473
3738
|
|
|
3474
3739
|
Examples:
|
|
3475
3740
|
deepline db query --sql "select * from companies limit 20"
|
|
3476
3741
|
deepline db query --sql "select domain, name from companies limit 20" --json
|
|
3477
3742
|
deepline db psql --sql "select count(*) from contacts" --json
|
|
3743
|
+
deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
|
|
3744
|
+
deepline db query --sql "select domain, name from companies limit 20" --format markdown
|
|
3478
3745
|
`
|
|
3479
|
-
).requiredOption("--sql <sql>", "SQL statement").option("--max-rows <n>", "Maximum returned rows").option("--json", "Emit raw JSON response. Also automatic when stdout is piped").action(async (options) => {
|
|
3746
|
+
).requiredOption("--sql <sql>", "SQL statement").option("--max-rows <n>", "Maximum returned rows").option("--format <format>", "Output format: table, json, csv, or markdown").option("--out <path>", "Write csv or markdown output to a file").option("--json", "Emit raw JSON response. Also automatic when stdout is piped").action(async (options) => {
|
|
3480
3747
|
process.exitCode = await handleDbQuery([
|
|
3481
3748
|
"--sql",
|
|
3482
3749
|
options.sql,
|
|
3483
3750
|
...options.maxRows ? ["--max-rows", options.maxRows] : [],
|
|
3751
|
+
...options.format ? ["--format", options.format] : [],
|
|
3752
|
+
...options.out ? ["--out", options.out] : [],
|
|
3484
3753
|
...options.json ? ["--json"] : []
|
|
3485
3754
|
]);
|
|
3486
3755
|
});
|
|
@@ -3661,13 +3930,13 @@ import {
|
|
|
3661
3930
|
readFileSync as readFileSync5,
|
|
3662
3931
|
readdirSync,
|
|
3663
3932
|
realpathSync,
|
|
3664
|
-
writeFileSync as
|
|
3933
|
+
writeFileSync as writeFileSync6
|
|
3665
3934
|
} from "fs";
|
|
3666
|
-
import { basename as basename3, dirname as dirname8, join as join6, resolve as
|
|
3935
|
+
import { basename as basename3, dirname as dirname8, join as join6, resolve as resolve9 } from "path";
|
|
3667
3936
|
|
|
3668
3937
|
// src/plays/bundle-play-file.ts
|
|
3669
3938
|
import { tmpdir as tmpdir2 } from "os";
|
|
3670
|
-
import { dirname as dirname7, join as join5, resolve as
|
|
3939
|
+
import { dirname as dirname7, join as join5, resolve as resolve8 } from "path";
|
|
3671
3940
|
import { fileURLToPath } from "url";
|
|
3672
3941
|
import { existsSync as existsSync5 } from "fs";
|
|
3673
3942
|
|
|
@@ -3676,7 +3945,7 @@ import { createHash } from "crypto";
|
|
|
3676
3945
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
3677
3946
|
import { mkdir as mkdir3, readFile, realpath, stat, writeFile as writeFile3 } from "fs/promises";
|
|
3678
3947
|
import { tmpdir } from "os";
|
|
3679
|
-
import { basename, dirname as dirname5, extname, isAbsolute, join as join3, resolve as
|
|
3948
|
+
import { basename, dirname as dirname5, extname, isAbsolute, join as join3, resolve as resolve6 } from "path";
|
|
3680
3949
|
import { builtinModules } from "module";
|
|
3681
3950
|
import { build } from "esbuild";
|
|
3682
3951
|
|
|
@@ -3768,7 +4037,7 @@ async function normalizeLocalPath(filePath) {
|
|
|
3768
4037
|
try {
|
|
3769
4038
|
return await realpath(filePath);
|
|
3770
4039
|
} catch {
|
|
3771
|
-
return
|
|
4040
|
+
return resolve6(filePath);
|
|
3772
4041
|
}
|
|
3773
4042
|
}
|
|
3774
4043
|
function createPlayWorkspace(entryFile) {
|
|
@@ -3937,7 +4206,7 @@ function readPackageVersionFromPackageJson(packageJsonPath, packageName) {
|
|
|
3937
4206
|
return null;
|
|
3938
4207
|
}
|
|
3939
4208
|
function findPackageJsonPathFrom(startDir, packageName) {
|
|
3940
|
-
let current =
|
|
4209
|
+
let current = resolve6(startDir);
|
|
3941
4210
|
while (true) {
|
|
3942
4211
|
const packageJsonPath = join3(
|
|
3943
4212
|
current,
|
|
@@ -3964,7 +4233,7 @@ function findPackageJsonPath(packageName, fromFile, adapter) {
|
|
|
3964
4233
|
];
|
|
3965
4234
|
const seen = /* @__PURE__ */ new Set();
|
|
3966
4235
|
for (const startDir of startDirs) {
|
|
3967
|
-
const normalized =
|
|
4236
|
+
const normalized = resolve6(startDir);
|
|
3968
4237
|
if (seen.has(normalized)) continue;
|
|
3969
4238
|
seen.add(normalized);
|
|
3970
4239
|
const packageJsonPath = findPackageJsonPathFrom(normalized, packageName);
|
|
@@ -4201,7 +4470,7 @@ async function resolveLocalImport(fromFile, specifier) {
|
|
|
4201
4470
|
if (specifier.startsWith("file:")) {
|
|
4202
4471
|
return normalizeLocalPath(new URL(specifier).pathname);
|
|
4203
4472
|
}
|
|
4204
|
-
const base = isAbsolute(specifier) ?
|
|
4473
|
+
const base = isAbsolute(specifier) ? resolve6(specifier) : resolve6(dirname5(fromFile), specifier);
|
|
4205
4474
|
const candidates = [base];
|
|
4206
4475
|
const explicitExtension = extname(base).toLowerCase();
|
|
4207
4476
|
if (!explicitExtension) {
|
|
@@ -4399,7 +4668,7 @@ function normalizeSourceMapForRuntime(sourceMapText) {
|
|
|
4399
4668
|
if (sourcePath.startsWith("data:") || sourcePath.startsWith("node:") || sourcePath.startsWith("/") || /^[a-zA-Z]+:\/\//.test(sourcePath)) {
|
|
4400
4669
|
return sourcePath;
|
|
4401
4670
|
}
|
|
4402
|
-
return
|
|
4671
|
+
return resolve6(process.cwd(), sourcePath);
|
|
4403
4672
|
});
|
|
4404
4673
|
parsed.sourceRoot = void 0;
|
|
4405
4674
|
return JSON.stringify(parsed);
|
|
@@ -4721,7 +4990,7 @@ function resolveExecutionProfile(override) {
|
|
|
4721
4990
|
// src/plays/local-file-discovery.ts
|
|
4722
4991
|
import { createHash as createHash2 } from "crypto";
|
|
4723
4992
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
4724
|
-
import { basename as basename2, dirname as dirname6, extname as extname2, isAbsolute as isAbsolute2, join as join4, relative, resolve as
|
|
4993
|
+
import { basename as basename2, dirname as dirname6, extname as extname2, isAbsolute as isAbsolute2, join as join4, relative, resolve as resolve7 } from "path";
|
|
4725
4994
|
var SOURCE_EXTENSIONS2 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
|
|
4726
4995
|
function sha2562(buffer) {
|
|
4727
4996
|
return createHash2("sha256").update(buffer).digest("hex");
|
|
@@ -4922,7 +5191,7 @@ function isPathInsideDirectory2(filePath, directory) {
|
|
|
4922
5191
|
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute2(relativePath);
|
|
4923
5192
|
}
|
|
4924
5193
|
async function resolveLocalImport2(fromFile, specifier) {
|
|
4925
|
-
const base = isAbsolute2(specifier) ?
|
|
5194
|
+
const base = isAbsolute2(specifier) ? resolve7(specifier) : resolve7(dirname6(fromFile), specifier);
|
|
4926
5195
|
const candidates = [base];
|
|
4927
5196
|
const explicitExtension = extname2(base).toLowerCase();
|
|
4928
5197
|
if (!explicitExtension) {
|
|
@@ -4940,13 +5209,13 @@ async function resolveLocalImport2(fromFile, specifier) {
|
|
|
4940
5209
|
throw new Error(`Could not resolve local import "${specifier}" from ${fromFile}`);
|
|
4941
5210
|
}
|
|
4942
5211
|
async function discoverPackagedLocalFiles(entryFile) {
|
|
4943
|
-
const absoluteEntryFile =
|
|
5212
|
+
const absoluteEntryFile = resolve7(entryFile);
|
|
4944
5213
|
const packagingRoot = dirname6(absoluteEntryFile);
|
|
4945
5214
|
const files = /* @__PURE__ */ new Map();
|
|
4946
5215
|
const unresolved = [];
|
|
4947
5216
|
const visitedFiles = /* @__PURE__ */ new Set();
|
|
4948
5217
|
const visitSourceFile = async (filePath) => {
|
|
4949
|
-
const absolutePath =
|
|
5218
|
+
const absolutePath = resolve7(filePath);
|
|
4950
5219
|
if (visitedFiles.has(absolutePath)) {
|
|
4951
5220
|
return;
|
|
4952
5221
|
}
|
|
@@ -4978,7 +5247,7 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
4978
5247
|
message: "Could not resolve this ctx.csv(...) path at submit time. Use a string literal, a top-level const string, or pass a runtime input like input.file."
|
|
4979
5248
|
});
|
|
4980
5249
|
} else {
|
|
4981
|
-
const absoluteCsvPath =
|
|
5250
|
+
const absoluteCsvPath = resolve7(dirname6(absolutePath), resolvedPath);
|
|
4982
5251
|
if (isAbsolute2(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
|
|
4983
5252
|
unresolved.push({
|
|
4984
5253
|
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
@@ -5018,23 +5287,23 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
5018
5287
|
// src/plays/bundle-play-file.ts
|
|
5019
5288
|
var PLAY_BUNDLE_CACHE_VERSION2 = 30;
|
|
5020
5289
|
var MODULE_DIR = dirname7(fileURLToPath(import.meta.url));
|
|
5021
|
-
var SDK_PACKAGE_ROOT =
|
|
5022
|
-
var SOURCE_REPO_ROOT =
|
|
5290
|
+
var SDK_PACKAGE_ROOT = resolve8(MODULE_DIR, "..", "..");
|
|
5291
|
+
var SOURCE_REPO_ROOT = resolve8(SDK_PACKAGE_ROOT, "..");
|
|
5023
5292
|
var HAS_SOURCE_BUNDLING_SOURCES = existsSync5(
|
|
5024
|
-
|
|
5293
|
+
resolve8(SOURCE_REPO_ROOT, "apps", "play-runner-workers", "src", "entry.ts")
|
|
5025
5294
|
);
|
|
5026
|
-
var PACKAGED_REPO_ROOT =
|
|
5295
|
+
var PACKAGED_REPO_ROOT = resolve8(SDK_PACKAGE_ROOT, "dist", "repo");
|
|
5027
5296
|
var HAS_PACKAGED_BUNDLING_SOURCES = existsSync5(
|
|
5028
|
-
|
|
5297
|
+
resolve8(PACKAGED_REPO_ROOT, "apps", "play-runner-workers", "src", "entry.ts")
|
|
5029
5298
|
);
|
|
5030
|
-
var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED_BUNDLING_SOURCES ? PACKAGED_REPO_ROOT :
|
|
5031
|
-
var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ?
|
|
5032
|
-
var SDK_PACKAGE_JSON =
|
|
5033
|
-
var SDK_ENTRY_FILE =
|
|
5034
|
-
var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE :
|
|
5035
|
-
var SDK_WORKERS_ENTRY_FILE =
|
|
5036
|
-
var WORKERS_HARNESS_ENTRY_FILE =
|
|
5037
|
-
var WORKERS_HARNESS_FILES_DIR =
|
|
5299
|
+
var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED_BUNDLING_SOURCES ? PACKAGED_REPO_ROOT : resolve8(SDK_PACKAGE_ROOT, "..");
|
|
5300
|
+
var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? resolve8(SOURCE_REPO_ROOT, "sdk", "src") : HAS_PACKAGED_BUNDLING_SOURCES ? resolve8(PACKAGED_REPO_ROOT, "sdk", "src") : resolve8(SDK_PACKAGE_ROOT, "src");
|
|
5301
|
+
var SDK_PACKAGE_JSON = resolve8(SDK_PACKAGE_ROOT, "package.json");
|
|
5302
|
+
var SDK_ENTRY_FILE = resolve8(SDK_SOURCE_ROOT, "index.ts");
|
|
5303
|
+
var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : resolve8(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
5304
|
+
var SDK_WORKERS_ENTRY_FILE = resolve8(SDK_SOURCE_ROOT, "worker-play-entry.ts");
|
|
5305
|
+
var WORKERS_HARNESS_ENTRY_FILE = resolve8(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
|
|
5306
|
+
var WORKERS_HARNESS_FILES_DIR = resolve8(PROJECT_ROOT, "apps", "play-runner-workers", "src");
|
|
5038
5307
|
var hasWarnedAboutNonDevelopmentBundling = false;
|
|
5039
5308
|
function warnAboutNonDevelopmentBundling(filePath) {
|
|
5040
5309
|
if (hasWarnedAboutNonDevelopmentBundling) {
|
|
@@ -5058,7 +5327,7 @@ function defaultPlayBundleTarget() {
|
|
|
5058
5327
|
function createSdkPlayBundlingAdapter() {
|
|
5059
5328
|
return {
|
|
5060
5329
|
projectRoot: PROJECT_ROOT,
|
|
5061
|
-
nodeModulesDir:
|
|
5330
|
+
nodeModulesDir: resolve8(PROJECT_ROOT, "node_modules"),
|
|
5062
5331
|
cacheDir: join5(tmpdir2(), `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`),
|
|
5063
5332
|
sdkSourceRoot: SDK_SOURCE_ROOT,
|
|
5064
5333
|
sdkPackageJson: SDK_PACKAGE_JSON,
|
|
@@ -5296,7 +5565,7 @@ function traceCliSync(phase, fields, run) {
|
|
|
5296
5565
|
}
|
|
5297
5566
|
}
|
|
5298
5567
|
function sleep4(ms) {
|
|
5299
|
-
return new Promise((
|
|
5568
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
5300
5569
|
}
|
|
5301
5570
|
function parseReferencedPlayTarget(target) {
|
|
5302
5571
|
const trimmed = target.trim();
|
|
@@ -5342,7 +5611,7 @@ function formatPlayListReference(play) {
|
|
|
5342
5611
|
function defaultMaterializedPlayPath(reference) {
|
|
5343
5612
|
const playName = parseReferencedPlayTarget(reference).unqualifiedPlayName;
|
|
5344
5613
|
const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
5345
|
-
return
|
|
5614
|
+
return resolve9(`${safeName || "play"}.play.ts`);
|
|
5346
5615
|
}
|
|
5347
5616
|
function materializeRemotePlaySource(input) {
|
|
5348
5617
|
if (isFileTarget(input.target)) {
|
|
@@ -5357,10 +5626,10 @@ function materializeRemotePlaySource(input) {
|
|
|
5357
5626
|
if (existingSource === input.sourceCode) {
|
|
5358
5627
|
return { path: outputPath, status: "unchanged", created: false };
|
|
5359
5628
|
}
|
|
5360
|
-
|
|
5629
|
+
writeFileSync6(outputPath, input.sourceCode, "utf-8");
|
|
5361
5630
|
return { path: outputPath, status: "updated", created: false };
|
|
5362
5631
|
}
|
|
5363
|
-
|
|
5632
|
+
writeFileSync6(outputPath, input.sourceCode, "utf-8");
|
|
5364
5633
|
return { path: outputPath, status: "created", created: true };
|
|
5365
5634
|
}
|
|
5366
5635
|
function formatLoadedPlayMessage(materializedFile) {
|
|
@@ -5405,7 +5674,7 @@ function extractPlayName(code, filePath) {
|
|
|
5405
5674
|
throw buildMissingDefinePlayError(filePath);
|
|
5406
5675
|
}
|
|
5407
5676
|
function isFileTarget(target) {
|
|
5408
|
-
return existsSync6(
|
|
5677
|
+
return existsSync6(resolve9(target));
|
|
5409
5678
|
}
|
|
5410
5679
|
function looksLikeFilePath(target) {
|
|
5411
5680
|
if (target.trim().toLowerCase().startsWith("prebuilt/")) {
|
|
@@ -5424,7 +5693,7 @@ function parsePositiveInteger2(value, flagName) {
|
|
|
5424
5693
|
return parsed;
|
|
5425
5694
|
}
|
|
5426
5695
|
function parseJsonInput(raw) {
|
|
5427
|
-
const source = raw.startsWith("@") ? readFileSync5(
|
|
5696
|
+
const source = raw.startsWith("@") ? readFileSync5(resolve9(raw.slice(1)), "utf-8") : raw;
|
|
5428
5697
|
const parsed = JSON.parse(source);
|
|
5429
5698
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5430
5699
|
throw new Error("--input must be a JSON object.");
|
|
@@ -5525,7 +5794,7 @@ function fileInputBindingsFromStaticPipeline(staticPipeline) {
|
|
|
5525
5794
|
function isLocalFilePathValue(value) {
|
|
5526
5795
|
if (typeof value !== "string" || !value.trim()) return false;
|
|
5527
5796
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
5528
|
-
return existsSync6(
|
|
5797
|
+
return existsSync6(resolve9(value));
|
|
5529
5798
|
}
|
|
5530
5799
|
function inputContainsLocalFilePath(value) {
|
|
5531
5800
|
if (isLocalFilePathValue(value)) {
|
|
@@ -5551,7 +5820,7 @@ async function stageFileInputArgs(input) {
|
|
|
5551
5820
|
const localFiles = uniqueBindings.flatMap((binding) => {
|
|
5552
5821
|
const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
|
|
5553
5822
|
if (!isLocalFilePathValue(value)) return [];
|
|
5554
|
-
const absolutePath =
|
|
5823
|
+
const absolutePath = resolve9(value);
|
|
5555
5824
|
return [{ binding, absolutePath, logicalPath: basename3(absolutePath) }];
|
|
5556
5825
|
});
|
|
5557
5826
|
if (localFiles.length === 0) {
|
|
@@ -5587,9 +5856,9 @@ function stageFile(logicalPath, absolutePath) {
|
|
|
5587
5856
|
}
|
|
5588
5857
|
function normalizePlayPath(filePath) {
|
|
5589
5858
|
try {
|
|
5590
|
-
return realpathSync.native(
|
|
5859
|
+
return realpathSync.native(resolve9(filePath));
|
|
5591
5860
|
} catch {
|
|
5592
|
-
return
|
|
5861
|
+
return resolve9(filePath);
|
|
5593
5862
|
}
|
|
5594
5863
|
}
|
|
5595
5864
|
function formatBundlingErrors(filePath, errors) {
|
|
@@ -5742,11 +6011,42 @@ function isTransientPlayStreamError(error) {
|
|
|
5742
6011
|
text
|
|
5743
6012
|
);
|
|
5744
6013
|
}
|
|
6014
|
+
function playStatusErrorText(status) {
|
|
6015
|
+
const chunks = [];
|
|
6016
|
+
const progressError = status.progress?.error;
|
|
6017
|
+
if (typeof progressError === "string" && progressError.trim()) {
|
|
6018
|
+
chunks.push(progressError.trim());
|
|
6019
|
+
}
|
|
6020
|
+
const errorValue = status.error;
|
|
6021
|
+
if (typeof errorValue === "string" && errorValue.trim()) {
|
|
6022
|
+
chunks.push(errorValue.trim());
|
|
6023
|
+
}
|
|
6024
|
+
const errors = status.errors;
|
|
6025
|
+
if (Array.isArray(errors)) {
|
|
6026
|
+
for (const error of errors) {
|
|
6027
|
+
if (typeof error === "string" && error.trim()) {
|
|
6028
|
+
chunks.push(error.trim());
|
|
6029
|
+
} else if (error && typeof error === "object") {
|
|
6030
|
+
const message = error.message;
|
|
6031
|
+
if (typeof message === "string" && message.trim()) {
|
|
6032
|
+
chunks.push(message.trim());
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
}
|
|
6037
|
+
return chunks.join("; ") || status.status;
|
|
6038
|
+
}
|
|
6039
|
+
function isRetryablePendingStartFailure(status) {
|
|
6040
|
+
if (status.status !== "failed") return false;
|
|
6041
|
+
if (status.runId && status.runId !== "pending") return false;
|
|
6042
|
+
return isTransientPlayStreamError(new Error(playStatusErrorText(status)));
|
|
6043
|
+
}
|
|
5745
6044
|
var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
|
|
5746
6045
|
"completed",
|
|
5747
6046
|
"failed",
|
|
5748
6047
|
"cancelled"
|
|
5749
6048
|
]);
|
|
6049
|
+
var PLAY_START_TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
|
|
5750
6050
|
function getEventPayload(event) {
|
|
5751
6051
|
return event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
5752
6052
|
}
|
|
@@ -5908,6 +6208,34 @@ async function waitForPlayCompletionByStream(input) {
|
|
|
5908
6208
|
);
|
|
5909
6209
|
}
|
|
5910
6210
|
async function startAndWaitForPlayCompletionByStream(input) {
|
|
6211
|
+
for (let attempt = 0; attempt <= PLAY_START_TRANSIENT_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
6212
|
+
const status = await startAndWaitForPlayCompletionByStreamOnce(input);
|
|
6213
|
+
const retryDelayMs = PLAY_START_TRANSIENT_RETRY_DELAYS_MS[attempt];
|
|
6214
|
+
if (retryDelayMs === void 0 || !isRetryablePendingStartFailure(status)) {
|
|
6215
|
+
return status;
|
|
6216
|
+
}
|
|
6217
|
+
if (!input.jsonOutput) {
|
|
6218
|
+
input.progress.writeLine(
|
|
6219
|
+
`[play watch] start failed before run id with a transient error; retrying (${playStatusErrorText(status)})`
|
|
6220
|
+
);
|
|
6221
|
+
}
|
|
6222
|
+
recordCliTrace({
|
|
6223
|
+
phase: "cli.play_start_stream_retry",
|
|
6224
|
+
ms: 0,
|
|
6225
|
+
ok: true,
|
|
6226
|
+
playName: input.playName,
|
|
6227
|
+
attempt: attempt + 1,
|
|
6228
|
+
reason: playStatusErrorText(status)
|
|
6229
|
+
});
|
|
6230
|
+
await sleep4(retryDelayMs);
|
|
6231
|
+
}
|
|
6232
|
+
throw new DeeplineError(
|
|
6233
|
+
`Play ${input.playName} did not start after retrying transient start failures.`,
|
|
6234
|
+
void 0,
|
|
6235
|
+
"PLAY_START_RETRY_EXHAUSTED"
|
|
6236
|
+
);
|
|
6237
|
+
}
|
|
6238
|
+
async function startAndWaitForPlayCompletionByStreamOnce(input) {
|
|
5911
6239
|
const startedAt = Date.now();
|
|
5912
6240
|
const dashboardUrl = buildPlayDashboardUrl(
|
|
5913
6241
|
input.client.baseUrl,
|
|
@@ -6234,14 +6562,23 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
6234
6562
|
}
|
|
6235
6563
|
return [];
|
|
6236
6564
|
}
|
|
6237
|
-
function buildRunNextCommands(
|
|
6565
|
+
function buildRunNextCommands(status) {
|
|
6566
|
+
const runId = status.runId?.trim();
|
|
6567
|
+
if (!runId) {
|
|
6568
|
+
const playName = extractRunPlayName(status);
|
|
6569
|
+
return playName ? {
|
|
6570
|
+
list: `deepline runs list --play ${playName} --json`
|
|
6571
|
+
} : {
|
|
6572
|
+
list: "deepline runs list --json"
|
|
6573
|
+
};
|
|
6574
|
+
}
|
|
6238
6575
|
const commands = {
|
|
6239
6576
|
get: `deepline runs get ${runId} --json`,
|
|
6240
6577
|
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
6241
6578
|
logs: `deepline runs logs ${runId} --out run.log --json`
|
|
6242
6579
|
};
|
|
6243
|
-
if (dashboardUrl) {
|
|
6244
|
-
commands.open = `Open ${dashboardUrl} to see results.`;
|
|
6580
|
+
if (status.dashboardUrl) {
|
|
6581
|
+
commands.open = `Open ${status.dashboardUrl} to see results.`;
|
|
6245
6582
|
}
|
|
6246
6583
|
return commands;
|
|
6247
6584
|
}
|
|
@@ -6264,6 +6601,121 @@ function getTimestampField(value, key) {
|
|
|
6264
6601
|
}
|
|
6265
6602
|
return typeof field === "string" && field.trim() ? field : null;
|
|
6266
6603
|
}
|
|
6604
|
+
function getObjectField(value, key) {
|
|
6605
|
+
const field = getRecordField(value, key);
|
|
6606
|
+
return field && typeof field === "object" && !Array.isArray(field) ? field : null;
|
|
6607
|
+
}
|
|
6608
|
+
function formatCreditAmount(value) {
|
|
6609
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
6610
|
+
return String(value ?? "-");
|
|
6611
|
+
}
|
|
6612
|
+
return Number(value.toFixed(8)).toString();
|
|
6613
|
+
}
|
|
6614
|
+
function extractJsonObjectFromText(text) {
|
|
6615
|
+
const start = text.indexOf("{");
|
|
6616
|
+
if (start < 0) {
|
|
6617
|
+
return null;
|
|
6618
|
+
}
|
|
6619
|
+
const suffix = text.slice(start);
|
|
6620
|
+
try {
|
|
6621
|
+
const parsed = JSON.parse(suffix);
|
|
6622
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
6623
|
+
} catch {
|
|
6624
|
+
let depth = 0;
|
|
6625
|
+
let inString = false;
|
|
6626
|
+
let escaped = false;
|
|
6627
|
+
for (let index = start; index < text.length; index += 1) {
|
|
6628
|
+
const char = text[index];
|
|
6629
|
+
if (inString) {
|
|
6630
|
+
if (escaped) {
|
|
6631
|
+
escaped = false;
|
|
6632
|
+
} else if (char === "\\") {
|
|
6633
|
+
escaped = true;
|
|
6634
|
+
} else if (char === '"') {
|
|
6635
|
+
inString = false;
|
|
6636
|
+
}
|
|
6637
|
+
continue;
|
|
6638
|
+
}
|
|
6639
|
+
if (char === '"') {
|
|
6640
|
+
inString = true;
|
|
6641
|
+
} else if (char === "{") {
|
|
6642
|
+
depth += 1;
|
|
6643
|
+
} else if (char === "}") {
|
|
6644
|
+
depth -= 1;
|
|
6645
|
+
if (depth === 0) {
|
|
6646
|
+
try {
|
|
6647
|
+
const parsed = JSON.parse(text.slice(start, index + 1));
|
|
6648
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
6649
|
+
} catch {
|
|
6650
|
+
return null;
|
|
6651
|
+
}
|
|
6652
|
+
}
|
|
6653
|
+
}
|
|
6654
|
+
}
|
|
6655
|
+
}
|
|
6656
|
+
return null;
|
|
6657
|
+
}
|
|
6658
|
+
function extractBillingFromText(text) {
|
|
6659
|
+
if (!text) {
|
|
6660
|
+
return null;
|
|
6661
|
+
}
|
|
6662
|
+
const parsed = extractJsonObjectFromText(text);
|
|
6663
|
+
return getObjectField(parsed, "billing");
|
|
6664
|
+
}
|
|
6665
|
+
function extractToolIdFromErrorText(text) {
|
|
6666
|
+
if (!text) {
|
|
6667
|
+
return null;
|
|
6668
|
+
}
|
|
6669
|
+
const lowerToolMatch = /\btool\s+([A-Za-z0-9_.:-]+)\s+\d{3}\b/.exec(text);
|
|
6670
|
+
if (lowerToolMatch?.[1]) {
|
|
6671
|
+
return lowerToolMatch[1];
|
|
6672
|
+
}
|
|
6673
|
+
const upperToolMatch = /\bTool\s+([A-Za-z0-9_.:-]+)\s+failed\b/.exec(text);
|
|
6674
|
+
return upperToolMatch?.[1] ?? null;
|
|
6675
|
+
}
|
|
6676
|
+
function isInsufficientCreditsBilling(billing) {
|
|
6677
|
+
return billing?.kind === "insufficient_credits";
|
|
6678
|
+
}
|
|
6679
|
+
function extractBillingForStatus(status, error) {
|
|
6680
|
+
const errorBilling = getObjectField(status, "errorBilling");
|
|
6681
|
+
if (errorBilling) {
|
|
6682
|
+
return errorBilling;
|
|
6683
|
+
}
|
|
6684
|
+
const directErrors = getRecordField(status, "errors");
|
|
6685
|
+
if (Array.isArray(directErrors)) {
|
|
6686
|
+
for (const entry of directErrors) {
|
|
6687
|
+
const billing = getObjectField(entry, "billing");
|
|
6688
|
+
if (billing) {
|
|
6689
|
+
return billing;
|
|
6690
|
+
}
|
|
6691
|
+
}
|
|
6692
|
+
}
|
|
6693
|
+
const progressError = getStringField(status.progress, "error");
|
|
6694
|
+
return extractBillingFromText(error) ?? extractBillingFromText(progressError) ?? getObjectField(status, "billing");
|
|
6695
|
+
}
|
|
6696
|
+
function formatInsufficientCreditsMessage(input) {
|
|
6697
|
+
const operation = getStringField(input.billing, "operation_id") ?? getStringField(input.billing, "operation") ?? extractToolIdFromErrorText(input.error) ?? getStringField(input.billing, "provider") ?? "tool call";
|
|
6698
|
+
const balance = formatCreditAmount(input.billing.balance_credits);
|
|
6699
|
+
const required = formatCreditAmount(input.billing.required_credits);
|
|
6700
|
+
const recommended = formatCreditAmount(
|
|
6701
|
+
input.billing.recommended_add_credits ?? input.billing.needed_credits
|
|
6702
|
+
);
|
|
6703
|
+
const billingUrl = getStringField(input.billing, "billing_url");
|
|
6704
|
+
const workspace = getStringField(input.billing, "workspace_id") ?? getStringField(input.billing, "workspaceId");
|
|
6705
|
+
const workspaceSuffix = workspace ? ` (workspace=${workspace})` : "";
|
|
6706
|
+
const addSuffix = billingUrl && recommended !== "-" ? ` Add >=${recommended} at ${billingUrl}.` : billingUrl ? ` Add credits at ${billingUrl}.` : "";
|
|
6707
|
+
return `Workspace balance ${balance} < required ${required} for ${operation}${workspaceSuffix}.${addSuffix}`;
|
|
6708
|
+
}
|
|
6709
|
+
function formatPlayErrorForDisplay(status, error) {
|
|
6710
|
+
if (!error) {
|
|
6711
|
+
return null;
|
|
6712
|
+
}
|
|
6713
|
+
const billing = extractBillingForStatus(status, error);
|
|
6714
|
+
if (isInsufficientCreditsBilling(billing)) {
|
|
6715
|
+
return formatInsufficientCreditsMessage({ billing, error });
|
|
6716
|
+
}
|
|
6717
|
+
return error;
|
|
6718
|
+
}
|
|
6267
6719
|
function normalizeRunStatusForEnvelope(status) {
|
|
6268
6720
|
const run = status.run ?? null;
|
|
6269
6721
|
return {
|
|
@@ -6314,18 +6766,33 @@ function normalizeErrorsForEnvelope(status, error) {
|
|
|
6314
6766
|
if (Array.isArray(directErrors)) {
|
|
6315
6767
|
return directErrors.filter(
|
|
6316
6768
|
(entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)
|
|
6317
|
-
)
|
|
6769
|
+
).map((entry) => {
|
|
6770
|
+
const message2 = typeof entry.message === "string" && entry.message.trim() ? entry.message : error;
|
|
6771
|
+
const billing2 = getObjectField(entry, "billing");
|
|
6772
|
+
if (!isInsufficientCreditsBilling(billing2) || !message2) {
|
|
6773
|
+
return entry;
|
|
6774
|
+
}
|
|
6775
|
+
return {
|
|
6776
|
+
...entry,
|
|
6777
|
+
message: formatInsufficientCreditsMessage({ billing: billing2, error: message2 }),
|
|
6778
|
+
billing: stripProviderSpendFromBilling(billing2)
|
|
6779
|
+
};
|
|
6780
|
+
});
|
|
6318
6781
|
}
|
|
6319
6782
|
if (!error) {
|
|
6320
6783
|
return [];
|
|
6321
6784
|
}
|
|
6785
|
+
const nextCommands = buildRunNextCommands(status);
|
|
6786
|
+
const billing = extractBillingForStatus(status, error);
|
|
6787
|
+
const message = formatPlayErrorForDisplay(status, error) ?? error;
|
|
6322
6788
|
return [
|
|
6323
6789
|
{
|
|
6324
6790
|
code: getStringField(status, "errorCode") ?? "RUN_FAILED",
|
|
6325
6791
|
phase: getStringField(status, "errorPhase") ?? "runtime",
|
|
6326
|
-
message
|
|
6792
|
+
message,
|
|
6327
6793
|
retryable: typeof getRecordField(status, "retryable") === "boolean" ? getRecordField(status, "retryable") : null,
|
|
6328
|
-
|
|
6794
|
+
...billing ? { billing: stripProviderSpendFromBilling(billing) } : {},
|
|
6795
|
+
nextAction: nextCommands.get ?? nextCommands.list
|
|
6329
6796
|
}
|
|
6330
6797
|
];
|
|
6331
6798
|
}
|
|
@@ -6374,6 +6841,7 @@ function compactPlayStatus(status) {
|
|
|
6374
6841
|
) : null;
|
|
6375
6842
|
const progressError = status.progress?.error;
|
|
6376
6843
|
const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
|
|
6844
|
+
const displayError = formatPlayErrorForDisplay(status, error);
|
|
6377
6845
|
return {
|
|
6378
6846
|
runId: status.runId,
|
|
6379
6847
|
apiVersion: status.apiVersion ?? 1,
|
|
@@ -6386,13 +6854,13 @@ function compactPlayStatus(status) {
|
|
|
6386
6854
|
steps: normalizeStepsForEnvelope(status),
|
|
6387
6855
|
errors: normalizeErrorsForEnvelope(status, error),
|
|
6388
6856
|
logs: normalizeLogsForEnvelope(status),
|
|
6389
|
-
...
|
|
6857
|
+
...displayError ? { error: displayError } : {},
|
|
6390
6858
|
...warnings.length > 0 ? { warnings } : {},
|
|
6391
6859
|
...result !== void 0 ? { result } : {},
|
|
6392
6860
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
6393
6861
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
6394
6862
|
...billing ? { billing } : {},
|
|
6395
|
-
next: buildRunNextCommands(status
|
|
6863
|
+
next: buildRunNextCommands(status)
|
|
6396
6864
|
};
|
|
6397
6865
|
}
|
|
6398
6866
|
function enrichPlayStatusWithDatasetStats(status) {
|
|
@@ -6466,7 +6934,8 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6466
6934
|
lines.push(...formatDatasetStatsLines(datasetStats));
|
|
6467
6935
|
const progressError = status.progress?.error;
|
|
6468
6936
|
if (progressError && typeof progressError === "string") {
|
|
6469
|
-
|
|
6937
|
+
const displayError = formatPlayErrorForDisplay(status, progressError) ?? progressError;
|
|
6938
|
+
lines.push(` error: ${displayError.slice(0, 200)}`);
|
|
6470
6939
|
}
|
|
6471
6940
|
const renderedServerView = renderServerResultView(status.resultView);
|
|
6472
6941
|
if (result) {
|
|
@@ -6490,8 +6959,11 @@ var RUN_EXPORT_PAGE_SIZE = 5e3;
|
|
|
6490
6959
|
function shellSingleQuote(value) {
|
|
6491
6960
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
6492
6961
|
}
|
|
6962
|
+
function sqlStringLiteral(value) {
|
|
6963
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
6964
|
+
}
|
|
6493
6965
|
function runExportRetryCommand(runId, outPath, datasetPath) {
|
|
6494
|
-
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(
|
|
6966
|
+
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(resolve9(outPath))}`;
|
|
6495
6967
|
}
|
|
6496
6968
|
function extractRunPlayName(status) {
|
|
6497
6969
|
const run = status.run;
|
|
@@ -6508,6 +6980,26 @@ function extractRunPlayName(status) {
|
|
|
6508
6980
|
}
|
|
6509
6981
|
return null;
|
|
6510
6982
|
}
|
|
6983
|
+
function normalizeCustomerDbIdentifier(value) {
|
|
6984
|
+
return value.split("/").pop().replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
6985
|
+
}
|
|
6986
|
+
function buildCustomerDbQueryPlan(input) {
|
|
6987
|
+
const playName = extractRunPlayName(input.status);
|
|
6988
|
+
const tableNamespace = input.rowsInfo.tableNamespace?.trim();
|
|
6989
|
+
if (!playName || !tableNamespace || input.rowsInfo.totalRows <= 0) {
|
|
6990
|
+
return null;
|
|
6991
|
+
}
|
|
6992
|
+
const tableName = `${normalizeCustomerDbIdentifier(playName)}_${normalizeCustomerDbIdentifier(
|
|
6993
|
+
tableNamespace
|
|
6994
|
+
)}`;
|
|
6995
|
+
const sql = `select * from "storage"."${tableName}" where _run_id = ${sqlStringLiteral(input.status.runId)} limit ${input.rowsInfo.totalRows}`;
|
|
6996
|
+
const base = `deepline customer-db query --sql ${shellSingleQuote(sql)} --max-rows ${input.rowsInfo.totalRows}`;
|
|
6997
|
+
return {
|
|
6998
|
+
sql,
|
|
6999
|
+
json: `${base} --json`,
|
|
7000
|
+
csv: `${base} --format csv --out ${shellSingleQuote(resolve9(input.outPath))}`
|
|
7001
|
+
};
|
|
7002
|
+
}
|
|
6511
7003
|
function exportableSheetRow(row) {
|
|
6512
7004
|
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
6513
7005
|
return null;
|
|
@@ -6679,6 +7171,60 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
|
|
|
6679
7171
|
}
|
|
6680
7172
|
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6681
7173
|
}
|
|
7174
|
+
function extractActiveRunsFromError(error) {
|
|
7175
|
+
if (!(error instanceof DeeplineError)) {
|
|
7176
|
+
return [];
|
|
7177
|
+
}
|
|
7178
|
+
const response = error.details?.response;
|
|
7179
|
+
if (!response || typeof response !== "object" || Array.isArray(response)) {
|
|
7180
|
+
return [];
|
|
7181
|
+
}
|
|
7182
|
+
const details = response.details;
|
|
7183
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) {
|
|
7184
|
+
return [];
|
|
7185
|
+
}
|
|
7186
|
+
const activeRuns = details.activeRuns;
|
|
7187
|
+
return Array.isArray(activeRuns) ? activeRuns.filter(
|
|
7188
|
+
(run) => Boolean(run) && typeof run === "object" && !Array.isArray(run)
|
|
7189
|
+
) : [];
|
|
7190
|
+
}
|
|
7191
|
+
function activeRunId(run) {
|
|
7192
|
+
return getStringField(run, "workflowId") ?? getStringField(run, "runId");
|
|
7193
|
+
}
|
|
7194
|
+
function formatActiveRunConflictError(input) {
|
|
7195
|
+
const lines = [
|
|
7196
|
+
`Active run exists for ${input.playName}. Use --force to supersede, or inspect/stop the active run first.`
|
|
7197
|
+
];
|
|
7198
|
+
for (const run of input.activeRuns.slice(0, 3)) {
|
|
7199
|
+
const runId = activeRunId(run);
|
|
7200
|
+
if (!runId) {
|
|
7201
|
+
continue;
|
|
7202
|
+
}
|
|
7203
|
+
const status = getStringField(run, "status");
|
|
7204
|
+
const startedAt = getStringField(run, "startedAt") ?? getStringField(run, "startTime");
|
|
7205
|
+
lines.push(
|
|
7206
|
+
` active: ${runId}${status ? ` status=${status}` : ""}${startedAt ? ` startedAt=${startedAt}` : ""}`
|
|
7207
|
+
);
|
|
7208
|
+
lines.push(` get: deepline runs get ${runId} --json`);
|
|
7209
|
+
lines.push(
|
|
7210
|
+
` stop: deepline runs stop ${runId} --reason "stale lock" --json`
|
|
7211
|
+
);
|
|
7212
|
+
}
|
|
7213
|
+
lines.push(` rerun: add --force to the same deepline plays run command`);
|
|
7214
|
+
return lines.join("\n");
|
|
7215
|
+
}
|
|
7216
|
+
function normalizePlayStartError(error, playName) {
|
|
7217
|
+
const activeRuns = extractActiveRunsFromError(error);
|
|
7218
|
+
if (activeRuns.length === 0) {
|
|
7219
|
+
return error;
|
|
7220
|
+
}
|
|
7221
|
+
return new DeeplineError(
|
|
7222
|
+
formatActiveRunConflictError({ playName, activeRuns }),
|
|
7223
|
+
error instanceof DeeplineError ? error.statusCode : 409,
|
|
7224
|
+
"ACTIVE_RUN_EXISTS",
|
|
7225
|
+
error instanceof DeeplineError ? error.details : void 0
|
|
7226
|
+
);
|
|
7227
|
+
}
|
|
6682
7228
|
function renderServerResultView(value) {
|
|
6683
7229
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6684
7230
|
return { lines: [], actions: [] };
|
|
@@ -6914,11 +7460,11 @@ function shouldUseLocalOnlyPlayCheck() {
|
|
|
6914
7460
|
async function handlePlayCheck(args) {
|
|
6915
7461
|
const options = parsePlayCheckOptions(args);
|
|
6916
7462
|
if (!isFileTarget(options.target)) {
|
|
6917
|
-
const resolved =
|
|
7463
|
+
const resolved = resolve9(options.target);
|
|
6918
7464
|
console.error(`File not found: ${resolved}`);
|
|
6919
7465
|
return 1;
|
|
6920
7466
|
}
|
|
6921
|
-
const absolutePlayPath =
|
|
7467
|
+
const absolutePlayPath = resolve9(options.target);
|
|
6922
7468
|
const sourceCode = readFileSync5(absolutePlayPath, "utf-8");
|
|
6923
7469
|
let graph;
|
|
6924
7470
|
try {
|
|
@@ -6982,7 +7528,7 @@ async function handleFileBackedRun(options) {
|
|
|
6982
7528
|
}
|
|
6983
7529
|
const client = new DeeplineClient();
|
|
6984
7530
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
6985
|
-
const absolutePlayPath =
|
|
7531
|
+
const absolutePlayPath = resolve9(options.target.path);
|
|
6986
7532
|
progress.phase("compiling play");
|
|
6987
7533
|
const sourceCode = traceCliSync(
|
|
6988
7534
|
"cli.play_file_read_source",
|
|
@@ -7068,6 +7614,8 @@ async function handleFileBackedRun(options) {
|
|
|
7068
7614
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
7069
7615
|
noOpen: options.noOpen,
|
|
7070
7616
|
progress
|
|
7617
|
+
}).catch((error) => {
|
|
7618
|
+
throw normalizePlayStartError(error, playName);
|
|
7071
7619
|
})
|
|
7072
7620
|
);
|
|
7073
7621
|
if (finalStatus.status === "completed") {
|
|
@@ -7086,7 +7634,9 @@ async function handleFileBackedRun(options) {
|
|
|
7086
7634
|
const started = await traceCliSpan(
|
|
7087
7635
|
"cli.play_start_unwatched",
|
|
7088
7636
|
{ targetKind: "file", playName },
|
|
7089
|
-
() => client.startPlayRun(startRequest)
|
|
7637
|
+
() => client.startPlayRun(startRequest).catch((error) => {
|
|
7638
|
+
throw normalizePlayStartError(error, playName);
|
|
7639
|
+
})
|
|
7090
7640
|
);
|
|
7091
7641
|
const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
7092
7642
|
openPlayDashboard({
|
|
@@ -7203,6 +7753,8 @@ async function handleNamedRun(options) {
|
|
|
7203
7753
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
7204
7754
|
noOpen: options.noOpen,
|
|
7205
7755
|
progress
|
|
7756
|
+
}).catch((error) => {
|
|
7757
|
+
throw normalizePlayStartError(error, playName);
|
|
7206
7758
|
})
|
|
7207
7759
|
);
|
|
7208
7760
|
if (finalStatus.status === "completed") {
|
|
@@ -7221,7 +7773,9 @@ async function handleNamedRun(options) {
|
|
|
7221
7773
|
const started = await traceCliSpan(
|
|
7222
7774
|
"cli.play_start_unwatched",
|
|
7223
7775
|
{ targetKind: "name", playName },
|
|
7224
|
-
() => client.startPlayRun(startRequest)
|
|
7776
|
+
() => client.startPlayRun(startRequest).catch((error) => {
|
|
7777
|
+
throw normalizePlayStartError(error, playName);
|
|
7778
|
+
})
|
|
7225
7779
|
);
|
|
7226
7780
|
const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
7227
7781
|
openPlayDashboard({
|
|
@@ -7247,7 +7801,7 @@ async function handlePlayRun(args) {
|
|
|
7247
7801
|
if (isFileTarget(options.target.path)) {
|
|
7248
7802
|
return handleFileBackedRun(options);
|
|
7249
7803
|
}
|
|
7250
|
-
const resolved =
|
|
7804
|
+
const resolved = resolve9(options.target.path);
|
|
7251
7805
|
console.error(`File not found: ${resolved}`);
|
|
7252
7806
|
const dir = dirname8(resolved);
|
|
7253
7807
|
if (existsSync6(dir)) {
|
|
@@ -7394,14 +7948,14 @@ async function handleRunLogs(args) {
|
|
|
7394
7948
|
continue;
|
|
7395
7949
|
}
|
|
7396
7950
|
if (arg === "--out" && args[index + 1]) {
|
|
7397
|
-
outPath =
|
|
7951
|
+
outPath = resolve9(args[++index]);
|
|
7398
7952
|
}
|
|
7399
7953
|
}
|
|
7400
7954
|
const client = new DeeplineClient();
|
|
7401
7955
|
const status = await client.runs.get(runId);
|
|
7402
7956
|
const logs = status.progress?.logs ?? [];
|
|
7403
7957
|
if (outPath) {
|
|
7404
|
-
|
|
7958
|
+
writeFileSync6(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
7405
7959
|
printCommandEnvelope({
|
|
7406
7960
|
runId: status.runId,
|
|
7407
7961
|
log_path: outPath,
|
|
@@ -7457,7 +8011,7 @@ async function handleRunStop(args) {
|
|
|
7457
8011
|
return 0;
|
|
7458
8012
|
}
|
|
7459
8013
|
async function handleRunExport(args) {
|
|
7460
|
-
const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--json]";
|
|
8014
|
+
const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--metadata-out export.json] [--json]";
|
|
7461
8015
|
let runId;
|
|
7462
8016
|
try {
|
|
7463
8017
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -7467,14 +8021,19 @@ async function handleRunExport(args) {
|
|
|
7467
8021
|
}
|
|
7468
8022
|
let outPath = null;
|
|
7469
8023
|
let datasetPath = null;
|
|
8024
|
+
let metadataOutPath = null;
|
|
7470
8025
|
for (let index = 0; index < args.length; index += 1) {
|
|
7471
8026
|
const arg = args[index];
|
|
7472
8027
|
if (arg === "--out" && args[index + 1]) {
|
|
7473
|
-
outPath =
|
|
8028
|
+
outPath = resolve9(args[++index]);
|
|
7474
8029
|
continue;
|
|
7475
8030
|
}
|
|
7476
8031
|
if (arg === "--dataset" && args[index + 1]) {
|
|
7477
8032
|
datasetPath = args[++index];
|
|
8033
|
+
continue;
|
|
8034
|
+
}
|
|
8035
|
+
if (arg === "--metadata-out" && args[index + 1]) {
|
|
8036
|
+
metadataOutPath = resolve9(args[++index]);
|
|
7478
8037
|
}
|
|
7479
8038
|
}
|
|
7480
8039
|
if (!outPath) {
|
|
@@ -7486,15 +8045,59 @@ async function handleRunExport(args) {
|
|
|
7486
8045
|
const exportResult = await exportPlayStatusRows(client, status, outPath, {
|
|
7487
8046
|
datasetPath
|
|
7488
8047
|
});
|
|
7489
|
-
|
|
8048
|
+
const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
|
|
8049
|
+
const queryPlan = exportResult && outPath ? buildCustomerDbQueryPlan({
|
|
8050
|
+
status,
|
|
8051
|
+
rowsInfo: exportResult.rowsInfo,
|
|
8052
|
+
outPath
|
|
8053
|
+
}) : null;
|
|
8054
|
+
const next = {
|
|
8055
|
+
...buildRunNextCommands(status),
|
|
8056
|
+
export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source),
|
|
8057
|
+
...queryPlan ? {
|
|
8058
|
+
queryJson: queryPlan.json,
|
|
8059
|
+
queryCsv: queryPlan.csv
|
|
8060
|
+
} : {}
|
|
8061
|
+
};
|
|
8062
|
+
const payload = {
|
|
7490
8063
|
runId: status.runId,
|
|
7491
8064
|
...datasetPath ? { dataset: datasetPath } : {},
|
|
7492
8065
|
csv_path: exportResult?.path ?? null,
|
|
8066
|
+
source,
|
|
7493
8067
|
rowCount: exportResult?.rowsInfo.totalRows ?? null,
|
|
7494
8068
|
columns: exportResult?.rowsInfo.columns ?? [],
|
|
8069
|
+
...queryPlan ? {
|
|
8070
|
+
query: {
|
|
8071
|
+
sql: queryPlan.sql,
|
|
8072
|
+
json: queryPlan.json,
|
|
8073
|
+
csv: queryPlan.csv
|
|
8074
|
+
}
|
|
8075
|
+
} : {},
|
|
8076
|
+
...metadataOutPath ? { metadata_path: metadataOutPath } : {},
|
|
7495
8077
|
local: { csv_path: exportResult?.path ?? null },
|
|
7496
|
-
|
|
7497
|
-
|
|
8078
|
+
next,
|
|
8079
|
+
render: {
|
|
8080
|
+
sections: [
|
|
8081
|
+
{
|
|
8082
|
+
title: "run export",
|
|
8083
|
+
lines: [
|
|
8084
|
+
`Exported ${status.runId} to ${exportResult?.path ?? outPath}`,
|
|
8085
|
+
...source ? [`source=${source}`] : [],
|
|
8086
|
+
...queryPlan ? [`query=${queryPlan.json}`] : []
|
|
8087
|
+
]
|
|
8088
|
+
}
|
|
8089
|
+
]
|
|
8090
|
+
}
|
|
8091
|
+
};
|
|
8092
|
+
if (metadataOutPath) {
|
|
8093
|
+
writeFileSync6(
|
|
8094
|
+
metadataOutPath,
|
|
8095
|
+
`${JSON.stringify(payload, null, 2)}
|
|
8096
|
+
`,
|
|
8097
|
+
"utf-8"
|
|
8098
|
+
);
|
|
8099
|
+
}
|
|
8100
|
+
printCommandEnvelope(payload, { json: argsWantJson(args) });
|
|
7498
8101
|
return 0;
|
|
7499
8102
|
}
|
|
7500
8103
|
async function handlePlayGet(args) {
|
|
@@ -7511,10 +8114,10 @@ async function handlePlayGet(args) {
|
|
|
7511
8114
|
for (let index = 1; index < args.length; index += 1) {
|
|
7512
8115
|
const arg = args[index];
|
|
7513
8116
|
if (arg === "--out" && args[index + 1]) {
|
|
7514
|
-
outPath =
|
|
8117
|
+
outPath = resolve9(args[++index]);
|
|
7515
8118
|
}
|
|
7516
8119
|
}
|
|
7517
|
-
const playName = isFileTarget(target) ? extractPlayName(readFileSync5(
|
|
8120
|
+
const playName = isFileTarget(target) ? extractPlayName(readFileSync5(resolve9(target), "utf-8"), resolve9(target)) : parseReferencedPlayTarget(target).playName;
|
|
7518
8121
|
const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
|
|
7519
8122
|
const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
|
|
7520
8123
|
const materializedFile = outPath ? materializeRemotePlaySource({
|
|
@@ -7806,7 +8409,7 @@ async function handlePlayPublish(args) {
|
|
|
7806
8409
|
}
|
|
7807
8410
|
let graph;
|
|
7808
8411
|
try {
|
|
7809
|
-
graph = await collectBundledPlayGraph(
|
|
8412
|
+
graph = await collectBundledPlayGraph(resolve9(playName));
|
|
7810
8413
|
await compileBundledPlayGraphManifests(client, graph);
|
|
7811
8414
|
await publishImportedPlayDependencies(client, graph);
|
|
7812
8415
|
} catch (error) {
|
|
@@ -8292,30 +8895,34 @@ Examples:
|
|
|
8292
8895
|
Notes:
|
|
8293
8896
|
Writes a returned dataset handle to the requested local CSV path. Use runs get
|
|
8294
8897
|
first to inspect dataset paths like result.rows or result.nested.contacts.
|
|
8898
|
+
--metadata-out writes the same export metadata object returned by --json,
|
|
8899
|
+
including source and follow-on customer-db query commands when available.
|
|
8295
8900
|
|
|
8296
8901
|
Examples:
|
|
8297
8902
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
8298
8903
|
deepline runs export play/my-play/run/20260501t000000-000 --dataset result.rows --out output.csv
|
|
8904
|
+
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --metadata-out output.meta.json
|
|
8299
8905
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --json
|
|
8300
8906
|
`
|
|
8301
|
-
).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) => {
|
|
8907
|
+
).requiredOption("--out <path>", "Output CSV path").option("--dataset <path>", "Returned dataset handle path, such as result.rows").option("--metadata-out <path>", "Write export metadata JSON to a file").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
8302
8908
|
process.exitCode = await handleRunExport([
|
|
8303
8909
|
runId,
|
|
8304
8910
|
...options.dataset ? ["--dataset", options.dataset] : [],
|
|
8305
8911
|
"--out",
|
|
8306
8912
|
options.out,
|
|
8913
|
+
...options.metadataOut ? ["--metadata-out", options.metadataOut] : [],
|
|
8307
8914
|
...options.json ? ["--json"] : []
|
|
8308
8915
|
]);
|
|
8309
8916
|
});
|
|
8310
8917
|
}
|
|
8311
8918
|
|
|
8312
8919
|
// src/cli/commands/tools.ts
|
|
8313
|
-
import { chmodSync, mkdtempSync, writeFileSync as
|
|
8920
|
+
import { chmodSync, mkdtempSync, writeFileSync as writeFileSync8 } from "fs";
|
|
8314
8921
|
import { tmpdir as tmpdir3 } from "os";
|
|
8315
8922
|
import { join as join8 } from "path";
|
|
8316
8923
|
|
|
8317
8924
|
// src/tool-output.ts
|
|
8318
|
-
import { mkdirSync as mkdirSync4, writeFileSync as
|
|
8925
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
8319
8926
|
import { homedir as homedir3 } from "os";
|
|
8320
8927
|
import { join as join7 } from "path";
|
|
8321
8928
|
function isPlainObject(value) {
|
|
@@ -8340,6 +8947,25 @@ function normalizeRows(value) {
|
|
|
8340
8947
|
}
|
|
8341
8948
|
function candidateRoots(payload) {
|
|
8342
8949
|
const roots = [{ path: null, value: payload }];
|
|
8950
|
+
if (isPlainObject(payload) && isPlainObject(payload.toolExecutionResult)) {
|
|
8951
|
+
roots.push({ path: "toolExecutionResult", value: payload.toolExecutionResult });
|
|
8952
|
+
const toolOutput = payload.toolExecutionResult.toolOutput;
|
|
8953
|
+
if (isPlainObject(toolOutput)) {
|
|
8954
|
+
roots.push({ path: "toolExecutionResult.toolOutput", value: toolOutput });
|
|
8955
|
+
if (Object.prototype.hasOwnProperty.call(toolOutput, "raw")) {
|
|
8956
|
+
roots.push({
|
|
8957
|
+
path: "toolExecutionResult.toolOutput.raw",
|
|
8958
|
+
value: toolOutput.raw
|
|
8959
|
+
});
|
|
8960
|
+
}
|
|
8961
|
+
}
|
|
8962
|
+
}
|
|
8963
|
+
if (isPlainObject(payload) && isPlainObject(payload.output)) {
|
|
8964
|
+
roots.push({ path: "output", value: payload.output });
|
|
8965
|
+
if (Object.prototype.hasOwnProperty.call(payload.output, "body")) {
|
|
8966
|
+
roots.push({ path: "output.body", value: payload.output.body });
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8343
8969
|
if (isPlainObject(payload) && isPlainObject(payload.result)) {
|
|
8344
8970
|
roots.push({ path: "result", value: payload.result });
|
|
8345
8971
|
if (isPlainObject(payload.result.data)) {
|
|
@@ -8399,7 +9025,7 @@ function ensureOutputDir() {
|
|
|
8399
9025
|
function writeJsonOutputFile(payload, stem) {
|
|
8400
9026
|
const outputDir = ensureOutputDir();
|
|
8401
9027
|
const outputPath = join7(outputDir, `${stem}_${Date.now()}.json`);
|
|
8402
|
-
|
|
9028
|
+
writeFileSync7(outputPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
8403
9029
|
return outputPath;
|
|
8404
9030
|
}
|
|
8405
9031
|
function writeCsvOutputFile(rows, stem) {
|
|
@@ -8427,7 +9053,7 @@ function writeCsvOutputFile(rows, stem) {
|
|
|
8427
9053
|
for (const row of rows) {
|
|
8428
9054
|
lines.push(columns.map((column) => escapeCell(row[column])).join(","));
|
|
8429
9055
|
}
|
|
8430
|
-
|
|
9056
|
+
writeFileSync7(outputPath, `${lines.join("\n")}
|
|
8431
9057
|
`, "utf-8");
|
|
8432
9058
|
const previewRows = rows.slice(0, 5);
|
|
8433
9059
|
const previewColumns = columns.slice(0, 5);
|
|
@@ -8473,8 +9099,7 @@ async function listTools(args) {
|
|
|
8473
9099
|
title: `${items.length} tools available:`,
|
|
8474
9100
|
lines: items.flatMap((item) => {
|
|
8475
9101
|
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8476
|
-
|
|
8477
|
-
return [`${item.toolId}${cats}`, ` ${item.description}${listHint}`];
|
|
9102
|
+
return [`${item.toolId}${cats}`, ` ${item.description}`];
|
|
8478
9103
|
})
|
|
8479
9104
|
}
|
|
8480
9105
|
]
|
|
@@ -8553,7 +9178,7 @@ Common commands:
|
|
|
8553
9178
|
deepline tools execute hunter_email_verifier --input '{"email":"a@b.com"}'
|
|
8554
9179
|
|
|
8555
9180
|
Output:
|
|
8556
|
-
Use describe for tool contracts.
|
|
9181
|
+
Use describe for tool contracts.
|
|
8557
9182
|
Use execute to run a tool. run is accepted as a compatibility alias.
|
|
8558
9183
|
`
|
|
8559
9184
|
);
|
|
@@ -8600,8 +9225,8 @@ Examples:
|
|
|
8600
9225
|
`
|
|
8601
9226
|
Notes:
|
|
8602
9227
|
Shows the tool contract, input schema, output schema, Deepline cost, aliases,
|
|
8603
|
-
and metadata. describe is the
|
|
8604
|
-
|
|
9228
|
+
and metadata. describe is the supported discovery verb. get is removed in
|
|
9229
|
+
the V2 SDK CLI; use describe for the same metadata surface.
|
|
8605
9230
|
|
|
8606
9231
|
Examples:
|
|
8607
9232
|
deepline tools describe hunter_email_verifier
|
|
@@ -8614,7 +9239,22 @@ Examples:
|
|
|
8614
9239
|
...options.json ? ["--json"] : []
|
|
8615
9240
|
]);
|
|
8616
9241
|
});
|
|
8617
|
-
addToolMetadataCommand(tools.command("describe <toolId>")
|
|
9242
|
+
addToolMetadataCommand(tools.command("describe <toolId>"));
|
|
9243
|
+
tools.command("get <toolId>").description("Deprecated. Use tools describe.").addHelpText(
|
|
9244
|
+
"after",
|
|
9245
|
+
`
|
|
9246
|
+
Examples:
|
|
9247
|
+
deepline tools describe hunter_email_verifier --json
|
|
9248
|
+
`
|
|
9249
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (toolId, options) => {
|
|
9250
|
+
const message = `tools get has been removed from the V2 SDK CLI. Use: deepline tools describe ${toolId} --json`;
|
|
9251
|
+
if (options.json || shouldEmitJson()) {
|
|
9252
|
+
printJsonError({ message, code: "TOOLS_GET_REMOVED" });
|
|
9253
|
+
} else {
|
|
9254
|
+
console.error(message);
|
|
9255
|
+
}
|
|
9256
|
+
process.exitCode = 2;
|
|
9257
|
+
});
|
|
8618
9258
|
tools.command("execute <toolId>").alias("run").description("Execute a tool by id.").addHelpText(
|
|
8619
9259
|
"after",
|
|
8620
9260
|
`
|
|
@@ -8695,6 +9335,7 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
8695
9335
|
const stepContributions = arrayField(tool, "stepContributions", "step_contributions");
|
|
8696
9336
|
const playExpansion = recordField(tool, "playExpansion", "play_expansion");
|
|
8697
9337
|
const samples = recordField(tool, "samples");
|
|
9338
|
+
const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
|
|
8698
9339
|
console.log(`Tool: ${toolId}`);
|
|
8699
9340
|
if (displayName) {
|
|
8700
9341
|
console.log(" Display name:");
|
|
@@ -8758,14 +9399,16 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
8758
9399
|
console.log(" Tip: pass --payload with a JSON object.");
|
|
8759
9400
|
}
|
|
8760
9401
|
printSamples(samples);
|
|
9402
|
+
printUsageGuidance(usageGuidance);
|
|
8761
9403
|
if (isPlayTool(tool)) {
|
|
8762
9404
|
console.log(" Play contract:");
|
|
8763
9405
|
console.log(" - This is a deepline-native waterfall; the returned rows are extracted by target getters, not by hand-authored payload shape.");
|
|
8764
9406
|
if (playExpansion && typeof playExpansion.group === "string" && playExpansion.group.trim()) {
|
|
8765
9407
|
console.log(` - Output alias/runtime group is: ${playExpansion.group.trim()}`);
|
|
8766
9408
|
}
|
|
8767
|
-
const
|
|
8768
|
-
const
|
|
9409
|
+
const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult");
|
|
9410
|
+
const extractedValues = arrayField(toolExecutionResult, "extractedValues", "extracted_values");
|
|
9411
|
+
const targets = extractedValues.map((entry) => isRecord3(entry) && typeof entry.name === "string" ? entry.name : "").filter(Boolean).sort();
|
|
8769
9412
|
if (targets.length) {
|
|
8770
9413
|
console.log(` - Built-in extract targets: ${targets.join(", ")}`);
|
|
8771
9414
|
}
|
|
@@ -8784,7 +9427,53 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
8784
9427
|
} else {
|
|
8785
9428
|
console.log(` deepline tools execute ${toolId} --payload '{...}'`);
|
|
8786
9429
|
}
|
|
8787
|
-
console.log(" deepline tools
|
|
9430
|
+
console.log(" deepline tools describe <tool_id> --json");
|
|
9431
|
+
}
|
|
9432
|
+
function printUsageGuidance(usageGuidance) {
|
|
9433
|
+
if (Object.keys(usageGuidance).length === 0) return;
|
|
9434
|
+
const execute = stringField(usageGuidance, "execute");
|
|
9435
|
+
const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult", "tool_execution_result");
|
|
9436
|
+
const toolOutput = recordField(toolExecutionResult, "toolOutput", "tool_output");
|
|
9437
|
+
const extractedLists = extractionEntries(toolExecutionResult, "extractedLists", "extracted_lists");
|
|
9438
|
+
const extractedValues = extractionEntries(toolExecutionResult, "extractedValues", "extracted_values");
|
|
9439
|
+
console.log(" Usage guidance:");
|
|
9440
|
+
if (execute) console.log(` ${execute}`);
|
|
9441
|
+
const raw = pathField(toolOutput, "raw");
|
|
9442
|
+
const meta = pathField(toolOutput, "meta");
|
|
9443
|
+
if (raw) console.log(` Raw tool output: ${raw}`);
|
|
9444
|
+
if (meta) console.log(` Tool output metadata: ${meta}`);
|
|
9445
|
+
printExtractions("Extracted lists", extractedLists);
|
|
9446
|
+
printExtractions("Extracted values", extractedValues);
|
|
9447
|
+
}
|
|
9448
|
+
function pathField(record, key) {
|
|
9449
|
+
const value = record[key];
|
|
9450
|
+
if (typeof value === "string") return value.trim();
|
|
9451
|
+
if (isRecord3(value)) return stringField(value, "path");
|
|
9452
|
+
return "";
|
|
9453
|
+
}
|
|
9454
|
+
function extractionEntries(record, camelKey, snakeKey) {
|
|
9455
|
+
const value = record[camelKey] ?? record[snakeKey];
|
|
9456
|
+
if (Array.isArray(value)) return value;
|
|
9457
|
+
if (!isRecord3(value)) return [];
|
|
9458
|
+
return Object.entries(value).map(
|
|
9459
|
+
([name, entry]) => isRecord3(entry) ? { name, ...entry } : { name }
|
|
9460
|
+
);
|
|
9461
|
+
}
|
|
9462
|
+
function printExtractions(label, entries) {
|
|
9463
|
+
if (!entries.length) return;
|
|
9464
|
+
console.log(` ${label}:`);
|
|
9465
|
+
for (const entry of entries) {
|
|
9466
|
+
if (!isRecord3(entry)) continue;
|
|
9467
|
+
const name = stringField(entry, "name");
|
|
9468
|
+
const expression = stringField(entry, "expression");
|
|
9469
|
+
const details = recordField(entry, "details");
|
|
9470
|
+
const rawToolOutputPaths = arrayField(details, "rawToolOutputPaths", "raw_tool_output_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
|
|
9471
|
+
const candidatePaths = arrayField(details, "candidatePaths", "candidate_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
|
|
9472
|
+
if (!name || !expression) continue;
|
|
9473
|
+
const paths = candidatePaths.length ? candidatePaths : rawToolOutputPaths;
|
|
9474
|
+
const pathSuffix = paths.length ? ` from ${paths.join(", ")}` : "";
|
|
9475
|
+
console.log(` - ${name}: ${expression}${pathSuffix}`);
|
|
9476
|
+
}
|
|
8788
9477
|
}
|
|
8789
9478
|
function printToolCost(input) {
|
|
8790
9479
|
const { cost, billingSource, deeplineCredits, deeplineUsdPerPricingUnit } = input;
|
|
@@ -8852,6 +9541,17 @@ function samplePayload(samples, key) {
|
|
|
8852
9541
|
function commandEnvelopeFromRawResponse(rawResponse) {
|
|
8853
9542
|
return isRecord3(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
|
|
8854
9543
|
}
|
|
9544
|
+
function listExtractorPathsFromUsageGuidance(tool) {
|
|
9545
|
+
const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
|
|
9546
|
+
const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord3(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
|
|
9547
|
+
return extractedLists.flatMap((entry) => {
|
|
9548
|
+
const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
|
|
9549
|
+
if (!Array.isArray(paths)) return [];
|
|
9550
|
+
return paths.map(
|
|
9551
|
+
(path) => path.trim().replace(/^toolExecutionResult\.toolOutput\.raw\.?/, "").replace(/^\./, "")
|
|
9552
|
+
).filter(Boolean);
|
|
9553
|
+
});
|
|
9554
|
+
}
|
|
8855
9555
|
function isPlayTool(tool) {
|
|
8856
9556
|
const provider = typeof tool.provider === "string" ? tool.provider : "";
|
|
8857
9557
|
return provider === "deepline_native" && Boolean(recordField(tool, "playExpansion", "play_expansion"));
|
|
@@ -8925,6 +9625,7 @@ function parseExecuteOptions(args) {
|
|
|
8925
9625
|
const params = {};
|
|
8926
9626
|
let outputFormat = "auto";
|
|
8927
9627
|
let noPreview = false;
|
|
9628
|
+
let fullOutput = false;
|
|
8928
9629
|
for (let index = 1; index < args.length; index += 1) {
|
|
8929
9630
|
const arg = args[index];
|
|
8930
9631
|
if ((arg === "--param" || arg === "-p") && args[index + 1]) {
|
|
@@ -8951,6 +9652,7 @@ function parseExecuteOptions(args) {
|
|
|
8951
9652
|
}
|
|
8952
9653
|
if (arg === "--full-output") {
|
|
8953
9654
|
outputFormat = "json";
|
|
9655
|
+
fullOutput = true;
|
|
8954
9656
|
continue;
|
|
8955
9657
|
}
|
|
8956
9658
|
if (arg === "--no-preview") {
|
|
@@ -8959,7 +9661,7 @@ function parseExecuteOptions(args) {
|
|
|
8959
9661
|
}
|
|
8960
9662
|
throw new Error(`Unknown option: ${arg}`);
|
|
8961
9663
|
}
|
|
8962
|
-
return { toolId, params, outputFormat, noPreview };
|
|
9664
|
+
return { toolId, params, outputFormat, noPreview, fullOutput };
|
|
8963
9665
|
}
|
|
8964
9666
|
function safeFileStem(value) {
|
|
8965
9667
|
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
@@ -8991,7 +9693,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
8991
9693
|
description: ${JSON.stringify(`Seed ${input.toolId} rows for workflow expansion.`)},
|
|
8992
9694
|
});
|
|
8993
9695
|
|
|
8994
|
-
const list = Object.values(result.
|
|
9696
|
+
const list = Object.values(result.extractedLists)[0];
|
|
8995
9697
|
const rows = (list?.get() ?? []).slice(0, 100);
|
|
8996
9698
|
// ${sampleRows}
|
|
8997
9699
|
// columns: ${columns}
|
|
@@ -9008,7 +9710,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
9008
9710
|
};
|
|
9009
9711
|
});
|
|
9010
9712
|
`;
|
|
9011
|
-
|
|
9713
|
+
writeFileSync8(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
9012
9714
|
return {
|
|
9013
9715
|
path: scriptPath,
|
|
9014
9716
|
projectDir,
|
|
@@ -9019,7 +9721,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
9019
9721
|
function buildToolExecuteBaseEnvelope(input) {
|
|
9020
9722
|
const envelope = commandEnvelopeFromRawResponse(input.rawResponse);
|
|
9021
9723
|
const summaryEntries = Object.entries(input.summary);
|
|
9022
|
-
const
|
|
9724
|
+
const outputPreview = input.listConversion ? {
|
|
9023
9725
|
kind: "list",
|
|
9024
9726
|
rowCount: input.listConversion.rows.length,
|
|
9025
9727
|
columns: Object.keys(input.listConversion.rows[0] ?? {}),
|
|
@@ -9030,6 +9732,7 @@ function buildToolExecuteBaseEnvelope(input) {
|
|
|
9030
9732
|
kind: summaryEntries.length > 0 ? "object" : "raw",
|
|
9031
9733
|
summary: input.summary
|
|
9032
9734
|
};
|
|
9735
|
+
const envelopeHasCanonicalOutput = isRecord3(envelope.toolExecutionResult) && isRecord3(envelope.toolExecutionResult.toolOutput) && Object.prototype.hasOwnProperty.call(envelope.toolExecutionResult.toolOutput, "raw");
|
|
9033
9736
|
const actions = input.listConversion ? [
|
|
9034
9737
|
{
|
|
9035
9738
|
label: "next",
|
|
@@ -9038,7 +9741,7 @@ function buildToolExecuteBaseEnvelope(input) {
|
|
|
9038
9741
|
] : [];
|
|
9039
9742
|
return {
|
|
9040
9743
|
...envelope,
|
|
9041
|
-
output,
|
|
9744
|
+
...envelopeHasCanonicalOutput ? { output_preview: outputPreview } : { output: outputPreview },
|
|
9042
9745
|
...summaryEntries.length > 0 ? { summary: input.summary } : {},
|
|
9043
9746
|
next: input.listConversion ? {
|
|
9044
9747
|
expandToPlay: "Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
|
|
@@ -9091,8 +9794,12 @@ async function executeTool(args) {
|
|
|
9091
9794
|
throw error;
|
|
9092
9795
|
}
|
|
9093
9796
|
const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
|
|
9797
|
+
if (parsed.fullOutput) {
|
|
9798
|
+
printJson(rawResponse);
|
|
9799
|
+
return 0;
|
|
9800
|
+
}
|
|
9094
9801
|
const listConversion = tryConvertToList(rawResponse, {
|
|
9095
|
-
listExtractorPaths: metadata
|
|
9802
|
+
listExtractorPaths: listExtractorPathsFromUsageGuidance(metadata)
|
|
9096
9803
|
});
|
|
9097
9804
|
const summary = extractSummaryFields(rawResponse);
|
|
9098
9805
|
const baseEnvelope = buildToolExecuteBaseEnvelope({
|
|
@@ -9224,7 +9931,7 @@ async function executeTool(args) {
|
|
|
9224
9931
|
// src/cli/commands/update.ts
|
|
9225
9932
|
import { spawn } from "child_process";
|
|
9226
9933
|
import { existsSync as existsSync7 } from "fs";
|
|
9227
|
-
import { dirname as dirname9, join as join9, resolve as
|
|
9934
|
+
import { dirname as dirname9, join as join9, resolve as resolve10 } from "path";
|
|
9228
9935
|
function posixShellQuote(value) {
|
|
9229
9936
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
9230
9937
|
}
|
|
@@ -9243,7 +9950,7 @@ function buildSourceUpdateCommand(sourceRoot) {
|
|
|
9243
9950
|
return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
|
|
9244
9951
|
}
|
|
9245
9952
|
function findRepoBackedSdkRoot(startPath) {
|
|
9246
|
-
let current =
|
|
9953
|
+
let current = resolve10(startPath);
|
|
9247
9954
|
while (true) {
|
|
9248
9955
|
if (existsSync7(join9(current, "sdk", "package.json")) && existsSync7(join9(current, "sdk", "bin", "deepline-dev.ts"))) {
|
|
9249
9956
|
return current;
|
|
@@ -9254,7 +9961,7 @@ function findRepoBackedSdkRoot(startPath) {
|
|
|
9254
9961
|
}
|
|
9255
9962
|
}
|
|
9256
9963
|
function resolveUpdatePlan() {
|
|
9257
|
-
const entrypoint = process.argv[1] ?
|
|
9964
|
+
const entrypoint = process.argv[1] ? resolve10(process.argv[1]) : "";
|
|
9258
9965
|
const sourceRoot = entrypoint ? findRepoBackedSdkRoot(dirname9(entrypoint)) : null;
|
|
9259
9966
|
if (sourceRoot) {
|
|
9260
9967
|
return {
|
|
@@ -9348,7 +10055,7 @@ Examples:
|
|
|
9348
10055
|
|
|
9349
10056
|
// src/cli/skills-sync.ts
|
|
9350
10057
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
9351
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as
|
|
10058
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync9 } from "fs";
|
|
9352
10059
|
import { homedir as homedir4 } from "os";
|
|
9353
10060
|
import { dirname as dirname10, join as join10 } from "path";
|
|
9354
10061
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
@@ -9375,9 +10082,45 @@ function readLocalSkillsVersion(baseUrl) {
|
|
|
9375
10082
|
function writeLocalSkillsVersion(baseUrl, version) {
|
|
9376
10083
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
9377
10084
|
mkdirSync5(dirname10(path), { recursive: true });
|
|
9378
|
-
|
|
10085
|
+
writeFileSync9(path, `${version}
|
|
9379
10086
|
`, "utf-8");
|
|
9380
10087
|
}
|
|
10088
|
+
function installedSdkSkillHasStalePositionalExecuteExamples() {
|
|
10089
|
+
const home = process.env.HOME?.trim() || homedir4();
|
|
10090
|
+
const roots = [
|
|
10091
|
+
join10(home, ".claude", "skills", SDK_SKILL_NAME),
|
|
10092
|
+
join10(home, ".agents", "skills", SDK_SKILL_NAME)
|
|
10093
|
+
];
|
|
10094
|
+
const staleMarkers = [
|
|
10095
|
+
"ctx.tools.execute(key",
|
|
10096
|
+
"ctx.tools.execute('",
|
|
10097
|
+
'ctx.tools.execute("',
|
|
10098
|
+
"rowCtx.tools.execute('",
|
|
10099
|
+
'rowCtx.tools.execute("'
|
|
10100
|
+
];
|
|
10101
|
+
const scan = (dir) => {
|
|
10102
|
+
for (const entry of readdirSync2(dir)) {
|
|
10103
|
+
const path = join10(dir, entry);
|
|
10104
|
+
const stat3 = statSync(path);
|
|
10105
|
+
if (stat3.isDirectory()) {
|
|
10106
|
+
if (scan(path)) return true;
|
|
10107
|
+
continue;
|
|
10108
|
+
}
|
|
10109
|
+
if (!entry.endsWith(".md")) continue;
|
|
10110
|
+
const text = readFileSync6(path, "utf-8");
|
|
10111
|
+
if (staleMarkers.some((marker) => text.includes(marker))) return true;
|
|
10112
|
+
}
|
|
10113
|
+
return false;
|
|
10114
|
+
};
|
|
10115
|
+
for (const root of roots) {
|
|
10116
|
+
try {
|
|
10117
|
+
if (existsSync8(root) && scan(root)) return true;
|
|
10118
|
+
} catch {
|
|
10119
|
+
continue;
|
|
10120
|
+
}
|
|
10121
|
+
}
|
|
10122
|
+
return false;
|
|
10123
|
+
}
|
|
9381
10124
|
async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
9382
10125
|
const controller = new AbortController();
|
|
9383
10126
|
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
|
|
@@ -9413,7 +10156,7 @@ function buildSkillsInstallArgs(baseUrl) {
|
|
|
9413
10156
|
"skills",
|
|
9414
10157
|
"add",
|
|
9415
10158
|
packageUrl,
|
|
9416
|
-
"--
|
|
10159
|
+
"--agent",
|
|
9417
10160
|
...SKILL_AGENTS,
|
|
9418
10161
|
"--global",
|
|
9419
10162
|
"--yes",
|
|
@@ -9429,7 +10172,7 @@ function buildBunxSkillsInstallArgs(baseUrl) {
|
|
|
9429
10172
|
"skills",
|
|
9430
10173
|
"add",
|
|
9431
10174
|
packageUrl,
|
|
9432
|
-
"--
|
|
10175
|
+
"--agent",
|
|
9433
10176
|
...SKILL_AGENTS,
|
|
9434
10177
|
"--global",
|
|
9435
10178
|
"--yes",
|
|
@@ -9469,7 +10212,7 @@ function resolveSkillsInstallCommands(baseUrl) {
|
|
|
9469
10212
|
return [npxInstall];
|
|
9470
10213
|
}
|
|
9471
10214
|
function runOneSkillsInstall(install) {
|
|
9472
|
-
return new Promise((
|
|
10215
|
+
return new Promise((resolve11) => {
|
|
9473
10216
|
const child = spawn2(install.command, install.args, {
|
|
9474
10217
|
stdio: ["ignore", "ignore", "pipe"],
|
|
9475
10218
|
env: process.env
|
|
@@ -9479,7 +10222,7 @@ function runOneSkillsInstall(install) {
|
|
|
9479
10222
|
stderr += chunk.toString("utf-8");
|
|
9480
10223
|
});
|
|
9481
10224
|
child.on("error", (error) => {
|
|
9482
|
-
|
|
10225
|
+
resolve11({
|
|
9483
10226
|
ok: false,
|
|
9484
10227
|
detail: `failed to start ${install.command}: ${error.message}`,
|
|
9485
10228
|
manualCommand: install.manualCommand
|
|
@@ -9487,11 +10230,11 @@ function runOneSkillsInstall(install) {
|
|
|
9487
10230
|
});
|
|
9488
10231
|
child.on("close", (code) => {
|
|
9489
10232
|
if (code === 0) {
|
|
9490
|
-
|
|
10233
|
+
resolve11({ ok: true, detail: "", manualCommand: install.manualCommand });
|
|
9491
10234
|
return;
|
|
9492
10235
|
}
|
|
9493
10236
|
const detail = stderr.trim();
|
|
9494
|
-
|
|
10237
|
+
resolve11({
|
|
9495
10238
|
ok: false,
|
|
9496
10239
|
detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
|
|
9497
10240
|
manualCommand: install.manualCommand
|
|
@@ -9530,10 +10273,19 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
|
9530
10273
|
attemptedSync = true;
|
|
9531
10274
|
const localVersion = readLocalSkillsVersion(baseUrl);
|
|
9532
10275
|
const update = await fetchSkillsUpdate(baseUrl, localVersion);
|
|
9533
|
-
|
|
10276
|
+
const hasStaleInstalledSkill = installedSdkSkillHasStalePositionalExecuteExamples();
|
|
10277
|
+
if (!update?.needsUpdate && !hasStaleInstalledSkill || !update?.remoteVersion) {
|
|
10278
|
+
return;
|
|
10279
|
+
}
|
|
9534
10280
|
writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-sdk skill...");
|
|
9535
10281
|
const installed = await runSkillsInstall(baseUrl);
|
|
9536
10282
|
if (!installed) return;
|
|
10283
|
+
if (installedSdkSkillHasStalePositionalExecuteExamples()) {
|
|
10284
|
+
process.stderr.write(
|
|
10285
|
+
"SDK skills sync completed, but installed deepline-sdk docs still contain stale positional ctx.tools.execute examples.\n"
|
|
10286
|
+
);
|
|
10287
|
+
return;
|
|
10288
|
+
}
|
|
9537
10289
|
writeLocalSkillsVersion(baseUrl, update.remoteVersion);
|
|
9538
10290
|
writeSdkSkillsStatusLine("SDK skills are up to date.");
|
|
9539
10291
|
}
|