deepline 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cli/index.js +509 -353
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +513 -358
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +250 -305
- package/dist/index.d.ts +250 -305
- package/dist/index.js +174 -286
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +174 -285
- package/dist/index.mjs.map +1 -1
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +23 -13
- package/dist/repo/apps/play-runner-workers/src/entry.ts +581 -1220
- package/dist/repo/sdk/src/cli/commands/play.ts +381 -247
- package/dist/repo/sdk/src/cli/commands/tools.ts +1 -1
- package/dist/repo/sdk/src/cli/dataset-stats.ts +86 -12
- package/dist/repo/sdk/src/client.ts +54 -51
- package/dist/repo/sdk/src/index.ts +7 -16
- package/dist/repo/sdk/src/play.ts +122 -135
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +6 -3
- package/dist/repo/sdk/src/tool-output.ts +0 -111
- package/dist/repo/sdk/src/types.ts +2 -0
- package/dist/repo/sdk/src/version.ts +1 -1
- package/dist/repo/sdk/src/worker-play-entry.ts +3 -0
- package/dist/repo/shared_libs/play-runtime/context.ts +510 -267
- package/dist/repo/shared_libs/play-runtime/csv-rename.ts +180 -0
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +13 -1
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +139 -114
- package/dist/repo/shared_libs/plays/bundling/index.ts +68 -5
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +1 -1
- package/dist/repo/shared_libs/plays/dataset.ts +1 -1
- package/dist/repo/shared_libs/plays/runtime-validation.ts +8 -28
- package/package.json +1 -1
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +0 -184
package/dist/index.mjs
CHANGED
|
@@ -149,7 +149,7 @@ function resolveConfig(options) {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
// src/version.ts
|
|
152
|
-
var SDK_VERSION = "0.1.
|
|
152
|
+
var SDK_VERSION = "0.1.12";
|
|
153
153
|
var SDK_API_CONTRACT = "2026-04-plays-v1";
|
|
154
154
|
|
|
155
155
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
@@ -424,6 +424,9 @@ function sleep(ms) {
|
|
|
424
424
|
|
|
425
425
|
// src/client.ts
|
|
426
426
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
427
|
+
function isRecord(value) {
|
|
428
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
429
|
+
}
|
|
427
430
|
function normalizePlayStatus(raw) {
|
|
428
431
|
const status = typeof raw.status === "string" ? raw.status : typeof raw.temporalStatus === "string" ? mapLegacyTemporalStatus(raw.temporalStatus) : "running";
|
|
429
432
|
const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
|
|
@@ -476,12 +479,27 @@ var DeeplineClient = class {
|
|
|
476
479
|
).filter((field) => Boolean(field?.name)) : [];
|
|
477
480
|
return fields.length > 0 ? { fields } : schema;
|
|
478
481
|
}
|
|
479
|
-
|
|
480
|
-
|
|
482
|
+
schemaMetadata(schema, key) {
|
|
483
|
+
if (!isRecord(schema)) return null;
|
|
484
|
+
const value = schema[key];
|
|
485
|
+
return isRecord(value) ? value : null;
|
|
486
|
+
}
|
|
487
|
+
playRunCommand(play, options) {
|
|
488
|
+
const target = play.reference || play.name;
|
|
489
|
+
if (options?.csvInput) {
|
|
490
|
+
const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
|
|
491
|
+
return `deepline plays run ${target} --${inputField} leads.csv --watch`;
|
|
492
|
+
}
|
|
493
|
+
return `deepline plays run ${target} --input '{...}' --watch`;
|
|
481
494
|
}
|
|
482
495
|
summarizePlayListItem(play, options) {
|
|
483
496
|
const aliases = play.aliases?.length ? play.aliases : [play.name];
|
|
484
|
-
const
|
|
497
|
+
const csvInput = this.schemaMetadata(play.inputSchema, "csvInput");
|
|
498
|
+
const rowOutputSchema = this.schemaMetadata(
|
|
499
|
+
play.outputSchema,
|
|
500
|
+
"rowOutputSchema"
|
|
501
|
+
);
|
|
502
|
+
const runCommand = this.playRunCommand(play, { csvInput });
|
|
485
503
|
return {
|
|
486
504
|
name: play.name,
|
|
487
505
|
...play.reference ? { reference: play.reference } : {},
|
|
@@ -493,6 +511,8 @@ var DeeplineClient = class {
|
|
|
493
511
|
aliases,
|
|
494
512
|
inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
|
|
495
513
|
outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
|
|
514
|
+
...csvInput ? { csvInput } : {},
|
|
515
|
+
...rowOutputSchema ? { rowOutputSchema } : {},
|
|
496
516
|
runCommand,
|
|
497
517
|
examples: [runCommand],
|
|
498
518
|
currentPublishedVersion: play.currentPublishedVersion ?? null,
|
|
@@ -559,50 +579,14 @@ var DeeplineClient = class {
|
|
|
559
579
|
);
|
|
560
580
|
}
|
|
561
581
|
/**
|
|
562
|
-
* Execute a tool and return the
|
|
563
|
-
*
|
|
564
|
-
* Sends the input payload to the tool and returns the `.result` field from the
|
|
565
|
-
* response. For the full response envelope (including job_id, credits, etc.),
|
|
566
|
-
* use {@link executeToolRaw}.
|
|
567
|
-
*
|
|
568
|
-
* @param toolId - Tool identifier (e.g. `"test_company_search"`)
|
|
569
|
-
* @param input - Tool-specific input parameters
|
|
570
|
-
* @returns The tool's output (shape varies by tool)
|
|
571
|
-
* @throws {@link DeeplineError} if the tool execution fails
|
|
582
|
+
* Execute a tool and return the standard execution envelope.
|
|
572
583
|
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
* });
|
|
578
|
-
* console.log(company); // { name: "Stripe", industry: "Financial Services", ... }
|
|
579
|
-
* ```
|
|
584
|
+
* The `result.data` field contains the provider payload. `result.meta`
|
|
585
|
+
* contains provider/upstream metadata such as HTTP status or paging details.
|
|
586
|
+
* Top-level fields such as `status`, `job_id`, and `billing` describe the
|
|
587
|
+
* Deepline execution.
|
|
580
588
|
*/
|
|
581
589
|
async executeTool(toolId, input) {
|
|
582
|
-
const res = await this.http.post(
|
|
583
|
-
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
584
|
-
{ payload: input }
|
|
585
|
-
);
|
|
586
|
-
return res.result ?? res;
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* Execute a tool and return the full response envelope.
|
|
590
|
-
*
|
|
591
|
-
* Unlike {@link executeTool}, this returns the complete API response including
|
|
592
|
-
* `job_id`, `status`, `credits`, and the raw `result` object.
|
|
593
|
-
*
|
|
594
|
-
* @param toolId - Tool identifier
|
|
595
|
-
* @param input - Tool-specific input parameters
|
|
596
|
-
* @returns Full response with job metadata and result
|
|
597
|
-
*
|
|
598
|
-
* @example
|
|
599
|
-
* ```typescript
|
|
600
|
-
* const raw = await client.executeToolRaw('test_company_search', { domain: 'stripe.com' });
|
|
601
|
-
* console.log(`Job: ${raw.job_id}, Credits: ${raw.credits}`);
|
|
602
|
-
* console.log(`Result:`, raw.result);
|
|
603
|
-
* ```
|
|
604
|
-
*/
|
|
605
|
-
async executeToolRaw(toolId, input) {
|
|
606
590
|
return this.http.post(
|
|
607
591
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
608
592
|
{ payload: input }
|
|
@@ -1224,235 +1208,6 @@ var DeeplineClient = class {
|
|
|
1224
1208
|
}
|
|
1225
1209
|
};
|
|
1226
1210
|
|
|
1227
|
-
// src/tool-output.ts
|
|
1228
|
-
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1229
|
-
import { homedir as homedir2 } from "os";
|
|
1230
|
-
import { join as join2 } from "path";
|
|
1231
|
-
function isPlainObject(value) {
|
|
1232
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1233
|
-
}
|
|
1234
|
-
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1235
|
-
var PHONE_KEY_PATTERN = /(^|[_-])(phone|mobile|cell|telephone|tel)([_-]|$)|phone|mobile|telephone/i;
|
|
1236
|
-
function normalizeScalarString(value) {
|
|
1237
|
-
if (typeof value === "string") {
|
|
1238
|
-
const trimmed = value.trim();
|
|
1239
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
1240
|
-
}
|
|
1241
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1242
|
-
return String(value);
|
|
1243
|
-
}
|
|
1244
|
-
return null;
|
|
1245
|
-
}
|
|
1246
|
-
function looksLikeEmail(value) {
|
|
1247
|
-
const candidate = normalizeScalarString(value);
|
|
1248
|
-
if (!candidate || !EMAIL_PATTERN.test(candidate)) return null;
|
|
1249
|
-
return candidate;
|
|
1250
|
-
}
|
|
1251
|
-
function looksLikePhone(value) {
|
|
1252
|
-
const candidate = normalizeScalarString(value);
|
|
1253
|
-
if (!candidate) return null;
|
|
1254
|
-
const digits = candidate.replace(/\D/g, "");
|
|
1255
|
-
if (digits.length < 7 || digits.length > 16) return null;
|
|
1256
|
-
return candidate;
|
|
1257
|
-
}
|
|
1258
|
-
function findEmail(value, depth = 0) {
|
|
1259
|
-
if (depth > 6) return null;
|
|
1260
|
-
const direct = looksLikeEmail(value);
|
|
1261
|
-
if (direct) return direct;
|
|
1262
|
-
if (Array.isArray(value)) {
|
|
1263
|
-
for (const entry of value) {
|
|
1264
|
-
const nested = findEmail(entry, depth + 1);
|
|
1265
|
-
if (nested) return nested;
|
|
1266
|
-
}
|
|
1267
|
-
return null;
|
|
1268
|
-
}
|
|
1269
|
-
if (!isPlainObject(value)) return null;
|
|
1270
|
-
for (const [key, child] of Object.entries(value)) {
|
|
1271
|
-
if (/email/i.test(key)) {
|
|
1272
|
-
const keyed = looksLikeEmail(child);
|
|
1273
|
-
if (keyed) return keyed;
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
for (const child of Object.values(value)) {
|
|
1277
|
-
const nested = findEmail(child, depth + 1);
|
|
1278
|
-
if (nested) return nested;
|
|
1279
|
-
}
|
|
1280
|
-
return null;
|
|
1281
|
-
}
|
|
1282
|
-
function findPhone(value, depth = 0) {
|
|
1283
|
-
if (depth > 6) return null;
|
|
1284
|
-
if (Array.isArray(value)) {
|
|
1285
|
-
for (const entry of value) {
|
|
1286
|
-
const nested = findPhone(entry, depth + 1);
|
|
1287
|
-
if (nested) return nested;
|
|
1288
|
-
}
|
|
1289
|
-
return null;
|
|
1290
|
-
}
|
|
1291
|
-
if (!isPlainObject(value)) return null;
|
|
1292
|
-
for (const [key, child] of Object.entries(value)) {
|
|
1293
|
-
if (PHONE_KEY_PATTERN.test(key)) {
|
|
1294
|
-
const keyed = looksLikePhone(child);
|
|
1295
|
-
if (keyed) return keyed;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
for (const child of Object.values(value)) {
|
|
1299
|
-
const nested = findPhone(child, depth + 1);
|
|
1300
|
-
if (nested) return nested;
|
|
1301
|
-
}
|
|
1302
|
-
return null;
|
|
1303
|
-
}
|
|
1304
|
-
var DeeplineToolCallResult = class {
|
|
1305
|
-
constructor(value) {
|
|
1306
|
-
this.value = value;
|
|
1307
|
-
}
|
|
1308
|
-
value;
|
|
1309
|
-
getEmail() {
|
|
1310
|
-
return findEmail(this.value);
|
|
1311
|
-
}
|
|
1312
|
-
getPhone() {
|
|
1313
|
-
return findPhone(this.value);
|
|
1314
|
-
}
|
|
1315
|
-
tryList(options) {
|
|
1316
|
-
return tryConvertToList(this.value, options)?.rows ?? null;
|
|
1317
|
-
}
|
|
1318
|
-
};
|
|
1319
|
-
function createToolCallResult(value) {
|
|
1320
|
-
return new DeeplineToolCallResult(value);
|
|
1321
|
-
}
|
|
1322
|
-
function getByDottedPath(root, dottedPath) {
|
|
1323
|
-
let current = root;
|
|
1324
|
-
for (const segment of String(dottedPath || "").split(".").filter(Boolean)) {
|
|
1325
|
-
if (!isPlainObject(current) || !(segment in current)) {
|
|
1326
|
-
return null;
|
|
1327
|
-
}
|
|
1328
|
-
current = current[segment];
|
|
1329
|
-
}
|
|
1330
|
-
return current;
|
|
1331
|
-
}
|
|
1332
|
-
function normalizeRows(value) {
|
|
1333
|
-
if (!Array.isArray(value)) return null;
|
|
1334
|
-
return value.map((entry) => {
|
|
1335
|
-
if (isPlainObject(entry)) return entry;
|
|
1336
|
-
return { value: entry };
|
|
1337
|
-
});
|
|
1338
|
-
}
|
|
1339
|
-
function candidateRoots(payload) {
|
|
1340
|
-
const roots = [{ path: null, value: payload }];
|
|
1341
|
-
if (isPlainObject(payload) && isPlainObject(payload.result)) {
|
|
1342
|
-
roots.push({ path: "result", value: payload.result });
|
|
1343
|
-
if (isPlainObject(payload.result.data)) {
|
|
1344
|
-
roots.push({ path: "result.data", value: payload.result.data });
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
return roots;
|
|
1348
|
-
}
|
|
1349
|
-
function findBestArrayCandidate(value, pathPrefix = "", depth = 0) {
|
|
1350
|
-
if (depth > 5) return null;
|
|
1351
|
-
const directRows = normalizeRows(value);
|
|
1352
|
-
const hasObjectRow = directRows?.some((row) => Object.keys(row).some((key) => key !== "value")) ?? false;
|
|
1353
|
-
let best = directRows && directRows.length > 0 && hasObjectRow ? { path: pathPrefix, rows: directRows } : null;
|
|
1354
|
-
if (!isPlainObject(value)) {
|
|
1355
|
-
return best;
|
|
1356
|
-
}
|
|
1357
|
-
for (const [key, child] of Object.entries(value)) {
|
|
1358
|
-
const childPath = pathPrefix ? `${pathPrefix}.${key}` : key;
|
|
1359
|
-
const candidate = findBestArrayCandidate(child, childPath, depth + 1);
|
|
1360
|
-
if (!candidate) continue;
|
|
1361
|
-
if (!best || candidate.rows.length > best.rows.length) {
|
|
1362
|
-
best = candidate;
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
return best;
|
|
1366
|
-
}
|
|
1367
|
-
function tryConvertToList(payload, options) {
|
|
1368
|
-
const listExtractorPaths = Array.isArray(options?.listExtractorPaths) ? options?.listExtractorPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
|
|
1369
|
-
if (listExtractorPaths.length > 0) {
|
|
1370
|
-
for (const root of candidateRoots(payload)) {
|
|
1371
|
-
for (const extractorPath of listExtractorPaths) {
|
|
1372
|
-
const resolved = getByDottedPath(root.value, extractorPath);
|
|
1373
|
-
const rows = normalizeRows(resolved);
|
|
1374
|
-
if (rows && rows.length > 0) {
|
|
1375
|
-
const sourcePath = root.path ? `${root.path}.${extractorPath}` : extractorPath;
|
|
1376
|
-
return { rows, strategy: "configured_paths", sourcePath };
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
for (const root of candidateRoots(payload)) {
|
|
1382
|
-
const candidate = findBestArrayCandidate(root.value, root.path ?? "");
|
|
1383
|
-
if (!candidate || candidate.rows.length === 0) continue;
|
|
1384
|
-
return {
|
|
1385
|
-
rows: candidate.rows,
|
|
1386
|
-
strategy: "auto_detected",
|
|
1387
|
-
sourcePath: candidate.path || root.path
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1390
|
-
return null;
|
|
1391
|
-
}
|
|
1392
|
-
function ensureOutputDir() {
|
|
1393
|
-
const outputDir = join2(homedir2(), ".local", "share", "deepline", "data");
|
|
1394
|
-
mkdirSync2(outputDir, { recursive: true });
|
|
1395
|
-
return outputDir;
|
|
1396
|
-
}
|
|
1397
|
-
function writeJsonOutputFile(payload, stem) {
|
|
1398
|
-
const outputDir = ensureOutputDir();
|
|
1399
|
-
const outputPath = join2(outputDir, `${stem}_${Date.now()}.json`);
|
|
1400
|
-
writeFileSync2(outputPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
1401
|
-
return outputPath;
|
|
1402
|
-
}
|
|
1403
|
-
function writeCsvOutputFile(rows, stem) {
|
|
1404
|
-
const outputDir = ensureOutputDir();
|
|
1405
|
-
const outputPath = join2(outputDir, `${stem}_${Date.now()}.csv`);
|
|
1406
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1407
|
-
const columns = [];
|
|
1408
|
-
for (const row of rows) {
|
|
1409
|
-
for (const key of Object.keys(row)) {
|
|
1410
|
-
if (!seen.has(key)) {
|
|
1411
|
-
seen.add(key);
|
|
1412
|
-
columns.push(key);
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
const escapeCell = (value) => {
|
|
1417
|
-
const normalized = value == null ? "" : typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : JSON.stringify(value);
|
|
1418
|
-
if (/[",\n]/.test(normalized)) {
|
|
1419
|
-
return `"${normalized.replace(/"/g, '""')}"`;
|
|
1420
|
-
}
|
|
1421
|
-
return normalized;
|
|
1422
|
-
};
|
|
1423
|
-
const lines = [];
|
|
1424
|
-
lines.push(columns.map(escapeCell).join(","));
|
|
1425
|
-
for (const row of rows) {
|
|
1426
|
-
lines.push(columns.map((column) => escapeCell(row[column])).join(","));
|
|
1427
|
-
}
|
|
1428
|
-
writeFileSync2(outputPath, `${lines.join("\n")}
|
|
1429
|
-
`, "utf-8");
|
|
1430
|
-
const previewRows = rows.slice(0, 5);
|
|
1431
|
-
const previewColumns = columns.slice(0, 5);
|
|
1432
|
-
const preview = [
|
|
1433
|
-
previewColumns.join(","),
|
|
1434
|
-
...previewRows.map((row) => previewColumns.map((column) => escapeCell(row[column])).join(","))
|
|
1435
|
-
].join("\n");
|
|
1436
|
-
return {
|
|
1437
|
-
path: outputPath,
|
|
1438
|
-
rowCount: rows.length,
|
|
1439
|
-
columns,
|
|
1440
|
-
preview
|
|
1441
|
-
};
|
|
1442
|
-
}
|
|
1443
|
-
function extractSummaryFields(payload) {
|
|
1444
|
-
const candidates = candidateRoots(payload);
|
|
1445
|
-
for (const candidate of candidates) {
|
|
1446
|
-
if (!isPlainObject(candidate.value)) continue;
|
|
1447
|
-
const summaryEntries = Object.entries(candidate.value).filter(([, value]) => {
|
|
1448
|
-
return value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
1449
|
-
});
|
|
1450
|
-
if (summaryEntries.length === 0) continue;
|
|
1451
|
-
return Object.fromEntries(summaryEntries);
|
|
1452
|
-
}
|
|
1453
|
-
return {};
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
1211
|
// src/play.ts
|
|
1457
1212
|
var DeeplineConditionalStepResolver = class _DeeplineConditionalStepResolver {
|
|
1458
1213
|
constructor(when2, run, elseValue) {
|
|
@@ -1591,12 +1346,8 @@ var DeeplineContext = class {
|
|
|
1591
1346
|
* ```typescript
|
|
1592
1347
|
* const tools = await ctx.tools.list();
|
|
1593
1348
|
* const meta = await ctx.tools.get('apollo_people_search');
|
|
1594
|
-
* const
|
|
1595
|
-
*
|
|
1596
|
-
* input: { domain: 'stripe.com' },
|
|
1597
|
-
* });
|
|
1598
|
-
* const rows = result.tryList({ listExtractorPaths: ['people'] });
|
|
1599
|
-
* const email = result.getEmail();
|
|
1349
|
+
* const companyLookup = await ctx.tools.execute('test_company_search', { domain: 'stripe.com' });
|
|
1350
|
+
* const company = companyLookup.result.data;
|
|
1600
1351
|
* ```
|
|
1601
1352
|
*/
|
|
1602
1353
|
get tools() {
|
|
@@ -1605,10 +1356,8 @@ var DeeplineContext = class {
|
|
|
1605
1356
|
list: () => this.client.listTools(),
|
|
1606
1357
|
/** Get detailed metadata for a tool. */
|
|
1607
1358
|
get: (toolId) => this.client.getTool(toolId),
|
|
1608
|
-
/** Execute a tool and return
|
|
1609
|
-
execute: async (
|
|
1610
|
-
await this.client.executeTool(request.tool, request.input)
|
|
1611
|
-
)
|
|
1359
|
+
/** Execute a tool and return the standard execution envelope. */
|
|
1360
|
+
execute: async (toolId, input) => this.client.executeTool(toolId, input)
|
|
1612
1361
|
};
|
|
1613
1362
|
}
|
|
1614
1363
|
get plays() {
|
|
@@ -1811,6 +1560,147 @@ function getDefinedPlayMetadata(value) {
|
|
|
1811
1560
|
}
|
|
1812
1561
|
return candidate;
|
|
1813
1562
|
}
|
|
1563
|
+
|
|
1564
|
+
// src/tool-output.ts
|
|
1565
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1566
|
+
import { homedir as homedir2 } from "os";
|
|
1567
|
+
import { join as join2 } from "path";
|
|
1568
|
+
function isPlainObject(value) {
|
|
1569
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1570
|
+
}
|
|
1571
|
+
function getByDottedPath(root, dottedPath) {
|
|
1572
|
+
let current = root;
|
|
1573
|
+
for (const segment of String(dottedPath || "").split(".").filter(Boolean)) {
|
|
1574
|
+
if (!isPlainObject(current) || !(segment in current)) {
|
|
1575
|
+
return null;
|
|
1576
|
+
}
|
|
1577
|
+
current = current[segment];
|
|
1578
|
+
}
|
|
1579
|
+
return current;
|
|
1580
|
+
}
|
|
1581
|
+
function normalizeRows(value) {
|
|
1582
|
+
if (!Array.isArray(value)) return null;
|
|
1583
|
+
return value.map((entry) => {
|
|
1584
|
+
if (isPlainObject(entry)) return entry;
|
|
1585
|
+
return { value: entry };
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
function candidateRoots(payload) {
|
|
1589
|
+
const roots = [{ path: null, value: payload }];
|
|
1590
|
+
if (isPlainObject(payload) && isPlainObject(payload.result)) {
|
|
1591
|
+
roots.push({ path: "result", value: payload.result });
|
|
1592
|
+
if (isPlainObject(payload.result.data)) {
|
|
1593
|
+
roots.push({ path: "result.data", value: payload.result.data });
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
return roots;
|
|
1597
|
+
}
|
|
1598
|
+
function findBestArrayCandidate(value, pathPrefix = "", depth = 0) {
|
|
1599
|
+
if (depth > 5) return null;
|
|
1600
|
+
const directRows = normalizeRows(value);
|
|
1601
|
+
const hasObjectRow = directRows?.some((row) => Object.keys(row).some((key) => key !== "value")) ?? false;
|
|
1602
|
+
let best = directRows && directRows.length > 0 && hasObjectRow ? { path: pathPrefix, rows: directRows } : null;
|
|
1603
|
+
if (!isPlainObject(value)) {
|
|
1604
|
+
return best;
|
|
1605
|
+
}
|
|
1606
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1607
|
+
const childPath = pathPrefix ? `${pathPrefix}.${key}` : key;
|
|
1608
|
+
const candidate = findBestArrayCandidate(child, childPath, depth + 1);
|
|
1609
|
+
if (!candidate) continue;
|
|
1610
|
+
if (!best || candidate.rows.length > best.rows.length) {
|
|
1611
|
+
best = candidate;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
return best;
|
|
1615
|
+
}
|
|
1616
|
+
function tryConvertToList(payload, options) {
|
|
1617
|
+
const listExtractorPaths = Array.isArray(options?.listExtractorPaths) ? options?.listExtractorPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
|
|
1618
|
+
if (listExtractorPaths.length > 0) {
|
|
1619
|
+
for (const root of candidateRoots(payload)) {
|
|
1620
|
+
for (const extractorPath of listExtractorPaths) {
|
|
1621
|
+
const resolved = getByDottedPath(root.value, extractorPath);
|
|
1622
|
+
const rows = normalizeRows(resolved);
|
|
1623
|
+
if (rows && rows.length > 0) {
|
|
1624
|
+
const sourcePath = root.path ? `${root.path}.${extractorPath}` : extractorPath;
|
|
1625
|
+
return { rows, strategy: "configured_paths", sourcePath };
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
for (const root of candidateRoots(payload)) {
|
|
1631
|
+
const candidate = findBestArrayCandidate(root.value, root.path ?? "");
|
|
1632
|
+
if (!candidate || candidate.rows.length === 0) continue;
|
|
1633
|
+
return {
|
|
1634
|
+
rows: candidate.rows,
|
|
1635
|
+
strategy: "auto_detected",
|
|
1636
|
+
sourcePath: candidate.path || root.path
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
return null;
|
|
1640
|
+
}
|
|
1641
|
+
function ensureOutputDir() {
|
|
1642
|
+
const outputDir = join2(homedir2(), ".local", "share", "deepline", "data");
|
|
1643
|
+
mkdirSync2(outputDir, { recursive: true });
|
|
1644
|
+
return outputDir;
|
|
1645
|
+
}
|
|
1646
|
+
function writeJsonOutputFile(payload, stem) {
|
|
1647
|
+
const outputDir = ensureOutputDir();
|
|
1648
|
+
const outputPath = join2(outputDir, `${stem}_${Date.now()}.json`);
|
|
1649
|
+
writeFileSync2(outputPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
1650
|
+
return outputPath;
|
|
1651
|
+
}
|
|
1652
|
+
function writeCsvOutputFile(rows, stem) {
|
|
1653
|
+
const outputDir = ensureOutputDir();
|
|
1654
|
+
const outputPath = join2(outputDir, `${stem}_${Date.now()}.csv`);
|
|
1655
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1656
|
+
const columns = [];
|
|
1657
|
+
for (const row of rows) {
|
|
1658
|
+
for (const key of Object.keys(row)) {
|
|
1659
|
+
if (!seen.has(key)) {
|
|
1660
|
+
seen.add(key);
|
|
1661
|
+
columns.push(key);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
const escapeCell = (value) => {
|
|
1666
|
+
const normalized = value == null ? "" : typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : JSON.stringify(value);
|
|
1667
|
+
if (/[",\n]/.test(normalized)) {
|
|
1668
|
+
return `"${normalized.replace(/"/g, '""')}"`;
|
|
1669
|
+
}
|
|
1670
|
+
return normalized;
|
|
1671
|
+
};
|
|
1672
|
+
const lines = [];
|
|
1673
|
+
lines.push(columns.map(escapeCell).join(","));
|
|
1674
|
+
for (const row of rows) {
|
|
1675
|
+
lines.push(columns.map((column) => escapeCell(row[column])).join(","));
|
|
1676
|
+
}
|
|
1677
|
+
writeFileSync2(outputPath, `${lines.join("\n")}
|
|
1678
|
+
`, "utf-8");
|
|
1679
|
+
const previewRows = rows.slice(0, 5);
|
|
1680
|
+
const previewColumns = columns.slice(0, 5);
|
|
1681
|
+
const preview = [
|
|
1682
|
+
previewColumns.join(","),
|
|
1683
|
+
...previewRows.map((row) => previewColumns.map((column) => escapeCell(row[column])).join(","))
|
|
1684
|
+
].join("\n");
|
|
1685
|
+
return {
|
|
1686
|
+
path: outputPath,
|
|
1687
|
+
rowCount: rows.length,
|
|
1688
|
+
columns,
|
|
1689
|
+
preview
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
function extractSummaryFields(payload) {
|
|
1693
|
+
const candidates = candidateRoots(payload);
|
|
1694
|
+
for (const candidate of candidates) {
|
|
1695
|
+
if (!isPlainObject(candidate.value)) continue;
|
|
1696
|
+
const summaryEntries = Object.entries(candidate.value).filter(([, value]) => {
|
|
1697
|
+
return value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
1698
|
+
});
|
|
1699
|
+
if (summaryEntries.length === 0) continue;
|
|
1700
|
+
return Object.fromEntries(summaryEntries);
|
|
1701
|
+
}
|
|
1702
|
+
return {};
|
|
1703
|
+
}
|
|
1814
1704
|
export {
|
|
1815
1705
|
AuthError,
|
|
1816
1706
|
ConfigError,
|
|
@@ -1822,7 +1712,6 @@ export {
|
|
|
1822
1712
|
RateLimitError,
|
|
1823
1713
|
SDK_API_CONTRACT,
|
|
1824
1714
|
SDK_VERSION,
|
|
1825
|
-
createToolCallResult,
|
|
1826
1715
|
defineInput,
|
|
1827
1716
|
definePlay,
|
|
1828
1717
|
defineWorkflow,
|