deepline 0.1.93 → 0.1.94
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 +2702 -509
- package/dist/cli/index.mjs +2725 -525
- package/dist/index.d.mts +158 -103
- package/dist/index.d.ts +158 -103
- package/dist/index.js +129 -39
- package/dist/index.mjs +129 -39
- package/dist/repo/apps/play-runner-workers/src/entry.ts +23 -8
- package/dist/repo/sdk/src/client.ts +123 -0
- package/dist/repo/sdk/src/play.ts +51 -0
- package/dist/repo/sdk/src/release.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/email-status.ts +10 -36
- package/dist/repo/shared_libs/play-runtime/extractor-targets.ts +3 -3
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +44 -0
- package/dist/repo/shared_libs/plays/secret-guardrails.ts +22 -11
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
|
-
import { mkdtemp as mkdtemp2, rm as rm2, writeFile as
|
|
5
|
-
import { join as
|
|
4
|
+
import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile6 } from "fs/promises";
|
|
5
|
+
import { join as join14 } from "path";
|
|
6
6
|
import { tmpdir as tmpdir5 } from "os";
|
|
7
7
|
import { Command as Command3 } from "commander";
|
|
8
8
|
|
|
@@ -206,10 +206,10 @@ import { join as join2 } from "path";
|
|
|
206
206
|
|
|
207
207
|
// src/release.ts
|
|
208
208
|
var SDK_RELEASE = {
|
|
209
|
-
version: "0.1.
|
|
209
|
+
version: "0.1.94",
|
|
210
210
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
211
211
|
supportPolicy: {
|
|
212
|
-
latest: "0.1.
|
|
212
|
+
latest: "0.1.94",
|
|
213
213
|
minimumSupported: "0.1.53",
|
|
214
214
|
deprecatedBelow: "0.1.53"
|
|
215
215
|
}
|
|
@@ -561,7 +561,7 @@ function decodeSseFrame(frame) {
|
|
|
561
561
|
return parsed;
|
|
562
562
|
}
|
|
563
563
|
function sleep(ms) {
|
|
564
|
-
return new Promise((
|
|
564
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
565
565
|
}
|
|
566
566
|
|
|
567
567
|
// src/stream-reconnect.ts
|
|
@@ -1177,7 +1177,7 @@ async function* observeRunEvents(options) {
|
|
|
1177
1177
|
const logPageQuery = convexServer.makeFunctionReference(
|
|
1178
1178
|
OBSERVER_LOG_PAGE_QUERY
|
|
1179
1179
|
);
|
|
1180
|
-
const
|
|
1180
|
+
const client2 = new convexBrowser.ConvexClient(grant.convexUrl, {
|
|
1181
1181
|
...webSocketConstructor ? { webSocketConstructor } : {},
|
|
1182
1182
|
unsavedChangesWarning: false
|
|
1183
1183
|
});
|
|
@@ -1189,7 +1189,7 @@ async function* observeRunEvents(options) {
|
|
|
1189
1189
|
wake = null;
|
|
1190
1190
|
};
|
|
1191
1191
|
let lastForcedRefreshAt = 0;
|
|
1192
|
-
|
|
1192
|
+
client2.setAuth(async ({ forceRefreshToken }) => {
|
|
1193
1193
|
const now = Date.now();
|
|
1194
1194
|
if (!forceRefreshToken && grant.expiresAt - now > GRANT_REFRESH_MARGIN_MS) {
|
|
1195
1195
|
return grant.token;
|
|
@@ -1216,7 +1216,7 @@ async function* observeRunEvents(options) {
|
|
|
1216
1216
|
return null;
|
|
1217
1217
|
}
|
|
1218
1218
|
});
|
|
1219
|
-
const unsubscribe =
|
|
1219
|
+
const unsubscribe = client2.onUpdate(
|
|
1220
1220
|
snapshotQuery,
|
|
1221
1221
|
{ workflowId: runId },
|
|
1222
1222
|
(run) => push({ kind: "run", run: run ?? null }),
|
|
@@ -1230,7 +1230,7 @@ async function* observeRunEvents(options) {
|
|
|
1230
1230
|
const watchdog = setInterval(() => {
|
|
1231
1231
|
const now = Date.now();
|
|
1232
1232
|
try {
|
|
1233
|
-
const connectionState =
|
|
1233
|
+
const connectionState = client2.connectionState();
|
|
1234
1234
|
if (connectionState.isWebSocketConnected) {
|
|
1235
1235
|
disconnectedSince = null;
|
|
1236
1236
|
warnedReconnecting = false;
|
|
@@ -1268,14 +1268,14 @@ async function* observeRunEvents(options) {
|
|
|
1268
1268
|
try {
|
|
1269
1269
|
for (; ; ) {
|
|
1270
1270
|
if (queue.length === 0) {
|
|
1271
|
-
const waitForItem = new Promise((
|
|
1272
|
-
wake =
|
|
1271
|
+
const waitForItem = new Promise((resolve15) => {
|
|
1272
|
+
wake = resolve15;
|
|
1273
1273
|
});
|
|
1274
1274
|
if (!sawFirstSnapshot) {
|
|
1275
1275
|
const timedOut = await Promise.race([
|
|
1276
1276
|
waitForItem.then(() => false),
|
|
1277
1277
|
new Promise(
|
|
1278
|
-
(
|
|
1278
|
+
(resolve15) => setTimeout(() => resolve15(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
|
|
1279
1279
|
)
|
|
1280
1280
|
]);
|
|
1281
1281
|
if (timedOut && queue.length === 0) {
|
|
@@ -1311,7 +1311,7 @@ async function* observeRunEvents(options) {
|
|
|
1311
1311
|
const gap = resolvePlayRunLogGap(snapshot, diffState.lastLogSeq);
|
|
1312
1312
|
if (gap && diffState.lastLogSeq > 0) {
|
|
1313
1313
|
const backfilled = await backfillLogGap({
|
|
1314
|
-
queryLogPage: (afterSeq, limit) =>
|
|
1314
|
+
queryLogPage: (afterSeq, limit) => client2.query(logPageQuery, {
|
|
1315
1315
|
workflowId: runId,
|
|
1316
1316
|
afterSeq,
|
|
1317
1317
|
limit
|
|
@@ -1361,7 +1361,7 @@ async function* observeRunEvents(options) {
|
|
|
1361
1361
|
unsubscribe();
|
|
1362
1362
|
} catch {
|
|
1363
1363
|
}
|
|
1364
|
-
await
|
|
1364
|
+
await client2.close().catch(() => void 0);
|
|
1365
1365
|
}
|
|
1366
1366
|
}
|
|
1367
1367
|
|
|
@@ -1372,7 +1372,7 @@ var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
|
|
|
1372
1372
|
var V2_EXECUTE_RESPONSE_CONTRACT = "v2-tool-response";
|
|
1373
1373
|
var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
|
|
1374
1374
|
function sleep2(ms) {
|
|
1375
|
-
return new Promise((
|
|
1375
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
1376
1376
|
}
|
|
1377
1377
|
function isTransientCompileManifestError(error) {
|
|
1378
1378
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
@@ -2275,6 +2275,93 @@ var DeeplineClient = class {
|
|
|
2275
2275
|
);
|
|
2276
2276
|
return response.runs ?? [];
|
|
2277
2277
|
}
|
|
2278
|
+
// ---------------------------------------------------------------------------
|
|
2279
|
+
// Legacy workflows (double-shipped). Thin pass-throughs over the live cloud
|
|
2280
|
+
// `/api/v2/workflows/*` API so the SDK CLI keeps existing cloud workflows
|
|
2281
|
+
// working while users migrate them to plays via `workflows transform`. Kept
|
|
2282
|
+
// intentionally minimal — workflows are a deprecated surface.
|
|
2283
|
+
// ---------------------------------------------------------------------------
|
|
2284
|
+
/** List the org's workflows. `GET /api/v2/workflows`. */
|
|
2285
|
+
async listWorkflows(options) {
|
|
2286
|
+
const params = new URLSearchParams();
|
|
2287
|
+
if (typeof options?.limit === "number") {
|
|
2288
|
+
params.set("limit", String(options.limit));
|
|
2289
|
+
}
|
|
2290
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2291
|
+
return this.http.get(`/api/v2/workflows${query}`);
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Fetch a single workflow (including its published-revision config — the
|
|
2295
|
+
* input to `compileWorkflowConfigToPlay`). `GET /api/v2/workflows/:id`.
|
|
2296
|
+
*/
|
|
2297
|
+
async getWorkflow(id) {
|
|
2298
|
+
return this.http.get(`/api/v2/workflows/${encodeURIComponent(id)}`);
|
|
2299
|
+
}
|
|
2300
|
+
/** Delete a workflow. `DELETE /api/v2/workflows/:id`. */
|
|
2301
|
+
async deleteWorkflow(id) {
|
|
2302
|
+
return this.http.delete(`/api/v2/workflows/${encodeURIComponent(id)}`);
|
|
2303
|
+
}
|
|
2304
|
+
/** Turn a workflow off. `POST /api/v2/workflows/:id/disable`. */
|
|
2305
|
+
async disableWorkflow(id) {
|
|
2306
|
+
return this.http.post(
|
|
2307
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/disable`,
|
|
2308
|
+
{}
|
|
2309
|
+
);
|
|
2310
|
+
}
|
|
2311
|
+
/** Turn a workflow back on. `POST /api/v2/workflows/:id/enable`. */
|
|
2312
|
+
async enableWorkflow(id) {
|
|
2313
|
+
return this.http.post(
|
|
2314
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/enable`,
|
|
2315
|
+
{}
|
|
2316
|
+
);
|
|
2317
|
+
}
|
|
2318
|
+
/** Create/update a workflow from config. `POST /api/v2/workflows/apply`. */
|
|
2319
|
+
async applyWorkflow(body) {
|
|
2320
|
+
return this.http.post("/api/v2/workflows/apply", body);
|
|
2321
|
+
}
|
|
2322
|
+
/** Validate a workflow config without saving. `POST /api/v2/workflows/lint`. */
|
|
2323
|
+
async lintWorkflow(body) {
|
|
2324
|
+
return this.http.post("/api/v2/workflows/lint", body);
|
|
2325
|
+
}
|
|
2326
|
+
/** Fetch live workflow request schemas. `GET /api/v2/workflows/schema`. */
|
|
2327
|
+
async getWorkflowSchema(subject) {
|
|
2328
|
+
const params = new URLSearchParams();
|
|
2329
|
+
if (subject) params.set("subject", subject);
|
|
2330
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2331
|
+
return this.http.get(`/api/v2/workflows/schema${query}`);
|
|
2332
|
+
}
|
|
2333
|
+
/** Queue a workflow run. `POST /api/v2/workflows/call`. */
|
|
2334
|
+
async callWorkflow(body) {
|
|
2335
|
+
return this.http.post("/api/v2/workflows/call", body);
|
|
2336
|
+
}
|
|
2337
|
+
/** List a workflow's runs. `GET /api/v2/workflows/:id/runs`. */
|
|
2338
|
+
async listWorkflowRuns(id, options) {
|
|
2339
|
+
const params = new URLSearchParams();
|
|
2340
|
+
if (typeof options?.limit === "number") {
|
|
2341
|
+
params.set("limit", String(options.limit));
|
|
2342
|
+
}
|
|
2343
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2344
|
+
return this.http.get(
|
|
2345
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/runs${query}`
|
|
2346
|
+
);
|
|
2347
|
+
}
|
|
2348
|
+
/** Fetch one workflow run. `GET /api/v2/workflows/:id/runs/:runId`. */
|
|
2349
|
+
async getWorkflowRun(id, runId) {
|
|
2350
|
+
return this.http.get(
|
|
2351
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
|
|
2352
|
+
runId
|
|
2353
|
+
)}`
|
|
2354
|
+
);
|
|
2355
|
+
}
|
|
2356
|
+
/** Cancel a workflow run. `POST /api/v2/workflows/:id/runs/:runId/cancel`. */
|
|
2357
|
+
async cancelWorkflowRun(id, runId) {
|
|
2358
|
+
return this.http.post(
|
|
2359
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
|
|
2360
|
+
runId
|
|
2361
|
+
)}/cancel`,
|
|
2362
|
+
{}
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2278
2365
|
/**
|
|
2279
2366
|
* Get a run by id using the public runs resource model.
|
|
2280
2367
|
*
|
|
@@ -3174,6 +3261,9 @@ function openInBrowser(url) {
|
|
|
3174
3261
|
} catch {
|
|
3175
3262
|
}
|
|
3176
3263
|
}
|
|
3264
|
+
function sleep3(ms) {
|
|
3265
|
+
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
3266
|
+
}
|
|
3177
3267
|
function collectLocalEnvInfo() {
|
|
3178
3268
|
return {
|
|
3179
3269
|
os: `${process.platform} ${process.arch}`,
|
|
@@ -3211,14 +3301,38 @@ function parseMaybeJsonObject(value) {
|
|
|
3211
3301
|
return value;
|
|
3212
3302
|
}
|
|
3213
3303
|
}
|
|
3304
|
+
function failureMessageFromRecord(value) {
|
|
3305
|
+
const status = typeof value.status === "string" ? value.status.trim().toLowerCase() : "";
|
|
3306
|
+
const directError = typeof value.error === "string" ? value.error.trim() : typeof value.last_error === "string" ? value.last_error.trim() : "";
|
|
3307
|
+
const result = value.result && typeof value.result === "object" && !Array.isArray(value.result) ? value.result : null;
|
|
3308
|
+
const resultError = typeof result?.error === "string" ? result.error.trim() : typeof result?.message === "string" ? result.message.trim() : "";
|
|
3309
|
+
if (!directError && !resultError && status !== "error" && status !== "failed") {
|
|
3310
|
+
return null;
|
|
3311
|
+
}
|
|
3312
|
+
return directError || resultError || `Column status: ${status}`;
|
|
3313
|
+
}
|
|
3214
3314
|
function flattenObjectColumns(row) {
|
|
3215
3315
|
const flattened = {};
|
|
3216
3316
|
for (const [key, rawValue] of Object.entries(row)) {
|
|
3217
3317
|
const value = parseMaybeJsonObject(rawValue);
|
|
3318
|
+
if (key === "_metadata") {
|
|
3319
|
+
flattened[key] = value && typeof value === "object" ? JSON.stringify(value) : value;
|
|
3320
|
+
continue;
|
|
3321
|
+
}
|
|
3218
3322
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
)
|
|
3323
|
+
const record = value;
|
|
3324
|
+
const hasMatchedEnvelope = Object.prototype.hasOwnProperty.call(record, "matched_result") || Object.prototype.hasOwnProperty.call(record, "matchedResult");
|
|
3325
|
+
if (hasMatchedEnvelope) {
|
|
3326
|
+
flattened[key] = JSON.stringify(record);
|
|
3327
|
+
} else {
|
|
3328
|
+
const failureMessage = failureMessageFromRecord(record);
|
|
3329
|
+
if (failureMessage) {
|
|
3330
|
+
flattened[key] = failureMessage;
|
|
3331
|
+
} else if (Object.prototype.hasOwnProperty.call(record, "result")) {
|
|
3332
|
+
flattened[key] = JSON.stringify(record);
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
for (const [nestedKey, nestedValue] of Object.entries(record)) {
|
|
3222
3336
|
flattened[`${key}.${nestedKey}`] = nestedValue && typeof nestedValue === "object" ? JSON.stringify(nestedValue) : nestedValue;
|
|
3223
3337
|
}
|
|
3224
3338
|
continue;
|
|
@@ -3474,8 +3588,8 @@ function buildCandidateUrls2(url) {
|
|
|
3474
3588
|
return [url];
|
|
3475
3589
|
}
|
|
3476
3590
|
}
|
|
3477
|
-
function
|
|
3478
|
-
return new Promise((
|
|
3591
|
+
function sleep4(ms) {
|
|
3592
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
3479
3593
|
}
|
|
3480
3594
|
function printDeeplineLogo() {
|
|
3481
3595
|
if (process.stdout.isTTY && (process.stdout.columns ?? 80) >= 70) {
|
|
@@ -3578,7 +3692,7 @@ async function handleRegister(args) {
|
|
|
3578
3692
|
return EXIT_AUTH;
|
|
3579
3693
|
}
|
|
3580
3694
|
if (s >= 500 || s === 0 || s === 400) {
|
|
3581
|
-
await
|
|
3695
|
+
await sleep4(2e3);
|
|
3582
3696
|
continue;
|
|
3583
3697
|
}
|
|
3584
3698
|
if (s >= 400) {
|
|
@@ -3608,7 +3722,7 @@ async function handleRegister(args) {
|
|
|
3608
3722
|
);
|
|
3609
3723
|
return EXIT_AUTH;
|
|
3610
3724
|
}
|
|
3611
|
-
await
|
|
3725
|
+
await sleep4(2e3);
|
|
3612
3726
|
}
|
|
3613
3727
|
}
|
|
3614
3728
|
async function handleWait(args) {
|
|
@@ -3645,7 +3759,7 @@ async function handleWait(args) {
|
|
|
3645
3759
|
return EXIT_AUTH;
|
|
3646
3760
|
}
|
|
3647
3761
|
if (status >= 500 || status === 0 || status === 400) {
|
|
3648
|
-
await
|
|
3762
|
+
await sleep4(2e3);
|
|
3649
3763
|
continue;
|
|
3650
3764
|
}
|
|
3651
3765
|
if (status >= 400) {
|
|
@@ -3673,7 +3787,7 @@ async function handleWait(args) {
|
|
|
3673
3787
|
console.error("That approval link expired. Run: deepline auth register");
|
|
3674
3788
|
return EXIT_AUTH;
|
|
3675
3789
|
}
|
|
3676
|
-
await
|
|
3790
|
+
await sleep4(2e3);
|
|
3677
3791
|
}
|
|
3678
3792
|
console.error(
|
|
3679
3793
|
"Still pending. Approve the browser link, then run: deepline auth wait"
|
|
@@ -4489,6 +4603,9 @@ function isRecord3(value) {
|
|
|
4489
4603
|
function isSerializedDataset(value) {
|
|
4490
4604
|
return isRecord3(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
|
|
4491
4605
|
}
|
|
4606
|
+
function isPackagedDatasetOutput(value) {
|
|
4607
|
+
return isRecord3(value) && value.kind === "dataset" && isRecord3(value.preview) && Array.isArray(value.preview.rows);
|
|
4608
|
+
}
|
|
4492
4609
|
function pathParts(path) {
|
|
4493
4610
|
return path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
4494
4611
|
}
|
|
@@ -4536,6 +4653,12 @@ function inferColumns(rows) {
|
|
|
4536
4653
|
}
|
|
4537
4654
|
return columns;
|
|
4538
4655
|
}
|
|
4656
|
+
function columnsFromDatasetSummary(summary) {
|
|
4657
|
+
if (!isRecord3(summary) || !isRecord3(summary.columnStats)) {
|
|
4658
|
+
return [];
|
|
4659
|
+
}
|
|
4660
|
+
return Object.keys(summary.columnStats).filter((column) => column);
|
|
4661
|
+
}
|
|
4539
4662
|
function canonicalRowsInfoFromCandidate(input2) {
|
|
4540
4663
|
const candidate = input2;
|
|
4541
4664
|
if (isSerializedDataset(candidate.value)) {
|
|
@@ -4558,6 +4681,28 @@ function canonicalRowsInfoFromCandidate(input2) {
|
|
|
4558
4681
|
tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
|
|
4559
4682
|
};
|
|
4560
4683
|
}
|
|
4684
|
+
if (isPackagedDatasetOutput(candidate.value)) {
|
|
4685
|
+
const rawRows = rowArray(candidate.value.preview?.rows) ?? [];
|
|
4686
|
+
const totalRows2 = readNumber(candidate.value.preview?.totalRows) ?? readNumber(candidate.value.rowCount) ?? rawRows.length;
|
|
4687
|
+
const explicitColumns = Array.isArray(candidate.value.columns) ? candidate.value.columns.filter(
|
|
4688
|
+
(column) => typeof column === "string"
|
|
4689
|
+
) : [];
|
|
4690
|
+
const rawColumns = explicitColumns.length > 0 ? explicitColumns : columnsFromDatasetSummary(candidate.value.summary);
|
|
4691
|
+
const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
|
|
4692
|
+
rows: rawRows,
|
|
4693
|
+
columns: rawColumns.length > 0 ? rawColumns : inferColumns(rawRows)
|
|
4694
|
+
});
|
|
4695
|
+
return {
|
|
4696
|
+
rows: rows2,
|
|
4697
|
+
totalRows: totalRows2,
|
|
4698
|
+
columns,
|
|
4699
|
+
columnsExplicit: rawColumns.length > 0,
|
|
4700
|
+
complete: rows2.length === totalRows2,
|
|
4701
|
+
source: candidate.source,
|
|
4702
|
+
datasetId: typeof candidate.value.datasetId === "string" ? candidate.value.datasetId : null,
|
|
4703
|
+
tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
|
|
4704
|
+
};
|
|
4705
|
+
}
|
|
4561
4706
|
if (candidate.serializedOnly) {
|
|
4562
4707
|
return null;
|
|
4563
4708
|
}
|
|
@@ -4590,6 +4735,14 @@ function collectDatasetCandidates(input2) {
|
|
|
4590
4735
|
});
|
|
4591
4736
|
return;
|
|
4592
4737
|
}
|
|
4738
|
+
if (isPackagedDatasetOutput(input2.value)) {
|
|
4739
|
+
input2.output.push({
|
|
4740
|
+
source: input2.path,
|
|
4741
|
+
value: input2.value,
|
|
4742
|
+
total: input2.total
|
|
4743
|
+
});
|
|
4744
|
+
return;
|
|
4745
|
+
}
|
|
4593
4746
|
if (!isRecord3(input2.value)) {
|
|
4594
4747
|
return;
|
|
4595
4748
|
}
|
|
@@ -4662,6 +4815,19 @@ function collectCanonicalRowsInfos(statusOrResult) {
|
|
|
4662
4815
|
}
|
|
4663
4816
|
);
|
|
4664
4817
|
}
|
|
4818
|
+
if (Array.isArray(result.steps)) {
|
|
4819
|
+
result.steps.forEach((step, index) => {
|
|
4820
|
+
if (!isRecord3(step) || !isRecord3(step.output)) {
|
|
4821
|
+
return;
|
|
4822
|
+
}
|
|
4823
|
+
const source = typeof step.output.path === "string" ? step.output.path : typeof step.id === "string" ? `steps.${step.id}.output` : `steps.${index}.output`;
|
|
4824
|
+
candidates.push({
|
|
4825
|
+
source,
|
|
4826
|
+
value: step.output,
|
|
4827
|
+
total: step.output.rowCount ?? (isRecord3(step.output.preview) ? step.output.preview.totalRows : void 0) ?? (isRecord3(step.progress) ? step.progress.total : void 0)
|
|
4828
|
+
});
|
|
4829
|
+
});
|
|
4830
|
+
}
|
|
4665
4831
|
collectDatasetCandidates({
|
|
4666
4832
|
value: result,
|
|
4667
4833
|
path: "result",
|
|
@@ -4671,12 +4837,14 @@ function collectCanonicalRowsInfos(statusOrResult) {
|
|
|
4671
4837
|
const seen = /* @__PURE__ */ new Set();
|
|
4672
4838
|
const infos = [];
|
|
4673
4839
|
for (const candidate of candidates) {
|
|
4674
|
-
if (seen.has(candidate.source)) {
|
|
4675
|
-
continue;
|
|
4676
|
-
}
|
|
4677
|
-
seen.add(candidate.source);
|
|
4678
4840
|
const info = canonicalRowsInfoFromCandidate(candidate);
|
|
4679
4841
|
if (info) {
|
|
4842
|
+
if (info.source) {
|
|
4843
|
+
if (seen.has(info.source)) {
|
|
4844
|
+
continue;
|
|
4845
|
+
}
|
|
4846
|
+
seen.add(info.source);
|
|
4847
|
+
}
|
|
4680
4848
|
infos.push(info);
|
|
4681
4849
|
}
|
|
4682
4850
|
}
|
|
@@ -4697,15 +4865,17 @@ function collectSerializedDatasetRowsInfos(statusOrResult) {
|
|
|
4697
4865
|
const seen = /* @__PURE__ */ new Set();
|
|
4698
4866
|
const infos = [];
|
|
4699
4867
|
for (const candidate of candidates) {
|
|
4700
|
-
if (seen.has(candidate.source)) {
|
|
4701
|
-
continue;
|
|
4702
|
-
}
|
|
4703
|
-
seen.add(candidate.source);
|
|
4704
4868
|
const info = canonicalRowsInfoFromCandidate({
|
|
4705
4869
|
...candidate,
|
|
4706
4870
|
serializedOnly: true
|
|
4707
4871
|
});
|
|
4708
4872
|
if (info) {
|
|
4873
|
+
if (info.source) {
|
|
4874
|
+
if (seen.has(info.source)) {
|
|
4875
|
+
continue;
|
|
4876
|
+
}
|
|
4877
|
+
seen.add(info.source);
|
|
4878
|
+
}
|
|
4709
4879
|
infos.push(info);
|
|
4710
4880
|
}
|
|
4711
4881
|
}
|
|
@@ -5215,10 +5385,10 @@ async function handleDbQuery(args) {
|
|
|
5215
5385
|
}
|
|
5216
5386
|
const jsonOutput = argsWantJson(args);
|
|
5217
5387
|
const explicitJsonOutput = args.includes("--json");
|
|
5218
|
-
const
|
|
5388
|
+
const client2 = new DeeplineClient();
|
|
5219
5389
|
let result;
|
|
5220
5390
|
try {
|
|
5221
|
-
result = await
|
|
5391
|
+
result = await client2.queryCustomerDb({ sql, maxRows });
|
|
5222
5392
|
} catch (error) {
|
|
5223
5393
|
console.error(formatDbQueryError(sql, error));
|
|
5224
5394
|
return 1;
|
|
@@ -5372,7 +5542,14 @@ Examples:
|
|
|
5372
5542
|
}
|
|
5373
5543
|
|
|
5374
5544
|
// src/cli/commands/enrich.ts
|
|
5375
|
-
import {
|
|
5545
|
+
import {
|
|
5546
|
+
mkdir as mkdir4,
|
|
5547
|
+
mkdtemp,
|
|
5548
|
+
readFile as readFile3,
|
|
5549
|
+
rm,
|
|
5550
|
+
stat as stat3,
|
|
5551
|
+
writeFile as writeFile4
|
|
5552
|
+
} from "fs/promises";
|
|
5376
5553
|
import { homedir as homedir4, tmpdir as tmpdir3 } from "os";
|
|
5377
5554
|
import { join as join8, resolve as resolve11 } from "path";
|
|
5378
5555
|
|
|
@@ -5474,23 +5651,28 @@ function shannonEntropy(value) {
|
|
|
5474
5651
|
return entropy - p * Math.log2(p);
|
|
5475
5652
|
}, 0);
|
|
5476
5653
|
}
|
|
5477
|
-
function
|
|
5654
|
+
function collectInlineSecretFindings(sourceCode) {
|
|
5478
5655
|
const findings = [];
|
|
5479
|
-
for (const match of
|
|
5656
|
+
for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
|
|
5480
5657
|
findings.push(`process.env.${match[1]}`);
|
|
5481
5658
|
}
|
|
5482
|
-
if (PRIVATE_KEY_PATTERN.test(
|
|
5483
|
-
if (BEARER_LITERAL_PATTERN.test(
|
|
5484
|
-
|
|
5659
|
+
if (PRIVATE_KEY_PATTERN.test(sourceCode)) findings.push("private key block");
|
|
5660
|
+
if (BEARER_LITERAL_PATTERN.test(sourceCode))
|
|
5661
|
+
findings.push("bearer token literal");
|
|
5662
|
+
if (ASSIGNMENT_SECRET_LITERAL_PATTERN.test(sourceCode)) {
|
|
5485
5663
|
findings.push("secret-looking assignment literal");
|
|
5486
5664
|
}
|
|
5487
|
-
for (const match of
|
|
5665
|
+
for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
|
|
5488
5666
|
const literal = match[1] ?? "";
|
|
5489
5667
|
if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
|
|
5490
5668
|
findings.push("high-entropy string literal");
|
|
5491
5669
|
break;
|
|
5492
5670
|
}
|
|
5493
5671
|
}
|
|
5672
|
+
return [...new Set(findings)];
|
|
5673
|
+
}
|
|
5674
|
+
function validatePlaySourceHasNoInlineSecrets(input2) {
|
|
5675
|
+
const findings = collectInlineSecretFindings(input2.sourceCode);
|
|
5494
5676
|
if (!findings.length) return;
|
|
5495
5677
|
throw new Error(
|
|
5496
5678
|
[
|
|
@@ -8438,13 +8620,13 @@ export default definePlay(${jsString(input2.options.name)}, async (ctx, input: I
|
|
|
8438
8620
|
|
|
8439
8621
|
`;
|
|
8440
8622
|
}
|
|
8441
|
-
async function describePlayMaybe(
|
|
8442
|
-
return playRef ?
|
|
8623
|
+
async function describePlayMaybe(client2, playRef) {
|
|
8624
|
+
return playRef ? client2.describePlay(playRef, { compact: true }) : null;
|
|
8443
8625
|
}
|
|
8444
|
-
function loadTools(
|
|
8445
|
-
return Promise.all(providers.map((provider) =>
|
|
8626
|
+
function loadTools(client2, providers) {
|
|
8627
|
+
return Promise.all(providers.map((provider) => client2.getTool(provider)));
|
|
8446
8628
|
}
|
|
8447
|
-
async function loadBootstrapContracts(
|
|
8629
|
+
async function loadBootstrapContracts(client2, options) {
|
|
8448
8630
|
const [
|
|
8449
8631
|
sourceTools,
|
|
8450
8632
|
sourcePlay,
|
|
@@ -8454,13 +8636,13 @@ async function loadBootstrapContracts(client, options) {
|
|
|
8454
8636
|
emailTools,
|
|
8455
8637
|
phoneTools
|
|
8456
8638
|
] = await Promise.all([
|
|
8457
|
-
loadTools(
|
|
8458
|
-
describePlayMaybe(
|
|
8459
|
-
describePlayMaybe(
|
|
8460
|
-
describePlayMaybe(
|
|
8461
|
-
describePlayMaybe(
|
|
8462
|
-
loadTools(
|
|
8463
|
-
loadTools(
|
|
8639
|
+
loadTools(client2, sourceProviders(options)),
|
|
8640
|
+
describePlayMaybe(client2, sourcePlayRef(options)),
|
|
8641
|
+
describePlayMaybe(client2, stagePlayRef(options.people)),
|
|
8642
|
+
describePlayMaybe(client2, stagePlayRef(options.email)),
|
|
8643
|
+
describePlayMaybe(client2, stagePlayRef(options.phone)),
|
|
8644
|
+
loadTools(client2, stageProviders(options.email)),
|
|
8645
|
+
loadTools(client2, stageProviders(options.phone))
|
|
8464
8646
|
]);
|
|
8465
8647
|
const contracts = {
|
|
8466
8648
|
sourceTools,
|
|
@@ -8509,8 +8691,8 @@ function renderPlayBootstrapError(error) {
|
|
|
8509
8691
|
}
|
|
8510
8692
|
async function runPlayBootstrap(args) {
|
|
8511
8693
|
const options = parsePlayBootstrapOptions(args);
|
|
8512
|
-
const
|
|
8513
|
-
const contracts = await loadBootstrapContracts(
|
|
8694
|
+
const client2 = new DeeplineClient();
|
|
8695
|
+
const contracts = await loadBootstrapContracts(client2, options);
|
|
8514
8696
|
const csvContext = loadCsvContext(options.from);
|
|
8515
8697
|
const source = generateBootstrapPlaySource({
|
|
8516
8698
|
options,
|
|
@@ -8916,8 +9098,8 @@ function traceCliSync(phase, fields, run) {
|
|
|
8916
9098
|
throw error;
|
|
8917
9099
|
}
|
|
8918
9100
|
}
|
|
8919
|
-
function
|
|
8920
|
-
return new Promise((
|
|
9101
|
+
function sleep5(ms) {
|
|
9102
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
8921
9103
|
}
|
|
8922
9104
|
function parseReferencedPlayTarget2(target) {
|
|
8923
9105
|
const trimmed = target.trim();
|
|
@@ -8939,9 +9121,9 @@ function buildBarePrebuiltReferenceError(input2) {
|
|
|
8939
9121
|
`Prebuilt play "${input2.requested}" must be referenced as "${input2.reference}". Use the prebuilt/ namespace anywhere you run, describe, get, or link to Deepline-managed plays.`
|
|
8940
9122
|
);
|
|
8941
9123
|
}
|
|
8942
|
-
async function assertCanonicalNamedPlayReference(
|
|
9124
|
+
async function assertCanonicalNamedPlayReference(client2, target) {
|
|
8943
9125
|
const parsed = parseReferencedPlayTarget2(target);
|
|
8944
|
-
const detail = await
|
|
9126
|
+
const detail = await client2.getPlay(parsed.playName);
|
|
8945
9127
|
if (detail.play.ownerType === "deepline" && !isPrebuiltReferenceTarget(target)) {
|
|
8946
9128
|
throw buildBarePrebuiltReferenceError({
|
|
8947
9129
|
requested: target,
|
|
@@ -9019,9 +9201,9 @@ To make your own version:
|
|
|
9019
9201
|
5. Your play will then live under your workspace namespace.`
|
|
9020
9202
|
);
|
|
9021
9203
|
}
|
|
9022
|
-
async function ensureEditableRemotePlay(
|
|
9204
|
+
async function ensureEditableRemotePlay(client2, target) {
|
|
9023
9205
|
const parsed = parseReferencedPlayTarget2(target);
|
|
9024
|
-
const detail = await
|
|
9206
|
+
const detail = await client2.getPlay(parsed.playName);
|
|
9025
9207
|
if (detail.play.ownerType === "deepline") {
|
|
9026
9208
|
throw buildReadonlyPrebuiltPlayError(formatPlayReference(detail.play));
|
|
9027
9209
|
}
|
|
@@ -9300,7 +9482,7 @@ async function collectBundledPlayGraph(entryFile, profile = null) {
|
|
|
9300
9482
|
const root = await visit(entryFile);
|
|
9301
9483
|
return { root, nodes };
|
|
9302
9484
|
}
|
|
9303
|
-
async function compileBundledPlayGraphManifests(
|
|
9485
|
+
async function compileBundledPlayGraphManifests(client2, graph) {
|
|
9304
9486
|
const compiling = /* @__PURE__ */ new Map();
|
|
9305
9487
|
const compileNode = (node) => {
|
|
9306
9488
|
const existing = compiling.get(node.filePath);
|
|
@@ -9316,7 +9498,7 @@ async function compileBundledPlayGraphManifests(client, graph) {
|
|
|
9316
9498
|
await compileNode(child);
|
|
9317
9499
|
}
|
|
9318
9500
|
const name = node.playName ?? extractPlayName(node.sourceCode, node.filePath);
|
|
9319
|
-
node.compilerManifest = await
|
|
9501
|
+
node.compilerManifest = await client2.compilePlayManifest({
|
|
9320
9502
|
name,
|
|
9321
9503
|
sourceCode: node.sourceCode,
|
|
9322
9504
|
sourceFiles: node.sourceFiles,
|
|
@@ -9350,7 +9532,7 @@ function requireCompilerManifest(node) {
|
|
|
9350
9532
|
}
|
|
9351
9533
|
return node.compilerManifest;
|
|
9352
9534
|
}
|
|
9353
|
-
async function publishImportedPlayDependencies(
|
|
9535
|
+
async function publishImportedPlayDependencies(client2, graph) {
|
|
9354
9536
|
const published = /* @__PURE__ */ new Set();
|
|
9355
9537
|
const publishNode = async (filePath, skipPublish) => {
|
|
9356
9538
|
const absolutePath = normalizePlayPath(filePath);
|
|
@@ -9369,7 +9551,7 @@ async function publishImportedPlayDependencies(client, graph) {
|
|
|
9369
9551
|
`Imported play ${absolutePath} must export definePlay(...) so it can be published for runtime composition.`
|
|
9370
9552
|
);
|
|
9371
9553
|
}
|
|
9372
|
-
await
|
|
9554
|
+
await client2.registerPlayArtifact({
|
|
9373
9555
|
name: node.playName,
|
|
9374
9556
|
sourceCode: node.sourceCode,
|
|
9375
9557
|
sourceFiles: node.sourceFiles,
|
|
@@ -9945,7 +10127,7 @@ async function waitForPlayCompletionByStream(input2) {
|
|
|
9945
10127
|
reason
|
|
9946
10128
|
});
|
|
9947
10129
|
const remainingBeforeSleep = remainingWaitMs();
|
|
9948
|
-
await
|
|
10130
|
+
await sleep5(
|
|
9949
10131
|
remainingBeforeSleep === null ? delayMs : Math.min(delayMs, Math.max(1, remainingBeforeSleep))
|
|
9950
10132
|
);
|
|
9951
10133
|
}
|
|
@@ -9970,7 +10152,7 @@ async function startAndWaitForPlayCompletionByStream(input2) {
|
|
|
9970
10152
|
attempt: attempt + 1,
|
|
9971
10153
|
reason: playStatusErrorText(status)
|
|
9972
10154
|
});
|
|
9973
|
-
await
|
|
10155
|
+
await sleep5(retryDelayMs);
|
|
9974
10156
|
}
|
|
9975
10157
|
throw new DeeplineError(
|
|
9976
10158
|
`Play ${input2.playName} did not start after retrying transient start failures.`,
|
|
@@ -11019,7 +11201,7 @@ async function resolvePlayRunOutputStatus(input2) {
|
|
|
11019
11201
|
full: input2.fullJson
|
|
11020
11202
|
});
|
|
11021
11203
|
for (let attempt = 0; attempt < 3 && streamedTextPackageIncomplete && refreshedStatus.status === "completed" && playRunPackageStepCount(getPlayRunPackage(refreshedStatus)) === 0; attempt += 1) {
|
|
11022
|
-
await
|
|
11204
|
+
await sleep5(250);
|
|
11023
11205
|
refreshedStatus = await input2.client.getPlayStatus(runId, {
|
|
11024
11206
|
billing: false,
|
|
11025
11207
|
full: input2.fullJson
|
|
@@ -11142,7 +11324,7 @@ async function fetchBackingDatasetRows(input2) {
|
|
|
11142
11324
|
source: `${input2.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
|
|
11143
11325
|
};
|
|
11144
11326
|
}
|
|
11145
|
-
async function exportPlayStatusRows(
|
|
11327
|
+
async function exportPlayStatusRows(client2, status, outPath, options = {}) {
|
|
11146
11328
|
if (!outPath) {
|
|
11147
11329
|
return null;
|
|
11148
11330
|
}
|
|
@@ -11177,7 +11359,7 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
|
|
|
11177
11359
|
let fetchedRowsInfo = null;
|
|
11178
11360
|
try {
|
|
11179
11361
|
fetchedRowsInfo = await fetchBackingDatasetRows({
|
|
11180
|
-
client,
|
|
11362
|
+
client: client2,
|
|
11181
11363
|
status,
|
|
11182
11364
|
rowsInfo
|
|
11183
11365
|
});
|
|
@@ -11196,7 +11378,7 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
|
|
|
11196
11378
|
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
11197
11379
|
}
|
|
11198
11380
|
if (attempt < attempts && retryDelayMs > 0) {
|
|
11199
|
-
await
|
|
11381
|
+
await sleep5(retryDelayMs);
|
|
11200
11382
|
}
|
|
11201
11383
|
}
|
|
11202
11384
|
if (!rowsInfo.complete) {
|
|
@@ -11714,12 +11896,12 @@ function toolGetterHintFromMetadata(toolId, tool) {
|
|
|
11714
11896
|
raw: checkHintExpression(toolResponse.raw) || "result.toolResponse.raw"
|
|
11715
11897
|
};
|
|
11716
11898
|
}
|
|
11717
|
-
async function buildToolGetterHints(
|
|
11899
|
+
async function buildToolGetterHints(client2, staticPipeline) {
|
|
11718
11900
|
const toolIds = collectStaticPipelineToolIds(staticPipeline);
|
|
11719
11901
|
return Promise.all(
|
|
11720
11902
|
toolIds.map(async (toolId) => {
|
|
11721
11903
|
try {
|
|
11722
|
-
const tool = await
|
|
11904
|
+
const tool = await client2.getTool(toolId);
|
|
11723
11905
|
return toolGetterHintFromMetadata(toolId, tool);
|
|
11724
11906
|
} catch (error) {
|
|
11725
11907
|
return {
|
|
@@ -11759,10 +11941,10 @@ function printToolGetterHints(hints) {
|
|
|
11759
11941
|
async function handlePlayCheck(args) {
|
|
11760
11942
|
const options = parsePlayCheckOptions(args);
|
|
11761
11943
|
if (!isFileTarget(options.target)) {
|
|
11762
|
-
const
|
|
11944
|
+
const client3 = new DeeplineClient();
|
|
11763
11945
|
try {
|
|
11764
|
-
await assertCanonicalNamedPlayReference(
|
|
11765
|
-
const play = await
|
|
11946
|
+
await assertCanonicalNamedPlayReference(client3, options.target);
|
|
11947
|
+
const play = await client3.describePlay(
|
|
11766
11948
|
parseReferencedPlayTarget2(options.target).playName,
|
|
11767
11949
|
{ compact: true }
|
|
11768
11950
|
);
|
|
@@ -11844,8 +12026,8 @@ async function handlePlayCheck(args) {
|
|
|
11844
12026
|
}
|
|
11845
12027
|
return 0;
|
|
11846
12028
|
}
|
|
11847
|
-
const
|
|
11848
|
-
const result = await
|
|
12029
|
+
const client2 = new DeeplineClient();
|
|
12030
|
+
const result = await client2.checkPlayArtifact({
|
|
11849
12031
|
name: playName,
|
|
11850
12032
|
sourceCode: graph.root.sourceCode,
|
|
11851
12033
|
sourceFiles: graph.root.sourceFiles,
|
|
@@ -11857,7 +12039,7 @@ async function handlePlayCheck(args) {
|
|
|
11857
12039
|
errors: result.errors,
|
|
11858
12040
|
sourceCode: graph.root.sourceCode
|
|
11859
12041
|
}),
|
|
11860
|
-
toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(
|
|
12042
|
+
toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client2, result.staticPipeline)
|
|
11861
12043
|
};
|
|
11862
12044
|
if (options.jsonOutput) {
|
|
11863
12045
|
process.stdout.write(
|
|
@@ -11883,7 +12065,7 @@ async function handleFileBackedRun(options) {
|
|
|
11883
12065
|
if (options.target.kind !== "file") {
|
|
11884
12066
|
throw new Error("Expected a file-backed play run target.");
|
|
11885
12067
|
}
|
|
11886
|
-
const
|
|
12068
|
+
const client2 = new DeeplineClient();
|
|
11887
12069
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
11888
12070
|
const absolutePlayPath = resolve10(options.target.path);
|
|
11889
12071
|
progress.phase("compiling play");
|
|
@@ -11903,7 +12085,7 @@ async function handleFileBackedRun(options) {
|
|
|
11903
12085
|
await traceCliSpan(
|
|
11904
12086
|
"cli.play_file_compile_manifests",
|
|
11905
12087
|
{ targetKind: "file" },
|
|
11906
|
-
() => compileBundledPlayGraphManifests(
|
|
12088
|
+
() => compileBundledPlayGraphManifests(client2, graph)
|
|
11907
12089
|
);
|
|
11908
12090
|
progress.phase("compiled play");
|
|
11909
12091
|
} catch (error) {
|
|
@@ -11921,7 +12103,7 @@ async function handleFileBackedRun(options) {
|
|
|
11921
12103
|
await traceCliSpan(
|
|
11922
12104
|
"cli.play_file_publish_imports",
|
|
11923
12105
|
{ targetKind: "file" },
|
|
11924
|
-
() => publishImportedPlayDependencies(
|
|
12106
|
+
() => publishImportedPlayDependencies(client2, graph)
|
|
11925
12107
|
);
|
|
11926
12108
|
} catch (error) {
|
|
11927
12109
|
progress.fail();
|
|
@@ -11942,7 +12124,7 @@ async function handleFileBackedRun(options) {
|
|
|
11942
12124
|
bindingCount: fileInputBindings.length
|
|
11943
12125
|
},
|
|
11944
12126
|
() => stageFileInputArgs({
|
|
11945
|
-
client,
|
|
12127
|
+
client: client2,
|
|
11946
12128
|
runtimeInput,
|
|
11947
12129
|
bindings: fileInputBindings,
|
|
11948
12130
|
progress
|
|
@@ -11967,7 +12149,7 @@ async function handleFileBackedRun(options) {
|
|
|
11967
12149
|
"cli.play_start_watch",
|
|
11968
12150
|
{ targetKind: "file", playName },
|
|
11969
12151
|
() => startAndWaitForPlayCompletionByStream({
|
|
11970
|
-
client,
|
|
12152
|
+
client: client2,
|
|
11971
12153
|
request: startRequest,
|
|
11972
12154
|
playName,
|
|
11973
12155
|
jsonOutput: options.jsonOutput,
|
|
@@ -11986,7 +12168,7 @@ async function handleFileBackedRun(options) {
|
|
|
11986
12168
|
}
|
|
11987
12169
|
const outputStatus = withTerminalPlayIdentity(
|
|
11988
12170
|
await resolvePlayRunOutputStatus({
|
|
11989
|
-
client,
|
|
12171
|
+
client: client2,
|
|
11990
12172
|
status: finalStatus,
|
|
11991
12173
|
fullJson: options.fullJson,
|
|
11992
12174
|
jsonOutput: options.jsonOutput
|
|
@@ -12006,11 +12188,11 @@ async function handleFileBackedRun(options) {
|
|
|
12006
12188
|
const started = await traceCliSpan(
|
|
12007
12189
|
"cli.play_start_unwatched",
|
|
12008
12190
|
{ targetKind: "file", playName },
|
|
12009
|
-
() =>
|
|
12191
|
+
() => client2.startPlayRun(startRequest).catch((error) => {
|
|
12010
12192
|
throw normalizePlayStartError(error, playName);
|
|
12011
12193
|
})
|
|
12012
12194
|
);
|
|
12013
|
-
const resolvedDashboardUrl = buildPlayDashboardUrl(
|
|
12195
|
+
const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
|
|
12014
12196
|
openPlayDashboard({
|
|
12015
12197
|
dashboardUrl: resolvedDashboardUrl,
|
|
12016
12198
|
noOpen: options.noOpen
|
|
@@ -12046,7 +12228,7 @@ async function handleNamedRun(options) {
|
|
|
12046
12228
|
if (options.target.kind !== "name") {
|
|
12047
12229
|
throw new Error("Expected a named play run target.");
|
|
12048
12230
|
}
|
|
12049
|
-
const
|
|
12231
|
+
const client2 = new DeeplineClient();
|
|
12050
12232
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
12051
12233
|
const playName = options.target.name;
|
|
12052
12234
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
@@ -12059,7 +12241,7 @@ async function handleNamedRun(options) {
|
|
|
12059
12241
|
return traceCliSpan(
|
|
12060
12242
|
"cli.play_load_definition",
|
|
12061
12243
|
{ targetKind: "name", playName, skipped: false },
|
|
12062
|
-
() => assertCanonicalNamedPlayReference(
|
|
12244
|
+
() => assertCanonicalNamedPlayReference(client2, playName)
|
|
12063
12245
|
);
|
|
12064
12246
|
})() : (recordCliTrace({
|
|
12065
12247
|
phase: "cli.play_load_definition",
|
|
@@ -12079,7 +12261,7 @@ async function handleNamedRun(options) {
|
|
|
12079
12261
|
hasExplicitRevisionId: Boolean(options.revisionId)
|
|
12080
12262
|
},
|
|
12081
12263
|
() => resolveNamedRunRevisionId({
|
|
12082
|
-
client,
|
|
12264
|
+
client: client2,
|
|
12083
12265
|
playName,
|
|
12084
12266
|
revisionId: options.revisionId,
|
|
12085
12267
|
selector: options.revisionSelector
|
|
@@ -12097,7 +12279,7 @@ async function handleNamedRun(options) {
|
|
|
12097
12279
|
bindingCount: fileInputBindings.length
|
|
12098
12280
|
},
|
|
12099
12281
|
() => stageFileInputArgs({
|
|
12100
|
-
client,
|
|
12282
|
+
client: client2,
|
|
12101
12283
|
runtimeInput,
|
|
12102
12284
|
bindings: fileInputBindings,
|
|
12103
12285
|
progress
|
|
@@ -12118,7 +12300,7 @@ async function handleNamedRun(options) {
|
|
|
12118
12300
|
"cli.play_start_watch",
|
|
12119
12301
|
{ targetKind: "name", playName },
|
|
12120
12302
|
() => startAndWaitForPlayCompletionByStream({
|
|
12121
|
-
client,
|
|
12303
|
+
client: client2,
|
|
12122
12304
|
request: startRequest,
|
|
12123
12305
|
playName,
|
|
12124
12306
|
jsonOutput: options.jsonOutput,
|
|
@@ -12137,7 +12319,7 @@ async function handleNamedRun(options) {
|
|
|
12137
12319
|
}
|
|
12138
12320
|
const outputStatus = withTerminalPlayIdentity(
|
|
12139
12321
|
await resolvePlayRunOutputStatus({
|
|
12140
|
-
client,
|
|
12322
|
+
client: client2,
|
|
12141
12323
|
status: finalStatus,
|
|
12142
12324
|
fullJson: options.fullJson,
|
|
12143
12325
|
jsonOutput: options.jsonOutput
|
|
@@ -12157,11 +12339,11 @@ async function handleNamedRun(options) {
|
|
|
12157
12339
|
const started = await traceCliSpan(
|
|
12158
12340
|
"cli.play_start_unwatched",
|
|
12159
12341
|
{ targetKind: "name", playName },
|
|
12160
|
-
() =>
|
|
12342
|
+
() => client2.startPlayRun(startRequest).catch((error) => {
|
|
12161
12343
|
throw normalizePlayStartError(error, playName);
|
|
12162
12344
|
})
|
|
12163
12345
|
);
|
|
12164
|
-
const resolvedDashboardUrl = buildPlayDashboardUrl(
|
|
12346
|
+
const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
|
|
12165
12347
|
openPlayDashboard({
|
|
12166
12348
|
dashboardUrl: resolvedDashboardUrl,
|
|
12167
12349
|
noOpen: options.noOpen
|
|
@@ -12235,8 +12417,8 @@ async function handleRunGet(args) {
|
|
|
12235
12417
|
console.error(error instanceof Error ? error.message : usage);
|
|
12236
12418
|
return 1;
|
|
12237
12419
|
}
|
|
12238
|
-
const
|
|
12239
|
-
const status = await
|
|
12420
|
+
const client2 = new DeeplineClient();
|
|
12421
|
+
const status = await client2.runs.get(runId, {
|
|
12240
12422
|
full: args.includes("--full")
|
|
12241
12423
|
});
|
|
12242
12424
|
writePlayResult(status, argsWantJson(args), {
|
|
@@ -12266,8 +12448,8 @@ async function handleRunsList(args) {
|
|
|
12266
12448
|
console.error(usage);
|
|
12267
12449
|
return 1;
|
|
12268
12450
|
}
|
|
12269
|
-
const
|
|
12270
|
-
const runs = (await
|
|
12451
|
+
const client2 = new DeeplineClient();
|
|
12452
|
+
const runs = (await client2.runs.list({
|
|
12271
12453
|
play: playName,
|
|
12272
12454
|
...statusFilter ? { status: statusFilter } : {}
|
|
12273
12455
|
})).map((run) => ({
|
|
@@ -12320,9 +12502,9 @@ async function handleRunTail(args) {
|
|
|
12320
12502
|
return 1;
|
|
12321
12503
|
}
|
|
12322
12504
|
}
|
|
12323
|
-
const
|
|
12505
|
+
const client2 = new DeeplineClient();
|
|
12324
12506
|
const jsonOutput = argsWantJson(args);
|
|
12325
|
-
const status = await
|
|
12507
|
+
const status = await client2.runs.tail(runId, {
|
|
12326
12508
|
// Human mode only: in --json mode emit nothing non-protocol.
|
|
12327
12509
|
onReconnect: jsonOutput ? void 0 : ({ reason }) => {
|
|
12328
12510
|
process.stderr.write(
|
|
@@ -12355,9 +12537,9 @@ async function handleRunLogs(args) {
|
|
|
12355
12537
|
outPath = resolve10(args[++index]);
|
|
12356
12538
|
}
|
|
12357
12539
|
}
|
|
12358
|
-
const
|
|
12540
|
+
const client2 = new DeeplineClient();
|
|
12359
12541
|
if (outPath) {
|
|
12360
|
-
const result2 = await
|
|
12542
|
+
const result2 = await client2.runs.logs(runId, { all: true });
|
|
12361
12543
|
const logs = result2.entries;
|
|
12362
12544
|
writeFileSync7(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
12363
12545
|
printCommandEnvelope(
|
|
@@ -12386,7 +12568,7 @@ async function handleRunLogs(args) {
|
|
|
12386
12568
|
);
|
|
12387
12569
|
return 0;
|
|
12388
12570
|
}
|
|
12389
|
-
const result = await
|
|
12571
|
+
const result = await client2.runs.logs(runId, { limit });
|
|
12390
12572
|
printCommandEnvelope(
|
|
12391
12573
|
{
|
|
12392
12574
|
runId: result.runId,
|
|
@@ -12426,8 +12608,8 @@ async function handleRunStop(args) {
|
|
|
12426
12608
|
reason = args[++index];
|
|
12427
12609
|
}
|
|
12428
12610
|
}
|
|
12429
|
-
const
|
|
12430
|
-
const result = await
|
|
12611
|
+
const client2 = new DeeplineClient();
|
|
12612
|
+
const result = await client2.runs.stop(runId, { reason });
|
|
12431
12613
|
const stopNotConfirmed = result.stopped === false || result.staleSchedulerState === true;
|
|
12432
12614
|
if (stopNotConfirmed) {
|
|
12433
12615
|
const detail = typeof result.error === "string" && result.error.trim() ? result.error.trim() : result.staleSchedulerState === true ? "scheduler state for the run is stale" : "the server did not confirm the stop";
|
|
@@ -12499,9 +12681,9 @@ async function handleRunExport(args) {
|
|
|
12499
12681
|
console.error(usage);
|
|
12500
12682
|
return 1;
|
|
12501
12683
|
}
|
|
12502
|
-
const
|
|
12503
|
-
const status = await
|
|
12504
|
-
const exportResult = await exportPlayStatusRows(
|
|
12684
|
+
const client2 = new DeeplineClient();
|
|
12685
|
+
const status = await client2.getPlayStatus(runId, { full: true });
|
|
12686
|
+
const exportResult = await exportPlayStatusRows(client2, status, outPath, {
|
|
12505
12687
|
datasetPath
|
|
12506
12688
|
});
|
|
12507
12689
|
const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
|
|
@@ -12554,7 +12736,7 @@ async function handlePlayGet(args) {
|
|
|
12554
12736
|
);
|
|
12555
12737
|
return 2;
|
|
12556
12738
|
}
|
|
12557
|
-
const
|
|
12739
|
+
const client2 = new DeeplineClient();
|
|
12558
12740
|
const explicitJson = args.includes("--json");
|
|
12559
12741
|
const sourceOutput = args.includes("--source");
|
|
12560
12742
|
const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
|
|
@@ -12566,7 +12748,7 @@ async function handlePlayGet(args) {
|
|
|
12566
12748
|
}
|
|
12567
12749
|
}
|
|
12568
12750
|
const playName = isFileTarget(target) ? extractPlayName(readFileSync6(resolve10(target), "utf-8"), resolve10(target)) : parseReferencedPlayTarget2(target).playName;
|
|
12569
|
-
const detail = isFileTarget(target) ? await
|
|
12751
|
+
const detail = isFileTarget(target) ? await client2.getPlay(playName) : await assertCanonicalNamedPlayReference(client2, target);
|
|
12570
12752
|
const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
|
|
12571
12753
|
const materializedFile = outPath ? materializeRemotePlaySource({
|
|
12572
12754
|
target,
|
|
@@ -12646,10 +12828,10 @@ async function handlePlayVersions(args) {
|
|
|
12646
12828
|
console.error("Usage: deepline play versions --name <name> [--json]");
|
|
12647
12829
|
return 1;
|
|
12648
12830
|
}
|
|
12649
|
-
const
|
|
12831
|
+
const client2 = new DeeplineClient();
|
|
12650
12832
|
const jsonOutput = argsWantJson(args);
|
|
12651
|
-
await assertCanonicalNamedPlayReference(
|
|
12652
|
-
const versions = await
|
|
12833
|
+
await assertCanonicalNamedPlayReference(client2, playName);
|
|
12834
|
+
const versions = await client2.listPlayVersions(
|
|
12653
12835
|
parseReferencedPlayTarget2(playName).playName
|
|
12654
12836
|
);
|
|
12655
12837
|
if (jsonOutput) {
|
|
@@ -12668,8 +12850,8 @@ async function handlePlayVersions(args) {
|
|
|
12668
12850
|
}
|
|
12669
12851
|
async function handlePlayList(args) {
|
|
12670
12852
|
const jsonOutput = argsWantJson(args);
|
|
12671
|
-
const
|
|
12672
|
-
const plays = await
|
|
12853
|
+
const client2 = new DeeplineClient();
|
|
12854
|
+
const plays = await client2.listPlays();
|
|
12673
12855
|
if (jsonOutput) {
|
|
12674
12856
|
process.stdout.write(`${JSON.stringify(plays)}
|
|
12675
12857
|
`);
|
|
@@ -12852,8 +13034,8 @@ async function handlePlaySearch(args) {
|
|
|
12852
13034
|
console.error(error instanceof Error ? error.message : String(error));
|
|
12853
13035
|
return 1;
|
|
12854
13036
|
}
|
|
12855
|
-
const
|
|
12856
|
-
const plays = await
|
|
13037
|
+
const client2 = new DeeplineClient();
|
|
13038
|
+
const plays = await client2.searchPlays({
|
|
12857
13039
|
query: options.query,
|
|
12858
13040
|
compact: options.compact,
|
|
12859
13041
|
scope: options.scope
|
|
@@ -12945,8 +13127,8 @@ async function handlePlayGrep(args) {
|
|
|
12945
13127
|
}
|
|
12946
13128
|
}
|
|
12947
13129
|
const compact = args.includes("--compact");
|
|
12948
|
-
const
|
|
12949
|
-
const plays = (await
|
|
13130
|
+
const client2 = new DeeplineClient();
|
|
13131
|
+
const plays = (await client2.listPlays({
|
|
12950
13132
|
grep: query,
|
|
12951
13133
|
grepMode: mode
|
|
12952
13134
|
})).filter(
|
|
@@ -13003,9 +13185,9 @@ async function handlePlayDescribe(args) {
|
|
|
13003
13185
|
);
|
|
13004
13186
|
return 2;
|
|
13005
13187
|
}
|
|
13006
|
-
const
|
|
13007
|
-
await assertCanonicalNamedPlayReference(
|
|
13008
|
-
const play = await
|
|
13188
|
+
const client2 = new DeeplineClient();
|
|
13189
|
+
await assertCanonicalNamedPlayReference(client2, playName);
|
|
13190
|
+
const play = await client2.describePlay(
|
|
13009
13191
|
parseReferencedPlayTarget2(playName).playName,
|
|
13010
13192
|
{
|
|
13011
13193
|
compact: args.includes("--compact")
|
|
@@ -13042,7 +13224,7 @@ async function handlePlayPublish(args) {
|
|
|
13042
13224
|
console.error("Choose only one live target: --latest or --revision-id.");
|
|
13043
13225
|
return 1;
|
|
13044
13226
|
}
|
|
13045
|
-
const
|
|
13227
|
+
const client2 = new DeeplineClient();
|
|
13046
13228
|
if (isFileTarget(playName)) {
|
|
13047
13229
|
if (revisionId || useLatest) {
|
|
13048
13230
|
console.error(
|
|
@@ -13060,12 +13242,12 @@ async function handlePlayPublish(args) {
|
|
|
13060
13242
|
await traceCliSpan(
|
|
13061
13243
|
"cli.play_publish_compile_manifests",
|
|
13062
13244
|
{ targetKind: "file", nodeCount: graph.nodes.size },
|
|
13063
|
-
() => compileBundledPlayGraphManifests(
|
|
13245
|
+
() => compileBundledPlayGraphManifests(client2, graph)
|
|
13064
13246
|
);
|
|
13065
13247
|
await traceCliSpan(
|
|
13066
13248
|
"cli.play_publish_imports",
|
|
13067
13249
|
{ targetKind: "file", nodeCount: graph.nodes.size },
|
|
13068
|
-
() => publishImportedPlayDependencies(
|
|
13250
|
+
() => publishImportedPlayDependencies(client2, graph)
|
|
13069
13251
|
);
|
|
13070
13252
|
} catch (error) {
|
|
13071
13253
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -13079,7 +13261,7 @@ async function handlePlayPublish(args) {
|
|
|
13079
13261
|
playName: rootPlayName,
|
|
13080
13262
|
artifactHash: graph.root.artifact.artifactHash
|
|
13081
13263
|
},
|
|
13082
|
-
() =>
|
|
13264
|
+
() => client2.registerPlayArtifact({
|
|
13083
13265
|
name: rootPlayName,
|
|
13084
13266
|
sourceCode: graph.root.sourceCode,
|
|
13085
13267
|
sourceFiles: graph.root.sourceFiles,
|
|
@@ -13103,7 +13285,7 @@ async function handlePlayPublish(args) {
|
|
|
13103
13285
|
}
|
|
13104
13286
|
const resolvedName = parseReferencedPlayTarget2(playName).playName;
|
|
13105
13287
|
if (useLatest) {
|
|
13106
|
-
const versions = await
|
|
13288
|
+
const versions = await client2.listPlayVersions(resolvedName);
|
|
13107
13289
|
const latest = versions[0];
|
|
13108
13290
|
if (!latest?._id) {
|
|
13109
13291
|
console.error(`No saved revisions found for ${resolvedName}.`);
|
|
@@ -13112,12 +13294,12 @@ async function handlePlayPublish(args) {
|
|
|
13112
13294
|
revisionId = latest._id;
|
|
13113
13295
|
}
|
|
13114
13296
|
try {
|
|
13115
|
-
await ensureEditableRemotePlay(
|
|
13297
|
+
await ensureEditableRemotePlay(client2, resolvedName);
|
|
13116
13298
|
} catch (error) {
|
|
13117
13299
|
console.error(error instanceof Error ? error.message : String(error));
|
|
13118
13300
|
return 1;
|
|
13119
13301
|
}
|
|
13120
|
-
const result = await
|
|
13302
|
+
const result = await client2.publishPlayVersion(
|
|
13121
13303
|
resolvedName,
|
|
13122
13304
|
revisionId ? { revisionId } : {}
|
|
13123
13305
|
);
|
|
@@ -13138,10 +13320,10 @@ async function handlePlayDelete(args) {
|
|
|
13138
13320
|
);
|
|
13139
13321
|
return 1;
|
|
13140
13322
|
}
|
|
13141
|
-
const
|
|
13323
|
+
const client2 = new DeeplineClient();
|
|
13142
13324
|
let detail;
|
|
13143
13325
|
try {
|
|
13144
|
-
detail = await
|
|
13326
|
+
detail = await client2.getPlay(parseReferencedPlayTarget2(playName).playName);
|
|
13145
13327
|
} catch (error) {
|
|
13146
13328
|
console.error(error instanceof Error ? error.message : String(error));
|
|
13147
13329
|
return 1;
|
|
@@ -13152,7 +13334,7 @@ async function handlePlayDelete(args) {
|
|
|
13152
13334
|
);
|
|
13153
13335
|
return 1;
|
|
13154
13336
|
}
|
|
13155
|
-
const result = await
|
|
13337
|
+
const result = await client2.deletePlay(
|
|
13156
13338
|
parseReferencedPlayTarget2(formatPlayReference(detail.play)).playName
|
|
13157
13339
|
);
|
|
13158
13340
|
if (argsWantJson(args)) {
|
|
@@ -13817,11 +13999,11 @@ async function handlePlaySharePublish(args) {
|
|
|
13817
13999
|
return 2;
|
|
13818
14000
|
}
|
|
13819
14001
|
const name = parseReferencedPlayTarget2(target).playName;
|
|
13820
|
-
const
|
|
14002
|
+
const client2 = new DeeplineClient();
|
|
13821
14003
|
let revisionId = shareFlagValue(args, "--revision-id");
|
|
13822
14004
|
let resolvedVersion;
|
|
13823
14005
|
if (!revisionId) {
|
|
13824
|
-
const versions = await
|
|
14006
|
+
const versions = await client2.listPlayVersions(name);
|
|
13825
14007
|
if (versions.length === 0) {
|
|
13826
14008
|
console.error(
|
|
13827
14009
|
`No saved revisions for ${name}. Run \`deepline plays set-live ${name}\` first.`
|
|
@@ -13842,7 +14024,7 @@ async function handlePlaySharePublish(args) {
|
|
|
13842
14024
|
resolvedVersion = chosen.version;
|
|
13843
14025
|
}
|
|
13844
14026
|
if (!args.includes("--no-run-check")) {
|
|
13845
|
-
const runs = await
|
|
14027
|
+
const runs = await client2.listRuns({ play: name });
|
|
13846
14028
|
const hasCompleted = runs.some(
|
|
13847
14029
|
(r) => String(r.status).toLowerCase() === "completed"
|
|
13848
14030
|
);
|
|
@@ -13881,7 +14063,7 @@ async function handlePlaySharePublish(args) {
|
|
|
13881
14063
|
}
|
|
13882
14064
|
return 0;
|
|
13883
14065
|
}
|
|
13884
|
-
const status = await
|
|
14066
|
+
const status = await client2.publishSharePage(name, request);
|
|
13885
14067
|
if (argsWantJson(args)) {
|
|
13886
14068
|
process.stdout.write(`${JSON.stringify(status)}
|
|
13887
14069
|
`);
|
|
@@ -13929,11 +14111,11 @@ async function handlePlayShareRegenerate(args) {
|
|
|
13929
14111
|
return 2;
|
|
13930
14112
|
}
|
|
13931
14113
|
const name = parseReferencedPlayTarget2(target).playName;
|
|
13932
|
-
const
|
|
14114
|
+
const client2 = new DeeplineClient();
|
|
13933
14115
|
let revisionId = shareFlagValue(args, "--revision-id");
|
|
13934
14116
|
const versionFlag = shareFlagValue(args, "--version");
|
|
13935
14117
|
if (!revisionId && versionFlag) {
|
|
13936
|
-
const versions = await
|
|
14118
|
+
const versions = await client2.listPlayVersions(name);
|
|
13937
14119
|
const chosen = versions.find((r) => r.version === Number(versionFlag));
|
|
13938
14120
|
if (!chosen?._id) {
|
|
13939
14121
|
console.error(`Version ${versionFlag} not found for ${name}.`);
|
|
@@ -13941,7 +14123,7 @@ async function handlePlayShareRegenerate(args) {
|
|
|
13941
14123
|
}
|
|
13942
14124
|
revisionId = chosen._id;
|
|
13943
14125
|
}
|
|
13944
|
-
const status = await
|
|
14126
|
+
const status = await client2.regenerateSharePage(
|
|
13945
14127
|
name,
|
|
13946
14128
|
revisionId ? { revisionId } : {}
|
|
13947
14129
|
);
|
|
@@ -13978,52 +14160,158 @@ async function handlePlayShareUnpublish(args) {
|
|
|
13978
14160
|
return 0;
|
|
13979
14161
|
}
|
|
13980
14162
|
|
|
13981
|
-
//
|
|
13982
|
-
var
|
|
13983
|
-
"
|
|
13984
|
-
"
|
|
13985
|
-
"
|
|
13986
|
-
"
|
|
13987
|
-
"
|
|
13988
|
-
"
|
|
13989
|
-
"
|
|
13990
|
-
"
|
|
13991
|
-
"
|
|
13992
|
-
"
|
|
13993
|
-
"
|
|
13994
|
-
"
|
|
13995
|
-
"
|
|
13996
|
-
"
|
|
13997
|
-
"
|
|
13998
|
-
"
|
|
13999
|
-
"
|
|
14000
|
-
"
|
|
14001
|
-
"in",
|
|
14002
|
-
"instanceof",
|
|
14003
|
-
"new",
|
|
14004
|
-
"return",
|
|
14005
|
-
"super",
|
|
14006
|
-
"switch",
|
|
14007
|
-
"this",
|
|
14008
|
-
"throw",
|
|
14009
|
-
"try",
|
|
14010
|
-
"typeof",
|
|
14011
|
-
"var",
|
|
14012
|
-
"void",
|
|
14013
|
-
"while",
|
|
14014
|
-
"with",
|
|
14015
|
-
"yield"
|
|
14163
|
+
// ../shared_libs/plays/tool-codegen.ts
|
|
14164
|
+
var KNOWN_GETTER_NAMES = /* @__PURE__ */ new Set([
|
|
14165
|
+
"company_domain",
|
|
14166
|
+
"company_linkedin_url",
|
|
14167
|
+
"company_name",
|
|
14168
|
+
"domain",
|
|
14169
|
+
"email",
|
|
14170
|
+
"email_status",
|
|
14171
|
+
"first_name",
|
|
14172
|
+
"full_name",
|
|
14173
|
+
"job_change",
|
|
14174
|
+
"job_change_status",
|
|
14175
|
+
"job_changed",
|
|
14176
|
+
"last_name",
|
|
14177
|
+
"linkedin",
|
|
14178
|
+
"personal_email",
|
|
14179
|
+
"phone",
|
|
14180
|
+
"phone_status",
|
|
14181
|
+
"status",
|
|
14182
|
+
"title"
|
|
14016
14183
|
]);
|
|
14184
|
+
function renderToolCodegenString(value) {
|
|
14185
|
+
return JSON.stringify(value);
|
|
14186
|
+
}
|
|
14187
|
+
function renderToolRowPathExpression(path, rowName = "row") {
|
|
14188
|
+
const parts = String(path || "").replace(/\[(\d+)\]/g, ".$1").split(".").map((part) => part.trim()).filter(Boolean);
|
|
14189
|
+
if (parts.length === 0) return rowName;
|
|
14190
|
+
return parts.reduce((expr, part, index) => {
|
|
14191
|
+
if (index === 0) return `${rowName}[${renderToolCodegenString(part)}]`;
|
|
14192
|
+
return `(${expr} as Record<string, unknown> | null | undefined)?.[${renderToolCodegenString(part)}]`;
|
|
14193
|
+
}, rowName);
|
|
14194
|
+
}
|
|
14195
|
+
function renderToolPayloadExpression(value, rowName = "row") {
|
|
14196
|
+
if (Array.isArray(value)) {
|
|
14197
|
+
return `[${value.map((entry) => renderToolPayloadExpression(entry, rowName)).join(", ")}]`;
|
|
14198
|
+
}
|
|
14199
|
+
if (value && typeof value === "object") {
|
|
14200
|
+
const entries = Object.entries(value).sort(
|
|
14201
|
+
([left], [right]) => left.localeCompare(right)
|
|
14202
|
+
);
|
|
14203
|
+
return `{ ${entries.map(
|
|
14204
|
+
([key, entry]) => `${renderToolCodegenString(key)}: ${renderToolPayloadExpression(entry, rowName)}`
|
|
14205
|
+
).join(", ")} }`;
|
|
14206
|
+
}
|
|
14207
|
+
if (typeof value !== "string") return JSON.stringify(value);
|
|
14208
|
+
const exact = value.match(/^\{\{\s*([^{}]+?)\s*\}\}$/);
|
|
14209
|
+
if (exact) return renderToolRowPathExpression(exact[1] ?? "", rowName);
|
|
14210
|
+
const pieces = [];
|
|
14211
|
+
let cursor = 0;
|
|
14212
|
+
for (const match of value.matchAll(/\{\{\s*([^{}]+?)\s*\}\}/g)) {
|
|
14213
|
+
const index = match.index ?? 0;
|
|
14214
|
+
if (index > cursor) {
|
|
14215
|
+
pieces.push(renderToolCodegenString(value.slice(cursor, index)));
|
|
14216
|
+
}
|
|
14217
|
+
pieces.push(
|
|
14218
|
+
`String(${renderToolRowPathExpression(match[1] ?? "", rowName)} ?? '')`
|
|
14219
|
+
);
|
|
14220
|
+
cursor = index + match[0].length;
|
|
14221
|
+
}
|
|
14222
|
+
if (pieces.length === 0) return renderToolCodegenString(value);
|
|
14223
|
+
if (cursor < value.length) {
|
|
14224
|
+
pieces.push(renderToolCodegenString(value.slice(cursor)));
|
|
14225
|
+
}
|
|
14226
|
+
return pieces.join(" + ");
|
|
14227
|
+
}
|
|
14228
|
+
function renderExtractedValueGetterExpression(toolResultVar, targetKey) {
|
|
14229
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(targetKey) ? `${toolResultVar}.extractedValues.${targetKey}?.get()` : `${toolResultVar}.extractedValues[${renderToolCodegenString(targetKey)}]?.get()`;
|
|
14230
|
+
}
|
|
14231
|
+
function getterFromLegacyPath(path) {
|
|
14232
|
+
const last = String(path || "").replace(/\[(\d+)\]/g, ".$1").split(".").map((part) => part.trim()).filter(Boolean).at(-1);
|
|
14233
|
+
if (!last) return null;
|
|
14234
|
+
const normalized = last.replace(/[^A-Za-z0-9_]/g, "_");
|
|
14235
|
+
return KNOWN_GETTER_NAMES.has(normalized) ? normalized : null;
|
|
14236
|
+
}
|
|
14237
|
+
function getterFromLegacyExtractJs(extractJs, fallbackAlias) {
|
|
14238
|
+
const source = extractJs?.trim();
|
|
14239
|
+
if (!source) {
|
|
14240
|
+
const aliasGetter = fallbackAlias?.replace(/[^A-Za-z0-9_]/g, "_");
|
|
14241
|
+
return aliasGetter && KNOWN_GETTER_NAMES.has(aliasGetter) ? aliasGetter : null;
|
|
14242
|
+
}
|
|
14243
|
+
const directExtract = source.match(
|
|
14244
|
+
/\bextract\(\s*["'][^"']+["']\s*,\s*output_data\s*,\s*["']([^"']+)["']\s*\)/
|
|
14245
|
+
);
|
|
14246
|
+
if (directExtract?.[1]) {
|
|
14247
|
+
return getterFromLegacyPath(
|
|
14248
|
+
directExtract[1] === "linkedin_url" ? "linkedin" : directExtract[1]
|
|
14249
|
+
);
|
|
14250
|
+
}
|
|
14251
|
+
const pick = source.match(
|
|
14252
|
+
/\b(?:pick|extract|target)\(\s*(\[[\s\S]*?\]|["'][^"']+["'])\s*\)/
|
|
14253
|
+
);
|
|
14254
|
+
if (!pick?.[1]) return null;
|
|
14255
|
+
try {
|
|
14256
|
+
const parsed = JSON.parse(pick[1].replace(/'/g, '"'));
|
|
14257
|
+
const paths = Array.isArray(parsed) ? parsed : [parsed];
|
|
14258
|
+
for (const path of paths) {
|
|
14259
|
+
if (typeof path !== "string") continue;
|
|
14260
|
+
const getter = getterFromLegacyPath(path);
|
|
14261
|
+
if (getter) return getter;
|
|
14262
|
+
}
|
|
14263
|
+
} catch {
|
|
14264
|
+
return null;
|
|
14265
|
+
}
|
|
14266
|
+
return null;
|
|
14267
|
+
}
|
|
14268
|
+
|
|
14269
|
+
// src/cli/user-code-safety.ts
|
|
14270
|
+
var FORBIDDEN = [
|
|
14271
|
+
// Non-deterministic — breaks replay.
|
|
14272
|
+
{ pattern: /\bMath\s*\.\s*random\b/, reason: "Math.random()" },
|
|
14273
|
+
{ pattern: /\bDate\s*\.\s*now\b/, reason: "Date.now()" },
|
|
14274
|
+
{ pattern: /\bnew\s+Date\s*\(\s*\)/, reason: "new Date() with no argument" },
|
|
14275
|
+
{ pattern: /\bperformance\s*\.\s*now\b/, reason: "performance.now()" },
|
|
14276
|
+
{
|
|
14277
|
+
pattern: /\bcrypto\s*\.\s*(?:randomUUID|getRandomValues)\b/,
|
|
14278
|
+
reason: "crypto random"
|
|
14279
|
+
},
|
|
14280
|
+
// Sandbox escape / I/O.
|
|
14281
|
+
{ pattern: /(?<!\.)\bfetch\s*\(/, reason: "fetch()" },
|
|
14282
|
+
{ pattern: /(?<!\.)\bimport\s*\(/, reason: "dynamic import()" },
|
|
14283
|
+
{ pattern: /(?<!\.)\brequire\s*\(/, reason: "require()" },
|
|
14284
|
+
{ pattern: /(?<!\.)\beval\s*\(/, reason: "eval()" },
|
|
14285
|
+
{
|
|
14286
|
+
pattern: /(?<!\.)\bnew\s+Function\b|(?<!\.)\bFunction\s*\(/,
|
|
14287
|
+
reason: "the Function constructor"
|
|
14288
|
+
},
|
|
14289
|
+
{ pattern: /(?<!\.)\bprocess\b/, reason: "process" },
|
|
14290
|
+
{ pattern: /(?<!\.)\bglobalThis\b/, reason: "globalThis" },
|
|
14291
|
+
{ pattern: /(?<!\.)\b(?:window|self)\b/, reason: "window/self" },
|
|
14292
|
+
{ pattern: /(?<!\.)\bXMLHttpRequest\b/, reason: "XMLHttpRequest" },
|
|
14293
|
+
{ pattern: /(?<!\.)\bWebAssembly\b/, reason: "WebAssembly" }
|
|
14294
|
+
];
|
|
14295
|
+
function assertUserCodeIsSafe(code, label) {
|
|
14296
|
+
if (typeof code !== "string" || !code.trim()) return;
|
|
14297
|
+
for (const { pattern, reason } of FORBIDDEN) {
|
|
14298
|
+
if (pattern.test(code)) {
|
|
14299
|
+
throw new Error(
|
|
14300
|
+
`${label} uses ${reason}, which is not allowed in play code: it breaks deterministic replay or escapes the sandbox. Remove it and compute the value from the row/result instead.`
|
|
14301
|
+
);
|
|
14302
|
+
}
|
|
14303
|
+
}
|
|
14304
|
+
}
|
|
14305
|
+
|
|
14306
|
+
// src/cli/enrich-play-compiler.ts
|
|
14017
14307
|
function isWaterfall(command) {
|
|
14018
14308
|
return "with_waterfall" in command;
|
|
14019
14309
|
}
|
|
14020
|
-
function
|
|
14021
|
-
const
|
|
14022
|
-
|
|
14023
|
-
|
|
14024
|
-
|
|
14025
|
-
}
|
|
14026
|
-
return prefixed;
|
|
14310
|
+
function configHasRunJavascript(config) {
|
|
14311
|
+
const walk = (commands) => commands.some(
|
|
14312
|
+
(command) => isWaterfall(command) ? walk(command.commands) : command.tool === "run_javascript"
|
|
14313
|
+
);
|
|
14314
|
+
return walk(config.commands ?? []);
|
|
14027
14315
|
}
|
|
14028
14316
|
function stringLiteral(value) {
|
|
14029
14317
|
return JSON.stringify(value);
|
|
@@ -14050,12 +14338,23 @@ function commandCallId(command) {
|
|
|
14050
14338
|
function normalizeAlias(value) {
|
|
14051
14339
|
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
14052
14340
|
}
|
|
14053
|
-
function renderExecuteStep(command, options = {
|
|
14341
|
+
function renderExecuteStep(command, options = {
|
|
14342
|
+
force: false
|
|
14343
|
+
}) {
|
|
14344
|
+
if (command.play) {
|
|
14345
|
+
return renderPlayStep(command, options);
|
|
14346
|
+
}
|
|
14347
|
+
if (command.tool === "run_javascript" && options.inlineRunJavascript) {
|
|
14348
|
+
return renderInlineJavascriptStep(command, options);
|
|
14349
|
+
}
|
|
14350
|
+
if (options.idiomaticGetters) {
|
|
14351
|
+
return renderIdiomaticExecuteStep(command, options);
|
|
14352
|
+
}
|
|
14054
14353
|
const alias = stringLiteral(command.alias);
|
|
14055
14354
|
const callId = stringLiteral(commandCallId(command));
|
|
14056
14355
|
const tool = stringLiteral(command.tool);
|
|
14057
14356
|
const payload = stableJson(command.payload ?? {});
|
|
14058
|
-
const extractJs = command.extract_js ? `({ row, result, data, raw, pick, extract, target }) => { const input = row; const context = row;
|
|
14357
|
+
const extractJs = command.extract_js ? `({ row, result, data, raw, pick, extract, extractList, target, get }) => { const input = row; const context = row;
|
|
14059
14358
|
${indent(renderJavascriptBody(command.extract_js), 6)}
|
|
14060
14359
|
}` : "null";
|
|
14061
14360
|
const runIfJs = command.run_if_js ? `(row) => { const input = row; const context = row;
|
|
@@ -14065,6 +14364,8 @@ ${indent(renderJavascriptBody(command.run_if_js), 6)}
|
|
|
14065
14364
|
description: ${stringLiteral(command.description)}` : "";
|
|
14066
14365
|
const force = options.force ? `,
|
|
14067
14366
|
force: true` : "";
|
|
14367
|
+
const legacyEnvelope = options.legacyEnvelope ? `,
|
|
14368
|
+
legacyEnvelope: true` : "";
|
|
14068
14369
|
return [
|
|
14069
14370
|
`async (row, stepCtx) => {`,
|
|
14070
14371
|
...options.precheck ? [` if (${options.precheck}) return null;`] : [],
|
|
@@ -14076,253 +14377,842 @@ ${indent(renderJavascriptBody(command.run_if_js), 6)}
|
|
|
14076
14377
|
` extract: ${extractJs},`,
|
|
14077
14378
|
` runIf: ${runIfJs},`,
|
|
14078
14379
|
` row,`,
|
|
14079
|
-
` stepCtx${description}${force}`,
|
|
14380
|
+
` stepCtx${description}${force}${legacyEnvelope}`,
|
|
14381
|
+
` });`,
|
|
14382
|
+
`}`
|
|
14383
|
+
].join("\n");
|
|
14384
|
+
}
|
|
14385
|
+
function renderIdiomaticExecuteStep(command, options) {
|
|
14386
|
+
const callId = stringLiteral(commandCallId(command));
|
|
14387
|
+
const tool = stringLiteral(command.tool);
|
|
14388
|
+
const input2 = renderToolPayloadExpression(command.payload ?? {});
|
|
14389
|
+
const getter = getterFromLegacyExtractJs(command.extract_js, command.alias);
|
|
14390
|
+
const extraction = getter ? `${renderExtractedValueGetterExpression("result", getter)} ?? null` : "result";
|
|
14391
|
+
const runIfLines = command.run_if_js ? [
|
|
14392
|
+
` if (`,
|
|
14393
|
+
` !((row: Record<string, any>) => {`,
|
|
14394
|
+
` const input = row;`,
|
|
14395
|
+
` const context = row;`,
|
|
14396
|
+
indent(renderJavascriptBody(command.run_if_js), 6),
|
|
14397
|
+
` })(row as Record<string, any>)`,
|
|
14398
|
+
` ) return null;`
|
|
14399
|
+
] : [];
|
|
14400
|
+
return [
|
|
14401
|
+
`async (row, ctx) => {`,
|
|
14402
|
+
...options.precheck ? [` if (${options.precheck}) return null;`] : [],
|
|
14403
|
+
...runIfLines,
|
|
14404
|
+
` const result: any = await ctx.tools.execute({`,
|
|
14405
|
+
` id: ${callId},`,
|
|
14406
|
+
` tool: ${tool},`,
|
|
14407
|
+
` input: ${input2} as any,`,
|
|
14408
|
+
` description: ${stringLiteral((command.description ?? "").trim() || `Run ${command.alias} via ${command.tool}.`)},`,
|
|
14409
|
+
...options.force ? [` staleAfterSeconds: 1,`] : [],
|
|
14410
|
+
` });`,
|
|
14411
|
+
` return ${extraction};`,
|
|
14412
|
+
`}`
|
|
14413
|
+
].join("\n");
|
|
14414
|
+
}
|
|
14415
|
+
function renderInlineJavascriptStep(command, options) {
|
|
14416
|
+
const code = typeof command.payload?.code === "string" ? command.payload.code : "return null;";
|
|
14417
|
+
const runIfLines = command.run_if_js ? [
|
|
14418
|
+
` if (!((row: Record<string, any>) => { const input = row; const context = row;`,
|
|
14419
|
+
indent(renderJavascriptBody(command.run_if_js), 4),
|
|
14420
|
+
` })(row as Record<string, any>)) return null;`
|
|
14421
|
+
] : [];
|
|
14422
|
+
return [
|
|
14423
|
+
`async (row) => {`,
|
|
14424
|
+
...options.precheck ? [` if (${options.precheck}) return null;`] : [],
|
|
14425
|
+
...runIfLines,
|
|
14426
|
+
` return ((row: Record<string, any>, input: Record<string, any>, context: Record<string, any>) => {`,
|
|
14427
|
+
indent(renderJavascriptBody(code), 4),
|
|
14428
|
+
` })(row as Record<string, any>, row as Record<string, any>, row as Record<string, any>);`,
|
|
14429
|
+
`}`
|
|
14430
|
+
].join("\n");
|
|
14431
|
+
}
|
|
14432
|
+
function renderPlayStep(command, options) {
|
|
14433
|
+
const alias = stringLiteral(command.alias);
|
|
14434
|
+
const callId = stringLiteral(commandCallId(command));
|
|
14435
|
+
const playRef = stringLiteral(command.play?.ref ?? command.tool);
|
|
14436
|
+
const payload = stableJson(command.payload ?? {});
|
|
14437
|
+
const runIfJs = command.run_if_js ? `(row) => { const input = row; const context = row;
|
|
14438
|
+
${indent(renderJavascriptBody(command.run_if_js), 6)}
|
|
14439
|
+
}` : "null";
|
|
14440
|
+
const runIfLines = command.run_if_js ? [` const __dlRunIf = ${runIfJs};`, ` if (!__dlRunIf(row)) return null;`] : [];
|
|
14441
|
+
const playOptions = [
|
|
14442
|
+
` description: ${stringLiteral(command.description ?? command.alias)}`,
|
|
14443
|
+
...options.force ? [` staleAfterSeconds: 1`] : []
|
|
14444
|
+
].join(",\n");
|
|
14445
|
+
return [
|
|
14446
|
+
`async (row, stepCtx) => {`,
|
|
14447
|
+
...options.precheck ? [` if (${options.precheck}) return null;`] : [],
|
|
14448
|
+
...runIfLines,
|
|
14449
|
+
` const payload = __dlTemplate(${payload}, row) as Record<string, unknown>;`,
|
|
14450
|
+
` const result = await stepCtx.runPlay(${callId}, ${playRef}, payload, {`,
|
|
14451
|
+
playOptions,
|
|
14080
14452
|
` });`,
|
|
14453
|
+
` return __dlPlayResultValue(${alias}, result);`,
|
|
14081
14454
|
`}`
|
|
14082
14455
|
].join("\n");
|
|
14083
14456
|
}
|
|
14084
14457
|
function renderJavascriptBody(source) {
|
|
14458
|
+
assertUserCodeIsSafe(source, "play step code");
|
|
14085
14459
|
const trimmed = source.trim();
|
|
14460
|
+
if (/^(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/.test(trimmed)) {
|
|
14461
|
+
return `return (${trimmed});`;
|
|
14462
|
+
}
|
|
14086
14463
|
if (trimmed && !trimmed.includes("\n") && !trimmed.includes(";") && !/\breturn\b/.test(trimmed)) {
|
|
14087
14464
|
return `return (${trimmed});`;
|
|
14088
14465
|
}
|
|
14089
14466
|
return source;
|
|
14090
14467
|
}
|
|
14091
|
-
function
|
|
14092
|
-
const
|
|
14093
|
-
|
|
14094
|
-
`
|
|
14468
|
+
function renderColumnStep(alias, resolverSource, force) {
|
|
14469
|
+
const resolver = indent(resolverSource, 8);
|
|
14470
|
+
return [
|
|
14471
|
+
` .withColumn(${stringLiteral(alias)},`,
|
|
14472
|
+
force ? `${resolver},` : resolver,
|
|
14473
|
+
...force ? [` { staleAfterSeconds: 1 }`] : [],
|
|
14474
|
+
` )`
|
|
14475
|
+
].join("\n");
|
|
14476
|
+
}
|
|
14477
|
+
function metadataMode(command) {
|
|
14478
|
+
return /\bextractList\s*\(/.test(command.extract_js ?? "") ? "list" : "scalar";
|
|
14479
|
+
}
|
|
14480
|
+
function metadataEntryForCommand(command, waterfallGroupId) {
|
|
14481
|
+
const entry = {
|
|
14482
|
+
tool_id: command.tool
|
|
14483
|
+
};
|
|
14484
|
+
if (command.play?.ref) {
|
|
14485
|
+
entry.play_ref = command.play.ref;
|
|
14486
|
+
entry.kind = "play_call";
|
|
14487
|
+
}
|
|
14488
|
+
if (command.operation) {
|
|
14489
|
+
entry.operation = command.operation;
|
|
14490
|
+
}
|
|
14491
|
+
if (command.extract_js?.trim()) {
|
|
14492
|
+
entry.extract_js = command.extract_js.trim();
|
|
14493
|
+
}
|
|
14494
|
+
if (waterfallGroupId) {
|
|
14495
|
+
entry.waterfall = {
|
|
14496
|
+
group_id: waterfallGroupId,
|
|
14497
|
+
mode: metadataMode(command)
|
|
14498
|
+
};
|
|
14499
|
+
}
|
|
14500
|
+
return entry;
|
|
14501
|
+
}
|
|
14502
|
+
function collectMetadataColumns(commands, waterfallGroupId) {
|
|
14503
|
+
const columns = {};
|
|
14504
|
+
for (const command of commands) {
|
|
14505
|
+
if (isWaterfall(command)) {
|
|
14506
|
+
Object.assign(
|
|
14507
|
+
columns,
|
|
14508
|
+
collectMetadataColumns(command.commands, command.with_waterfall)
|
|
14509
|
+
);
|
|
14510
|
+
continue;
|
|
14511
|
+
}
|
|
14512
|
+
if (command.disabled) {
|
|
14513
|
+
continue;
|
|
14514
|
+
}
|
|
14515
|
+
columns[normalizeAlias(command.alias)] = metadataEntryForCommand(
|
|
14516
|
+
command,
|
|
14517
|
+
waterfallGroupId
|
|
14518
|
+
);
|
|
14519
|
+
}
|
|
14520
|
+
return columns;
|
|
14521
|
+
}
|
|
14522
|
+
function renderMetadataColumnStep(config) {
|
|
14523
|
+
const columns = collectMetadataColumns(config.commands);
|
|
14524
|
+
if (Object.keys(columns).length === 0) {
|
|
14525
|
+
return "";
|
|
14526
|
+
}
|
|
14527
|
+
return [
|
|
14528
|
+
` .withColumn('_metadata',`,
|
|
14529
|
+
` (row) => __dlMergeMetadata(row._metadata, ${stableJson({ columns })}),`,
|
|
14530
|
+
` { staleAfterSeconds: 1 }`,
|
|
14531
|
+
` )`
|
|
14532
|
+
].join("\n");
|
|
14533
|
+
}
|
|
14534
|
+
function renderWaterfallColumns(command, forceAliases, inlineRunJavascript, idiomaticGetters) {
|
|
14535
|
+
const activeChildren = command.commands.filter(
|
|
14536
|
+
(nested) => !isWaterfall(nested) && !nested.disabled
|
|
14095
14537
|
);
|
|
14096
|
-
const
|
|
14538
|
+
const minResults = typeof command.min_results === "number" ? Math.max(1, Math.trunc(command.min_results)) : 1;
|
|
14539
|
+
const columnSteps = activeChildren.map((nested, stepIndex) => {
|
|
14097
14540
|
if (isWaterfall(nested)) {
|
|
14098
14541
|
throw new Error("Nested with_waterfall blocks are not supported.");
|
|
14099
14542
|
}
|
|
14100
14543
|
if (nested.disabled) {
|
|
14101
14544
|
return null;
|
|
14102
14545
|
}
|
|
14103
|
-
const priorAliases =
|
|
14104
|
-
const
|
|
14105
|
-
return
|
|
14106
|
-
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
|
|
14110
|
-
|
|
14111
|
-
|
|
14112
|
-
|
|
14113
|
-
),
|
|
14114
|
-
|
|
14115
|
-
|
|
14546
|
+
const priorAliases = activeChildren.slice(0, stepIndex).map((prior) => prior.alias);
|
|
14547
|
+
const force = forceAliases.has(normalizeAlias(nested.alias));
|
|
14548
|
+
return renderColumnStep(
|
|
14549
|
+
nested.alias,
|
|
14550
|
+
renderExecuteStep(nested, {
|
|
14551
|
+
force,
|
|
14552
|
+
precheck: priorAliases.length > 0 ? `__dlWaterfallSatisfied(row, ${stableJson(priorAliases)}, ${minResults})` : void 0,
|
|
14553
|
+
legacyEnvelope: Boolean(nested.extract_js),
|
|
14554
|
+
inlineRunJavascript,
|
|
14555
|
+
idiomaticGetters
|
|
14556
|
+
}),
|
|
14557
|
+
force
|
|
14558
|
+
);
|
|
14116
14559
|
}).filter((line) => line !== null);
|
|
14117
|
-
const aliases =
|
|
14560
|
+
const aliases = activeChildren.map((nested) => nested.alias);
|
|
14561
|
+
const forceParent = forceAliases.has(normalizeAlias(command.with_waterfall)) || activeChildren.some(
|
|
14562
|
+
(nested) => forceAliases.has(normalizeAlias(nested.alias))
|
|
14563
|
+
);
|
|
14118
14564
|
const returnExpr = typeof command.min_results === "number" ? `__dlFirstMinResults(row, ${stableJson(aliases)}, ${Math.max(
|
|
14119
14565
|
1,
|
|
14120
14566
|
Math.trunc(command.min_results)
|
|
14121
14567
|
)})` : `__dlFirstMeaningful(row, ${stableJson(aliases)})`;
|
|
14122
|
-
|
|
14123
|
-
|
|
14124
|
-
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14568
|
+
if (columnSteps.length === 0) {
|
|
14569
|
+
return [];
|
|
14570
|
+
}
|
|
14571
|
+
return [
|
|
14572
|
+
...columnSteps,
|
|
14573
|
+
renderColumnStep(
|
|
14574
|
+
command.with_waterfall,
|
|
14575
|
+
`(row) => ${returnExpr}`,
|
|
14576
|
+
forceParent
|
|
14577
|
+
),
|
|
14578
|
+
renderColumnStep(
|
|
14579
|
+
`${command.with_waterfall}_source`,
|
|
14580
|
+
typeof command.min_results === "number" ? `(row) => __dlContributingAliases(row, ${stableJson(aliases)})` : `(row) => __dlFirstMeaningfulAlias(row, ${stableJson(aliases)})`,
|
|
14581
|
+
forceParent
|
|
14582
|
+
)
|
|
14583
|
+
];
|
|
14131
14584
|
}
|
|
14132
14585
|
function compileEnrichConfigToPlaySource(config, options = {}) {
|
|
14133
14586
|
const playName = options.playName ?? "deepline-enrich-v1-compat";
|
|
14134
14587
|
const mapName = options.mapName ?? "deepline_enrich_rows";
|
|
14588
|
+
const inlineRunJavascript = options.inlineRunJavascript ?? false;
|
|
14589
|
+
const idiomaticGetters = options.idiomaticGetters ?? false;
|
|
14135
14590
|
const forceAliases = new Set(
|
|
14136
14591
|
[...options.forceAliases ?? []].map((alias) => normalizeAlias(alias))
|
|
14137
14592
|
);
|
|
14138
|
-
const
|
|
14139
|
-
|
|
14140
|
-
config.commands.forEach((command, index) => {
|
|
14593
|
+
const columnSteps = [];
|
|
14594
|
+
config.commands.forEach((command) => {
|
|
14141
14595
|
if (isWaterfall(command)) {
|
|
14142
|
-
|
|
14143
|
-
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
source: rendered.source
|
|
14150
|
-
});
|
|
14151
|
-
mapSteps.push(
|
|
14152
|
-
` .step(${stringLiteral(command.with_waterfall)}, ${rendered.variableName})`
|
|
14596
|
+
columnSteps.push(
|
|
14597
|
+
...renderWaterfallColumns(
|
|
14598
|
+
command,
|
|
14599
|
+
forceAliases,
|
|
14600
|
+
inlineRunJavascript,
|
|
14601
|
+
idiomaticGetters
|
|
14602
|
+
)
|
|
14153
14603
|
);
|
|
14154
14604
|
return;
|
|
14155
14605
|
}
|
|
14156
14606
|
if (command.disabled) {
|
|
14157
14607
|
return;
|
|
14158
14608
|
}
|
|
14159
|
-
|
|
14160
|
-
|
|
14161
|
-
|
|
14162
|
-
|
|
14163
|
-
|
|
14164
|
-
|
|
14165
|
-
|
|
14166
|
-
|
|
14167
|
-
),
|
|
14168
|
-
|
|
14169
|
-
|
|
14609
|
+
const force = forceAliases.has(normalizeAlias(command.alias));
|
|
14610
|
+
columnSteps.push(
|
|
14611
|
+
renderColumnStep(
|
|
14612
|
+
command.alias,
|
|
14613
|
+
renderExecuteStep(command, {
|
|
14614
|
+
force,
|
|
14615
|
+
inlineRunJavascript,
|
|
14616
|
+
idiomaticGetters
|
|
14617
|
+
}),
|
|
14618
|
+
force
|
|
14619
|
+
)
|
|
14170
14620
|
);
|
|
14171
14621
|
});
|
|
14172
|
-
const
|
|
14173
|
-
const
|
|
14622
|
+
const columnStepSource = columnSteps.length > 0 ? columnSteps.join("\n") : ` .withColumn('noop', () => null)`;
|
|
14623
|
+
const metadataColumnSource = renderMetadataColumnStep(config);
|
|
14624
|
+
const body = [
|
|
14625
|
+
`export default definePlay(${stringLiteral(playName)}, async (ctx, input: EnrichInput) => {`,
|
|
14626
|
+
` const sourceRows = await ctx.csv<Record<string, unknown>>(input.file);`,
|
|
14627
|
+
` const rowStart = __dlNonNegativeInteger(input.rowStart, 0);`,
|
|
14628
|
+
` const rowEndExclusive = Number.isFinite(input.rowEnd) ? Math.max(rowStart, __dlWholeNumber(input.rowEnd, rowStart) + 1) : undefined;`,
|
|
14629
|
+
` const rows = sourceRows.slice(rowStart, rowEndExclusive, { key: 'deepline_enrich_selected_rows', sourceLabel: 'Selected enrich rows' });`,
|
|
14630
|
+
` const enriched = await ctx`,
|
|
14631
|
+
` .dataset(${stringLiteral(mapName)}, rows)`,
|
|
14632
|
+
columnStepSource,
|
|
14633
|
+
...metadataColumnSource ? [metadataColumnSource] : [],
|
|
14634
|
+
` .run({ key: (row, index) => __dlStableRowKey(row, index + rowStart) });`,
|
|
14635
|
+
` return { rows: enriched, count: await enriched.count() };`,
|
|
14636
|
+
`});`
|
|
14637
|
+
];
|
|
14638
|
+
const helpers = idiomaticGetters ? selectUsedHelpers(helperSource(), body.join("\n")) : helperSource();
|
|
14174
14639
|
return [
|
|
14175
|
-
|
|
14640
|
+
...inlineRunJavascript && configHasRunJavascript(config) ? ["// @ts-nocheck", "/* eslint-disable */", ""] : [],
|
|
14641
|
+
`import { definePlay } from 'deepline';`,
|
|
14176
14642
|
``,
|
|
14177
14643
|
`type EnrichInput = { file: string; rowStart?: number | null; rowEnd?: number | null };`,
|
|
14178
14644
|
``,
|
|
14179
|
-
|
|
14645
|
+
helpers,
|
|
14180
14646
|
``,
|
|
14181
|
-
|
|
14182
|
-
` const allRows = await ctx.csv<Record<string, unknown>>(input.file);`,
|
|
14183
|
-
` const rowStart = Number.isFinite(input.rowStart) ? Math.max(0, Math.trunc(Number(input.rowStart))) : 0;`,
|
|
14184
|
-
` const rowEnd = Number.isFinite(input.rowEnd) ? Math.max(rowStart, Math.trunc(Number(input.rowEnd))) : allRows.length;`,
|
|
14185
|
-
` const rows = allRows.slice(rowStart, rowEnd);`,
|
|
14186
|
-
...waterfallSource,
|
|
14187
|
-
` const enriched = await ctx`,
|
|
14188
|
-
` .map(${stringLiteral(mapName)}, rows)`,
|
|
14189
|
-
mapStepSource,
|
|
14190
|
-
` .run({ key: (row, index) => __dlStableRowKey(row, index + rowStart) });`,
|
|
14191
|
-
` return { rows: enriched, count: await enriched.count() };`,
|
|
14192
|
-
`});`,
|
|
14647
|
+
...body,
|
|
14193
14648
|
``
|
|
14194
14649
|
].join("\n");
|
|
14195
14650
|
}
|
|
14651
|
+
function selectUsedHelpers(helperBlock, referenceSource) {
|
|
14652
|
+
const referencesSymbol = (source, symbol) => new RegExp(`\\b${symbol}\\b`).test(source);
|
|
14653
|
+
const blocks = helperBlock.split("\n\n").map((source) => source.trim()).filter(Boolean).map((source) => ({
|
|
14654
|
+
name: source.match(/(?:function|type|const)\s+(__[A-Za-z]\w*)/)?.[1] ?? "",
|
|
14655
|
+
source
|
|
14656
|
+
}));
|
|
14657
|
+
const used = /* @__PURE__ */ new Set();
|
|
14658
|
+
for (const { name } of blocks) {
|
|
14659
|
+
if (name && referencesSymbol(referenceSource, name)) used.add(name);
|
|
14660
|
+
}
|
|
14661
|
+
let changed = true;
|
|
14662
|
+
while (changed) {
|
|
14663
|
+
changed = false;
|
|
14664
|
+
const usedSource = blocks.filter((block) => used.has(block.name)).map((block) => block.source).join("\n");
|
|
14665
|
+
for (const { name } of blocks) {
|
|
14666
|
+
if (!name || used.has(name)) continue;
|
|
14667
|
+
if (referencesSymbol(usedSource, name)) {
|
|
14668
|
+
used.add(name);
|
|
14669
|
+
changed = true;
|
|
14670
|
+
}
|
|
14671
|
+
}
|
|
14672
|
+
}
|
|
14673
|
+
return blocks.filter((block) => used.has(block.name)).map((block) => block.source).join("\n\n");
|
|
14674
|
+
}
|
|
14196
14675
|
function helperSource() {
|
|
14197
14676
|
return [
|
|
14198
|
-
`function
|
|
14199
|
-
`
|
|
14200
|
-
`
|
|
14201
|
-
`
|
|
14202
|
-
` .map((part) => part.trim())`,
|
|
14203
|
-
` .filter(Boolean)`,
|
|
14204
|
-
` .reduce((cursor: unknown, part: string) => {`,
|
|
14205
|
-
` if (!cursor || typeof cursor !== 'object') return undefined;`,
|
|
14206
|
-
` const record = cursor as Record<string, unknown>;`,
|
|
14207
|
-
` if (part in record) return record[part];`,
|
|
14208
|
-
` const data = record.data;`,
|
|
14209
|
-
` return data && typeof data === 'object' ? (data as Record<string, unknown>)[part] : undefined;`,
|
|
14210
|
-
` }, root);`,
|
|
14677
|
+
`function __dlWholeNumber(value: unknown, fallback: number): number {`,
|
|
14678
|
+
` const numeric = Number(value);`,
|
|
14679
|
+
` if (!Number.isFinite(numeric)) return fallback;`,
|
|
14680
|
+
` return Math.floor(numeric);`,
|
|
14211
14681
|
`}`,
|
|
14212
14682
|
``,
|
|
14213
|
-
`function
|
|
14214
|
-
` return
|
|
14683
|
+
`function __dlNonNegativeInteger(value: unknown, fallback: number): number {`,
|
|
14684
|
+
` return Math.max(0, __dlWholeNumber(value, fallback));`,
|
|
14215
14685
|
`}`,
|
|
14216
14686
|
``,
|
|
14217
|
-
`function
|
|
14218
|
-
`
|
|
14219
|
-
` const record = result as Record<string, unknown>;`,
|
|
14220
|
-
` return __dlGetByPath(record, 'toolOutput.raw') ?? __dlGetByPath(record, 'toolResponse.raw') ?? record.result ?? record.output ?? result;`,
|
|
14687
|
+
`function __dlAtLeastOneInteger(value: number): number {`,
|
|
14688
|
+
` return Math.max(1, Math.floor(Number(value)));`,
|
|
14221
14689
|
`}`,
|
|
14222
14690
|
``,
|
|
14223
|
-
`function
|
|
14224
|
-
`
|
|
14225
|
-
` if (value && typeof value === 'object') {`,
|
|
14226
|
-
` return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([key, entry]) => [key, __dlTemplate(entry, row)]));`,
|
|
14227
|
-
` }`,
|
|
14228
|
-
` if (typeof value !== 'string') return value;`,
|
|
14229
|
-
` const exact = value.match(/^\\{\\{\\s*([^{}]+?)\\s*\\}\\}$/);`,
|
|
14230
|
-
` if (exact) return __dlGetByPath(row, exact[1] || '');`,
|
|
14231
|
-
` return value.replace(/\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g, (_match, path) => {`,
|
|
14232
|
-
` const replacement = __dlGetByPath(row, String(path || ''));`,
|
|
14233
|
-
` return replacement === null || replacement === undefined ? '' : String(replacement);`,
|
|
14234
|
-
` });`,
|
|
14691
|
+
`function __dlRecord(value: unknown): value is Record<string, unknown> {`,
|
|
14692
|
+
` return Boolean(value && typeof value === 'object' && !Array.isArray(value));`,
|
|
14235
14693
|
`}`,
|
|
14236
14694
|
``,
|
|
14237
|
-
`function
|
|
14238
|
-
`
|
|
14239
|
-
`
|
|
14240
|
-
`
|
|
14695
|
+
`function __dlParseMetadata(value: unknown): Record<string, unknown> | null {`,
|
|
14696
|
+
` if (__dlRecord(value)) return value;`,
|
|
14697
|
+
` if (typeof value !== 'string') return null;`,
|
|
14698
|
+
` const trimmed = value.trim();`,
|
|
14699
|
+
` if (!trimmed) return null;`,
|
|
14700
|
+
` const candidates = [trimmed];`,
|
|
14701
|
+
` if (trimmed.includes('\\\\"')) candidates.push(trimmed.replace(/\\\\"/g, '"'));`,
|
|
14702
|
+
` for (const candidate of candidates) {`,
|
|
14703
|
+
` try {`,
|
|
14704
|
+
` const parsed = JSON.parse(candidate);`,
|
|
14705
|
+
` if (__dlRecord(parsed)) return parsed;`,
|
|
14706
|
+
` if (typeof parsed === 'string') {`,
|
|
14707
|
+
` const nested = JSON.parse(parsed);`,
|
|
14708
|
+
` if (__dlRecord(nested)) return nested;`,
|
|
14709
|
+
` }`,
|
|
14710
|
+
` } catch {}`,
|
|
14711
|
+
` }`,
|
|
14712
|
+
` return null;`,
|
|
14713
|
+
`}`,
|
|
14714
|
+
``,
|
|
14715
|
+
`function __dlMergeMetadata(existing: unknown, patch: Record<string, unknown>): Record<string, unknown> {`,
|
|
14716
|
+
` const base = __dlParseMetadata(existing) ?? {};`,
|
|
14717
|
+
` const baseColumns = __dlRecord(base.columns) ? base.columns : {};`,
|
|
14718
|
+
` const patchColumns = __dlRecord(patch.columns) ? patch.columns : {};`,
|
|
14719
|
+
` return {`,
|
|
14720
|
+
` ...base,`,
|
|
14721
|
+
` ...patch,`,
|
|
14722
|
+
` columns: {`,
|
|
14723
|
+
` ...baseColumns,`,
|
|
14724
|
+
` ...patchColumns,`,
|
|
14725
|
+
` },`,
|
|
14726
|
+
` };`,
|
|
14727
|
+
`}`,
|
|
14728
|
+
``,
|
|
14729
|
+
`function __dlPathParts(path: string): string[] {`,
|
|
14730
|
+
` const source = String(path || '');`,
|
|
14731
|
+
` const parts: string[] = [];`,
|
|
14732
|
+
` let current = '';`,
|
|
14733
|
+
` for (let index = 0; index < source.length; index += 1) {`,
|
|
14734
|
+
` const char = source.slice(index, index + 1);`,
|
|
14735
|
+
` if (char === '.' || char === '[' || char === ']') {`,
|
|
14736
|
+
` const trimmed = current.trim();`,
|
|
14737
|
+
` if (trimmed) parts.push(trimmed);`,
|
|
14738
|
+
` current = '';`,
|
|
14739
|
+
` continue;`,
|
|
14740
|
+
` }`,
|
|
14741
|
+
` current += char;`,
|
|
14742
|
+
` }`,
|
|
14743
|
+
` const trimmed = current.trim();`,
|
|
14744
|
+
` if (trimmed) parts.push(trimmed);`,
|
|
14745
|
+
` return parts;`,
|
|
14746
|
+
`}`,
|
|
14747
|
+
``,
|
|
14748
|
+
`function __dlGetByPath(root: unknown, path: string): unknown {`,
|
|
14749
|
+
` let cursor = root;`,
|
|
14750
|
+
` for (const part of __dlPathParts(path)) {`,
|
|
14751
|
+
` if (!cursor || typeof cursor !== 'object') return undefined;`,
|
|
14752
|
+
` const record = cursor as Record<string, unknown>;`,
|
|
14753
|
+
` if (part in record) {`,
|
|
14754
|
+
` cursor = record[part];`,
|
|
14755
|
+
` continue;`,
|
|
14756
|
+
` }`,
|
|
14757
|
+
` const data = record.data;`,
|
|
14758
|
+
` cursor = data && typeof data === 'object' ? (data as Record<string, unknown>)[part] : undefined;`,
|
|
14759
|
+
` }`,
|
|
14760
|
+
` return cursor;`,
|
|
14761
|
+
`}`,
|
|
14762
|
+
``,
|
|
14763
|
+
`function __dlMeaningful(value: unknown): boolean {`,
|
|
14764
|
+
` if (value && typeof value === 'object' && !Array.isArray(value)) {`,
|
|
14765
|
+
` const record = value as Record<string, unknown>;`,
|
|
14766
|
+
` const status = typeof record.status === 'string' ? record.status.toLowerCase() : '';`,
|
|
14767
|
+
` if (status === 'error' || status === 'failed') return false;`,
|
|
14768
|
+
` if (typeof record.error === 'string' && record.error.trim()) return false;`,
|
|
14769
|
+
` const result = record.result;`,
|
|
14770
|
+
` if (result && typeof result === 'object' && !Array.isArray(result)) {`,
|
|
14771
|
+
` const resultRecord = result as Record<string, unknown>;`,
|
|
14772
|
+
` if (typeof resultRecord.error === 'string' && resultRecord.error.trim()) return false;`,
|
|
14773
|
+
` if (typeof resultRecord.message === 'string' && resultRecord.message.trim()) return false;`,
|
|
14774
|
+
` }`,
|
|
14775
|
+
` if ('matched_result' in record) return __dlMeaningful(record.matched_result);`,
|
|
14776
|
+
` }`,
|
|
14777
|
+
` return value !== null && value !== undefined && !(typeof value === 'string' && value.trim() === '') && !(Array.isArray(value) && value.length === 0);`,
|
|
14778
|
+
`}`,
|
|
14779
|
+
``,
|
|
14780
|
+
`function __dlErrorPayload(value: unknown): boolean {`,
|
|
14781
|
+
` if (!value || typeof value !== 'object' || Array.isArray(value)) return false;`,
|
|
14782
|
+
` const record = value as Record<string, unknown>;`,
|
|
14783
|
+
` const status = typeof record.status === 'string' ? record.status.toLowerCase() : '';`,
|
|
14784
|
+
` const result = record.result;`,
|
|
14785
|
+
` const resultError = result && typeof result === 'object' && !Array.isArray(result) ? (result as Record<string, unknown>).error : null;`,
|
|
14786
|
+
` return status === 'error' || status === 'failed' || (typeof record.error === 'string' && record.error.trim() !== '') || (typeof resultError === 'string' && resultError.trim() !== '');`,
|
|
14787
|
+
`}`,
|
|
14788
|
+
``,
|
|
14789
|
+
`function __dlRawToolOutput(result: unknown): unknown {`,
|
|
14790
|
+
` if (!result || typeof result !== 'object') return result;`,
|
|
14791
|
+
` const record = result as Record<string, unknown>;`,
|
|
14792
|
+
` return __dlGetByPath(record, 'toolOutput.raw') ?? __dlGetByPath(record, 'toolResponse.raw') ?? record.result ?? record.output ?? result;`,
|
|
14793
|
+
`}`,
|
|
14794
|
+
``,
|
|
14795
|
+
`function __dlPushCandidate(candidates: unknown[], value: unknown): void {`,
|
|
14796
|
+
` if (value === null || value === undefined) return;`,
|
|
14797
|
+
` if (!candidates.includes(value)) candidates.push(value);`,
|
|
14798
|
+
`}`,
|
|
14799
|
+
``,
|
|
14800
|
+
`function __dlRawToolCandidates(raw: unknown): unknown[] {`,
|
|
14801
|
+
` const candidates: unknown[] = [raw];`,
|
|
14802
|
+
` if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return candidates;`,
|
|
14803
|
+
` const record = raw as Record<string, unknown>;`,
|
|
14804
|
+
` __dlPushCandidate(candidates, record.data);`,
|
|
14805
|
+
` __dlPushCandidate(candidates, record.result);`,
|
|
14806
|
+
` __dlPushCandidate(candidates, record.output);`,
|
|
14807
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'result.data'));`,
|
|
14808
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'output.body'));`,
|
|
14809
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'toolResponse.raw'));`,
|
|
14810
|
+
` __dlPushCandidate(candidates, __dlGetByPath(record, 'toolOutput.raw'));`,
|
|
14811
|
+
` return candidates;`,
|
|
14812
|
+
`}`,
|
|
14813
|
+
``,
|
|
14814
|
+
`function __dlLegacyResultData(value: unknown): unknown {`,
|
|
14815
|
+
` if (!value || typeof value !== 'object' || Array.isArray(value)) return value;`,
|
|
14816
|
+
` const record = value as Record<string, unknown>;`,
|
|
14817
|
+
` return 'data' in record ? record.data : value;`,
|
|
14818
|
+
`}`,
|
|
14819
|
+
``,
|
|
14820
|
+
`function __dlLegacyOutputData(result: unknown, raw: unknown): unknown {`,
|
|
14821
|
+
` const rawRecord = raw && typeof raw === 'object' && !Array.isArray(raw) ? (raw as Record<string, unknown>) : null;`,
|
|
14822
|
+
` const data = rawRecord && 'data' in rawRecord ? rawRecord.data : raw;`,
|
|
14823
|
+
` const existingResult = rawRecord && rawRecord.result && typeof rawRecord.result === 'object' && !Array.isArray(rawRecord.result) ? (rawRecord.result as Record<string, unknown>) : null;`,
|
|
14824
|
+
` const resultData = existingResult && 'data' in existingResult ? __dlLegacyResultData(existingResult.data) : __dlLegacyResultData(data);`,
|
|
14825
|
+
` const resultObject = resultData && typeof resultData === 'object' && !Array.isArray(resultData) ? (resultData as Record<string, unknown>) : {};`,
|
|
14826
|
+
` return {`,
|
|
14827
|
+
` ...(rawRecord ?? {}),`,
|
|
14828
|
+
` data,`,
|
|
14829
|
+
` result: { ...resultObject, ...(existingResult ?? {}), data: resultData },`,
|
|
14830
|
+
` raw,`,
|
|
14831
|
+
` toolResponse: { raw },`,
|
|
14832
|
+
` originalResult: result,`,
|
|
14833
|
+
` };`,
|
|
14834
|
+
`}`,
|
|
14835
|
+
``,
|
|
14836
|
+
`function __dlTemplate(value: unknown, row: Record<string, unknown>): unknown {`,
|
|
14837
|
+
` if (Array.isArray(value)) return value.map((entry) => __dlTemplate(entry, row));`,
|
|
14838
|
+
` if (value && typeof value === 'object') {`,
|
|
14839
|
+
` return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([key, entry]) => [key, __dlTemplate(entry, row)]));`,
|
|
14840
|
+
` }`,
|
|
14841
|
+
` if (typeof value !== 'string') return value;`,
|
|
14842
|
+
` const exact = value.match(/^\\{\\{\\s*([^{}]+?)\\s*\\}\\}$/);`,
|
|
14843
|
+
` if (exact) return __dlGetByPath(row, exact[1] || '');`,
|
|
14844
|
+
` let rendered = '';`,
|
|
14845
|
+
` let cursor = 0;`,
|
|
14846
|
+
` while (cursor < value.length) {`,
|
|
14847
|
+
` const open = value.indexOf('{{', cursor);`,
|
|
14848
|
+
` if (open < 0) {`,
|
|
14849
|
+
` rendered += value.slice(cursor);`,
|
|
14850
|
+
` break;`,
|
|
14851
|
+
` }`,
|
|
14852
|
+
` const close = value.indexOf('}}', open + 2);`,
|
|
14853
|
+
` if (close < 0) {`,
|
|
14854
|
+
` rendered += value.slice(cursor);`,
|
|
14855
|
+
` break;`,
|
|
14856
|
+
` }`,
|
|
14857
|
+
` rendered += value.slice(cursor, open);`,
|
|
14858
|
+
` const path = value.slice(open + 2, close).trim();`,
|
|
14859
|
+
` const replacement = __dlGetByPath(row, path);`,
|
|
14860
|
+
` rendered += replacement === null || replacement === undefined ? '' : String(replacement);`,
|
|
14861
|
+
` cursor = close + 2;`,
|
|
14862
|
+
` }`,
|
|
14863
|
+
` return rendered;`,
|
|
14864
|
+
`}`,
|
|
14865
|
+
``,
|
|
14866
|
+
`function __dlStableRowKey(row: Record<string, unknown>, index: number): string {`,
|
|
14867
|
+
` for (const key of ['id', 'ID', 'row_key', 'ROW_KEY', 'email', 'Email', 'linkedin_url', 'LINKEDIN_URL', 'domain', 'DOMAIN', 'name', 'Name']) {`,
|
|
14868
|
+
` const value = row[key];`,
|
|
14869
|
+
` if (__dlMeaningful(value)) return String(value);`,
|
|
14241
14870
|
` }`,
|
|
14242
14871
|
` return String(index);`,
|
|
14243
14872
|
`}`,
|
|
14244
14873
|
``,
|
|
14874
|
+
`function __dlScalarValue(value: unknown): unknown {`,
|
|
14875
|
+
` if (value && typeof value === 'object' && !Array.isArray(value)) {`,
|
|
14876
|
+
` const record = value as Record<string, unknown>;`,
|
|
14877
|
+
` if ('matched_result' in record) return __dlScalarValue(record.matched_result);`,
|
|
14878
|
+
` for (const key of ['value', 'email', 'output', 'result', 'data']) {`,
|
|
14879
|
+
` const nested = record[key];`,
|
|
14880
|
+
` if (__dlMeaningful(nested)) return __dlScalarValue(nested);`,
|
|
14881
|
+
` }`,
|
|
14882
|
+
` }`,
|
|
14883
|
+
` return value;`,
|
|
14884
|
+
`}`,
|
|
14885
|
+
``,
|
|
14245
14886
|
`function __dlFirstMeaningful(row: Record<string, unknown>, aliases: string[]): unknown {`,
|
|
14246
14887
|
` for (const alias of aliases) {`,
|
|
14247
14888
|
` const value = row[alias];`,
|
|
14248
|
-
` if (__dlMeaningful(value)) return value;`,
|
|
14889
|
+
` if (__dlMeaningful(value)) return __dlScalarValue(value);`,
|
|
14890
|
+
` }`,
|
|
14891
|
+
` return null;`,
|
|
14892
|
+
`}`,
|
|
14893
|
+
``,
|
|
14894
|
+
`function __dlFirstMeaningfulAlias(row: Record<string, unknown>, aliases: string[]): string | null {`,
|
|
14895
|
+
` for (const alias of aliases) {`,
|
|
14896
|
+
` if (__dlMeaningful(row[alias])) return alias;`,
|
|
14249
14897
|
` }`,
|
|
14250
14898
|
` return null;`,
|
|
14251
14899
|
`}`,
|
|
14252
14900
|
``,
|
|
14901
|
+
`function __dlListValue(value: unknown): unknown[] {`,
|
|
14902
|
+
` if (Array.isArray(value)) return value.filter(__dlMeaningful);`,
|
|
14903
|
+
` if (value && typeof value === 'object') {`,
|
|
14904
|
+
` const record = value as Record<string, unknown>;`,
|
|
14905
|
+
` if ('matched_result' in record) return __dlListValue(record.matched_result);`,
|
|
14906
|
+
` for (const key of ['result', 'value', 'data']) {`,
|
|
14907
|
+
` const nested = record[key];`,
|
|
14908
|
+
` const nestedList = __dlListValue(nested);`,
|
|
14909
|
+
` if (nestedList.length > 0) return nestedList;`,
|
|
14910
|
+
` }`,
|
|
14911
|
+
` return __dlMeaningful(value) ? [value] : [];`,
|
|
14912
|
+
` }`,
|
|
14913
|
+
` return __dlMeaningful(value) ? [value] : [];`,
|
|
14914
|
+
`}`,
|
|
14915
|
+
``,
|
|
14253
14916
|
`function __dlFirstMinResults(row: Record<string, unknown>, aliases: string[], minResults: number): unknown {`,
|
|
14254
14917
|
` const values: unknown[] = [];`,
|
|
14255
14918
|
` for (const alias of aliases) {`,
|
|
14256
|
-
`
|
|
14257
|
-
` if (
|
|
14258
|
-
` else if (__dlMeaningful(value)) values.push(value);`,
|
|
14259
|
-
` if (values.length >= minResults) return values.slice(0, minResults);`,
|
|
14919
|
+
` values.push(...__dlListValue(row[alias]));`,
|
|
14920
|
+
` if (values.length >= minResults) return values;`,
|
|
14260
14921
|
` }`,
|
|
14261
14922
|
` return values.length > 0 ? values : null;`,
|
|
14262
14923
|
`}`,
|
|
14263
14924
|
``,
|
|
14925
|
+
`function __dlContributingAliases(row: Record<string, unknown>, aliases: string[]): string[] | null {`,
|
|
14926
|
+
` const sources: string[] = [];`,
|
|
14927
|
+
` for (const alias of aliases) {`,
|
|
14928
|
+
` if (__dlListValue(row[alias]).length > 0) sources.push(alias);`,
|
|
14929
|
+
` }`,
|
|
14930
|
+
` return sources.length > 0 ? sources : null;`,
|
|
14931
|
+
`}`,
|
|
14932
|
+
``,
|
|
14264
14933
|
`function __dlWaterfallSatisfied(row: Record<string, unknown>, aliases: string[], minResults: number): boolean {`,
|
|
14265
14934
|
` let count = 0;`,
|
|
14266
14935
|
` for (const alias of aliases) {`,
|
|
14267
|
-
`
|
|
14268
|
-
` if (
|
|
14269
|
-
` else if (__dlMeaningful(value)) count += 1;`,
|
|
14270
|
-
` if (count >= Math.max(1, Math.trunc(minResults))) return true;`,
|
|
14936
|
+
` count += __dlListValue(row[alias]).length;`,
|
|
14937
|
+
` if (count >= __dlAtLeastOneInteger(minResults)) return true;`,
|
|
14271
14938
|
` }`,
|
|
14272
14939
|
` return false;`,
|
|
14273
14940
|
`}`,
|
|
14274
14941
|
``,
|
|
14275
|
-
`function
|
|
14942
|
+
`function __dlKeyPaths(key: string): string[] {`,
|
|
14943
|
+
` const normalized = String(key || '').trim();`,
|
|
14944
|
+
` if (normalized === 'email') return ['email', 'email_address', 'person.email', 'contact.email', 'data.email', 'result.data.email'];`,
|
|
14945
|
+
` if (normalized === 'personal_email') return ['personal_email', 'email', 'email_address', 'data.personal_email', 'data.email'];`,
|
|
14946
|
+
` if (normalized === 'phone') return ['phone', 'phone_number', 'mobile_phone', 'mobile_phone_number', 'data.phone'];`,
|
|
14947
|
+
` if (normalized === 'linkedin') return ['linkedin', 'linkedin_url', 'linkedin_profile', 'profile_url', 'person.linkedin', 'person.linkedin_url'];`,
|
|
14948
|
+
` if (normalized === 'full_name') return ['full_name', 'name', 'person.full_name', 'person.name'];`,
|
|
14949
|
+
` if (normalized === 'first_name') return ['first_name', 'person.first_name'];`,
|
|
14950
|
+
` if (normalized === 'last_name') return ['last_name', 'person.last_name'];`,
|
|
14951
|
+
` if (normalized === 'title') return ['title', 'job_title', 'current_title', 'headline', 'person.title'];`,
|
|
14952
|
+
` if (normalized === 'company_name') return ['company_name', 'company.name', 'organization.name'];`,
|
|
14953
|
+
` if (normalized === 'company_domain') return ['company_domain', 'domain', 'company.domain', 'organization.domain'];`,
|
|
14954
|
+
` if (normalized === 'status') return ['status', 'verdict', 'state'];`,
|
|
14955
|
+
` if (normalized === 'email_status') return ['email_status', 'status', 'verdict', 'data.email_status'];`,
|
|
14956
|
+
` return [normalized];`,
|
|
14957
|
+
`}`,
|
|
14958
|
+
``,
|
|
14959
|
+
`function __dlSelectorKeys(selector: unknown): string[] | null {`,
|
|
14960
|
+
` if (!selector || typeof selector !== 'object' || Array.isArray(selector)) return null;`,
|
|
14961
|
+
` const keys = (selector as { keys?: unknown }).keys;`,
|
|
14962
|
+
` if (!Array.isArray(keys) || keys.length === 0) return null;`,
|
|
14963
|
+
` const out: string[] = [];`,
|
|
14964
|
+
` for (const key of keys) {`,
|
|
14965
|
+
` if (typeof key === 'string' && key.trim()) out.push(key.trim());`,
|
|
14966
|
+
` }`,
|
|
14967
|
+
` return out.length > 0 ? out : null;`,
|
|
14968
|
+
`}`,
|
|
14969
|
+
``,
|
|
14970
|
+
`function __dlFirstByPaths(payload: unknown, paths: string[] | string): unknown {`,
|
|
14971
|
+
` const candidates = Array.isArray(paths) ? paths : [paths];`,
|
|
14972
|
+
` for (const path of candidates) {`,
|
|
14973
|
+
` for (const candidate of __dlRawToolCandidates(payload)) {`,
|
|
14974
|
+
` const value = __dlGetByPath(candidate, String(path));`,
|
|
14975
|
+
` if (__dlMeaningful(value)) return value;`,
|
|
14976
|
+
` }`,
|
|
14977
|
+
` }`,
|
|
14978
|
+
` return null;`,
|
|
14979
|
+
`}`,
|
|
14980
|
+
``,
|
|
14981
|
+
`function __dlExtractTarget(payload: unknown, key: string): unknown {`,
|
|
14982
|
+
` return __dlFirstByPaths(payload, __dlKeyPaths(key));`,
|
|
14983
|
+
`}`,
|
|
14984
|
+
``,
|
|
14985
|
+
`function __dlExtractedValue(result: unknown, key: string): unknown {`,
|
|
14986
|
+
` if (!result || typeof result !== 'object' || Array.isArray(result)) return undefined;`,
|
|
14987
|
+
` const extractedValues = (result as Record<string, unknown>).extractedValues;`,
|
|
14988
|
+
` if (!extractedValues || typeof extractedValues !== 'object' || Array.isArray(extractedValues)) return undefined;`,
|
|
14989
|
+
` const accessor = (extractedValues as Record<string, unknown>)[key];`,
|
|
14990
|
+
` if (!accessor || typeof accessor !== 'object' || Array.isArray(accessor)) return undefined;`,
|
|
14991
|
+
` const record = accessor as Record<string, unknown>;`,
|
|
14992
|
+
` const get = record.get;`,
|
|
14993
|
+
` if (typeof get === 'function') {`,
|
|
14994
|
+
` try {`,
|
|
14995
|
+
` return (get as () => unknown)();`,
|
|
14996
|
+
` } catch {`,
|
|
14997
|
+
` return undefined;`,
|
|
14998
|
+
` }`,
|
|
14999
|
+
` }`,
|
|
15000
|
+
` return record.value;`,
|
|
15001
|
+
`}`,
|
|
15002
|
+
``,
|
|
15003
|
+
`function __dlLegacyExtractorPayload(payload: unknown, raw: unknown): unknown {`,
|
|
15004
|
+
` if (payload === undefined || payload === null) return raw;`,
|
|
15005
|
+
` if (payload && typeof payload === 'object' && !Array.isArray(payload) && Object.keys(payload as Record<string, unknown>).length === 0) return raw;`,
|
|
15006
|
+
` return payload;`,
|
|
15007
|
+
`}`,
|
|
15008
|
+
``,
|
|
15009
|
+
`function __dlLegacyMatchedEnvelope(value: unknown, raw: unknown): unknown {`,
|
|
15010
|
+
` if (value && typeof value === 'object' && !Array.isArray(value) && 'matched_result' in (value as Record<string, unknown>)) return value;`,
|
|
15011
|
+
` const legacyOutput = __dlLegacyOutputData(undefined, raw) as Record<string, unknown>;`,
|
|
15012
|
+
` const legacyResult = legacyOutput.result && typeof legacyOutput.result === 'object' && !Array.isArray(legacyOutput.result) ? legacyOutput.result : raw;`,
|
|
15013
|
+
` return { matched_result: value, result: legacyResult };`,
|
|
15014
|
+
`}`,
|
|
15015
|
+
``,
|
|
15016
|
+
`function __dlString(value: unknown): string | null {`,
|
|
15017
|
+
` return typeof value === 'string' && value.trim() ? value.trim() : null;`,
|
|
15018
|
+
`}`,
|
|
15019
|
+
``,
|
|
15020
|
+
`// NOTE: email_status is NOT normalized here. It is materialized once by the`,
|
|
15021
|
+
`// provider's emailStatus({...}) contract via buildEmailStatus and read back`,
|
|
15022
|
+
`// through __dlExtractedValue(result, 'email_status') below. There is no`,
|
|
15023
|
+
`// legacy string coarsening \u2014 see CONTEXT.md "Provider Email Status Contract".`,
|
|
15024
|
+
``,
|
|
15025
|
+
`function __dlListPathCandidates(selector: unknown): string[] {`,
|
|
15026
|
+
` const paths: string[] = [];`,
|
|
15027
|
+
` const push = (path: string) => { if (path && !paths.includes(path)) paths.push(path); };`,
|
|
15028
|
+
` if (Array.isArray(selector)) {`,
|
|
15029
|
+
` for (const path of selector) push(String(path));`,
|
|
15030
|
+
` } else if (typeof selector === 'string') {`,
|
|
15031
|
+
` push(selector);`,
|
|
15032
|
+
` }`,
|
|
15033
|
+
` for (const path of ['data', 'data.people', 'data.persons', 'data.contacts', 'data.results', 'data.leads', 'data.items', 'result.data', 'result.people', 'result.persons', 'result.contacts', 'result.results', 'result.leads', 'result.items', 'people', 'persons', 'contacts', 'results', 'leads', 'items', 'output.body', 'body']) push(path);`,
|
|
15034
|
+
` return paths;`,
|
|
15035
|
+
`}`,
|
|
15036
|
+
``,
|
|
15037
|
+
`function __dlFindList(payload: unknown, selector: unknown): unknown[] {`,
|
|
15038
|
+
` if (Array.isArray(payload)) return payload;`,
|
|
15039
|
+
` for (const path of __dlListPathCandidates(selector)) {`,
|
|
15040
|
+
` for (const candidate of __dlRawToolCandidates(payload)) {`,
|
|
15041
|
+
` const value = __dlGetByPath(candidate, path);`,
|
|
15042
|
+
` if (Array.isArray(value)) return value;`,
|
|
15043
|
+
` }`,
|
|
15044
|
+
` }`,
|
|
15045
|
+
` const queue = __dlRawToolCandidates(payload);`,
|
|
15046
|
+
` const seen: unknown[] = [];`,
|
|
15047
|
+
` for (let index = 0; index < queue.length; index += 1) {`,
|
|
15048
|
+
` const current = queue[index];`,
|
|
15049
|
+
` if (!current || typeof current !== 'object' || seen.includes(current)) continue;`,
|
|
15050
|
+
` seen.push(current);`,
|
|
15051
|
+
` for (const value of Object.values(current as Record<string, unknown>)) {`,
|
|
15052
|
+
` if (Array.isArray(value)) return value;`,
|
|
15053
|
+
` if (value && typeof value === 'object') queue.push(value);`,
|
|
15054
|
+
` }`,
|
|
15055
|
+
` }`,
|
|
15056
|
+
` return [];`,
|
|
15057
|
+
`}`,
|
|
15058
|
+
``,
|
|
15059
|
+
`function __dlDeriveFullName(row: Record<string, unknown>): string | null {`,
|
|
15060
|
+
` const existing = __dlFirstByPaths(row, ['full_name', 'name']);`,
|
|
15061
|
+
` if (typeof existing === 'string' && existing.trim()) return existing.trim();`,
|
|
15062
|
+
` const first = __dlFirstByPaths(row, ['first_name', 'firstName']);`,
|
|
15063
|
+
` const last = __dlFirstByPaths(row, ['last_name', 'lastName']);`,
|
|
15064
|
+
` const parts = [first, last].filter((part): part is string => typeof part === 'string').map((part) => part.trim()).filter(Boolean);`,
|
|
15065
|
+
` return parts.length > 0 ? parts.join(' ') : null;`,
|
|
15066
|
+
`}`,
|
|
15067
|
+
``,
|
|
15068
|
+
`function __dlProjectListRows(rows: unknown[], keys: string[], payload: unknown): Array<Record<string, unknown>> {`,
|
|
15069
|
+
` const projected: Array<Record<string, unknown>> = [];`,
|
|
15070
|
+
` for (const row of rows) {`,
|
|
15071
|
+
` const record = row && typeof row === 'object' && !Array.isArray(row) ? (row as Record<string, unknown>) : { value: row };`,
|
|
15072
|
+
` const out: Record<string, unknown> = {};`,
|
|
15073
|
+
` for (const key of keys) {`,
|
|
15074
|
+
` let value = __dlExtractTarget(record, key);`,
|
|
15075
|
+
` if (!__dlMeaningful(value) && key === 'full_name') value = __dlDeriveFullName(record);`,
|
|
15076
|
+
` if (!__dlMeaningful(value)) value = __dlExtractTarget(payload, key);`,
|
|
15077
|
+
` out[key] = value === undefined ? null : value;`,
|
|
15078
|
+
` }`,
|
|
15079
|
+
` if (Object.values(out).some(__dlMeaningful)) projected.push(out);`,
|
|
15080
|
+
` }`,
|
|
15081
|
+
` return projected;`,
|
|
15082
|
+
`}`,
|
|
15083
|
+
``,
|
|
15084
|
+
`type __DlExtractorHelpers = { row: Record<string, unknown>; result: unknown; data: unknown; raw: unknown; pick: (paths: string[] | string) => unknown; extract: (...args: unknown[]) => unknown; extractList: (...args: unknown[]) => unknown[]; target: (paths: string[] | string) => unknown; get: (path: string) => unknown };`,
|
|
15085
|
+
``,
|
|
15086
|
+
`function __dlErrorMessage(error: unknown): string {`,
|
|
15087
|
+
` if (error instanceof Error && error.message) return error.message;`,
|
|
15088
|
+
` if (typeof error === 'string' && error.trim()) return error.trim();`,
|
|
15089
|
+
` return 'Extractor failed';`,
|
|
15090
|
+
`}`,
|
|
15091
|
+
``,
|
|
15092
|
+
`function __dlExtractorFailure(error: unknown): unknown {`,
|
|
15093
|
+
` const message = __dlErrorMessage(error);`,
|
|
15094
|
+
` return { matched_result: null, result: { error: message, message } };`,
|
|
15095
|
+
`}`,
|
|
15096
|
+
``,
|
|
15097
|
+
`function __dlExtract(alias: string, result: unknown, row: Record<string, unknown>, extractor: ((args: __DlExtractorHelpers) => unknown) | null, legacyEnvelope = false): unknown {`,
|
|
14276
15098
|
` const raw = __dlRawToolOutput(result);`,
|
|
14277
15099
|
` if (!extractor) return raw;`,
|
|
14278
15100
|
` const pick = (paths: string[] | string) => {`,
|
|
14279
15101
|
` const candidates = Array.isArray(paths) ? paths : [paths];`,
|
|
14280
15102
|
` for (const path of candidates) {`,
|
|
14281
|
-
`
|
|
14282
|
-
`
|
|
15103
|
+
` if (typeof path === 'string') {`,
|
|
15104
|
+
` const extractedValue = __dlExtractedValue(result, path);`,
|
|
15105
|
+
` if (__dlMeaningful(extractedValue)) return extractedValue;`,
|
|
15106
|
+
` }`,
|
|
15107
|
+
` for (const candidate of __dlRawToolCandidates(raw)) {`,
|
|
15108
|
+
` const value = __dlGetByPath(candidate, String(path));`,
|
|
15109
|
+
` if (__dlMeaningful(value)) return value;`,
|
|
15110
|
+
` }`,
|
|
14283
15111
|
` }`,
|
|
14284
15112
|
` return null;`,
|
|
14285
15113
|
` };`,
|
|
14286
|
-
` const
|
|
14287
|
-
`
|
|
14288
|
-
`
|
|
15114
|
+
` const extract = (...args: unknown[]): unknown => {`,
|
|
15115
|
+
` const payload = args.length >= 3 ? __dlLegacyExtractorPayload(args[1], raw) : raw;`,
|
|
15116
|
+
` const selector = args.length >= 3 ? args[2] : args[0];`,
|
|
15117
|
+
` if (selector === undefined) return payload;`,
|
|
15118
|
+
` const keys = __dlSelectorKeys(selector);`,
|
|
15119
|
+
` if (keys) return Object.fromEntries(keys.map((key) => [key, __dlExtractTarget(payload, key) ?? null]));`,
|
|
15120
|
+
` if (Array.isArray(selector)) return __dlFirstByPaths(payload, selector.map(String));`,
|
|
15121
|
+
` if (typeof selector === 'string') {`,
|
|
15122
|
+
` const extractedValue = __dlExtractedValue(result, selector);`,
|
|
15123
|
+
` if (__dlMeaningful(extractedValue)) return __dlLegacyMatchedEnvelope(extractedValue, payload);`,
|
|
15124
|
+
` return __dlExtractTarget(payload, selector) ?? __dlFirstByPaths(payload, selector);`,
|
|
15125
|
+
` }`,
|
|
15126
|
+
` return selector;`,
|
|
15127
|
+
` };`,
|
|
15128
|
+
` const extractList = (...args: unknown[]): unknown[] => {`,
|
|
15129
|
+
` const payload = args.length >= 3 ? __dlLegacyExtractorPayload(args[1], raw) : raw;`,
|
|
15130
|
+
` const selector = args.length >= 3 ? args[2] : args[0];`,
|
|
15131
|
+
` const rows = __dlFindList(payload, selector);`,
|
|
15132
|
+
` const keys = __dlSelectorKeys(selector);`,
|
|
15133
|
+
` return keys ? __dlProjectListRows(rows, keys, payload) : rows;`,
|
|
15134
|
+
` };`,
|
|
15135
|
+
` const get = (path: string): unknown => __dlExtractedValue(result, path) ?? __dlFirstByPaths(raw, path);`,
|
|
15136
|
+
` let resolved: unknown;`,
|
|
15137
|
+
` try {`,
|
|
15138
|
+
` const extracted = extractor({ row, result, data: raw, raw, pick, extract, extractList, target: pick, get });`,
|
|
15139
|
+
` resolved = typeof extracted === 'function' ? (extracted as (outputData: unknown) => unknown)(__dlLegacyOutputData(result, raw)) : extracted;`,
|
|
15140
|
+
` } catch (error) {`,
|
|
15141
|
+
` return __dlExtractorFailure(error);`,
|
|
15142
|
+
` }`,
|
|
15143
|
+
` if (resolved && typeof resolved === 'object' && !Array.isArray(resolved) && alias in (resolved as Record<string, unknown>)) {`,
|
|
15144
|
+
` const aliasValue = (resolved as Record<string, unknown>)[alias];`,
|
|
15145
|
+
` return legacyEnvelope && __dlMeaningful(aliasValue) ? __dlLegacyMatchedEnvelope(aliasValue, raw) : aliasValue;`,
|
|
15146
|
+
` }`,
|
|
15147
|
+
` if (Array.isArray(resolved)) return __dlLegacyMatchedEnvelope(resolved, raw);`,
|
|
15148
|
+
` if ((resolved === null || resolved === undefined) && __dlErrorPayload(raw)) return raw;`,
|
|
15149
|
+
` if (legacyEnvelope && __dlMeaningful(resolved)) return __dlLegacyMatchedEnvelope(resolved, raw);`,
|
|
15150
|
+
` return resolved === undefined ? raw : resolved;`,
|
|
15151
|
+
`}`,
|
|
15152
|
+
``,
|
|
15153
|
+
`function __dlMergeContextRecord(existing: unknown, defaults: Record<string, unknown>): Record<string, unknown> {`,
|
|
15154
|
+
` if (!existing || typeof existing !== 'object' || Array.isArray(existing)) return { ...defaults };`,
|
|
15155
|
+
` return { ...defaults, ...(existing as Record<string, unknown>) };`,
|
|
15156
|
+
`}`,
|
|
15157
|
+
``,
|
|
15158
|
+
`function __dlRuntimePayload(tool: string, payload: Record<string, unknown>, row: Record<string, unknown>): Record<string, unknown> {`,
|
|
15159
|
+
` if (tool !== 'run_javascript') return payload;`,
|
|
15160
|
+
` return {`,
|
|
15161
|
+
` ...payload,`,
|
|
15162
|
+
` row: __dlMergeContextRecord(payload.row, row),`,
|
|
15163
|
+
` input: __dlMergeContextRecord(payload.input, row),`,
|
|
15164
|
+
` context: __dlMergeContextRecord(payload.context, row),`,
|
|
15165
|
+
` };`,
|
|
15166
|
+
`}`,
|
|
15167
|
+
``,
|
|
15168
|
+
`function __dlAliasCandidates(alias: string): string[] {`,
|
|
15169
|
+
` const aliases: string[] = [];`,
|
|
15170
|
+
` for (const candidate of [alias, alias.replace(/-/g, '_'), alias.replace(/_/g, '-')]) {`,
|
|
15171
|
+
` if (candidate && !aliases.includes(candidate)) aliases.push(candidate);`,
|
|
15172
|
+
` }`,
|
|
15173
|
+
` return aliases;`,
|
|
15174
|
+
`}`,
|
|
15175
|
+
``,
|
|
15176
|
+
`function __dlPlayResultValue(alias: string, result: unknown): unknown {`,
|
|
15177
|
+
` if (!result || typeof result !== 'object' || Array.isArray(result)) return result;`,
|
|
15178
|
+
` const record = result as Record<string, unknown>;`,
|
|
15179
|
+
` const aliases = __dlAliasCandidates(alias);`,
|
|
15180
|
+
` for (const key of aliases) {`,
|
|
15181
|
+
` if (key in record && __dlMeaningful(record[key])) return __dlScalarValue(record[key]);`,
|
|
14289
15182
|
` }`,
|
|
14290
|
-
`
|
|
15183
|
+
` const values = Object.values(record).filter(__dlMeaningful);`,
|
|
15184
|
+
` if (values.length === 1) return __dlScalarValue(values[0]);`,
|
|
15185
|
+
` return result;`,
|
|
14291
15186
|
`}`,
|
|
14292
15187
|
``,
|
|
14293
|
-
`async function __dlRunCommand(input: { alias: string; callId: string; tool: string; payload: Record<string, unknown>; extract: ((args:
|
|
15188
|
+
`async function __dlRunCommand(input: { alias: string; callId: string; tool: string; payload: Record<string, unknown>; extract: ((args: __DlExtractorHelpers) => unknown) | null; runIf: ((row: Record<string, unknown>) => unknown) | null; row: Record<string, unknown>; stepCtx: { tools: { execute: (request: Record<string, unknown>) => Promise<unknown> } }; description?: string; force?: boolean; legacyEnvelope?: boolean }): Promise<unknown> {`,
|
|
14294
15189
|
` if (input.runIf) {`,
|
|
14295
15190
|
` const shouldRun = input.runIf(input.row);`,
|
|
14296
15191
|
` if (!shouldRun) return null;`,
|
|
14297
15192
|
` }`,
|
|
15193
|
+
` const payload = __dlRuntimePayload(input.tool, __dlTemplate(input.payload, input.row) as Record<string, unknown>, input.row);`,
|
|
14298
15194
|
` const result = await input.stepCtx.tools.execute({`,
|
|
14299
15195
|
` id: input.callId,`,
|
|
14300
15196
|
` tool: input.tool,`,
|
|
14301
|
-
` input:
|
|
15197
|
+
` input: payload,`,
|
|
14302
15198
|
` ...(input.description ? { description: input.description } : {}),`,
|
|
14303
|
-
` ...(input.force ? { staleAfterSeconds:
|
|
15199
|
+
` ...(input.force ? { staleAfterSeconds: 1 } : {}),`,
|
|
14304
15200
|
` });`,
|
|
14305
|
-
` return __dlExtract(input.alias, result, input.row, input.extract);`,
|
|
15201
|
+
` return __dlExtract(input.alias, result, input.row, input.extract, Boolean(input.legacyEnvelope));`,
|
|
14306
15202
|
`}`
|
|
14307
15203
|
].join("\n");
|
|
14308
15204
|
}
|
|
14309
15205
|
|
|
14310
15206
|
// src/cli/commands/enrich.ts
|
|
15207
|
+
var ENRICH_EXPORT_PAGE_SIZE = 5e3;
|
|
15208
|
+
var EXIT_SERVER2 = 5;
|
|
15209
|
+
var ENRICH_DEBUG_T0 = Date.now();
|
|
14311
15210
|
var PLAN_SHAPING_OPTION_NAMES = [
|
|
14312
15211
|
"with",
|
|
14313
15212
|
"withWaterfall",
|
|
14314
15213
|
"minResults",
|
|
14315
15214
|
"endWaterfall"
|
|
14316
15215
|
];
|
|
14317
|
-
var ENRICH_DEPRECATION_NOTICE = {
|
|
14318
|
-
status: "deprecated",
|
|
14319
|
-
message: "The enrich compatibility command is deprecated. This run generates a temporary .play.ts file and executes it through plays run.",
|
|
14320
|
-
generatedPlayFile: "Temporary compatibility play file; deleted after the command exits.",
|
|
14321
|
-
printGeneratedPlayCommand: "deepline enrich <same args> --dry-run > enrich.play.ts",
|
|
14322
|
-
recommendedCommand: `deepline plays run enrich.play.ts --input '{"file":"<input.csv>"}'`
|
|
14323
|
-
};
|
|
14324
|
-
var ENRICH_DEPRECATION_TEXT = `${ENRICH_DEPRECATION_NOTICE.message} Print the generated play with: ${ENRICH_DEPRECATION_NOTICE.printGeneratedPlayCommand}. Then run: ${ENRICH_DEPRECATION_NOTICE.recommendedCommand}
|
|
14325
|
-
`;
|
|
14326
15216
|
function optionWasProvided(args, ...flags) {
|
|
14327
15217
|
return args.some(
|
|
14328
15218
|
(arg) => flags.some((flag) => arg === flag || arg.startsWith(`${flag}=`))
|
|
@@ -14334,9 +15224,103 @@ function normalizeAlias2(value) {
|
|
|
14334
15224
|
function hasPlanShapingArgs(args) {
|
|
14335
15225
|
return optionWasProvided(args, "--with") || optionWasProvided(args, "--with-waterfall") || optionWasProvided(args, "--min-results") || optionWasProvided(args, "--end-waterfall");
|
|
14336
15226
|
}
|
|
14337
|
-
function
|
|
14338
|
-
|
|
14339
|
-
|
|
15227
|
+
function enrichDebugEnabled(env = process.env) {
|
|
15228
|
+
const raw = String(env.DEEPLINE_DEBUG_ENRICH ?? "").trim().toLowerCase();
|
|
15229
|
+
if (["1", "true", "yes", "on"].includes(raw)) {
|
|
15230
|
+
return true;
|
|
15231
|
+
}
|
|
15232
|
+
const nodeEnv = String(env.NODE_ENV ?? "").trim().toLowerCase();
|
|
15233
|
+
const deeplineEnv = String(env.DEEPLINE_ENV ?? "").trim().toLowerCase();
|
|
15234
|
+
return nodeEnv === "development" || ["dev", "development"].includes(deeplineEnv);
|
|
15235
|
+
}
|
|
15236
|
+
function decodeStringLiteral(raw) {
|
|
15237
|
+
try {
|
|
15238
|
+
const parsed = JSON.parse(raw);
|
|
15239
|
+
return typeof parsed === "string" ? parsed : null;
|
|
15240
|
+
} catch {
|
|
15241
|
+
const quote = raw[0];
|
|
15242
|
+
if ((quote === "'" || quote === '"') && raw[raw.length - 1] === quote) {
|
|
15243
|
+
return raw.slice(1, -1).replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
15244
|
+
}
|
|
15245
|
+
return null;
|
|
15246
|
+
}
|
|
15247
|
+
}
|
|
15248
|
+
function addStringArrayLiteralTargets(targets, arrayLiteral) {
|
|
15249
|
+
for (const match of arrayLiteral.matchAll(/(["'])(?:\\.|(?!\1).)*\1/g)) {
|
|
15250
|
+
const parsed = decodeStringLiteral(match[0] ?? "");
|
|
15251
|
+
if (parsed?.trim()) {
|
|
15252
|
+
targets.add(parsed.trim());
|
|
15253
|
+
}
|
|
15254
|
+
}
|
|
15255
|
+
}
|
|
15256
|
+
function extractExtractorTargetKeys(source) {
|
|
15257
|
+
const targets = /* @__PURE__ */ new Set();
|
|
15258
|
+
for (const match of source.matchAll(/\bkeys\s*:\s*(\[[^\]]*\])/g)) {
|
|
15259
|
+
addStringArrayLiteralTargets(targets, match[1] ?? "");
|
|
15260
|
+
}
|
|
15261
|
+
for (const match of source.matchAll(
|
|
15262
|
+
/\btarget\s*\(\s*(["'][^"']+["'])\s*\)/g
|
|
15263
|
+
)) {
|
|
15264
|
+
const parsed = decodeStringLiteral(match[1] ?? "");
|
|
15265
|
+
if (parsed?.trim()) {
|
|
15266
|
+
targets.add(parsed.trim());
|
|
15267
|
+
}
|
|
15268
|
+
}
|
|
15269
|
+
for (const match of source.matchAll(
|
|
15270
|
+
/\bextract\s*\(\s*["'][^"']+["']\s*,[^,]*,\s*(["'][^"']+["'])\s*\)/g
|
|
15271
|
+
)) {
|
|
15272
|
+
const parsed = decodeStringLiteral(match[1] ?? "");
|
|
15273
|
+
if (parsed?.trim()) {
|
|
15274
|
+
targets.add(parsed.trim());
|
|
15275
|
+
}
|
|
15276
|
+
}
|
|
15277
|
+
return [...targets].sort();
|
|
15278
|
+
}
|
|
15279
|
+
function flattenEnrichStepCommands(commands) {
|
|
15280
|
+
const steps = [];
|
|
15281
|
+
for (const command of commands) {
|
|
15282
|
+
if ("with_waterfall" in command) {
|
|
15283
|
+
steps.push(...flattenEnrichStepCommands(command.commands));
|
|
15284
|
+
continue;
|
|
15285
|
+
}
|
|
15286
|
+
if (!command.disabled) {
|
|
15287
|
+
steps.push(command);
|
|
15288
|
+
}
|
|
15289
|
+
}
|
|
15290
|
+
return steps;
|
|
15291
|
+
}
|
|
15292
|
+
function buildEnrichDebugValidationLines(config) {
|
|
15293
|
+
return flattenEnrichStepCommands(config.commands).filter(
|
|
15294
|
+
(command) => typeof command.extract_js === "string" && command.extract_js.trim()
|
|
15295
|
+
).map((command) => {
|
|
15296
|
+
const source = command.extract_js ?? "";
|
|
15297
|
+
const targets = extractExtractorTargetKeys(source);
|
|
15298
|
+
const mode = /\bextractList\s*\(/.test(source) ? "list" : "scalar";
|
|
15299
|
+
return [
|
|
15300
|
+
"validate extractor",
|
|
15301
|
+
`column=${command.alias}`,
|
|
15302
|
+
`tool_id=${command.tool || "(none)"}`,
|
|
15303
|
+
`mode=${mode}`,
|
|
15304
|
+
"has_result_sample=false",
|
|
15305
|
+
"sample_keys=<none>",
|
|
15306
|
+
`targets=${JSON.stringify(targets)}`
|
|
15307
|
+
].join(" ");
|
|
15308
|
+
});
|
|
15309
|
+
}
|
|
15310
|
+
function emitEnrichDebug(message) {
|
|
15311
|
+
if (!enrichDebugEnabled()) {
|
|
15312
|
+
return;
|
|
15313
|
+
}
|
|
15314
|
+
const now = /* @__PURE__ */ new Date();
|
|
15315
|
+
const elapsedMs = Date.now() - ENRICH_DEBUG_T0;
|
|
15316
|
+
process.stderr.write(
|
|
15317
|
+
`[deepline:enrich] ${now.toISOString()} +${elapsedMs}ms ${message}
|
|
15318
|
+
`
|
|
15319
|
+
);
|
|
15320
|
+
}
|
|
15321
|
+
function emitEnrichDebugValidationLines(config) {
|
|
15322
|
+
for (const line of buildEnrichDebugValidationLines(config)) {
|
|
15323
|
+
emitEnrichDebug(line);
|
|
14340
15324
|
}
|
|
14341
15325
|
}
|
|
14342
15326
|
function expandAtFilePath(rawPath) {
|
|
@@ -14473,15 +15457,18 @@ async function buildPlanArgs(args) {
|
|
|
14473
15457
|
"--csv",
|
|
14474
15458
|
"--output",
|
|
14475
15459
|
"--config",
|
|
15460
|
+
"--name",
|
|
14476
15461
|
"--rows",
|
|
14477
|
-
"--with-force"
|
|
15462
|
+
"--with-force",
|
|
15463
|
+
"--timeout"
|
|
14478
15464
|
]);
|
|
14479
15465
|
const localBooleanOptions = /* @__PURE__ */ new Set([
|
|
14480
15466
|
"--dry-run",
|
|
14481
15467
|
"--json",
|
|
14482
15468
|
"--force",
|
|
14483
15469
|
"--all",
|
|
14484
|
-
"--in-place"
|
|
15470
|
+
"--in-place",
|
|
15471
|
+
"--no-open"
|
|
14485
15472
|
]);
|
|
14486
15473
|
const planArgs = [];
|
|
14487
15474
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -14547,7 +15534,7 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
|
|
|
14547
15534
|
const output2 = resolve11(outputPath);
|
|
14548
15535
|
if (input2 === output2) {
|
|
14549
15536
|
throw new Error(
|
|
14550
|
-
"--output must be
|
|
15537
|
+
"--input and --output must be different files when not using --in-place."
|
|
14551
15538
|
);
|
|
14552
15539
|
}
|
|
14553
15540
|
try {
|
|
@@ -14557,7 +15544,7 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
|
|
|
14557
15544
|
]);
|
|
14558
15545
|
if (inputInfo.dev === outputInfo.dev && inputInfo.ino === outputInfo.ino) {
|
|
14559
15546
|
throw new Error(
|
|
14560
|
-
"--output must be
|
|
15547
|
+
"--input and --output must be different files when not using --in-place."
|
|
14561
15548
|
);
|
|
14562
15549
|
}
|
|
14563
15550
|
} catch (error) {
|
|
@@ -14571,6 +15558,18 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
|
|
|
14571
15558
|
throw error;
|
|
14572
15559
|
}
|
|
14573
15560
|
}
|
|
15561
|
+
async function regularFileExists(path) {
|
|
15562
|
+
try {
|
|
15563
|
+
const info = await stat3(resolve11(path));
|
|
15564
|
+
return info.isFile();
|
|
15565
|
+
} catch (error) {
|
|
15566
|
+
const code = error && typeof error === "object" ? error.code : void 0;
|
|
15567
|
+
if (code === "ENOENT") {
|
|
15568
|
+
return false;
|
|
15569
|
+
}
|
|
15570
|
+
throw error;
|
|
15571
|
+
}
|
|
15572
|
+
}
|
|
14574
15573
|
async function readConfig(path) {
|
|
14575
15574
|
const source = await readFile3(resolve11(path), "utf8");
|
|
14576
15575
|
let parsed;
|
|
@@ -14593,27 +15592,30 @@ function parseRows(value, all) {
|
|
|
14593
15592
|
const trimmed = value.trim();
|
|
14594
15593
|
const range = trimmed.match(/^(\d*)\s*:\s*(\d*)$/);
|
|
14595
15594
|
if (range) {
|
|
14596
|
-
if (!range[1]
|
|
15595
|
+
if (!range[1]) {
|
|
14597
15596
|
throw new Error(
|
|
14598
|
-
"--rows must be a zero-based row number or
|
|
15597
|
+
"--rows must be a zero-based row number or inclusive range like 0:10."
|
|
14599
15598
|
);
|
|
14600
15599
|
}
|
|
14601
15600
|
const start = range[1] ? Number.parseInt(range[1], 10) : 0;
|
|
14602
15601
|
const end = range[2] ? Number.parseInt(range[2], 10) : null;
|
|
15602
|
+
if (end !== null && end < start) {
|
|
15603
|
+
throw new Error("Invalid --rows range: end must be >= start.");
|
|
15604
|
+
}
|
|
14603
15605
|
return { rowStart: start, rowEnd: end };
|
|
14604
15606
|
}
|
|
14605
15607
|
if (!/^\d+$/.test(trimmed)) {
|
|
14606
15608
|
throw new Error(
|
|
14607
|
-
"--rows must be a zero-based row number or
|
|
15609
|
+
"--rows must be a zero-based row number or inclusive range like 0:10."
|
|
14608
15610
|
);
|
|
14609
15611
|
}
|
|
14610
15612
|
const single = Number.parseInt(trimmed, 10);
|
|
14611
15613
|
if (!Number.isFinite(single) || single < 0) {
|
|
14612
15614
|
throw new Error(
|
|
14613
|
-
"--rows must be a zero-based row number or
|
|
15615
|
+
"--rows must be a zero-based row number or inclusive range like 0:10."
|
|
14614
15616
|
);
|
|
14615
15617
|
}
|
|
14616
|
-
return { rowStart: single, rowEnd: single
|
|
15618
|
+
return { rowStart: single, rowEnd: single };
|
|
14617
15619
|
}
|
|
14618
15620
|
function summarizePlan(config, playSource) {
|
|
14619
15621
|
const steps = [];
|
|
@@ -14692,76 +15694,715 @@ function parseWithForceAliases(values) {
|
|
|
14692
15694
|
}
|
|
14693
15695
|
}
|
|
14694
15696
|
}
|
|
14695
|
-
return aliases;
|
|
15697
|
+
return aliases;
|
|
15698
|
+
}
|
|
15699
|
+
function resolveForceAliases(config, options) {
|
|
15700
|
+
const { allAliases, waterfallGroups } = collectCommandAliases(config);
|
|
15701
|
+
if (options.force) {
|
|
15702
|
+
return new Set(allAliases);
|
|
15703
|
+
}
|
|
15704
|
+
const requested = parseWithForceAliases(options.withForce);
|
|
15705
|
+
const unknown = [...requested].filter((alias) => !allAliases.has(alias));
|
|
15706
|
+
if (unknown.length > 0) {
|
|
15707
|
+
throw new Error(
|
|
15708
|
+
`--with-force references unknown --with column alias(es): ${unknown.sort().join(", ")}.`
|
|
15709
|
+
);
|
|
15710
|
+
}
|
|
15711
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
15712
|
+
for (const alias of requested) {
|
|
15713
|
+
const children = waterfallGroups.get(alias);
|
|
15714
|
+
if (children) {
|
|
15715
|
+
resolved.add(alias);
|
|
15716
|
+
for (const child of children) {
|
|
15717
|
+
resolved.add(child);
|
|
15718
|
+
}
|
|
15719
|
+
} else {
|
|
15720
|
+
resolved.add(alias);
|
|
15721
|
+
}
|
|
15722
|
+
}
|
|
15723
|
+
return resolved;
|
|
15724
|
+
}
|
|
15725
|
+
function isPersistedFailureCell(value) {
|
|
15726
|
+
const raw = String(value ?? "").trim();
|
|
15727
|
+
if (!raw) {
|
|
15728
|
+
return false;
|
|
15729
|
+
}
|
|
15730
|
+
if (cellFailureError(value)) {
|
|
15731
|
+
return true;
|
|
15732
|
+
}
|
|
15733
|
+
return raw.includes("Error:") || raw.includes("Traceback");
|
|
15734
|
+
}
|
|
15735
|
+
function isPersistedFailureStatusCell(value) {
|
|
15736
|
+
const raw = String(value ?? "").trim().toLowerCase();
|
|
15737
|
+
return raw === "error" || raw === "failed" || raw === "failure";
|
|
15738
|
+
}
|
|
15739
|
+
function stripFailureCompanionSuffix(key) {
|
|
15740
|
+
const lowerKey = key.toLowerCase();
|
|
15741
|
+
for (const suffix of [".error", "__error"]) {
|
|
15742
|
+
if (lowerKey.endsWith(suffix)) {
|
|
15743
|
+
return { alias: key.slice(0, -suffix.length), kind: "error" };
|
|
15744
|
+
}
|
|
15745
|
+
}
|
|
15746
|
+
for (const suffix of [".status", "__status"]) {
|
|
15747
|
+
if (lowerKey.endsWith(suffix)) {
|
|
15748
|
+
return { alias: key.slice(0, -suffix.length), kind: "status" };
|
|
15749
|
+
}
|
|
15750
|
+
}
|
|
15751
|
+
return null;
|
|
15752
|
+
}
|
|
15753
|
+
function collectFailedInputAliases(config, sourceCsvPath, rows) {
|
|
15754
|
+
const { scalarAliases } = collectCommandAliases(config);
|
|
15755
|
+
if (scalarAliases.size === 0) {
|
|
15756
|
+
return /* @__PURE__ */ new Set();
|
|
15757
|
+
}
|
|
15758
|
+
const inputRows = readCsvRows(sourceCsvPath);
|
|
15759
|
+
if (inputRows.length === 0) {
|
|
15760
|
+
return /* @__PURE__ */ new Set();
|
|
15761
|
+
}
|
|
15762
|
+
const start = rows.rowStart ?? 0;
|
|
15763
|
+
const maxEnd = Math.max(start, inputRows.length - 1);
|
|
15764
|
+
const inclusiveEnd = rows.rowEnd === null || rows.rowEnd === void 0 ? maxEnd : Math.min(maxEnd, rows.rowEnd);
|
|
15765
|
+
const failedAliases = /* @__PURE__ */ new Set();
|
|
15766
|
+
for (let index = start; index <= inclusiveEnd; index += 1) {
|
|
15767
|
+
const row = inputRows[index];
|
|
15768
|
+
if (!row) {
|
|
15769
|
+
continue;
|
|
15770
|
+
}
|
|
15771
|
+
for (const [key, value] of Object.entries(row)) {
|
|
15772
|
+
const alias = normalizeAlias2(key);
|
|
15773
|
+
if (scalarAliases.has(alias) && isPersistedFailureCell(value)) {
|
|
15774
|
+
failedAliases.add(alias);
|
|
15775
|
+
continue;
|
|
15776
|
+
}
|
|
15777
|
+
const companion = stripFailureCompanionSuffix(key);
|
|
15778
|
+
if (!companion) {
|
|
15779
|
+
continue;
|
|
15780
|
+
}
|
|
15781
|
+
const companionAlias = normalizeAlias2(companion.alias);
|
|
15782
|
+
if (!scalarAliases.has(companionAlias)) {
|
|
15783
|
+
continue;
|
|
15784
|
+
}
|
|
15785
|
+
if (companion.kind === "error" ? isPersistedFailureCell(value) : isPersistedFailureStatusCell(value)) {
|
|
15786
|
+
failedAliases.add(companionAlias);
|
|
15787
|
+
}
|
|
15788
|
+
}
|
|
15789
|
+
}
|
|
15790
|
+
return failedAliases;
|
|
15791
|
+
}
|
|
15792
|
+
function parseJsonOutput(stdout) {
|
|
15793
|
+
const trimmed = stdout.trim();
|
|
15794
|
+
if (!trimmed) return null;
|
|
15795
|
+
try {
|
|
15796
|
+
return JSON.parse(trimmed);
|
|
15797
|
+
} catch {
|
|
15798
|
+
const start = trimmed.lastIndexOf("\n{");
|
|
15799
|
+
if (start >= 0) {
|
|
15800
|
+
return JSON.parse(trimmed.slice(start + 1));
|
|
15801
|
+
}
|
|
15802
|
+
throw new Error(
|
|
15803
|
+
"The generated play completed but did not emit parseable JSON."
|
|
15804
|
+
);
|
|
15805
|
+
}
|
|
15806
|
+
}
|
|
15807
|
+
function extractRunIdFromPlayOutput(stdout) {
|
|
15808
|
+
const runIdLine = stdout.match(/^\s*run id:\s*(play\/\S+\/run\/\S+)\s*$/im);
|
|
15809
|
+
if (runIdLine?.[1]) {
|
|
15810
|
+
return runIdLine[1];
|
|
15811
|
+
}
|
|
15812
|
+
return stdout.match(/\b(play\/\S+\/run\/\S+)/)?.[1] ?? null;
|
|
15813
|
+
}
|
|
15814
|
+
async function resolveWatchedGeneratedPlayStatus(input2) {
|
|
15815
|
+
const runId = extractRunIdFromPlayOutput(input2.stdout);
|
|
15816
|
+
if (!runId) {
|
|
15817
|
+
if (input2.exitCode === 0) {
|
|
15818
|
+
throw new Error(
|
|
15819
|
+
"The generated play completed but did not print a run id to inspect."
|
|
15820
|
+
);
|
|
15821
|
+
}
|
|
15822
|
+
return null;
|
|
15823
|
+
}
|
|
15824
|
+
return input2.client.runs.get(runId, { full: true });
|
|
15825
|
+
}
|
|
15826
|
+
function isRecord6(value) {
|
|
15827
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
15828
|
+
}
|
|
15829
|
+
async function captureStdout(run, options = {}) {
|
|
15830
|
+
const originalWrite = process.stdout.write.bind(process.stdout);
|
|
15831
|
+
let stdout = "";
|
|
15832
|
+
process.stdout.write = ((chunk, ..._args) => {
|
|
15833
|
+
stdout += typeof chunk === "string" ? chunk : String(chunk);
|
|
15834
|
+
if (options.passthrough) {
|
|
15835
|
+
return originalWrite(typeof chunk === "string" ? chunk : String(chunk));
|
|
15836
|
+
}
|
|
15837
|
+
return true;
|
|
15838
|
+
});
|
|
15839
|
+
try {
|
|
15840
|
+
const result = await run();
|
|
15841
|
+
return { result, stdout };
|
|
15842
|
+
} finally {
|
|
15843
|
+
process.stdout.write = originalWrite;
|
|
15844
|
+
}
|
|
15845
|
+
}
|
|
15846
|
+
function isPlayStartStreamEndedError(error) {
|
|
15847
|
+
return error instanceof DeeplineError && error.code === "PLAY_START_STREAM_ENDED" || error instanceof Error && error.message.includes(
|
|
15848
|
+
"Play start stream ended before a terminal status"
|
|
15849
|
+
);
|
|
15850
|
+
}
|
|
15851
|
+
async function runGeneratedEnrichPlay(runArgs, options = {}) {
|
|
15852
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
15853
|
+
try {
|
|
15854
|
+
return await captureStdout(() => handlePlayRun(runArgs), {
|
|
15855
|
+
passthrough: options.passthroughStdout
|
|
15856
|
+
});
|
|
15857
|
+
} catch (error) {
|
|
15858
|
+
if (attempt === 0 && isPlayStartStreamEndedError(error)) {
|
|
15859
|
+
await new Promise((resolve15) => setTimeout(resolve15, 250));
|
|
15860
|
+
continue;
|
|
15861
|
+
}
|
|
15862
|
+
throw error;
|
|
15863
|
+
}
|
|
15864
|
+
}
|
|
15865
|
+
return captureStdout(() => handlePlayRun(runArgs), {
|
|
15866
|
+
passthrough: options.passthroughStdout
|
|
15867
|
+
});
|
|
15868
|
+
}
|
|
15869
|
+
async function writeOutputCsv(outputPath, status, options) {
|
|
15870
|
+
let rowsInfo = extractCanonicalRowsInfo(status);
|
|
15871
|
+
if (!rowsInfo) {
|
|
15872
|
+
throw new Error("The generated play did not return row-shaped output.");
|
|
15873
|
+
}
|
|
15874
|
+
if (!rowsInfo.complete && options?.client) {
|
|
15875
|
+
rowsInfo = await fetchBackingRowsForCsvExport({
|
|
15876
|
+
client: options.client,
|
|
15877
|
+
status,
|
|
15878
|
+
rowsInfo
|
|
15879
|
+
}) ?? rowsInfo;
|
|
15880
|
+
}
|
|
15881
|
+
assertCompleteRowsForCsvExport(rowsInfo, status, outputPath);
|
|
15882
|
+
const merged = mergeRowsForCsvExport(rowsInfo.rows, options);
|
|
15883
|
+
const columns = orderEnrichCsvColumns(
|
|
15884
|
+
dataExportColumns(merged.rows, [
|
|
15885
|
+
...merged.preferredColumns,
|
|
15886
|
+
...rowsInfo.columns
|
|
15887
|
+
]),
|
|
15888
|
+
options?.config
|
|
15889
|
+
);
|
|
15890
|
+
await writeFile4(
|
|
15891
|
+
resolve11(outputPath),
|
|
15892
|
+
csvStringFromRows(merged.rows, columns),
|
|
15893
|
+
"utf8"
|
|
15894
|
+
);
|
|
15895
|
+
return {
|
|
15896
|
+
rows: merged.rows.length,
|
|
15897
|
+
path: resolve11(outputPath),
|
|
15898
|
+
enrichedRows: rowsInfo.rows
|
|
15899
|
+
};
|
|
15900
|
+
}
|
|
15901
|
+
function recordField(value, key) {
|
|
15902
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value[key] : void 0;
|
|
15903
|
+
}
|
|
15904
|
+
function extractRunId(status) {
|
|
15905
|
+
const candidates = [
|
|
15906
|
+
recordField(status, "runId"),
|
|
15907
|
+
recordField(recordField(status, "run"), "id")
|
|
15908
|
+
];
|
|
15909
|
+
for (const candidate of candidates) {
|
|
15910
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
15911
|
+
return candidate.trim();
|
|
15912
|
+
}
|
|
15913
|
+
}
|
|
15914
|
+
return null;
|
|
15915
|
+
}
|
|
15916
|
+
function extractPlayName2(status) {
|
|
15917
|
+
const run = recordField(status, "run");
|
|
15918
|
+
const candidates = [
|
|
15919
|
+
recordField(status, "playName"),
|
|
15920
|
+
recordField(status, "name"),
|
|
15921
|
+
recordField(run, "playName"),
|
|
15922
|
+
recordField(run, "name")
|
|
15923
|
+
];
|
|
15924
|
+
for (const candidate of candidates) {
|
|
15925
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
15926
|
+
return candidate.trim();
|
|
15927
|
+
}
|
|
15928
|
+
}
|
|
15929
|
+
return null;
|
|
15930
|
+
}
|
|
15931
|
+
function exportableSheetRow2(row) {
|
|
15932
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
15933
|
+
return null;
|
|
15934
|
+
}
|
|
15935
|
+
const record = row;
|
|
15936
|
+
const data = record.data;
|
|
15937
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
15938
|
+
return data;
|
|
15939
|
+
}
|
|
15940
|
+
const fallback = { ...record };
|
|
15941
|
+
for (const key of [
|
|
15942
|
+
"key",
|
|
15943
|
+
"status",
|
|
15944
|
+
"cellMeta",
|
|
15945
|
+
"inputIndex",
|
|
15946
|
+
"runId",
|
|
15947
|
+
"error",
|
|
15948
|
+
"stage",
|
|
15949
|
+
"provider",
|
|
15950
|
+
"seq",
|
|
15951
|
+
"createdAt",
|
|
15952
|
+
"updatedAt"
|
|
15953
|
+
]) {
|
|
15954
|
+
delete fallback[key];
|
|
15955
|
+
}
|
|
15956
|
+
return fallback;
|
|
15957
|
+
}
|
|
15958
|
+
function collectHardFailureAliases(config) {
|
|
15959
|
+
const aliases = [];
|
|
15960
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15961
|
+
for (const command of config.commands) {
|
|
15962
|
+
if ("with_waterfall" in command) {
|
|
15963
|
+
continue;
|
|
15964
|
+
}
|
|
15965
|
+
if (command.disabled) {
|
|
15966
|
+
continue;
|
|
15967
|
+
}
|
|
15968
|
+
const alias = command.alias.trim();
|
|
15969
|
+
if (!alias || seen.has(alias)) {
|
|
15970
|
+
continue;
|
|
15971
|
+
}
|
|
15972
|
+
seen.add(alias);
|
|
15973
|
+
aliases.push(alias);
|
|
15974
|
+
}
|
|
15975
|
+
return aliases;
|
|
15976
|
+
}
|
|
15977
|
+
function cellFailureError(value) {
|
|
15978
|
+
const parsed = parseMaybeJsonObject(value);
|
|
15979
|
+
if (!isRecord6(parsed)) {
|
|
15980
|
+
return null;
|
|
15981
|
+
}
|
|
15982
|
+
const status = typeof parsed.status === "string" ? parsed.status.trim().toLowerCase() : "";
|
|
15983
|
+
const directError = typeof parsed.error === "string" ? parsed.error.trim() : typeof parsed.last_error === "string" ? parsed.last_error.trim() : "";
|
|
15984
|
+
const result = isRecord6(parsed.result) ? parsed.result : null;
|
|
15985
|
+
const resultError = typeof result?.error === "string" ? result.error.trim() : typeof result?.message === "string" ? result.message.trim() : "";
|
|
15986
|
+
if (!directError && !resultError && status !== "error" && status !== "failed") {
|
|
15987
|
+
return null;
|
|
15988
|
+
}
|
|
15989
|
+
const message = directError || resultError || `Column status: ${status}`;
|
|
15990
|
+
return {
|
|
15991
|
+
message,
|
|
15992
|
+
...typeof parsed.operation === "string" && parsed.operation.trim() ? { operation: parsed.operation.trim() } : {},
|
|
15993
|
+
...typeof parsed.provider === "string" && parsed.provider.trim() ? { provider: parsed.provider.trim() } : {}
|
|
15994
|
+
};
|
|
15995
|
+
}
|
|
15996
|
+
function buildEnrichWaterfallSummaryLines(config, rows) {
|
|
15997
|
+
if (rows.length === 0) {
|
|
15998
|
+
return [];
|
|
15999
|
+
}
|
|
16000
|
+
const lines = [];
|
|
16001
|
+
for (const command of config.commands) {
|
|
16002
|
+
if (!("with_waterfall" in command)) {
|
|
16003
|
+
continue;
|
|
16004
|
+
}
|
|
16005
|
+
for (const child of command.commands) {
|
|
16006
|
+
if ("with_waterfall" in child || child.disabled) {
|
|
16007
|
+
continue;
|
|
16008
|
+
}
|
|
16009
|
+
const failures = rows.filter(
|
|
16010
|
+
(row) => cellFailureError(row[child.alias])
|
|
16011
|
+
).length;
|
|
16012
|
+
if (failures > 0) {
|
|
16013
|
+
lines.push(
|
|
16014
|
+
`Column '${child.alias}' had high failure rate (${failures}/${rows.length}); continuing waterfall group '${command.with_waterfall}'.`
|
|
16015
|
+
);
|
|
16016
|
+
}
|
|
16017
|
+
}
|
|
16018
|
+
}
|
|
16019
|
+
return lines;
|
|
16020
|
+
}
|
|
16021
|
+
function collectEnrichFailureJobs(input2) {
|
|
16022
|
+
const aliases = collectHardFailureAliases(input2.config);
|
|
16023
|
+
if (aliases.length === 0 || input2.rows.length === 0) {
|
|
16024
|
+
return [];
|
|
16025
|
+
}
|
|
16026
|
+
const rowOffset = input2.rowStart ?? 0;
|
|
16027
|
+
const jobs = [];
|
|
16028
|
+
input2.rows.forEach((row, rowIndex) => {
|
|
16029
|
+
aliases.forEach((alias, aliasIndex) => {
|
|
16030
|
+
const failure = cellFailureError(row[alias]);
|
|
16031
|
+
if (!failure) {
|
|
16032
|
+
return;
|
|
16033
|
+
}
|
|
16034
|
+
const rowId = rowOffset + rowIndex;
|
|
16035
|
+
jobs.push({
|
|
16036
|
+
job_id: `row-${rowId}-${alias}`,
|
|
16037
|
+
row_id: rowId,
|
|
16038
|
+
col_index: aliasIndex,
|
|
16039
|
+
column: alias,
|
|
16040
|
+
status: "failed",
|
|
16041
|
+
last_error: failure.message,
|
|
16042
|
+
error: failure.message,
|
|
16043
|
+
...failure.operation ? { operation: failure.operation } : {},
|
|
16044
|
+
...failure.provider ? { provider: failure.provider } : {}
|
|
16045
|
+
});
|
|
16046
|
+
});
|
|
16047
|
+
});
|
|
16048
|
+
return jobs;
|
|
16049
|
+
}
|
|
16050
|
+
function summarizeFailedJobError(value) {
|
|
16051
|
+
const text = String(value ?? "").trim();
|
|
16052
|
+
return text.length > 500 ? `${text.slice(0, 497)}...` : text;
|
|
16053
|
+
}
|
|
16054
|
+
function formatFailureReportCommand(reportPath) {
|
|
16055
|
+
const quoted = reportPath.replace(/'/g, `'\\''`);
|
|
16056
|
+
return `Inspect failed jobs: jq -r '.jobs[] | [.job_id,.row_id,.col_index,.column,.status,.last_error] | @tsv' '${quoted}'`;
|
|
16057
|
+
}
|
|
16058
|
+
async function persistEnrichFailureReport(input2) {
|
|
16059
|
+
if (input2.jobs.length === 0) {
|
|
16060
|
+
return null;
|
|
16061
|
+
}
|
|
16062
|
+
const stateDir = join8(homedir4(), ".local", "deepline", "runtime", "state");
|
|
16063
|
+
await mkdir4(stateDir, { recursive: true });
|
|
16064
|
+
const reportPath = join8(
|
|
16065
|
+
stateDir,
|
|
16066
|
+
`run-block-failures-${Math.floor(Date.now() / 1e3)}-${process.pid}.json`
|
|
16067
|
+
);
|
|
16068
|
+
const report = {
|
|
16069
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16070
|
+
api_url: input2.apiUrl,
|
|
16071
|
+
output_csv: input2.outputPath ?? "",
|
|
16072
|
+
summary: {
|
|
16073
|
+
failed_count: input2.jobs.length,
|
|
16074
|
+
canceled_count: 0,
|
|
16075
|
+
pending_count: 0,
|
|
16076
|
+
missing_count: 0,
|
|
16077
|
+
total_jobs: input2.jobs.length
|
|
16078
|
+
},
|
|
16079
|
+
jobs: input2.jobs,
|
|
16080
|
+
failed_jobs: input2.jobs,
|
|
16081
|
+
canceled_jobs: [],
|
|
16082
|
+
pending_jobs: [],
|
|
16083
|
+
missing_job_ids: []
|
|
16084
|
+
};
|
|
16085
|
+
if (input2.rows.rowStart !== null && input2.rows.rowEnd !== null) {
|
|
16086
|
+
report.rows = { start: input2.rows.rowStart, end: input2.rows.rowEnd };
|
|
16087
|
+
}
|
|
16088
|
+
await writeFile4(reportPath, `${JSON.stringify(report, null, 2)}
|
|
16089
|
+
`, "utf8");
|
|
16090
|
+
return reportPath;
|
|
16091
|
+
}
|
|
16092
|
+
async function maybeEmitEnrichFailureReport(input2) {
|
|
16093
|
+
const jobs = collectEnrichFailureJobs({
|
|
16094
|
+
config: input2.config,
|
|
16095
|
+
rows: input2.rows,
|
|
16096
|
+
rowStart: input2.rowRange.rowStart
|
|
16097
|
+
});
|
|
16098
|
+
if (jobs.length === 0) {
|
|
16099
|
+
return null;
|
|
16100
|
+
}
|
|
16101
|
+
const reportPath = await persistEnrichFailureReport({
|
|
16102
|
+
jobs,
|
|
16103
|
+
apiUrl: input2.client.baseUrl,
|
|
16104
|
+
outputPath: input2.outputPath,
|
|
16105
|
+
rows: input2.rowRange
|
|
16106
|
+
});
|
|
16107
|
+
process.stderr.write("Execution failed.\n");
|
|
16108
|
+
process.stderr.write("Failure preview (up to 5 failed jobs):\n");
|
|
16109
|
+
process.stderr.write("job_id row_id col_index column status error\n");
|
|
16110
|
+
for (const job of jobs.slice(0, 5)) {
|
|
16111
|
+
process.stderr.write(
|
|
16112
|
+
`${job.job_id} ${job.row_id} ${job.col_index} ${job.column} ${job.status} ${summarizeFailedJobError(job.last_error)}
|
|
16113
|
+
`
|
|
16114
|
+
);
|
|
16115
|
+
}
|
|
16116
|
+
if (jobs.length > 5) {
|
|
16117
|
+
process.stderr.write(`Showing 5 of ${jobs.length} failed jobs.
|
|
16118
|
+
`);
|
|
16119
|
+
}
|
|
16120
|
+
if (reportPath) {
|
|
16121
|
+
process.stderr.write(`Full failure report: ${reportPath}
|
|
16122
|
+
`);
|
|
16123
|
+
process.stderr.write(`${formatFailureReportCommand(reportPath)}
|
|
16124
|
+
`);
|
|
16125
|
+
process.stderr.write("Enrichment failed. See failure report above.\n");
|
|
16126
|
+
return { path: reportPath, jobs };
|
|
16127
|
+
}
|
|
16128
|
+
process.stderr.write("Enrichment failed.\n");
|
|
16129
|
+
return { path: "", jobs };
|
|
16130
|
+
}
|
|
16131
|
+
function mergePreferredColumns(preferredColumns, rows) {
|
|
16132
|
+
const columns = [];
|
|
16133
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16134
|
+
for (const column of preferredColumns) {
|
|
16135
|
+
if (!column || seen.has(column)) {
|
|
16136
|
+
continue;
|
|
16137
|
+
}
|
|
16138
|
+
seen.add(column);
|
|
16139
|
+
columns.push(column);
|
|
16140
|
+
}
|
|
16141
|
+
for (const row of rows) {
|
|
16142
|
+
for (const column of Object.keys(row)) {
|
|
16143
|
+
if (seen.has(column)) {
|
|
16144
|
+
continue;
|
|
16145
|
+
}
|
|
16146
|
+
seen.add(column);
|
|
16147
|
+
columns.push(column);
|
|
16148
|
+
}
|
|
16149
|
+
}
|
|
16150
|
+
return columns;
|
|
16151
|
+
}
|
|
16152
|
+
async function fetchBackingRowsForCsvExport(input2) {
|
|
16153
|
+
const runId = extractRunId(input2.status);
|
|
16154
|
+
const playName = extractPlayName2(input2.status);
|
|
16155
|
+
const tableNamespace = input2.rowsInfo.tableNamespace?.trim();
|
|
16156
|
+
if (!runId || !playName || !tableNamespace) {
|
|
16157
|
+
return null;
|
|
16158
|
+
}
|
|
16159
|
+
const sheetRows = [];
|
|
16160
|
+
let offset = 0;
|
|
16161
|
+
let expectedTotal = input2.rowsInfo.totalRows;
|
|
16162
|
+
while (true) {
|
|
16163
|
+
const page = await input2.client.runs.exportDatasetRows({
|
|
16164
|
+
playName,
|
|
16165
|
+
tableNamespace,
|
|
16166
|
+
runId,
|
|
16167
|
+
limit: ENRICH_EXPORT_PAGE_SIZE,
|
|
16168
|
+
offset
|
|
16169
|
+
});
|
|
16170
|
+
sheetRows.push(...page.rows);
|
|
16171
|
+
const summaryTotal = page.summary?.stats?.total;
|
|
16172
|
+
if (typeof summaryTotal === "number" && Number.isFinite(summaryTotal)) {
|
|
16173
|
+
expectedTotal = Math.max(expectedTotal, Math.trunc(summaryTotal));
|
|
16174
|
+
}
|
|
16175
|
+
if (page.rows.length < ENRICH_EXPORT_PAGE_SIZE || sheetRows.length >= expectedTotal) {
|
|
16176
|
+
break;
|
|
16177
|
+
}
|
|
16178
|
+
offset += page.rows.length;
|
|
16179
|
+
}
|
|
16180
|
+
const rows = sheetRows.map(exportableSheetRow2).filter((row) => Boolean(row));
|
|
16181
|
+
if (rows.length < input2.rowsInfo.totalRows) {
|
|
16182
|
+
return null;
|
|
16183
|
+
}
|
|
16184
|
+
return {
|
|
16185
|
+
...input2.rowsInfo,
|
|
16186
|
+
rows,
|
|
16187
|
+
columns: mergePreferredColumns(
|
|
16188
|
+
input2.rowsInfo.columnsExplicit ? input2.rowsInfo.columns : [],
|
|
16189
|
+
rows
|
|
16190
|
+
),
|
|
16191
|
+
totalRows: rows.length,
|
|
16192
|
+
complete: true,
|
|
16193
|
+
source: `${input2.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
|
|
16194
|
+
};
|
|
16195
|
+
}
|
|
16196
|
+
function mergeRowsForCsvExport(enrichedRows, options) {
|
|
16197
|
+
const rows = dataExportRows(normalizeEnrichRowsForCsvExport(enrichedRows));
|
|
16198
|
+
const range = options?.rows;
|
|
16199
|
+
if (range?.rowStart === null || range?.rowStart === void 0) {
|
|
16200
|
+
return { rows, preferredColumns: [] };
|
|
16201
|
+
}
|
|
16202
|
+
if (!options?.sourceCsvPath) {
|
|
16203
|
+
return { rows, preferredColumns: [] };
|
|
16204
|
+
}
|
|
16205
|
+
const parsedBaseRows = readCsvRows(options.sourceCsvPath);
|
|
16206
|
+
const preferredColumns = Object.keys(parsedBaseRows[0] ?? {});
|
|
16207
|
+
const baseRows = parsedBaseRows.map(
|
|
16208
|
+
(row) => ({ ...row })
|
|
16209
|
+
);
|
|
16210
|
+
const start = Math.max(0, range.rowStart);
|
|
16211
|
+
const maxEnd = Math.max(start, baseRows.length - 1);
|
|
16212
|
+
const inclusiveEnd = range.rowEnd === null ? maxEnd : Math.min(maxEnd, range.rowEnd);
|
|
16213
|
+
const merged = [...baseRows];
|
|
16214
|
+
let selectedIndex = 0;
|
|
16215
|
+
for (let rowIndex = start; rowIndex <= inclusiveEnd; rowIndex += 1) {
|
|
16216
|
+
const enriched = rows[selectedIndex++];
|
|
16217
|
+
if (!enriched) {
|
|
16218
|
+
break;
|
|
16219
|
+
}
|
|
16220
|
+
const base = merged[rowIndex] ?? {};
|
|
16221
|
+
merged[rowIndex] = {
|
|
16222
|
+
...base,
|
|
16223
|
+
...enriched
|
|
16224
|
+
};
|
|
16225
|
+
const metadata = mergeLegacyMetadataCell(
|
|
16226
|
+
base._metadata,
|
|
16227
|
+
enriched._metadata
|
|
16228
|
+
);
|
|
16229
|
+
if (metadata !== void 0) {
|
|
16230
|
+
merged[rowIndex]._metadata = metadata;
|
|
16231
|
+
}
|
|
16232
|
+
}
|
|
16233
|
+
return { rows: merged, preferredColumns };
|
|
16234
|
+
}
|
|
16235
|
+
function collectConfigScalarAliasOrder(config) {
|
|
16236
|
+
const aliases = [];
|
|
16237
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16238
|
+
const addAlias = (alias) => {
|
|
16239
|
+
const normalized = normalizeAlias2(alias);
|
|
16240
|
+
if (!normalized || seen.has(normalized)) {
|
|
16241
|
+
return;
|
|
16242
|
+
}
|
|
16243
|
+
seen.add(normalized);
|
|
16244
|
+
aliases.push(alias);
|
|
16245
|
+
};
|
|
16246
|
+
for (const command of config.commands) {
|
|
16247
|
+
if ("with_waterfall" in command) {
|
|
16248
|
+
for (const child of command.commands) {
|
|
16249
|
+
if ("with_waterfall" in child || child.disabled) {
|
|
16250
|
+
continue;
|
|
16251
|
+
}
|
|
16252
|
+
addAlias(child.alias);
|
|
16253
|
+
}
|
|
16254
|
+
continue;
|
|
16255
|
+
}
|
|
16256
|
+
if (!command.disabled) {
|
|
16257
|
+
addAlias(command.alias);
|
|
16258
|
+
}
|
|
16259
|
+
}
|
|
16260
|
+
return aliases;
|
|
16261
|
+
}
|
|
16262
|
+
function orderEnrichCsvColumns(columns, config) {
|
|
16263
|
+
if (!config || columns.length < 2) {
|
|
16264
|
+
return columns;
|
|
16265
|
+
}
|
|
16266
|
+
const aliasOrder = collectConfigScalarAliasOrder(config);
|
|
16267
|
+
if (aliasOrder.length < 2) {
|
|
16268
|
+
return columns;
|
|
16269
|
+
}
|
|
16270
|
+
const normalizedAliasOrder = aliasOrder.map(normalizeAlias2);
|
|
16271
|
+
const requestedColumnByAlias = /* @__PURE__ */ new Map();
|
|
16272
|
+
for (const column of columns) {
|
|
16273
|
+
const normalizedColumn = normalizeAlias2(column);
|
|
16274
|
+
if (normalizedAliasOrder.includes(normalizedColumn) && !requestedColumnByAlias.has(normalizedColumn)) {
|
|
16275
|
+
requestedColumnByAlias.set(normalizedColumn, column);
|
|
16276
|
+
}
|
|
16277
|
+
}
|
|
16278
|
+
const requestedColumns = normalizedAliasOrder.map((alias) => requestedColumnByAlias.get(alias)).filter((column) => Boolean(column));
|
|
16279
|
+
if (requestedColumns.length < 2) {
|
|
16280
|
+
return columns;
|
|
16281
|
+
}
|
|
16282
|
+
const requestedSet = new Set(requestedColumns);
|
|
16283
|
+
const firstRequestedIndex = columns.findIndex(
|
|
16284
|
+
(column) => requestedSet.has(column)
|
|
16285
|
+
);
|
|
16286
|
+
if (firstRequestedIndex < 0) {
|
|
16287
|
+
return columns;
|
|
16288
|
+
}
|
|
16289
|
+
const withoutRequested = columns.filter(
|
|
16290
|
+
(column) => !requestedSet.has(column)
|
|
16291
|
+
);
|
|
16292
|
+
withoutRequested.splice(firstRequestedIndex, 0, ...requestedColumns);
|
|
16293
|
+
return withoutRequested;
|
|
16294
|
+
}
|
|
16295
|
+
function isNonEmptyCsvCell(value) {
|
|
16296
|
+
return value !== null && value !== void 0 && String(value).trim() !== "";
|
|
16297
|
+
}
|
|
16298
|
+
function normalizeEnrichRowsForCsvExport(rows) {
|
|
16299
|
+
return rows.map((row) => {
|
|
16300
|
+
const metadata = legacyMetadataFromRow(row);
|
|
16301
|
+
if (!metadata) {
|
|
16302
|
+
return row;
|
|
16303
|
+
}
|
|
16304
|
+
const normalized = { ...row, _metadata: metadata };
|
|
16305
|
+
delete normalized.metadata;
|
|
16306
|
+
delete normalized["metadata.columns"];
|
|
16307
|
+
delete normalized.metadata__columns;
|
|
16308
|
+
for (const key of Object.keys(normalized)) {
|
|
16309
|
+
if (key.startsWith("metadata.columns.") || key.startsWith("metadata__columns.")) {
|
|
16310
|
+
delete normalized[key];
|
|
16311
|
+
}
|
|
16312
|
+
}
|
|
16313
|
+
return normalized;
|
|
16314
|
+
});
|
|
16315
|
+
}
|
|
16316
|
+
function legacyMetadataFromRow(row) {
|
|
16317
|
+
const direct = parseLegacyMetadataCell(row._metadata);
|
|
16318
|
+
if (direct && isRecord6(direct.columns)) {
|
|
16319
|
+
return direct;
|
|
16320
|
+
}
|
|
16321
|
+
const relocated = parseLegacyMetadataCell(row.metadata);
|
|
16322
|
+
if (relocated && isRecord6(relocated.columns)) {
|
|
16323
|
+
return relocated;
|
|
16324
|
+
}
|
|
16325
|
+
const flattenedColumns = parseLegacyMetadataCell(row["metadata.columns"]);
|
|
16326
|
+
if (flattenedColumns) {
|
|
16327
|
+
return { columns: flattenedColumns };
|
|
16328
|
+
}
|
|
16329
|
+
const flattenedUnderscoreColumns = parseLegacyMetadataCell(
|
|
16330
|
+
row.metadata__columns
|
|
16331
|
+
);
|
|
16332
|
+
if (flattenedUnderscoreColumns) {
|
|
16333
|
+
return { columns: flattenedUnderscoreColumns };
|
|
16334
|
+
}
|
|
16335
|
+
return null;
|
|
16336
|
+
}
|
|
16337
|
+
function parseLegacyMetadataCell(value) {
|
|
16338
|
+
const parsed = parseMaybeJsonObject(value);
|
|
16339
|
+
if (isRecord6(parsed)) {
|
|
16340
|
+
return parsed;
|
|
16341
|
+
}
|
|
16342
|
+
if (typeof value !== "string") {
|
|
16343
|
+
return null;
|
|
16344
|
+
}
|
|
16345
|
+
const trimmed = value.trim();
|
|
16346
|
+
if (!trimmed) {
|
|
16347
|
+
return null;
|
|
16348
|
+
}
|
|
16349
|
+
const candidates = [trimmed];
|
|
16350
|
+
if (trimmed.includes('\\"')) {
|
|
16351
|
+
candidates.push(trimmed.replace(/\\"/g, '"'));
|
|
16352
|
+
}
|
|
16353
|
+
for (const candidate of candidates) {
|
|
16354
|
+
try {
|
|
16355
|
+
const decoded = JSON.parse(candidate);
|
|
16356
|
+
if (isRecord6(decoded)) {
|
|
16357
|
+
return decoded;
|
|
16358
|
+
}
|
|
16359
|
+
if (typeof decoded === "string") {
|
|
16360
|
+
const nested = JSON.parse(decoded);
|
|
16361
|
+
if (isRecord6(nested)) {
|
|
16362
|
+
return nested;
|
|
16363
|
+
}
|
|
16364
|
+
}
|
|
16365
|
+
} catch {
|
|
16366
|
+
}
|
|
16367
|
+
}
|
|
16368
|
+
return null;
|
|
14696
16369
|
}
|
|
14697
|
-
function
|
|
14698
|
-
const
|
|
14699
|
-
|
|
14700
|
-
|
|
16370
|
+
function mergeLegacyMetadataRecords(base, enriched) {
|
|
16371
|
+
const baseColumns = isRecord6(base.columns) ? base.columns : null;
|
|
16372
|
+
const enrichedColumns = isRecord6(enriched.columns) ? enriched.columns : null;
|
|
16373
|
+
const merged = {
|
|
16374
|
+
...base,
|
|
16375
|
+
...enriched
|
|
16376
|
+
};
|
|
16377
|
+
if (baseColumns || enrichedColumns) {
|
|
16378
|
+
merged.columns = {
|
|
16379
|
+
...baseColumns ?? {},
|
|
16380
|
+
...enrichedColumns ?? {}
|
|
16381
|
+
};
|
|
14701
16382
|
}
|
|
14702
|
-
|
|
14703
|
-
|
|
14704
|
-
|
|
14705
|
-
|
|
14706
|
-
|
|
16383
|
+
return merged;
|
|
16384
|
+
}
|
|
16385
|
+
function mergeLegacyMetadataCell(base, enriched) {
|
|
16386
|
+
const baseRecord = parseLegacyMetadataCell(base);
|
|
16387
|
+
const enrichedRecord = parseLegacyMetadataCell(enriched);
|
|
16388
|
+
if (baseRecord && enrichedRecord) {
|
|
16389
|
+
return JSON.stringify(
|
|
16390
|
+
mergeLegacyMetadataRecords(baseRecord, enrichedRecord)
|
|
14707
16391
|
);
|
|
14708
16392
|
}
|
|
14709
|
-
|
|
14710
|
-
|
|
14711
|
-
const children = waterfallGroups.get(alias);
|
|
14712
|
-
if (children) {
|
|
14713
|
-
for (const child of children) {
|
|
14714
|
-
resolved.add(child);
|
|
14715
|
-
}
|
|
14716
|
-
} else {
|
|
14717
|
-
resolved.add(alias);
|
|
14718
|
-
}
|
|
16393
|
+
if (enrichedRecord) {
|
|
16394
|
+
return JSON.stringify(enrichedRecord);
|
|
14719
16395
|
}
|
|
14720
|
-
|
|
14721
|
-
|
|
14722
|
-
function parseJsonOutput(stdout) {
|
|
14723
|
-
const trimmed = stdout.trim();
|
|
14724
|
-
if (!trimmed) return null;
|
|
14725
|
-
try {
|
|
14726
|
-
return JSON.parse(trimmed);
|
|
14727
|
-
} catch {
|
|
14728
|
-
const start = trimmed.lastIndexOf("\n{");
|
|
14729
|
-
if (start >= 0) {
|
|
14730
|
-
return JSON.parse(trimmed.slice(start + 1));
|
|
14731
|
-
}
|
|
14732
|
-
throw new Error(
|
|
14733
|
-
"The generated play completed but did not emit parseable JSON."
|
|
14734
|
-
);
|
|
16396
|
+
if (baseRecord) {
|
|
16397
|
+
return JSON.stringify(baseRecord);
|
|
14735
16398
|
}
|
|
14736
|
-
|
|
14737
|
-
|
|
14738
|
-
const originalWrite = process.stdout.write.bind(process.stdout);
|
|
14739
|
-
let stdout = "";
|
|
14740
|
-
process.stdout.write = ((chunk, ..._args) => {
|
|
14741
|
-
stdout += typeof chunk === "string" ? chunk : String(chunk);
|
|
14742
|
-
return true;
|
|
14743
|
-
});
|
|
14744
|
-
try {
|
|
14745
|
-
const result = await run();
|
|
14746
|
-
return { result, stdout };
|
|
14747
|
-
} finally {
|
|
14748
|
-
process.stdout.write = originalWrite;
|
|
16399
|
+
if (isNonEmptyCsvCell(enriched)) {
|
|
16400
|
+
return enriched;
|
|
14749
16401
|
}
|
|
14750
|
-
|
|
14751
|
-
|
|
14752
|
-
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
14753
|
-
if (!rowsInfo) {
|
|
14754
|
-
throw new Error("The generated play did not return row-shaped output.");
|
|
16402
|
+
if (isNonEmptyCsvCell(base)) {
|
|
16403
|
+
return base;
|
|
14755
16404
|
}
|
|
14756
|
-
|
|
14757
|
-
const rows = dataExportRows(rowsInfo.rows);
|
|
14758
|
-
const columns = dataExportColumns(rows, rowsInfo.columns);
|
|
14759
|
-
await writeFile4(
|
|
14760
|
-
resolve11(outputPath),
|
|
14761
|
-
csvStringFromRows(rows, columns),
|
|
14762
|
-
"utf8"
|
|
14763
|
-
);
|
|
14764
|
-
return { rows: rows.length, path: resolve11(outputPath) };
|
|
16405
|
+
return void 0;
|
|
14765
16406
|
}
|
|
14766
16407
|
function assertCompleteRowsForCsvExport(rowsInfo, status, outputPath) {
|
|
14767
16408
|
if (rowsInfo.complete) {
|
|
@@ -14782,13 +16423,26 @@ async function compileConfig(input2) {
|
|
|
14782
16423
|
);
|
|
14783
16424
|
}
|
|
14784
16425
|
const config = await readConfig(input2.options.config);
|
|
14785
|
-
return (await input2.client.compileEnrichPlan({
|
|
16426
|
+
return (await input2.client.compileEnrichPlan({
|
|
16427
|
+
config,
|
|
16428
|
+
native_play_materialization: "inline_prebuilt"
|
|
16429
|
+
})).config;
|
|
14786
16430
|
}
|
|
14787
16431
|
const planArgs = await buildPlanArgs(input2.args);
|
|
14788
|
-
return (await input2.client.compileEnrichPlan({
|
|
16432
|
+
return (await input2.client.compileEnrichPlan({
|
|
16433
|
+
plan_args: planArgs,
|
|
16434
|
+
native_play_materialization: "inline_prebuilt"
|
|
16435
|
+
})).config;
|
|
14789
16436
|
}
|
|
14790
16437
|
function registerEnrichCommand(program) {
|
|
14791
|
-
program.command("enrich").allowUnknownOption(true).description("Run v1-style CSV enrichment through the V2 play runner.").
|
|
16438
|
+
program.command("enrich").allowUnknownOption(true).description("Run v1-style CSV enrichment through the V2 play runner.").configureHelp({
|
|
16439
|
+
optionTerm(option) {
|
|
16440
|
+
return option.long === "--timeout" ? "--timeout SECONDS" : option.flags;
|
|
16441
|
+
}
|
|
16442
|
+
}).option("--input <path>", "Input CSV path.").option("--csv <path>", "Alias for --input.").option("--output <path>", "Output CSV path.").option("--config <path>", "JSON enrich config.").requiredOption(
|
|
16443
|
+
"--name <name>",
|
|
16444
|
+
"Name for the compiled enrich play. Reuse a name to keep iterating on the same play; use a new name for a distinct enrichment so runs stay separate in the run ledger and share URLs."
|
|
16445
|
+
).option(
|
|
14792
16446
|
"--with <json>",
|
|
14793
16447
|
"Add a scalar enrich command.",
|
|
14794
16448
|
(value, previous = []) => [...previous, value]
|
|
@@ -14801,70 +16455,77 @@ function registerEnrichCommand(program) {
|
|
|
14801
16455
|
"Minimum list results for the current waterfall."
|
|
14802
16456
|
).option("--end-waterfall", "End the current waterfall group.").option(
|
|
14803
16457
|
"--rows <range>",
|
|
14804
|
-
"Zero-based row number or
|
|
16458
|
+
"Zero-based row number or inclusive range, e.g. 0:10."
|
|
14805
16459
|
).option("--all", "Run all rows.").option(
|
|
14806
16460
|
"--dry-run",
|
|
14807
16461
|
"Compile and print the generated plan without starting a run."
|
|
14808
|
-
).option("--json", "Emit JSON.").option("--force", "Force rerun for all enrich aliases.").option(
|
|
16462
|
+
).option("--json", "Emit JSON.").option("--no-open", "Do not open the play page in a browser.").option("--force", "Force rerun for all enrich aliases.").option(
|
|
14809
16463
|
"--with-force <aliases>",
|
|
14810
16464
|
"Force rerun for selected aliases.",
|
|
14811
16465
|
(value, previous = []) => [...previous, value]
|
|
14812
|
-
).option(
|
|
14813
|
-
"--
|
|
14814
|
-
"
|
|
16466
|
+
).option("--in-place", "Write enriched output back to the input CSV.").option(
|
|
16467
|
+
"--timeout <SECONDS>",
|
|
16468
|
+
"API read timeout for enrich status/API requests."
|
|
14815
16469
|
).action(async (options, _command) => {
|
|
14816
|
-
if (options.inPlace) {
|
|
14817
|
-
throw new Error(
|
|
14818
|
-
"--in-place is not supported by this V2 enrich runner yet. Use --output instead."
|
|
14819
|
-
);
|
|
16470
|
+
if (options.inPlace && options.dryRun) {
|
|
16471
|
+
throw new Error("--in-place is not supported with --dry-run.");
|
|
14820
16472
|
}
|
|
14821
16473
|
const inputCsv = options.input ?? options.csv;
|
|
14822
16474
|
if (!inputCsv) {
|
|
14823
16475
|
throw new Error("Missing required --input <csv> (or --csv <csv>).");
|
|
14824
16476
|
}
|
|
14825
16477
|
await assertInputCsvExists(inputCsv);
|
|
14826
|
-
if (options.output) {
|
|
16478
|
+
if (options.output && !options.inPlace) {
|
|
14827
16479
|
await assertSafeOutputPath(inputCsv, options.output);
|
|
14828
16480
|
}
|
|
14829
16481
|
if (!options.config && !hasPlanShapingArgs(currentEnrichArgs())) {
|
|
14830
16482
|
throw new Error("Pass --config or at least one --with enrich spec.");
|
|
14831
16483
|
}
|
|
14832
|
-
const
|
|
16484
|
+
const client2 = new DeeplineClient();
|
|
14833
16485
|
const args = currentEnrichArgs();
|
|
14834
|
-
const config = await compileConfig({ client, args, options });
|
|
14835
|
-
|
|
14836
|
-
const playSource = compileEnrichConfigToPlaySource(config, {
|
|
14837
|
-
forceAliases
|
|
14838
|
-
});
|
|
14839
|
-
const summary = summarizePlan(config, playSource);
|
|
14840
|
-
printDeprecationNotice(options);
|
|
16486
|
+
const config = await compileConfig({ client: client2, args, options });
|
|
16487
|
+
emitEnrichDebugValidationLines(config);
|
|
14841
16488
|
if (options.dryRun) {
|
|
16489
|
+
const forceAliases2 = resolveForceAliases(config, options);
|
|
16490
|
+
const playSource2 = compileEnrichConfigToPlaySource(config, {
|
|
16491
|
+
forceAliases: forceAliases2,
|
|
16492
|
+
playName: options.name
|
|
16493
|
+
});
|
|
16494
|
+
const summary = summarizePlan(config, playSource2);
|
|
14842
16495
|
if (options.json) {
|
|
14843
16496
|
printJson({
|
|
14844
16497
|
dryRun: true,
|
|
14845
|
-
deprecation: ENRICH_DEPRECATION_NOTICE,
|
|
14846
16498
|
input: resolve11(inputCsv),
|
|
14847
16499
|
output: options.output ? resolve11(options.output) : null,
|
|
14848
16500
|
plan: summary
|
|
14849
16501
|
});
|
|
14850
16502
|
return;
|
|
14851
16503
|
}
|
|
14852
|
-
process.stdout.write(`${
|
|
16504
|
+
process.stdout.write(`${playSource2}
|
|
14853
16505
|
`);
|
|
14854
16506
|
return;
|
|
14855
16507
|
}
|
|
14856
16508
|
const rows = parseRows(options.rows, options.all);
|
|
14857
|
-
|
|
14858
|
-
|
|
14859
|
-
|
|
14860
|
-
|
|
16509
|
+
const outputPath = options.inPlace ? inputCsv : options.output;
|
|
16510
|
+
const sourceCsvPath = !options.inPlace && outputPath && await regularFileExists(outputPath) ? outputPath : inputCsv;
|
|
16511
|
+
const forceAliases = resolveForceAliases(config, options);
|
|
16512
|
+
for (const alias of collectFailedInputAliases(
|
|
16513
|
+
config,
|
|
16514
|
+
sourceCsvPath,
|
|
16515
|
+
rows
|
|
16516
|
+
)) {
|
|
16517
|
+
forceAliases.add(alias);
|
|
14861
16518
|
}
|
|
16519
|
+
const playSource = compileEnrichConfigToPlaySource(config, {
|
|
16520
|
+
forceAliases,
|
|
16521
|
+
playName: options.name
|
|
16522
|
+
});
|
|
14862
16523
|
const tempDir = await mkdtemp(join8(tmpdir3(), "deepline-enrich-play-"));
|
|
14863
16524
|
const tempPlay = join8(tempDir, "deepline-enrich.play.ts");
|
|
14864
16525
|
try {
|
|
14865
16526
|
await writeFile4(tempPlay, playSource, "utf8");
|
|
14866
16527
|
const runtimeInput = {
|
|
14867
|
-
file: resolve11(
|
|
16528
|
+
file: resolve11(sourceCsvPath),
|
|
14868
16529
|
...rows.rowStart !== null ? { rowStart: rows.rowStart } : {},
|
|
14869
16530
|
...rows.rowEnd !== null ? { rowEnd: rows.rowEnd } : {}
|
|
14870
16531
|
};
|
|
@@ -14873,40 +16534,91 @@ function registerEnrichCommand(program) {
|
|
|
14873
16534
|
tempPlay,
|
|
14874
16535
|
"--input",
|
|
14875
16536
|
JSON.stringify(runtimeInput),
|
|
14876
|
-
"--watch"
|
|
14877
|
-
"--no-open",
|
|
14878
|
-
"--json"
|
|
16537
|
+
"--watch"
|
|
14879
16538
|
];
|
|
14880
|
-
|
|
14881
|
-
|
|
16539
|
+
if (options.noOpen) {
|
|
16540
|
+
runArgs.push("--no-open");
|
|
16541
|
+
}
|
|
16542
|
+
if (options.json) {
|
|
16543
|
+
runArgs.push("--json");
|
|
16544
|
+
} else {
|
|
16545
|
+
runArgs.push("--logs");
|
|
16546
|
+
}
|
|
16547
|
+
const timeoutSeconds = options.timeout ? Number.parseInt(options.timeout, 10) : null;
|
|
16548
|
+
if (timeoutSeconds !== null && Number.isFinite(timeoutSeconds) && timeoutSeconds > 0) {
|
|
16549
|
+
runArgs.push("--tail-timeout-ms", String(timeoutSeconds * 1e3));
|
|
16550
|
+
}
|
|
16551
|
+
const captured = await runGeneratedEnrichPlay(runArgs, {
|
|
16552
|
+
passthroughStdout: !options.json
|
|
16553
|
+
});
|
|
16554
|
+
const status = options.json ? parseJsonOutput(captured.stdout) : await resolveWatchedGeneratedPlayStatus({
|
|
16555
|
+
client: client2,
|
|
16556
|
+
stdout: captured.stdout,
|
|
16557
|
+
exitCode: captured.result
|
|
16558
|
+
});
|
|
14882
16559
|
if (captured.result !== 0) {
|
|
14883
16560
|
if (options.json) {
|
|
14884
16561
|
printJson({
|
|
14885
|
-
deprecation: ENRICH_DEPRECATION_NOTICE,
|
|
14886
16562
|
result: status
|
|
14887
16563
|
});
|
|
14888
|
-
} else {
|
|
14889
|
-
process.stdout.write(captured.stdout);
|
|
14890
16564
|
}
|
|
14891
16565
|
process.exitCode = captured.result;
|
|
14892
16566
|
return;
|
|
14893
16567
|
}
|
|
14894
|
-
const exportResult =
|
|
16568
|
+
const exportResult = outputPath ? await writeOutputCsv(outputPath, status, {
|
|
16569
|
+
client: client2,
|
|
16570
|
+
config,
|
|
16571
|
+
sourceCsvPath,
|
|
16572
|
+
rows
|
|
16573
|
+
}) : null;
|
|
16574
|
+
const rowsForFailureReport = exportResult?.enrichedRows ?? extractCanonicalRowsInfo(status)?.rows ?? [];
|
|
14895
16575
|
if (options.json) {
|
|
16576
|
+
const failureReport2 = await maybeEmitEnrichFailureReport({
|
|
16577
|
+
config,
|
|
16578
|
+
rows: rowsForFailureReport,
|
|
16579
|
+
rowRange: rows,
|
|
16580
|
+
client: client2,
|
|
16581
|
+
outputPath: exportResult?.path ?? outputPath ?? null
|
|
16582
|
+
});
|
|
14896
16583
|
printJson({
|
|
14897
|
-
ok:
|
|
14898
|
-
deprecation: ENRICH_DEPRECATION_NOTICE,
|
|
16584
|
+
ok: !failureReport2,
|
|
14899
16585
|
run: status,
|
|
14900
|
-
output: exportResult
|
|
16586
|
+
output: exportResult ? { rows: exportResult.rows, path: exportResult.path } : null,
|
|
16587
|
+
...failureReport2 ? {
|
|
16588
|
+
failure_report: {
|
|
16589
|
+
path: failureReport2.path,
|
|
16590
|
+
jobs: failureReport2.jobs.length
|
|
16591
|
+
}
|
|
16592
|
+
} : {}
|
|
14901
16593
|
});
|
|
16594
|
+
if (failureReport2) {
|
|
16595
|
+
process.exitCode = EXIT_SERVER2;
|
|
16596
|
+
}
|
|
14902
16597
|
return;
|
|
14903
16598
|
}
|
|
14904
|
-
process.stdout.write(captured.stdout);
|
|
14905
16599
|
if (exportResult) {
|
|
14906
16600
|
process.stderr.write(
|
|
14907
16601
|
`Wrote ${exportResult.rows} row(s) to ${exportResult.path}
|
|
14908
16602
|
`
|
|
14909
16603
|
);
|
|
16604
|
+
const waterfallSummaryLines = buildEnrichWaterfallSummaryLines(
|
|
16605
|
+
config,
|
|
16606
|
+
exportResult.enrichedRows
|
|
16607
|
+
);
|
|
16608
|
+
if (waterfallSummaryLines.length > 0) {
|
|
16609
|
+
process.stdout.write(`${waterfallSummaryLines.join("\n")}
|
|
16610
|
+
`);
|
|
16611
|
+
}
|
|
16612
|
+
}
|
|
16613
|
+
const failureReport = await maybeEmitEnrichFailureReport({
|
|
16614
|
+
config,
|
|
16615
|
+
rows: rowsForFailureReport,
|
|
16616
|
+
rowRange: rows,
|
|
16617
|
+
client: client2,
|
|
16618
|
+
outputPath: exportResult?.path ?? outputPath ?? null
|
|
16619
|
+
});
|
|
16620
|
+
if (failureReport) {
|
|
16621
|
+
process.exitCode = EXIT_SERVER2;
|
|
14910
16622
|
}
|
|
14911
16623
|
} finally {
|
|
14912
16624
|
await rm(tempDir, { recursive: true, force: true });
|
|
@@ -15146,7 +16858,7 @@ async function readHiddenLine(prompt) {
|
|
|
15146
16858
|
if (typeof input.setRawMode === "function") input.setRawMode(true);
|
|
15147
16859
|
let value = "";
|
|
15148
16860
|
input.resume();
|
|
15149
|
-
return await new Promise((
|
|
16861
|
+
return await new Promise((resolve15, reject) => {
|
|
15150
16862
|
let settled = false;
|
|
15151
16863
|
const cleanup = () => {
|
|
15152
16864
|
input.off("data", onData);
|
|
@@ -15161,7 +16873,7 @@ async function readHiddenLine(prompt) {
|
|
|
15161
16873
|
settled = true;
|
|
15162
16874
|
output.write("\n");
|
|
15163
16875
|
cleanup();
|
|
15164
|
-
|
|
16876
|
+
resolve15(line);
|
|
15165
16877
|
};
|
|
15166
16878
|
const fail = (error) => {
|
|
15167
16879
|
if (settled) return;
|
|
@@ -15225,8 +16937,8 @@ function preventShellHistoryLeak(forbidden) {
|
|
|
15225
16937
|
}
|
|
15226
16938
|
}
|
|
15227
16939
|
async function handleList(options) {
|
|
15228
|
-
const
|
|
15229
|
-
const secrets = await
|
|
16940
|
+
const client2 = new DeeplineClient();
|
|
16941
|
+
const secrets = await client2.listSecrets();
|
|
15230
16942
|
printCommandEnvelope(
|
|
15231
16943
|
{
|
|
15232
16944
|
secrets,
|
|
@@ -15245,8 +16957,8 @@ async function handleList(options) {
|
|
|
15245
16957
|
}
|
|
15246
16958
|
async function handleCheck(nameInput, options) {
|
|
15247
16959
|
const name = normalizeSecretName(nameInput);
|
|
15248
|
-
const
|
|
15249
|
-
const secret = await
|
|
16960
|
+
const client2 = new DeeplineClient();
|
|
16961
|
+
const secret = await client2.checkSecret(name);
|
|
15250
16962
|
printCommandEnvelope(
|
|
15251
16963
|
{
|
|
15252
16964
|
ok: Boolean(secret),
|
|
@@ -15560,12 +17272,12 @@ function matchesGrepQuery(value, query, mode) {
|
|
|
15560
17272
|
return terms.every((term) => haystack.includes(term));
|
|
15561
17273
|
}
|
|
15562
17274
|
async function listTools(args) {
|
|
15563
|
-
const
|
|
17275
|
+
const client2 = new DeeplineClient();
|
|
15564
17276
|
const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
|
|
15565
17277
|
const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
|
|
15566
17278
|
const compact = !args.includes("--full");
|
|
15567
17279
|
const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
15568
|
-
const items = (await
|
|
17280
|
+
const items = (await client2.listTools({
|
|
15569
17281
|
...categoryFilter ? { categories: categoryFilter } : {},
|
|
15570
17282
|
compact
|
|
15571
17283
|
})).map(toListedTool).filter(
|
|
@@ -15612,8 +17324,8 @@ async function searchTools(queryInput, options = {}) {
|
|
|
15612
17324
|
console.error("Usage: deepline tools search <query> [--json]");
|
|
15613
17325
|
return 1;
|
|
15614
17326
|
}
|
|
15615
|
-
const
|
|
15616
|
-
const result = await
|
|
17327
|
+
const client2 = new DeeplineClient();
|
|
17328
|
+
const result = await client2.searchTools({
|
|
15617
17329
|
query,
|
|
15618
17330
|
categories: options.categories,
|
|
15619
17331
|
searchTerms: options.searchTerms,
|
|
@@ -15636,10 +17348,10 @@ async function grepTools(queryInput, options = {}) {
|
|
|
15636
17348
|
console.error("Usage: deepline tools grep <query> [--json]");
|
|
15637
17349
|
return 1;
|
|
15638
17350
|
}
|
|
15639
|
-
const
|
|
17351
|
+
const client2 = new DeeplineClient();
|
|
15640
17352
|
const requestedCategories = options.categories ? options.categories.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
15641
17353
|
const mode = options.mode ?? "all";
|
|
15642
|
-
const tools = (await
|
|
17354
|
+
const tools = (await client2.listTools({
|
|
15643
17355
|
grep: query,
|
|
15644
17356
|
grepMode: mode,
|
|
15645
17357
|
...options.categories ? { categories: options.categories } : {},
|
|
@@ -15709,12 +17421,12 @@ function compactTool(tool) {
|
|
|
15709
17421
|
function playIdentifiers(play) {
|
|
15710
17422
|
return [play.name, play.reference, ...play.aliases ?? []].filter((value) => Boolean(value?.trim())).map((value) => value.trim());
|
|
15711
17423
|
}
|
|
15712
|
-
async function findPlayForToolId(
|
|
17424
|
+
async function findPlayForToolId(client2, toolId) {
|
|
15713
17425
|
const requested = toolId.trim();
|
|
15714
17426
|
if (!requested) {
|
|
15715
17427
|
return null;
|
|
15716
17428
|
}
|
|
15717
|
-
const plays = await
|
|
17429
|
+
const plays = await client2.searchPlays({ query: requested, compact: true });
|
|
15718
17430
|
return plays.find((play) => playIdentifiers(play).includes(requested)) ?? null;
|
|
15719
17431
|
}
|
|
15720
17432
|
function playAliasToolErrorMessage(toolId, play) {
|
|
@@ -15729,7 +17441,7 @@ function printPlayAliasToolError(toolId, play) {
|
|
|
15729
17441
|
function isPlayLikeTool(tool) {
|
|
15730
17442
|
const record = tool;
|
|
15731
17443
|
if (record.isPlay === true || record.is_play === true) return true;
|
|
15732
|
-
const playExpansion =
|
|
17444
|
+
const playExpansion = recordField2(record, "playExpansion", "play_expansion");
|
|
15733
17445
|
if (Object.keys(playExpansion).length > 0) return true;
|
|
15734
17446
|
const toolId = typeof record.toolId === "string" ? record.toolId : "";
|
|
15735
17447
|
return toolId.endsWith("_waterfall");
|
|
@@ -15963,12 +17675,12 @@ async function getTool(toolId, options = {}) {
|
|
|
15963
17675
|
console.error("Usage: deepline tools get <toolId> [--json]");
|
|
15964
17676
|
return 1;
|
|
15965
17677
|
}
|
|
15966
|
-
const
|
|
17678
|
+
const client2 = new DeeplineClient();
|
|
15967
17679
|
let tool;
|
|
15968
17680
|
try {
|
|
15969
|
-
tool = await
|
|
17681
|
+
tool = await client2.getTool(toolId);
|
|
15970
17682
|
} catch (error) {
|
|
15971
|
-
const play = await findPlayForToolId(
|
|
17683
|
+
const play = await findPlayForToolId(client2, toolId);
|
|
15972
17684
|
if (play) {
|
|
15973
17685
|
printPlayAliasToolError(toolId, play);
|
|
15974
17686
|
return 2;
|
|
@@ -16035,10 +17747,10 @@ async function getTool(toolId, options = {}) {
|
|
|
16035
17747
|
function toolContractJsonForDescribe(tool, requestedToolId) {
|
|
16036
17748
|
const toolId = String(tool.toolId || requestedToolId);
|
|
16037
17749
|
const inputFields = toolInputFieldsForDisplay(
|
|
16038
|
-
|
|
17750
|
+
recordField2(tool, "inputSchema", "input_schema")
|
|
16039
17751
|
);
|
|
16040
|
-
const usageGuidance =
|
|
16041
|
-
const toolExecutionResult =
|
|
17752
|
+
const usageGuidance = recordField2(tool, "usageGuidance", "usage_guidance");
|
|
17753
|
+
const toolExecutionResult = recordField2(
|
|
16042
17754
|
usageGuidance,
|
|
16043
17755
|
"toolExecutionResult",
|
|
16044
17756
|
"tool_execution_result"
|
|
@@ -16049,7 +17761,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
|
|
|
16049
17761
|
const extractedValues = extractionContractEntries(
|
|
16050
17762
|
arrayField(toolExecutionResult, "extractedValues", "extracted_values")
|
|
16051
17763
|
);
|
|
16052
|
-
const cost =
|
|
17764
|
+
const cost = recordField2(tool, "cost");
|
|
16053
17765
|
const deeplineCredits = numberField(
|
|
16054
17766
|
tool,
|
|
16055
17767
|
"deeplineCreditsPerPricingUnit",
|
|
@@ -16103,7 +17815,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
|
|
|
16103
17815
|
}
|
|
16104
17816
|
function extractionContractEntries(entries) {
|
|
16105
17817
|
return entries.flatMap((entry) => {
|
|
16106
|
-
if (!
|
|
17818
|
+
if (!isRecord7(entry)) return [];
|
|
16107
17819
|
const name = stringField(entry, "name");
|
|
16108
17820
|
const expression = stringField(entry, "expression");
|
|
16109
17821
|
return name && expression ? [{ name, expression }] : [];
|
|
@@ -16111,8 +17823,8 @@ function extractionContractEntries(entries) {
|
|
|
16111
17823
|
}
|
|
16112
17824
|
function printCompactToolContract(tool, requestedToolId) {
|
|
16113
17825
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
16114
|
-
const cost =
|
|
16115
|
-
const getters =
|
|
17826
|
+
const cost = isRecord7(contract.cost) ? contract.cost : {};
|
|
17827
|
+
const getters = isRecord7(contract.getters) ? contract.getters : {};
|
|
16116
17828
|
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
16117
17829
|
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
16118
17830
|
const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
|
|
@@ -16129,7 +17841,7 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
16129
17841
|
console.log("");
|
|
16130
17842
|
console.log("Inputs:");
|
|
16131
17843
|
for (const field of inputFields) {
|
|
16132
|
-
if (!
|
|
17844
|
+
if (!isRecord7(field)) continue;
|
|
16133
17845
|
const name = stringField(field, "name");
|
|
16134
17846
|
if (!name) continue;
|
|
16135
17847
|
const required = field.required ? "*" : "";
|
|
@@ -16142,7 +17854,7 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
16142
17854
|
}
|
|
16143
17855
|
console.log("");
|
|
16144
17856
|
printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
|
|
16145
|
-
const starterScript =
|
|
17857
|
+
const starterScript = isRecord7(contract.starterScript) ? contract.starterScript : {};
|
|
16146
17858
|
const starterPath = stringField(starterScript, "path");
|
|
16147
17859
|
if (starterPath) {
|
|
16148
17860
|
console.log("");
|
|
@@ -16156,14 +17868,14 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
16156
17868
|
console.log("Getters:");
|
|
16157
17869
|
if (listGetters.length) console.log("Lists:");
|
|
16158
17870
|
for (const entry of listGetters) {
|
|
16159
|
-
if (
|
|
17871
|
+
if (isRecord7(entry))
|
|
16160
17872
|
console.log(
|
|
16161
17873
|
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
16162
17874
|
);
|
|
16163
17875
|
}
|
|
16164
17876
|
if (valueGetters.length) console.log("Values:");
|
|
16165
17877
|
for (const entry of valueGetters) {
|
|
16166
|
-
if (
|
|
17878
|
+
if (isRecord7(entry))
|
|
16167
17879
|
console.log(
|
|
16168
17880
|
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
16169
17881
|
);
|
|
@@ -16176,7 +17888,7 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
16176
17888
|
}
|
|
16177
17889
|
function printToolPricingOnly(tool, requestedToolId, options = {}) {
|
|
16178
17890
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
16179
|
-
const cost =
|
|
17891
|
+
const cost = isRecord7(contract.cost) ? contract.cost : {};
|
|
16180
17892
|
const pricingModel = stringField(cost, "pricingModel") || "unknown";
|
|
16181
17893
|
const billingMode = stringField(cost, "billingMode") || "unknown";
|
|
16182
17894
|
const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
|
|
@@ -16196,7 +17908,7 @@ function printToolSchemaOnly(tool, requestedToolId) {
|
|
|
16196
17908
|
}
|
|
16197
17909
|
console.log("Inputs:");
|
|
16198
17910
|
for (const field of inputFields) {
|
|
16199
|
-
if (!
|
|
17911
|
+
if (!isRecord7(field)) continue;
|
|
16200
17912
|
const name = stringField(field, "name");
|
|
16201
17913
|
if (!name) continue;
|
|
16202
17914
|
const required = field.required ? "*" : "";
|
|
@@ -16222,27 +17934,27 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
|
|
|
16222
17934
|
` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`
|
|
16223
17935
|
);
|
|
16224
17936
|
console.log("});");
|
|
16225
|
-
const getters =
|
|
17937
|
+
const getters = isRecord7(contract.getters) ? contract.getters : {};
|
|
16226
17938
|
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
16227
17939
|
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
16228
|
-
const firstGetter = [...valueGetters, ...listGetters].find(
|
|
17940
|
+
const firstGetter = [...valueGetters, ...listGetters].find(isRecord7);
|
|
16229
17941
|
if (firstGetter) {
|
|
16230
17942
|
const name = stringField(firstGetter, "name") || "value";
|
|
16231
17943
|
const expression = stringField(firstGetter, "expression");
|
|
16232
17944
|
if (expression)
|
|
16233
17945
|
console.log(
|
|
16234
|
-
`const ${
|
|
17946
|
+
`const ${safeIdentifier2(name)} = ${expression.replace(/^toolExecutionResult\./, "result.")};`
|
|
16235
17947
|
);
|
|
16236
17948
|
}
|
|
16237
17949
|
console.log("```");
|
|
16238
17950
|
if (options.includeSamples !== false) {
|
|
16239
|
-
const samples =
|
|
17951
|
+
const samples = recordField2(tool, "samples");
|
|
16240
17952
|
printSamples(samples);
|
|
16241
17953
|
}
|
|
16242
17954
|
}
|
|
16243
17955
|
function printToolGettersOnly(tool, requestedToolId) {
|
|
16244
17956
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
16245
|
-
const getters =
|
|
17957
|
+
const getters = isRecord7(contract.getters) ? contract.getters : {};
|
|
16246
17958
|
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
16247
17959
|
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
16248
17960
|
console.log(`Getters: ${contract.toolId}`);
|
|
@@ -16255,7 +17967,7 @@ function printToolGettersOnly(tool, requestedToolId) {
|
|
|
16255
17967
|
if (listGetters.length) {
|
|
16256
17968
|
console.log("Lists:");
|
|
16257
17969
|
for (const entry of listGetters) {
|
|
16258
|
-
if (
|
|
17970
|
+
if (isRecord7(entry))
|
|
16259
17971
|
console.log(
|
|
16260
17972
|
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
16261
17973
|
);
|
|
@@ -16264,7 +17976,7 @@ function printToolGettersOnly(tool, requestedToolId) {
|
|
|
16264
17976
|
if (valueGetters.length) {
|
|
16265
17977
|
console.log("Values:");
|
|
16266
17978
|
for (const entry of valueGetters) {
|
|
16267
|
-
if (
|
|
17979
|
+
if (isRecord7(entry))
|
|
16268
17980
|
console.log(
|
|
16269
17981
|
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
16270
17982
|
);
|
|
@@ -16290,7 +18002,7 @@ function sampleValueForField(field) {
|
|
|
16290
18002
|
function samplePayloadForInputFields(fields) {
|
|
16291
18003
|
return Object.fromEntries(
|
|
16292
18004
|
fields.slice(0, 4).flatMap((field) => {
|
|
16293
|
-
if (!
|
|
18005
|
+
if (!isRecord7(field)) return [];
|
|
16294
18006
|
const name = stringField(field, "name");
|
|
16295
18007
|
if (!name) return [];
|
|
16296
18008
|
return [[name, sampleValueForField(field)]];
|
|
@@ -16300,7 +18012,7 @@ function samplePayloadForInputFields(fields) {
|
|
|
16300
18012
|
function stableStepIdForTool(toolId) {
|
|
16301
18013
|
return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
|
|
16302
18014
|
}
|
|
16303
|
-
function
|
|
18015
|
+
function safeIdentifier2(name) {
|
|
16304
18016
|
const cleaned = name.replace(/[^a-zA-Z0-9_$]+/g, "_").replace(/^[^a-zA-Z_$]+/, "");
|
|
16305
18017
|
return cleaned || "value";
|
|
16306
18018
|
}
|
|
@@ -16313,7 +18025,7 @@ function playResultExpression(entry) {
|
|
|
16313
18025
|
function toolMetadataJsonForDescribe(tool, requestedToolId) {
|
|
16314
18026
|
const toolId = String(tool.toolId || requestedToolId);
|
|
16315
18027
|
const inputFields = toolInputFieldsForDisplay(
|
|
16316
|
-
|
|
18028
|
+
recordField2(tool, "inputSchema", "input_schema")
|
|
16317
18029
|
);
|
|
16318
18030
|
const starterScript = seedToolListScript({
|
|
16319
18031
|
toolId,
|
|
@@ -16340,7 +18052,7 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
|
|
|
16340
18052
|
provider: tool.provider,
|
|
16341
18053
|
displayName: tool.displayName,
|
|
16342
18054
|
usageGuidance: usageGuidanceWithAccessDefaults(
|
|
16343
|
-
|
|
18055
|
+
recordField2(tool, "usageGuidance", "usage_guidance")
|
|
16344
18056
|
),
|
|
16345
18057
|
runtimeOutputHelp: {
|
|
16346
18058
|
contract: "tools describe shows declared schema and Deepline getters; it is not an observed provider response.",
|
|
@@ -16365,7 +18077,7 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
|
|
|
16365
18077
|
}
|
|
16366
18078
|
function usageGuidanceWithAccessDefaults(usageGuidance) {
|
|
16367
18079
|
if (Object.keys(usageGuidance).length === 0) return usageGuidance;
|
|
16368
|
-
const existingAccess =
|
|
18080
|
+
const existingAccess = recordField2(usageGuidance, "access");
|
|
16369
18081
|
return {
|
|
16370
18082
|
...usageGuidance,
|
|
16371
18083
|
access: {
|
|
@@ -16398,18 +18110,18 @@ function listedToolDescription(tool) {
|
|
|
16398
18110
|
}
|
|
16399
18111
|
function formatListedToolCost(tool) {
|
|
16400
18112
|
const record = tool;
|
|
16401
|
-
const pricing =
|
|
18113
|
+
const pricing = recordField2(record, "pricing");
|
|
16402
18114
|
const displayText = stringField(pricing, "displayText", "display_text");
|
|
16403
18115
|
return displayText ? `Cost: ${displayText}` : "";
|
|
16404
18116
|
}
|
|
16405
18117
|
function toolInputFieldsForDisplay(inputSchema) {
|
|
16406
18118
|
if (Array.isArray(inputSchema.fields))
|
|
16407
|
-
return inputSchema.fields.filter(
|
|
16408
|
-
const jsonSchema =
|
|
16409
|
-
const properties =
|
|
18119
|
+
return inputSchema.fields.filter(isRecord7);
|
|
18120
|
+
const jsonSchema = isRecord7(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
|
|
18121
|
+
const properties = isRecord7(jsonSchema.properties) ? jsonSchema.properties : {};
|
|
16410
18122
|
const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
|
|
16411
18123
|
return Object.entries(properties).map(([name, value]) => {
|
|
16412
|
-
const property =
|
|
18124
|
+
const property = isRecord7(value) ? value : {};
|
|
16413
18125
|
return {
|
|
16414
18126
|
name,
|
|
16415
18127
|
type: typeof property.type === "string" ? property.type : "unknown",
|
|
@@ -16438,15 +18150,15 @@ function printJsonPreview(label, payload) {
|
|
|
16438
18150
|
}
|
|
16439
18151
|
function samplePayload(samples, key) {
|
|
16440
18152
|
const entry = samples[key];
|
|
16441
|
-
if (!
|
|
18153
|
+
if (!isRecord7(entry)) return void 0;
|
|
16442
18154
|
return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
|
|
16443
18155
|
}
|
|
16444
18156
|
function commandEnvelopeFromRawResponse(rawResponse) {
|
|
16445
|
-
return
|
|
18157
|
+
return isRecord7(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
|
|
16446
18158
|
}
|
|
16447
18159
|
function listExtractorPathsFromUsageGuidance(tool) {
|
|
16448
18160
|
const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
|
|
16449
|
-
const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists :
|
|
18161
|
+
const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord7(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
|
|
16450
18162
|
return extractedLists.flatMap((entry) => {
|
|
16451
18163
|
const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
|
|
16452
18164
|
if (!Array.isArray(paths)) return [];
|
|
@@ -16462,7 +18174,7 @@ function formatDecimal(value) {
|
|
|
16462
18174
|
function formatUsd(value) {
|
|
16463
18175
|
return `$${formatDecimal(value)}`;
|
|
16464
18176
|
}
|
|
16465
|
-
function
|
|
18177
|
+
function isRecord7(value) {
|
|
16466
18178
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
16467
18179
|
}
|
|
16468
18180
|
function stringField(source, ...keys) {
|
|
@@ -16486,10 +18198,10 @@ function arrayField(source, ...keys) {
|
|
|
16486
18198
|
}
|
|
16487
18199
|
return [];
|
|
16488
18200
|
}
|
|
16489
|
-
function
|
|
18201
|
+
function recordField2(source, ...keys) {
|
|
16490
18202
|
for (const key of keys) {
|
|
16491
18203
|
const value = source[key];
|
|
16492
|
-
if (
|
|
18204
|
+
if (isRecord7(value)) return value;
|
|
16493
18205
|
}
|
|
16494
18206
|
return {};
|
|
16495
18207
|
}
|
|
@@ -16555,7 +18267,7 @@ function parseJsonObjectArgument(raw, flagName) {
|
|
|
16555
18267
|
}
|
|
16556
18268
|
throw invalidJsonError(flagName, message);
|
|
16557
18269
|
}
|
|
16558
|
-
if (!
|
|
18270
|
+
if (!isRecord7(parsed)) {
|
|
16559
18271
|
throw invalidJsonError(flagName, "expected an object.");
|
|
16560
18272
|
}
|
|
16561
18273
|
return parsed;
|
|
@@ -16678,7 +18390,7 @@ function buildToolExecuteBaseEnvelope(input2) {
|
|
|
16678
18390
|
kind: summaryEntries.length > 0 ? "object" : "raw",
|
|
16679
18391
|
summary: input2.summary
|
|
16680
18392
|
};
|
|
16681
|
-
const envelopeHasCanonicalOutput =
|
|
18393
|
+
const envelopeHasCanonicalOutput = isRecord7(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
|
|
16682
18394
|
const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote(JSON.stringify(input2.params))} --json`;
|
|
16683
18395
|
const actions = input2.listConversion ? [
|
|
16684
18396
|
{
|
|
@@ -16733,12 +18445,12 @@ async function executeTool(args) {
|
|
|
16733
18445
|
}
|
|
16734
18446
|
return 1;
|
|
16735
18447
|
}
|
|
16736
|
-
const
|
|
18448
|
+
const client2 = new DeeplineClient();
|
|
16737
18449
|
let metadata;
|
|
16738
18450
|
try {
|
|
16739
|
-
metadata = await
|
|
18451
|
+
metadata = await client2.getTool(parsed.toolId);
|
|
16740
18452
|
} catch (error) {
|
|
16741
|
-
const play = await findPlayForToolId(
|
|
18453
|
+
const play = await findPlayForToolId(client2, parsed.toolId);
|
|
16742
18454
|
if (play) {
|
|
16743
18455
|
if (argsWantJson(args)) {
|
|
16744
18456
|
printJsonError(
|
|
@@ -16762,7 +18474,7 @@ async function executeTool(args) {
|
|
|
16762
18474
|
}
|
|
16763
18475
|
return 2;
|
|
16764
18476
|
}
|
|
16765
|
-
const rawResponse = await
|
|
18477
|
+
const rawResponse = await client2.executeTool(parsed.toolId, parsed.params);
|
|
16766
18478
|
const listConversion = tryConvertToList(rawResponse, {
|
|
16767
18479
|
listExtractorPaths: listExtractorPathsFromUsageGuidance(metadata)
|
|
16768
18480
|
});
|
|
@@ -16787,7 +18499,7 @@ async function executeTool(args) {
|
|
|
16787
18499
|
{
|
|
16788
18500
|
...baseEnvelope,
|
|
16789
18501
|
local: {
|
|
16790
|
-
...
|
|
18502
|
+
...isRecord7(baseEnvelope.local) ? baseEnvelope.local : {},
|
|
16791
18503
|
payload_file: jsonPath
|
|
16792
18504
|
}
|
|
16793
18505
|
},
|
|
@@ -16908,10 +18620,499 @@ async function executeTool(args) {
|
|
|
16908
18620
|
return 0;
|
|
16909
18621
|
}
|
|
16910
18622
|
|
|
18623
|
+
// src/cli/commands/workflow.ts
|
|
18624
|
+
import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
18625
|
+
import { dirname as dirname9, join as join11, resolve as resolve13 } from "path";
|
|
18626
|
+
|
|
18627
|
+
// src/cli/workflow-to-play.ts
|
|
18628
|
+
import { createHash as createHash4 } from "crypto";
|
|
18629
|
+
var HITL_WAIT_FOR_SIGNAL_TOOL = "deepline_workflow_wait_for_signal";
|
|
18630
|
+
var HITL_SLACK_TOOL = "slack_message_with_hitl";
|
|
18631
|
+
var SUB_WORKFLOW_TOOL_PREFIX = "deepline_workflow_";
|
|
18632
|
+
var UNSUPPORTED_REASON_HINT = {
|
|
18633
|
+
unsafe_user_code: "a step's JavaScript (extract_js / run_if_js / run_javascript) uses non-deterministic or sandbox-escaping code (e.g. new Date(), Date.now(), Math.random(), fetch, require). Plays replay deterministically inside a sandbox, so the compiler rejects it \u2014 rewrite it to compute from the row/result before migrating.",
|
|
18634
|
+
hitl: "human-in-the-loop / wait-for-signal steps have no play-runtime equivalent yet.",
|
|
18635
|
+
sub_workflow: "sub-workflow calls have no play-runtime equivalent yet (a future ctx.runPlay bridge).",
|
|
18636
|
+
nested_waterfall: "nested with_waterfall blocks are not supported by the play compiler.",
|
|
18637
|
+
inline_secret: 'a step inlines a hardcoded secret (API key / token / private key). Plays forbid inline secrets \u2014 move it to a dashboard secret and read it with ctx.secrets.get("NAME") before migrating.'
|
|
18638
|
+
};
|
|
18639
|
+
var UnsupportedWorkflowCommandError = class extends Error {
|
|
18640
|
+
status = 400;
|
|
18641
|
+
unsupported;
|
|
18642
|
+
constructor(unsupported) {
|
|
18643
|
+
super(
|
|
18644
|
+
`Workflow uses ${unsupported.length} command${unsupported.length === 1 ? "" : "s"} that cannot run as a play yet: ${unsupported.map((entry) => `${entry.alias} (${entry.reason})`).join(", ")}.`
|
|
18645
|
+
);
|
|
18646
|
+
this.name = "UnsupportedWorkflowCommandError";
|
|
18647
|
+
this.unsupported = unsupported;
|
|
18648
|
+
}
|
|
18649
|
+
};
|
|
18650
|
+
function isWaterfall2(command) {
|
|
18651
|
+
return "with_waterfall" in command;
|
|
18652
|
+
}
|
|
18653
|
+
function stepInlinedText(command) {
|
|
18654
|
+
return [
|
|
18655
|
+
JSON.stringify(command.payload ?? {}),
|
|
18656
|
+
command.extract_js ?? "",
|
|
18657
|
+
command.run_if_js ?? ""
|
|
18658
|
+
].join("\n");
|
|
18659
|
+
}
|
|
18660
|
+
function stepUserCode(command) {
|
|
18661
|
+
const code = [];
|
|
18662
|
+
if (command.extract_js) code.push(command.extract_js);
|
|
18663
|
+
if (command.run_if_js) code.push(command.run_if_js);
|
|
18664
|
+
if (command.tool === "run_javascript" && typeof command.payload?.code === "string") {
|
|
18665
|
+
code.push(command.payload.code);
|
|
18666
|
+
}
|
|
18667
|
+
return code;
|
|
18668
|
+
}
|
|
18669
|
+
function hasUnsafeUserCode(command) {
|
|
18670
|
+
return stepUserCode(command).some((code) => {
|
|
18671
|
+
try {
|
|
18672
|
+
assertUserCodeIsSafe(code, "play step code");
|
|
18673
|
+
return false;
|
|
18674
|
+
} catch {
|
|
18675
|
+
return true;
|
|
18676
|
+
}
|
|
18677
|
+
});
|
|
18678
|
+
}
|
|
18679
|
+
function classifyUnsupportedCommand(command) {
|
|
18680
|
+
const { tool } = command;
|
|
18681
|
+
if (tool === HITL_SLACK_TOOL || tool === HITL_WAIT_FOR_SIGNAL_TOOL) {
|
|
18682
|
+
return "hitl";
|
|
18683
|
+
}
|
|
18684
|
+
if (tool.startsWith(SUB_WORKFLOW_TOOL_PREFIX)) return "sub_workflow";
|
|
18685
|
+
if (collectInlineSecretFindings(stepInlinedText(command)).length > 0) {
|
|
18686
|
+
return "inline_secret";
|
|
18687
|
+
}
|
|
18688
|
+
if (hasUnsafeUserCode(command)) return "unsafe_user_code";
|
|
18689
|
+
return null;
|
|
18690
|
+
}
|
|
18691
|
+
function collectStepOffenders(command, offenders) {
|
|
18692
|
+
if (command.disabled) return;
|
|
18693
|
+
const reason = classifyUnsupportedCommand(command);
|
|
18694
|
+
if (reason) {
|
|
18695
|
+
offenders.push({ alias: command.alias, tool: command.tool, reason });
|
|
18696
|
+
}
|
|
18697
|
+
}
|
|
18698
|
+
function findUnsupportedWorkflowCommands(config) {
|
|
18699
|
+
const offenders = [];
|
|
18700
|
+
for (const command of config.commands ?? []) {
|
|
18701
|
+
if (!isWaterfall2(command)) {
|
|
18702
|
+
collectStepOffenders(command, offenders);
|
|
18703
|
+
continue;
|
|
18704
|
+
}
|
|
18705
|
+
for (const child of command.commands ?? []) {
|
|
18706
|
+
if (isWaterfall2(child)) {
|
|
18707
|
+
offenders.push({
|
|
18708
|
+
alias: command.with_waterfall,
|
|
18709
|
+
tool: "with_waterfall",
|
|
18710
|
+
reason: "nested_waterfall"
|
|
18711
|
+
});
|
|
18712
|
+
continue;
|
|
18713
|
+
}
|
|
18714
|
+
collectStepOffenders(child, offenders);
|
|
18715
|
+
}
|
|
18716
|
+
}
|
|
18717
|
+
return offenders;
|
|
18718
|
+
}
|
|
18719
|
+
function validateWorkflowConfigForPlay(config) {
|
|
18720
|
+
const offenders = findUnsupportedWorkflowCommands(config);
|
|
18721
|
+
if (offenders.length > 0) {
|
|
18722
|
+
throw new UnsupportedWorkflowCommandError(offenders);
|
|
18723
|
+
}
|
|
18724
|
+
}
|
|
18725
|
+
function describeUnsupportedReason(reason) {
|
|
18726
|
+
return UNSUPPORTED_REASON_HINT[reason];
|
|
18727
|
+
}
|
|
18728
|
+
var MAX_PLAY_NAME_LENGTH = 39;
|
|
18729
|
+
function sanitizePlayNameSegment(value) {
|
|
18730
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
18731
|
+
}
|
|
18732
|
+
function deriveWorkflowPlayName(workflowName) {
|
|
18733
|
+
const base = sanitizePlayNameSegment(workflowName) || "workflow";
|
|
18734
|
+
const suffix = createHash4("sha256").update(workflowName).digest("hex").slice(0, 8);
|
|
18735
|
+
const reserved = suffix.length + 1;
|
|
18736
|
+
const allowedBase = Math.max(1, MAX_PLAY_NAME_LENGTH - reserved);
|
|
18737
|
+
let name = `${base.slice(0, allowedBase)}_${suffix}`;
|
|
18738
|
+
if (name.startsWith("prebuilt")) {
|
|
18739
|
+
name = `wf_${name}`.slice(0, MAX_PLAY_NAME_LENGTH);
|
|
18740
|
+
}
|
|
18741
|
+
return name;
|
|
18742
|
+
}
|
|
18743
|
+
function compileWorkflowConfigToPlay(config, options) {
|
|
18744
|
+
validateWorkflowConfigForPlay(config);
|
|
18745
|
+
const playName = deriveWorkflowPlayName(options.workflowName);
|
|
18746
|
+
const sourceCode = compileEnrichConfigToPlaySource(config, {
|
|
18747
|
+
playName,
|
|
18748
|
+
inlineRunJavascript: true,
|
|
18749
|
+
idiomaticGetters: true
|
|
18750
|
+
});
|
|
18751
|
+
const warnings = [];
|
|
18752
|
+
if (typeof config.cost_cap_usd_per_run === "number") {
|
|
18753
|
+
warnings.push(
|
|
18754
|
+
`cost_cap_usd_per_run ($${config.cost_cap_usd_per_run}) was not carried into the play \u2014 credits are not USD. Set billing.maxCreditsPerRun manually after checking your credit conversion.`
|
|
18755
|
+
);
|
|
18756
|
+
}
|
|
18757
|
+
return { playName, sourceCode, warnings };
|
|
18758
|
+
}
|
|
18759
|
+
|
|
18760
|
+
// src/cli/commands/workflow.ts
|
|
18761
|
+
var PLAY_EQUIVALENT = {
|
|
18762
|
+
list: "deepline plays list",
|
|
18763
|
+
get: "deepline plays get <name>",
|
|
18764
|
+
call: "deepline plays run <name>",
|
|
18765
|
+
apply: "deepline plays publish <file>",
|
|
18766
|
+
lint: "deepline plays check <file>",
|
|
18767
|
+
schema: "deepline plays describe <name>",
|
|
18768
|
+
runs: "deepline runs list --play <name>",
|
|
18769
|
+
run: "deepline runs get <run-id>",
|
|
18770
|
+
tail: "deepline runs tail <run-id>",
|
|
18771
|
+
cancel: "deepline runs stop <run-id>",
|
|
18772
|
+
delete: "deepline plays delete <name>"
|
|
18773
|
+
};
|
|
18774
|
+
var MIGRATE_HINT = "deepline workflows transform <id>";
|
|
18775
|
+
var JSON_OPTION_DESCRIPTION = "Emit JSON output. Also automatic when stdout is piped";
|
|
18776
|
+
function withJsonOption(command) {
|
|
18777
|
+
return command.option("--json", JSON_OPTION_DESCRIPTION);
|
|
18778
|
+
}
|
|
18779
|
+
var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set([
|
|
18780
|
+
"completed",
|
|
18781
|
+
"succeeded",
|
|
18782
|
+
"success",
|
|
18783
|
+
"failed",
|
|
18784
|
+
"error",
|
|
18785
|
+
"errored",
|
|
18786
|
+
"cancelled",
|
|
18787
|
+
"canceled",
|
|
18788
|
+
"done"
|
|
18789
|
+
]);
|
|
18790
|
+
function migrationDeprecation(command) {
|
|
18791
|
+
const use = PLAY_EQUIVALENT[command];
|
|
18792
|
+
if (!use) return null;
|
|
18793
|
+
return { use, migrate: MIGRATE_HINT };
|
|
18794
|
+
}
|
|
18795
|
+
function noticeToStderr(command) {
|
|
18796
|
+
const deprecation = migrationDeprecation(command);
|
|
18797
|
+
if (!deprecation) return;
|
|
18798
|
+
process.stderr.write(
|
|
18799
|
+
`note: \`deepline workflows ${command}\` is a legacy surface \u2014 workflows are becoming plays.
|
|
18800
|
+
equivalent: \`${deprecation.use}\`
|
|
18801
|
+
migrate this workflow: \`${MIGRATE_HINT}\`
|
|
18802
|
+
`
|
|
18803
|
+
);
|
|
18804
|
+
}
|
|
18805
|
+
function emitPassThrough(command, payload, options) {
|
|
18806
|
+
const json = shouldEmitJson(options.json);
|
|
18807
|
+
const deprecation = migrationDeprecation(command);
|
|
18808
|
+
if (json) {
|
|
18809
|
+
const enriched = payload && typeof payload === "object" && !Array.isArray(payload) ? { ...payload, deprecation } : { result: payload, deprecation };
|
|
18810
|
+
printJson(enriched);
|
|
18811
|
+
return;
|
|
18812
|
+
}
|
|
18813
|
+
printJson(payload);
|
|
18814
|
+
noticeToStderr(command);
|
|
18815
|
+
}
|
|
18816
|
+
function client() {
|
|
18817
|
+
return new DeeplineClient();
|
|
18818
|
+
}
|
|
18819
|
+
function readStatus(payload) {
|
|
18820
|
+
if (!payload || typeof payload !== "object") return null;
|
|
18821
|
+
const record = payload;
|
|
18822
|
+
if (typeof record.status === "string") return record.status;
|
|
18823
|
+
const run = record.run;
|
|
18824
|
+
if (run && typeof run === "object") {
|
|
18825
|
+
const status = run.status;
|
|
18826
|
+
if (typeof status === "string") return status;
|
|
18827
|
+
}
|
|
18828
|
+
return null;
|
|
18829
|
+
}
|
|
18830
|
+
async function readJsonOption(payload, file) {
|
|
18831
|
+
if (file) {
|
|
18832
|
+
const raw = await readFile4(resolve13(file), "utf8");
|
|
18833
|
+
return JSON.parse(raw);
|
|
18834
|
+
}
|
|
18835
|
+
if (payload) {
|
|
18836
|
+
return JSON.parse(payload);
|
|
18837
|
+
}
|
|
18838
|
+
throw new Error("Provide --payload <json> or --file <path>.");
|
|
18839
|
+
}
|
|
18840
|
+
async function transformOne(api, workflowId, outDir, publish) {
|
|
18841
|
+
const { workflow } = await api.getWorkflow(workflowId);
|
|
18842
|
+
if (!workflow) {
|
|
18843
|
+
return {
|
|
18844
|
+
ok: false,
|
|
18845
|
+
workflowId,
|
|
18846
|
+
workflowName: workflowId,
|
|
18847
|
+
reason: "Workflow not found."
|
|
18848
|
+
};
|
|
18849
|
+
}
|
|
18850
|
+
const revision = workflow.current_published_revision;
|
|
18851
|
+
if (!revision) {
|
|
18852
|
+
return {
|
|
18853
|
+
ok: false,
|
|
18854
|
+
workflowId,
|
|
18855
|
+
workflowName: workflow.name,
|
|
18856
|
+
reason: "Workflow has no published revision to transform."
|
|
18857
|
+
};
|
|
18858
|
+
}
|
|
18859
|
+
try {
|
|
18860
|
+
const compiled = compileWorkflowConfigToPlay(
|
|
18861
|
+
// The API returns the config as `unknown`; it is structurally the
|
|
18862
|
+
// EnrichCompiledConfig the compiler expects. Cast at this single boundary.
|
|
18863
|
+
revision.config,
|
|
18864
|
+
{ workflowName: workflow.name, version: revision.version }
|
|
18865
|
+
);
|
|
18866
|
+
const file = join11(resolve13(outDir), `${compiled.playName}.play.ts`);
|
|
18867
|
+
await mkdir5(dirname9(file), { recursive: true });
|
|
18868
|
+
await writeFile5(file, compiled.sourceCode, "utf8");
|
|
18869
|
+
let published = false;
|
|
18870
|
+
if (publish) {
|
|
18871
|
+
const code = await handlePlayPublish([file]);
|
|
18872
|
+
if (code !== 0) {
|
|
18873
|
+
return {
|
|
18874
|
+
ok: false,
|
|
18875
|
+
workflowId,
|
|
18876
|
+
workflowName: workflow.name,
|
|
18877
|
+
reason: `Wrote ${file} but publish failed (exit ${code}).`
|
|
18878
|
+
};
|
|
18879
|
+
}
|
|
18880
|
+
published = true;
|
|
18881
|
+
}
|
|
18882
|
+
return {
|
|
18883
|
+
ok: true,
|
|
18884
|
+
workflowId,
|
|
18885
|
+
workflowName: workflow.name,
|
|
18886
|
+
playName: compiled.playName,
|
|
18887
|
+
file,
|
|
18888
|
+
warnings: compiled.warnings,
|
|
18889
|
+
published
|
|
18890
|
+
};
|
|
18891
|
+
} catch (error) {
|
|
18892
|
+
if (error instanceof UnsupportedWorkflowCommandError) {
|
|
18893
|
+
return {
|
|
18894
|
+
ok: false,
|
|
18895
|
+
workflowId,
|
|
18896
|
+
workflowName: workflow.name,
|
|
18897
|
+
reason: error.unsupported.map((u) => `${u.alias} (${describeUnsupportedReason(u.reason)})`).join("; "),
|
|
18898
|
+
unsupported: error.unsupported
|
|
18899
|
+
};
|
|
18900
|
+
}
|
|
18901
|
+
throw error;
|
|
18902
|
+
}
|
|
18903
|
+
}
|
|
18904
|
+
function renderTransformReport(outcomes) {
|
|
18905
|
+
const lines = [];
|
|
18906
|
+
for (const outcome of outcomes) {
|
|
18907
|
+
if (outcome.ok) {
|
|
18908
|
+
lines.push(
|
|
18909
|
+
`\u2705 ${outcome.workflowName} \u2192 ${outcome.file}` + (outcome.published ? " (published)" : ` (next: deepline plays publish ${outcome.file})`)
|
|
18910
|
+
);
|
|
18911
|
+
for (const warning of outcome.warnings) {
|
|
18912
|
+
lines.push(` \u26A0\uFE0F ${warning}`);
|
|
18913
|
+
}
|
|
18914
|
+
} else {
|
|
18915
|
+
lines.push(`\u26A0\uFE0F ${outcome.workflowName} \u2014 skipped: ${outcome.reason}`);
|
|
18916
|
+
}
|
|
18917
|
+
}
|
|
18918
|
+
const ok = outcomes.filter((o) => o.ok).length;
|
|
18919
|
+
const total = outcomes.length;
|
|
18920
|
+
const manual = total - ok;
|
|
18921
|
+
lines.push("");
|
|
18922
|
+
lines.push(
|
|
18923
|
+
`${ok}/${total} transformed` + (manual > 0 ? `, ${manual} need manual attention` : "")
|
|
18924
|
+
);
|
|
18925
|
+
return `${lines.join("\n")}
|
|
18926
|
+
`;
|
|
18927
|
+
}
|
|
18928
|
+
async function handleTransform(id, options) {
|
|
18929
|
+
const api = client();
|
|
18930
|
+
const outDir = options.out ?? "./plays";
|
|
18931
|
+
const publish = options.publish === true;
|
|
18932
|
+
let ids;
|
|
18933
|
+
if (options.all) {
|
|
18934
|
+
const { workflows } = await api.listWorkflows();
|
|
18935
|
+
ids = workflows.map((w) => w.id);
|
|
18936
|
+
} else if (id) {
|
|
18937
|
+
ids = [id];
|
|
18938
|
+
} else {
|
|
18939
|
+
throw new Error("Provide a workflow <id> or --all.");
|
|
18940
|
+
}
|
|
18941
|
+
const outcomes = [];
|
|
18942
|
+
for (const workflowId of ids) {
|
|
18943
|
+
outcomes.push(await transformOne(api, workflowId, outDir, publish));
|
|
18944
|
+
}
|
|
18945
|
+
printCommandEnvelope(
|
|
18946
|
+
{ outcomes, deprecation: null },
|
|
18947
|
+
{
|
|
18948
|
+
json: shouldEmitJson(options.json),
|
|
18949
|
+
text: renderTransformReport(outcomes)
|
|
18950
|
+
}
|
|
18951
|
+
);
|
|
18952
|
+
return outcomes.every((o) => o.ok) ? 0 : 1;
|
|
18953
|
+
}
|
|
18954
|
+
async function handleList2(options) {
|
|
18955
|
+
const limit = options.limit ? Number(options.limit) : void 0;
|
|
18956
|
+
const result = await client().listWorkflows({
|
|
18957
|
+
limit: Number.isFinite(limit) ? limit : void 0
|
|
18958
|
+
});
|
|
18959
|
+
emitPassThrough("list", result, options);
|
|
18960
|
+
}
|
|
18961
|
+
async function handleGet(id, options) {
|
|
18962
|
+
emitPassThrough("get", await client().getWorkflow(id), options);
|
|
18963
|
+
}
|
|
18964
|
+
async function handleDelete(id, options) {
|
|
18965
|
+
emitPassThrough("delete", await client().deleteWorkflow(id), options);
|
|
18966
|
+
}
|
|
18967
|
+
async function handleDisable(id, options) {
|
|
18968
|
+
const result = await client().disableWorkflow(id);
|
|
18969
|
+
printJson(result);
|
|
18970
|
+
}
|
|
18971
|
+
async function handleEnable(id, options) {
|
|
18972
|
+
const result = await client().enableWorkflow(id);
|
|
18973
|
+
printJson(result);
|
|
18974
|
+
}
|
|
18975
|
+
async function handleApply(options) {
|
|
18976
|
+
const body = await readJsonOption(options.payload, options.file);
|
|
18977
|
+
emitPassThrough("apply", await client().applyWorkflow(body), options);
|
|
18978
|
+
}
|
|
18979
|
+
async function handleLint(options) {
|
|
18980
|
+
const body = await readJsonOption(options.payload, options.file);
|
|
18981
|
+
emitPassThrough("lint", await client().lintWorkflow(body), options);
|
|
18982
|
+
}
|
|
18983
|
+
async function handleSchema(options) {
|
|
18984
|
+
emitPassThrough(
|
|
18985
|
+
"schema",
|
|
18986
|
+
await client().getWorkflowSchema(options.subject),
|
|
18987
|
+
options
|
|
18988
|
+
);
|
|
18989
|
+
}
|
|
18990
|
+
async function handleCall(options) {
|
|
18991
|
+
const body = {};
|
|
18992
|
+
if (options.workflowId) body.workflow_id = options.workflowId;
|
|
18993
|
+
if (options.workflowName) body.workflow_name = options.workflowName;
|
|
18994
|
+
const input2 = options.payload ? JSON.parse(options.payload) : {};
|
|
18995
|
+
if (options.mode) input2.__execution_mode = options.mode;
|
|
18996
|
+
body.input = input2;
|
|
18997
|
+
emitPassThrough("call", await client().callWorkflow(body), options);
|
|
18998
|
+
}
|
|
18999
|
+
async function handleRuns(id, options) {
|
|
19000
|
+
const limit = options.limit ? Number(options.limit) : void 0;
|
|
19001
|
+
emitPassThrough(
|
|
19002
|
+
"runs",
|
|
19003
|
+
await client().listWorkflowRuns(id, {
|
|
19004
|
+
limit: Number.isFinite(limit) ? limit : void 0
|
|
19005
|
+
}),
|
|
19006
|
+
options
|
|
19007
|
+
);
|
|
19008
|
+
}
|
|
19009
|
+
async function handleRun(id, runId, options) {
|
|
19010
|
+
emitPassThrough("run", await client().getWorkflowRun(id, runId), options);
|
|
19011
|
+
}
|
|
19012
|
+
async function handleCancel(id, runId, options) {
|
|
19013
|
+
emitPassThrough(
|
|
19014
|
+
"cancel",
|
|
19015
|
+
await client().cancelWorkflowRun(id, runId),
|
|
19016
|
+
options
|
|
19017
|
+
);
|
|
19018
|
+
}
|
|
19019
|
+
async function handleTail(id, runId, options) {
|
|
19020
|
+
const api = client();
|
|
19021
|
+
const intervalMs = Math.max(500, Number(options.intervalMs ?? 2e3) || 2e3);
|
|
19022
|
+
const deadline = Date.now() + 10 * 6e4;
|
|
19023
|
+
for (; ; ) {
|
|
19024
|
+
const run = await api.getWorkflowRun(id, runId);
|
|
19025
|
+
emitPassThrough("tail", run, options);
|
|
19026
|
+
const status = readStatus(run);
|
|
19027
|
+
if (status && TERMINAL_RUN_STATUSES.has(status) || Date.now() > deadline) {
|
|
19028
|
+
return;
|
|
19029
|
+
}
|
|
19030
|
+
await sleep3(intervalMs);
|
|
19031
|
+
}
|
|
19032
|
+
}
|
|
19033
|
+
function registerWorkflowCommands(program) {
|
|
19034
|
+
const workflows = program.command("workflows").description(
|
|
19035
|
+
"Legacy cloud workflows (now becoming plays). Commands still work; use `workflows transform` to migrate one to a play."
|
|
19036
|
+
).addHelpText(
|
|
19037
|
+
"after",
|
|
19038
|
+
`
|
|
19039
|
+
Workflows are a legacy surface. They still run in the cloud and these commands
|
|
19040
|
+
keep working, but new work belongs in plays. Migrate a workflow with:
|
|
19041
|
+
|
|
19042
|
+
deepline workflows transform <id> # write a reviewable .play.ts
|
|
19043
|
+
deepline workflows transform --all --publish # migrate + publish them all
|
|
19044
|
+
|
|
19045
|
+
Equivalents: list\u2192plays list \xB7 call\u2192plays run \xB7 apply\u2192plays publish \xB7
|
|
19046
|
+
lint\u2192plays check \xB7 runs\u2192runs list.
|
|
19047
|
+
`
|
|
19048
|
+
);
|
|
19049
|
+
withJsonOption(
|
|
19050
|
+
workflows.command("transform [id]").description("Compile a workflow into a reviewable V2 play (.play.ts).").option("--all", "Transform every workflow in the workspace").option("--out <dir>", "Output directory for generated plays", "./plays").option("--publish", "Publish each clean play immediately")
|
|
19051
|
+
).addHelpText(
|
|
19052
|
+
"after",
|
|
19053
|
+
`
|
|
19054
|
+
Examples:
|
|
19055
|
+
deepline workflows transform wf_123 --out ./plays
|
|
19056
|
+
deepline workflows transform --all
|
|
19057
|
+
deepline workflows transform --all --publish --json
|
|
19058
|
+
|
|
19059
|
+
Notes:
|
|
19060
|
+
HITL, run_javascript steps, and sub-workflow calls cannot run as a play yet;
|
|
19061
|
+
those workflows are reported as needing manual attention and skipped.
|
|
19062
|
+
`
|
|
19063
|
+
).action(async (id, options) => {
|
|
19064
|
+
process.exitCode = await handleTransform(id, options);
|
|
19065
|
+
});
|
|
19066
|
+
withJsonOption(
|
|
19067
|
+
workflows.command("list").description("List workspace workflows.").option("--limit <n>", "Max workflows to return")
|
|
19068
|
+
).action(handleList2);
|
|
19069
|
+
withJsonOption(
|
|
19070
|
+
workflows.command("get <id>").description(
|
|
19071
|
+
"Fetch a workflow, including its published-revision config."
|
|
19072
|
+
)
|
|
19073
|
+
).action(handleGet);
|
|
19074
|
+
withJsonOption(
|
|
19075
|
+
workflows.command("disable <id>").description("Turn a workflow off.")
|
|
19076
|
+
).action(handleDisable);
|
|
19077
|
+
withJsonOption(
|
|
19078
|
+
workflows.command("enable <id>").description("Turn a workflow back on.")
|
|
19079
|
+
).action(handleEnable);
|
|
19080
|
+
withJsonOption(
|
|
19081
|
+
workflows.command("delete <id>").description("Delete a workflow and its revisions/runs.")
|
|
19082
|
+
).action(handleDelete);
|
|
19083
|
+
withJsonOption(
|
|
19084
|
+
workflows.command("apply").description("Create or update a workflow from config.").option("--payload <json>", "Inline config JSON").option("--file <path>", "Path to a config JSON file")
|
|
19085
|
+
).action(handleApply);
|
|
19086
|
+
withJsonOption(
|
|
19087
|
+
workflows.command("lint").description("Validate a workflow config without saving.").option("--payload <json>", "Inline config JSON").option("--file <path>", "Path to a config JSON file")
|
|
19088
|
+
).action(handleLint);
|
|
19089
|
+
withJsonOption(
|
|
19090
|
+
workflows.command("schema").description("Fetch live workflow request schemas.").option(
|
|
19091
|
+
"--subject <subject>",
|
|
19092
|
+
"Schema subject (apply|call|trigger|all|\u2026)"
|
|
19093
|
+
)
|
|
19094
|
+
).action(handleSchema);
|
|
19095
|
+
withJsonOption(
|
|
19096
|
+
workflows.command("call").description("Queue a workflow run.").option("--workflow-id <id>", "Workflow id").option("--workflow-name <name>", "Workflow name").option("--payload <json>", "Run input JSON").option("--mode <mode>", "live | dry_run | smoke_test")
|
|
19097
|
+
).action(handleCall);
|
|
19098
|
+
withJsonOption(
|
|
19099
|
+
workflows.command("runs <id>").description("List a workflow\u2019s runs.").option("--limit <n>", "Max runs to return")
|
|
19100
|
+
).action(handleRuns);
|
|
19101
|
+
withJsonOption(
|
|
19102
|
+
workflows.command("run <id> <runId>").description("Fetch a single workflow run.")
|
|
19103
|
+
).action(handleRun);
|
|
19104
|
+
withJsonOption(
|
|
19105
|
+
workflows.command("tail <id> <runId>").description("Poll a workflow run until it reaches a terminal status.").option("--interval-ms <n>", "Poll interval in ms (default 2000)")
|
|
19106
|
+
).action(handleTail);
|
|
19107
|
+
withJsonOption(
|
|
19108
|
+
workflows.command("cancel <id> <runId>").description("Cancel a running workflow run.")
|
|
19109
|
+
).action(handleCancel);
|
|
19110
|
+
}
|
|
19111
|
+
|
|
16911
19112
|
// src/cli/commands/update.ts
|
|
16912
19113
|
import { spawn } from "child_process";
|
|
16913
19114
|
import { existsSync as existsSync9 } from "fs";
|
|
16914
|
-
import { dirname as
|
|
19115
|
+
import { dirname as dirname10, join as join12, resolve as resolve14 } from "path";
|
|
16915
19116
|
function posixShellQuote(value) {
|
|
16916
19117
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
16917
19118
|
}
|
|
@@ -16930,19 +19131,19 @@ function buildSourceUpdateCommand(sourceRoot) {
|
|
|
16930
19131
|
return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
|
|
16931
19132
|
}
|
|
16932
19133
|
function findRepoBackedSdkRoot(startPath) {
|
|
16933
|
-
let current =
|
|
19134
|
+
let current = resolve14(startPath);
|
|
16934
19135
|
while (true) {
|
|
16935
|
-
if (existsSync9(
|
|
19136
|
+
if (existsSync9(join12(current, "sdk", "package.json")) && existsSync9(join12(current, "sdk", "bin", "deepline-dev.ts"))) {
|
|
16936
19137
|
return current;
|
|
16937
19138
|
}
|
|
16938
|
-
const parent =
|
|
19139
|
+
const parent = dirname10(current);
|
|
16939
19140
|
if (parent === current) return null;
|
|
16940
19141
|
current = parent;
|
|
16941
19142
|
}
|
|
16942
19143
|
}
|
|
16943
19144
|
function resolveUpdatePlan() {
|
|
16944
|
-
const entrypoint = process.argv[1] ?
|
|
16945
|
-
const sourceRoot = entrypoint ? findRepoBackedSdkRoot(
|
|
19145
|
+
const entrypoint = process.argv[1] ? resolve14(process.argv[1]) : "";
|
|
19146
|
+
const sourceRoot = entrypoint ? findRepoBackedSdkRoot(dirname10(entrypoint)) : null;
|
|
16946
19147
|
if (sourceRoot) {
|
|
16947
19148
|
return {
|
|
16948
19149
|
kind: "source",
|
|
@@ -17214,7 +19415,7 @@ import {
|
|
|
17214
19415
|
writeFileSync as writeFileSync10
|
|
17215
19416
|
} from "fs";
|
|
17216
19417
|
import { homedir as homedir6 } from "os";
|
|
17217
|
-
import { dirname as
|
|
19418
|
+
import { dirname as dirname11, join as join13 } from "path";
|
|
17218
19419
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
17219
19420
|
var SDK_SKILL_NAME = "deepline-plays";
|
|
17220
19421
|
var attemptedSync = false;
|
|
@@ -17234,14 +19435,14 @@ function readPluginSkillsVersion() {
|
|
|
17234
19435
|
const dir = activePluginSkillsDir();
|
|
17235
19436
|
if (!dir) return "";
|
|
17236
19437
|
try {
|
|
17237
|
-
return readFileSync8(
|
|
19438
|
+
return readFileSync8(join13(dir, ".version"), "utf-8").trim();
|
|
17238
19439
|
} catch {
|
|
17239
19440
|
return "";
|
|
17240
19441
|
}
|
|
17241
19442
|
}
|
|
17242
19443
|
function sdkSkillsVersionPath(baseUrl) {
|
|
17243
19444
|
const home = process.env.HOME?.trim() || homedir6();
|
|
17244
|
-
return
|
|
19445
|
+
return join13(
|
|
17245
19446
|
home,
|
|
17246
19447
|
".local",
|
|
17247
19448
|
"deepline",
|
|
@@ -17263,16 +19464,16 @@ function readLocalSkillsVersion(baseUrl) {
|
|
|
17263
19464
|
}
|
|
17264
19465
|
function writeLocalSkillsVersion(baseUrl, version) {
|
|
17265
19466
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
17266
|
-
mkdirSync5(
|
|
19467
|
+
mkdirSync5(dirname11(path), { recursive: true });
|
|
17267
19468
|
writeFileSync10(path, `${version}
|
|
17268
19469
|
`, "utf-8");
|
|
17269
19470
|
}
|
|
17270
19471
|
function installedSdkSkillHasStalePositionalExecuteExamples() {
|
|
17271
19472
|
const home = process.env.HOME?.trim() || homedir6();
|
|
17272
19473
|
const pluginSkillsDir = activePluginSkillsDir();
|
|
17273
|
-
const roots = pluginSkillsDir ? [
|
|
17274
|
-
|
|
17275
|
-
|
|
19474
|
+
const roots = pluginSkillsDir ? [join13(pluginSkillsDir, SDK_SKILL_NAME)] : [
|
|
19475
|
+
join13(home, ".claude", "skills", SDK_SKILL_NAME),
|
|
19476
|
+
join13(home, ".agents", "skills", SDK_SKILL_NAME)
|
|
17276
19477
|
];
|
|
17277
19478
|
const staleMarkers = [
|
|
17278
19479
|
"ctx.tools.execute(key",
|
|
@@ -17283,7 +19484,7 @@ function installedSdkSkillHasStalePositionalExecuteExamples() {
|
|
|
17283
19484
|
];
|
|
17284
19485
|
const scan = (dir) => {
|
|
17285
19486
|
for (const entry of readdirSync2(dir)) {
|
|
17286
|
-
const path =
|
|
19487
|
+
const path = join13(dir, entry);
|
|
17287
19488
|
const stat4 = statSync2(path);
|
|
17288
19489
|
if (stat4.isDirectory()) {
|
|
17289
19490
|
if (scan(path)) return true;
|
|
@@ -17369,7 +19570,7 @@ function resolveSkillsInstallCommands(baseUrl) {
|
|
|
17369
19570
|
return [npxInstall];
|
|
17370
19571
|
}
|
|
17371
19572
|
function runOneSkillsInstall(install) {
|
|
17372
|
-
return new Promise((
|
|
19573
|
+
return new Promise((resolve15) => {
|
|
17373
19574
|
const child = spawn2(install.command, install.args, {
|
|
17374
19575
|
stdio: ["ignore", "ignore", "pipe"],
|
|
17375
19576
|
env: process.env
|
|
@@ -17379,7 +19580,7 @@ function runOneSkillsInstall(install) {
|
|
|
17379
19580
|
stderr += chunk.toString("utf-8");
|
|
17380
19581
|
});
|
|
17381
19582
|
child.on("error", (error) => {
|
|
17382
|
-
|
|
19583
|
+
resolve15({
|
|
17383
19584
|
ok: false,
|
|
17384
19585
|
detail: `failed to start ${install.command}: ${error.message}`,
|
|
17385
19586
|
manualCommand: install.manualCommand
|
|
@@ -17387,11 +19588,11 @@ function runOneSkillsInstall(install) {
|
|
|
17387
19588
|
});
|
|
17388
19589
|
child.on("close", (code) => {
|
|
17389
19590
|
if (code === 0) {
|
|
17390
|
-
|
|
19591
|
+
resolve15({ ok: true, detail: "", manualCommand: install.manualCommand });
|
|
17391
19592
|
return;
|
|
17392
19593
|
}
|
|
17393
19594
|
const detail = stderr.trim();
|
|
17394
|
-
|
|
19595
|
+
resolve15({
|
|
17395
19596
|
ok: false,
|
|
17396
19597
|
detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
|
|
17397
19598
|
manualCommand: install.manualCommand
|
|
@@ -17479,10 +19680,10 @@ function shouldDeferSkillsSyncForCommand() {
|
|
|
17479
19680
|
return (command === "play" || command === "plays") && subcommand === "run" && args.includes("--json");
|
|
17480
19681
|
}
|
|
17481
19682
|
async function runPlayRunnerHealthCheck() {
|
|
17482
|
-
const dir = await mkdtemp2(
|
|
17483
|
-
const file =
|
|
19683
|
+
const dir = await mkdtemp2(join14(tmpdir5(), "deepline-health-play-"));
|
|
19684
|
+
const file = join14(dir, "health-check.play.ts");
|
|
17484
19685
|
try {
|
|
17485
|
-
await
|
|
19686
|
+
await writeFile6(
|
|
17486
19687
|
file,
|
|
17487
19688
|
[
|
|
17488
19689
|
"import { definePlay } from 'deepline';",
|
|
@@ -17579,14 +19780,12 @@ async function runPreflightCheck() {
|
|
|
17579
19780
|
connected: false,
|
|
17580
19781
|
error: preflightErrorMessage(error)
|
|
17581
19782
|
})),
|
|
17582
|
-
http.get("/api/v2/billing/balance").catch(
|
|
17583
|
-
|
|
17584
|
-
|
|
17585
|
-
|
|
17586
|
-
|
|
17587
|
-
|
|
17588
|
-
})
|
|
17589
|
-
)
|
|
19783
|
+
http.get("/api/v2/billing/balance").catch((error) => ({
|
|
19784
|
+
balance: null,
|
|
19785
|
+
balance_display: "unavailable",
|
|
19786
|
+
balance_status: "unknown",
|
|
19787
|
+
error: preflightErrorMessage(error)
|
|
19788
|
+
}))
|
|
17590
19789
|
]) : [
|
|
17591
19790
|
{
|
|
17592
19791
|
status: "not_connected",
|
|
@@ -17722,6 +19921,7 @@ Exit codes:
|
|
|
17722
19921
|
registerAuthCommands(program);
|
|
17723
19922
|
registerToolsCommands(program);
|
|
17724
19923
|
registerPlayCommands(program);
|
|
19924
|
+
registerWorkflowCommands(program);
|
|
17725
19925
|
registerSecretsCommands(program);
|
|
17726
19926
|
registerBillingCommands(program);
|
|
17727
19927
|
registerOrgCommands(program);
|
|
@@ -17774,8 +19974,8 @@ Examples:
|
|
|
17774
19974
|
`);
|
|
17775
19975
|
return;
|
|
17776
19976
|
}
|
|
17777
|
-
const
|
|
17778
|
-
const data = await
|
|
19977
|
+
const client2 = new DeeplineClient();
|
|
19978
|
+
const data = await client2.health();
|
|
17779
19979
|
process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
17780
19980
|
`);
|
|
17781
19981
|
} catch (error) {
|