deepline 0.1.47 → 0.1.49
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 +14 -14
- package/dist/cli/index.js +863 -260
- package/dist/cli/index.mjs +863 -260
- package/dist/index.d.mts +44 -34
- package/dist/index.d.ts +44 -34
- package/dist/index.js +57 -16
- package/dist/index.mjs +57 -16
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +5 -2
- package/dist/repo/apps/play-runner-workers/src/entry.ts +383 -305
- package/dist/repo/apps/play-runner-workers/src/runtime/receipts.ts +51 -21
- package/dist/repo/sdk/src/client.ts +29 -4
- package/dist/repo/sdk/src/play.ts +35 -42
- package/dist/repo/sdk/src/plays/harness-stub.ts +1 -1
- package/dist/repo/sdk/src/version.ts +1 -1
- package/dist/repo/sdk/src/worker-play-entry.ts +17 -67
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +34 -3
- package/dist/repo/shared_libs/plays/row-identity.ts +5 -59
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
|
-
import {
|
|
4
|
+
import { mkdtemp, rm, writeFile as writeFile4 } from "fs/promises";
|
|
5
|
+
import { join as join11 } from "path";
|
|
6
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
7
|
+
import { Command as Command3 } from "commander";
|
|
5
8
|
|
|
6
9
|
// src/config.ts
|
|
7
10
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -193,7 +196,7 @@ function resolveConfig(options) {
|
|
|
193
196
|
}
|
|
194
197
|
|
|
195
198
|
// src/version.ts
|
|
196
|
-
var SDK_VERSION = "0.1.
|
|
199
|
+
var SDK_VERSION = "0.1.49";
|
|
197
200
|
var SDK_API_CONTRACT = "2026-05-stripe-promo-checkout";
|
|
198
201
|
|
|
199
202
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
@@ -713,9 +716,18 @@ var DeeplineClient = class {
|
|
|
713
716
|
* console.log(`Found ${searchTools.length} search tools`);
|
|
714
717
|
* ```
|
|
715
718
|
*/
|
|
716
|
-
async listTools() {
|
|
719
|
+
async listTools(options) {
|
|
720
|
+
const params = new URLSearchParams();
|
|
721
|
+
if (options?.categories?.trim()) {
|
|
722
|
+
params.set("categories", options.categories.trim());
|
|
723
|
+
}
|
|
724
|
+
if (options?.grep?.trim()) {
|
|
725
|
+
params.set("grep", options.grep.trim());
|
|
726
|
+
params.set("grep_mode", options.grepMode ?? "all");
|
|
727
|
+
}
|
|
728
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
717
729
|
const res = await this.http.get(
|
|
718
|
-
|
|
730
|
+
`/api/v2/tools${suffix}`
|
|
719
731
|
);
|
|
720
732
|
return res.tools;
|
|
721
733
|
}
|
|
@@ -1350,9 +1362,17 @@ var DeeplineClient = class {
|
|
|
1350
1362
|
options?.reason ? { reason: options.reason } : {}
|
|
1351
1363
|
);
|
|
1352
1364
|
}
|
|
1353
|
-
async listPlays() {
|
|
1365
|
+
async listPlays(options) {
|
|
1366
|
+
const params = new URLSearchParams();
|
|
1367
|
+
if (options?.origin) params.set("origin", options.origin);
|
|
1368
|
+
if (options?.grep?.trim()) {
|
|
1369
|
+
params.set("grep", options.grep.trim());
|
|
1370
|
+
params.set("grep_mode", options.grepMode ?? "all");
|
|
1371
|
+
params.set("limit", "60");
|
|
1372
|
+
}
|
|
1373
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
1354
1374
|
const response = await this.http.get(
|
|
1355
|
-
|
|
1375
|
+
`/api/v2/plays${suffix}`
|
|
1356
1376
|
);
|
|
1357
1377
|
return response.plays ?? [];
|
|
1358
1378
|
}
|
|
@@ -2777,8 +2797,10 @@ async function handleCheckout(options) {
|
|
|
2777
2797
|
}
|
|
2778
2798
|
async function handleRedeemCode(code, options) {
|
|
2779
2799
|
const { http } = getAuthedHttpClient();
|
|
2780
|
-
const payload = await http.post("/api/v2/billing/checkout
|
|
2781
|
-
|
|
2800
|
+
const payload = await http.post("/api/v2/billing/checkout", {
|
|
2801
|
+
discountCode: code
|
|
2802
|
+
});
|
|
2803
|
+
const url = String(payload.url || payload.checkout_url || "");
|
|
2782
2804
|
if (!options.json && !options.noOpen && url) openInBrowser(url);
|
|
2783
2805
|
printCommandEnvelope({
|
|
2784
2806
|
...payload,
|
|
@@ -5668,6 +5690,16 @@ function extractPlayName(code, filePath) {
|
|
|
5668
5690
|
function isFileTarget(target) {
|
|
5669
5691
|
return existsSync6(resolve9(target));
|
|
5670
5692
|
}
|
|
5693
|
+
function looksLikeRunId(target) {
|
|
5694
|
+
return /^play\/[^/]+\/run\/[^/]+/.test(target.trim());
|
|
5695
|
+
}
|
|
5696
|
+
function formatPlayCommandReceivedRunIdError(input) {
|
|
5697
|
+
return `\`deepline plays ${input.command} <run-id>\` expects a play name, but this looks like a run id.
|
|
5698
|
+
Use:
|
|
5699
|
+
deepline runs get ${input.runId} --json
|
|
5700
|
+
deepline runs logs ${input.runId} --json
|
|
5701
|
+
deepline runs export ${input.runId} --out output.csv`;
|
|
5702
|
+
}
|
|
5671
5703
|
function looksLikeFilePath(target) {
|
|
5672
5704
|
if (target.trim().toLowerCase().startsWith("prebuilt/")) {
|
|
5673
5705
|
return false;
|
|
@@ -6094,6 +6126,69 @@ function describeLiveEventPhase(event) {
|
|
|
6094
6126
|
}
|
|
6095
6127
|
return null;
|
|
6096
6128
|
}
|
|
6129
|
+
function formatProgressLabel(raw) {
|
|
6130
|
+
const value = typeof raw === "string" && raw.trim() ? raw.trim() : "step";
|
|
6131
|
+
return value.replace(/^map:/, "").replace(/^tool:/, "");
|
|
6132
|
+
}
|
|
6133
|
+
function formatProgressCounts(input) {
|
|
6134
|
+
const completed = typeof input.completed === "number" && Number.isFinite(input.completed) ? input.completed : null;
|
|
6135
|
+
const total = typeof input.total === "number" && Number.isFinite(input.total) ? input.total : null;
|
|
6136
|
+
if (completed === null || total === null || total <= 0) {
|
|
6137
|
+
return null;
|
|
6138
|
+
}
|
|
6139
|
+
const percent = Math.max(0, Math.min(100, Math.round(completed / total * 100)));
|
|
6140
|
+
const failed = typeof input.failed === "number" && Number.isFinite(input.failed) && input.failed > 0 ? `, failed ${formatInteger(input.failed)}` : "";
|
|
6141
|
+
return `${formatInteger(completed)}/${formatInteger(total)} (${percent}%)${failed}`;
|
|
6142
|
+
}
|
|
6143
|
+
function getProgressLinesFromLiveEvent(event) {
|
|
6144
|
+
const payload = getEventPayload(event);
|
|
6145
|
+
if (event.type === "play.step.progress") {
|
|
6146
|
+
const counts = formatProgressCounts({
|
|
6147
|
+
completed: payload.completed,
|
|
6148
|
+
total: payload.total,
|
|
6149
|
+
failed: payload.failed
|
|
6150
|
+
});
|
|
6151
|
+
if (!counts) return [];
|
|
6152
|
+
return [`progress ${formatProgressLabel(payload.stepId)}: ${counts}`];
|
|
6153
|
+
}
|
|
6154
|
+
if (event.type !== "play.run.snapshot" && event.type !== "play.run.final_status") {
|
|
6155
|
+
return [];
|
|
6156
|
+
}
|
|
6157
|
+
const nodeStates = Array.isArray(payload.nodeStates) ? payload.nodeStates : Array.isArray(payload.steps) ? payload.steps : [];
|
|
6158
|
+
const lines = [];
|
|
6159
|
+
for (const state of nodeStates) {
|
|
6160
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) {
|
|
6161
|
+
continue;
|
|
6162
|
+
}
|
|
6163
|
+
const record = state;
|
|
6164
|
+
const progress = record.progress && typeof record.progress === "object" && !Array.isArray(record.progress) ? record.progress : null;
|
|
6165
|
+
if (!progress) {
|
|
6166
|
+
continue;
|
|
6167
|
+
}
|
|
6168
|
+
const counts = formatProgressCounts({
|
|
6169
|
+
completed: progress.completed,
|
|
6170
|
+
total: progress.total,
|
|
6171
|
+
failed: progress.failed
|
|
6172
|
+
});
|
|
6173
|
+
if (!counts) {
|
|
6174
|
+
continue;
|
|
6175
|
+
}
|
|
6176
|
+
lines.push(
|
|
6177
|
+
`progress ${formatProgressLabel(record.nodeId ?? progress.artifactTableNamespace)}: ${counts}`
|
|
6178
|
+
);
|
|
6179
|
+
}
|
|
6180
|
+
return lines;
|
|
6181
|
+
}
|
|
6182
|
+
function printPlayProgressLines(input) {
|
|
6183
|
+
for (const line of input.lines) {
|
|
6184
|
+
const signature = line.trim();
|
|
6185
|
+
if (!signature || input.state.lastProgressSignature === signature) {
|
|
6186
|
+
continue;
|
|
6187
|
+
}
|
|
6188
|
+
input.state.lastProgressSignature = signature;
|
|
6189
|
+
input.progress.writeLine(line);
|
|
6190
|
+
}
|
|
6191
|
+
}
|
|
6097
6192
|
function buildPlayDashboardUrl(baseUrl, playName) {
|
|
6098
6193
|
const trimmedBase = baseUrl.replace(/\/$/, "");
|
|
6099
6194
|
const encodedPlayName = encodeURIComponent(playName);
|
|
@@ -6167,6 +6262,13 @@ async function waitForPlayCompletionByStream(input) {
|
|
|
6167
6262
|
state: input.state,
|
|
6168
6263
|
progress: input.progress
|
|
6169
6264
|
});
|
|
6265
|
+
if (!input.jsonOutput) {
|
|
6266
|
+
printPlayProgressLines({
|
|
6267
|
+
lines: getProgressLinesFromLiveEvent(event),
|
|
6268
|
+
state: input.state,
|
|
6269
|
+
progress: input.progress
|
|
6270
|
+
});
|
|
6271
|
+
}
|
|
6170
6272
|
const status = getStatusFromLiveEvent(event);
|
|
6171
6273
|
if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
|
|
6172
6274
|
const finalStatus = await input.client.getPlayStatus(input.workflowId, {
|
|
@@ -6235,7 +6337,8 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
|
|
|
6235
6337
|
);
|
|
6236
6338
|
const state = {
|
|
6237
6339
|
lastLogIndex: 0,
|
|
6238
|
-
emittedRunnerStarted: false
|
|
6340
|
+
emittedRunnerStarted: false,
|
|
6341
|
+
lastProgressSignature: null
|
|
6239
6342
|
};
|
|
6240
6343
|
const controller = new AbortController();
|
|
6241
6344
|
let timedOut = false;
|
|
@@ -6263,11 +6366,6 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
|
|
|
6263
6366
|
}
|
|
6264
6367
|
const workflowId = lastKnownWorkflowId || "pending";
|
|
6265
6368
|
if (workflowId !== "pending" && !emittedDashboardUrl) {
|
|
6266
|
-
openPlayDashboard({
|
|
6267
|
-
dashboardUrl,
|
|
6268
|
-
jsonOutput: input.jsonOutput,
|
|
6269
|
-
noOpen: input.noOpen
|
|
6270
|
-
});
|
|
6271
6369
|
if (!input.jsonOutput) {
|
|
6272
6370
|
writeStartedPlayRun({
|
|
6273
6371
|
runId: workflowId,
|
|
@@ -6277,6 +6375,11 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
|
|
|
6277
6375
|
progress: input.progress
|
|
6278
6376
|
});
|
|
6279
6377
|
}
|
|
6378
|
+
openPlayDashboard({
|
|
6379
|
+
dashboardUrl,
|
|
6380
|
+
jsonOutput: input.jsonOutput,
|
|
6381
|
+
noOpen: input.noOpen
|
|
6382
|
+
});
|
|
6280
6383
|
input.progress.phase(`loading play on ${dashboardUrl}`);
|
|
6281
6384
|
emittedDashboardUrl = true;
|
|
6282
6385
|
}
|
|
@@ -6299,6 +6402,13 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
|
|
|
6299
6402
|
state,
|
|
6300
6403
|
progress: input.progress
|
|
6301
6404
|
});
|
|
6405
|
+
if (!input.jsonOutput) {
|
|
6406
|
+
printPlayProgressLines({
|
|
6407
|
+
lines: getProgressLinesFromLiveEvent(event),
|
|
6408
|
+
state,
|
|
6409
|
+
progress: input.progress
|
|
6410
|
+
});
|
|
6411
|
+
}
|
|
6302
6412
|
const finalStatus = getFinalStatusFromLiveEvent(event);
|
|
6303
6413
|
if (finalStatus) {
|
|
6304
6414
|
recordCliTrace({
|
|
@@ -6567,6 +6677,7 @@ function buildRunNextCommands(status) {
|
|
|
6567
6677
|
const commands = {
|
|
6568
6678
|
get: `deepline runs get ${runId} --json`,
|
|
6569
6679
|
full: `deepline runs get ${runId} --full --json`,
|
|
6680
|
+
export: `deepline runs export ${runId} --out output.csv`,
|
|
6570
6681
|
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
6571
6682
|
logs: `deepline runs logs ${runId} --out run.log --json`
|
|
6572
6683
|
};
|
|
@@ -6699,6 +6810,32 @@ function formatInsufficientCreditsMessage(input) {
|
|
|
6699
6810
|
const addSuffix = billingUrl && recommended !== "-" ? ` Add >=${recommended} at ${billingUrl}.` : billingUrl ? ` Add credits at ${billingUrl}.` : "";
|
|
6700
6811
|
return `Workspace balance ${balance} < required ${required} for ${operation}${workspaceSuffix}.${addSuffix}`;
|
|
6701
6812
|
}
|
|
6813
|
+
function buildInsufficientCreditsSummaryLines(input) {
|
|
6814
|
+
const progress = input.status.progress;
|
|
6815
|
+
const rowsInfo = extractCanonicalRowsInfo(input.status);
|
|
6816
|
+
const completed = getNumericField(progress, "completed") ?? getNumericField(progress, "completedRows") ?? rowsInfo?.rows.length ?? null;
|
|
6817
|
+
const total = getNumericField(progress, "total") ?? getNumericField(progress, "totalRows") ?? rowsInfo?.totalRows ?? null;
|
|
6818
|
+
const lines = [
|
|
6819
|
+
" status: stopped_insufficient_credits",
|
|
6820
|
+
completed === null ? " completed rows: unknown" : ` completed rows: ${formatInteger(completed)}${total !== null ? ` of ${formatInteger(total)}` : ""}`,
|
|
6821
|
+
" reusable receipts: yes; rerun after adding credits to continue from completed provider work"
|
|
6822
|
+
];
|
|
6823
|
+
const billingUrl = getStringField(input.billing, "billing_url");
|
|
6824
|
+
const recommended = formatCreditAmount(
|
|
6825
|
+
input.billing.recommended_add_credits ?? input.billing.needed_credits
|
|
6826
|
+
);
|
|
6827
|
+
if (billingUrl) {
|
|
6828
|
+
lines.push(
|
|
6829
|
+
recommended !== "-" ? ` add credits: add >=${recommended} at ${billingUrl}` : ` add credits: ${billingUrl}`
|
|
6830
|
+
);
|
|
6831
|
+
}
|
|
6832
|
+
const runId = input.status.runId?.trim();
|
|
6833
|
+
if (runId) {
|
|
6834
|
+
lines.push(` inspect: deepline runs get ${runId} --json`);
|
|
6835
|
+
lines.push(` export partial: deepline runs export ${runId} --out output.csv`);
|
|
6836
|
+
}
|
|
6837
|
+
return lines;
|
|
6838
|
+
}
|
|
6702
6839
|
function formatPlayErrorForDisplay(status, error) {
|
|
6703
6840
|
if (!error) {
|
|
6704
6841
|
return null;
|
|
@@ -6889,6 +7026,19 @@ function formatDatasetStatsLines(datasetStats) {
|
|
|
6889
7026
|
}
|
|
6890
7027
|
return lines;
|
|
6891
7028
|
}
|
|
7029
|
+
function buildJsonRunResultRenderLines(status) {
|
|
7030
|
+
const publicStatus = status.status ?? "running";
|
|
7031
|
+
const runId = status.runId ?? "unknown";
|
|
7032
|
+
const lines = [`${publicStatus} ${runId}`];
|
|
7033
|
+
const progressError = status.progress?.error;
|
|
7034
|
+
if (typeof progressError === "string") {
|
|
7035
|
+
const billing = extractBillingForStatus(status, progressError);
|
|
7036
|
+
if (isInsufficientCreditsBilling(billing)) {
|
|
7037
|
+
lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
|
|
7038
|
+
}
|
|
7039
|
+
}
|
|
7040
|
+
return lines;
|
|
7041
|
+
}
|
|
6892
7042
|
function writePlayResult(status, jsonOutput, options) {
|
|
6893
7043
|
if (jsonOutput) {
|
|
6894
7044
|
const payload2 = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status);
|
|
@@ -6898,9 +7048,7 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6898
7048
|
sections: [
|
|
6899
7049
|
{
|
|
6900
7050
|
title: "run result",
|
|
6901
|
-
lines:
|
|
6902
|
-
`${status.status ?? "running"} ${status.runId ?? "unknown"}`
|
|
6903
|
-
]
|
|
7051
|
+
lines: buildJsonRunResultRenderLines(status)
|
|
6904
7052
|
}
|
|
6905
7053
|
]
|
|
6906
7054
|
}
|
|
@@ -6927,6 +7075,10 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6927
7075
|
lines.push(...formatDatasetStatsLines(datasetStats));
|
|
6928
7076
|
const progressError = status.progress?.error;
|
|
6929
7077
|
if (progressError && typeof progressError === "string") {
|
|
7078
|
+
const billing = extractBillingForStatus(status, progressError);
|
|
7079
|
+
if (isInsufficientCreditsBilling(billing)) {
|
|
7080
|
+
lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
|
|
7081
|
+
}
|
|
6930
7082
|
const displayError = formatPlayErrorForDisplay(status, progressError) ?? progressError;
|
|
6931
7083
|
lines.push(` error: ${displayError.slice(0, 200)}`);
|
|
6932
7084
|
}
|
|
@@ -7306,24 +7458,31 @@ function writeStartedPlayRun(input) {
|
|
|
7306
7458
|
workflowId: input.runId,
|
|
7307
7459
|
name: input.playName,
|
|
7308
7460
|
status: input.status ?? "started",
|
|
7309
|
-
dashboardUrl: input.dashboardUrl
|
|
7461
|
+
dashboardUrl: input.dashboardUrl,
|
|
7462
|
+
next: {
|
|
7463
|
+
inspect: `deepline runs get ${input.runId} --json`,
|
|
7464
|
+
full: `deepline runs get ${input.runId} --full --json`,
|
|
7465
|
+
logs: `deepline runs logs ${input.runId} --json`,
|
|
7466
|
+
export: `deepline runs export ${input.runId} --out output.csv`,
|
|
7467
|
+
stop: `deepline runs stop ${input.runId} --reason "stale lock" --json`
|
|
7468
|
+
}
|
|
7310
7469
|
};
|
|
7470
|
+
const lines = [
|
|
7471
|
+
`Started ${input.playName}`,
|
|
7472
|
+
` run id: ${input.runId}`,
|
|
7473
|
+
` inspect: deepline runs get ${input.runId} --json`,
|
|
7474
|
+
` full debug: deepline runs get ${input.runId} --full --json`,
|
|
7475
|
+
` logs: deepline runs logs ${input.runId} --json`,
|
|
7476
|
+
` export after completion: deepline runs export ${input.runId} --out output.csv`,
|
|
7477
|
+
` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`
|
|
7478
|
+
];
|
|
7311
7479
|
if (input.jsonOutput) {
|
|
7312
7480
|
printCommandEnvelope({
|
|
7313
7481
|
...payload,
|
|
7314
|
-
render: { sections: [{ title: "play run", lines
|
|
7482
|
+
render: { sections: [{ title: "play run", lines }] }
|
|
7315
7483
|
}, { json: true });
|
|
7316
7484
|
return;
|
|
7317
7485
|
}
|
|
7318
|
-
const lines = [
|
|
7319
|
-
`Started ${input.playName}`,
|
|
7320
|
-
` run id: ${input.runId}`,
|
|
7321
|
-
` get status: deepline runs get ${input.runId} --json`,
|
|
7322
|
-
` logs: deepline runs logs ${input.runId} --json`,
|
|
7323
|
-
` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`,
|
|
7324
|
-
` result JSON: deepline runs get ${input.runId} --json`,
|
|
7325
|
-
` full debug JSON: deepline runs get ${input.runId} --full --json`
|
|
7326
|
-
];
|
|
7327
7486
|
if (input.dashboardUrl) {
|
|
7328
7487
|
lines.push(` play page: ${input.dashboardUrl}`);
|
|
7329
7488
|
}
|
|
@@ -7339,13 +7498,13 @@ function writeStartedPlayRun(input) {
|
|
|
7339
7498
|
` });
|
|
7340
7499
|
}
|
|
7341
7500
|
function parsePlayRunOptions(args) {
|
|
7342
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--
|
|
7501
|
+
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.map guidance.";
|
|
7343
7502
|
let filePath = null;
|
|
7344
7503
|
let playName = null;
|
|
7345
7504
|
let input = null;
|
|
7346
7505
|
let revisionId = null;
|
|
7347
7506
|
let revisionSelector = null;
|
|
7348
|
-
const watch = args.includes("--
|
|
7507
|
+
const watch = !args.includes("--no-wait");
|
|
7349
7508
|
let jsonOutput = watch ? args.includes("--json") : argsWantJson(args);
|
|
7350
7509
|
const emitLogs = !jsonOutput || args.includes("--logs");
|
|
7351
7510
|
const force = args.includes("--force");
|
|
@@ -7391,7 +7550,7 @@ function parsePlayRunOptions(args) {
|
|
|
7391
7550
|
waitTimeoutMs = parsePositiveInteger2(args[++index], arg);
|
|
7392
7551
|
continue;
|
|
7393
7552
|
}
|
|
7394
|
-
if (arg === "--json" || arg === "--wait" || arg === "--tail" || arg === "--watch" || arg === "--logs" || arg === "--force" || arg === "--no-open") {
|
|
7553
|
+
if (arg === "--json" || arg === "--wait" || arg === "--tail" || arg === "--watch" || arg === "--no-wait" || arg === "--logs" || arg === "--force" || arg === "--no-open") {
|
|
7395
7554
|
if (arg === "--watch") {
|
|
7396
7555
|
continue;
|
|
7397
7556
|
}
|
|
@@ -8247,6 +8406,12 @@ async function handlePlayGet(args) {
|
|
|
8247
8406
|
console.error("Usage: deepline play get <play-file.ts|play-name> [--json]");
|
|
8248
8407
|
return 1;
|
|
8249
8408
|
}
|
|
8409
|
+
if (looksLikeRunId(target)) {
|
|
8410
|
+
console.error(
|
|
8411
|
+
formatPlayCommandReceivedRunIdError({ command: "get", runId: target })
|
|
8412
|
+
);
|
|
8413
|
+
return 2;
|
|
8414
|
+
}
|
|
8250
8415
|
const client = new DeeplineClient();
|
|
8251
8416
|
const explicitJson = args.includes("--json");
|
|
8252
8417
|
const sourceOutput = args.includes("--source");
|
|
@@ -8361,8 +8526,20 @@ async function handlePlayVersions(args) {
|
|
|
8361
8526
|
}
|
|
8362
8527
|
async function handlePlayList(args) {
|
|
8363
8528
|
const jsonOutput = argsWantJson(args);
|
|
8529
|
+
const originArgIndex = args.findIndex((arg) => arg === "--origin");
|
|
8530
|
+
const rawOrigin = originArgIndex >= 0 ? args[originArgIndex + 1] : void 0;
|
|
8531
|
+
if (rawOrigin && rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
|
|
8532
|
+
throw new Error(`Invalid value for --origin: ${rawOrigin}`);
|
|
8533
|
+
}
|
|
8534
|
+
const origin = rawOrigin;
|
|
8364
8535
|
const client = new DeeplineClient();
|
|
8365
|
-
const plays = await client.listPlays(
|
|
8536
|
+
const plays = (await client.listPlays({
|
|
8537
|
+
...origin ? { origin } : {}
|
|
8538
|
+
})).filter((play) => {
|
|
8539
|
+
if (!origin) return true;
|
|
8540
|
+
const isPrebuilt = play.origin === "prebuilt" || play.ownerType === "deepline";
|
|
8541
|
+
return origin === "prebuilt" ? isPrebuilt : !isPrebuilt;
|
|
8542
|
+
});
|
|
8366
8543
|
if (jsonOutput) {
|
|
8367
8544
|
process.stdout.write(`${JSON.stringify(plays)}
|
|
8368
8545
|
`);
|
|
@@ -8465,6 +8642,54 @@ function printPlayDescription(play) {
|
|
|
8465
8642
|
);
|
|
8466
8643
|
}
|
|
8467
8644
|
}
|
|
8645
|
+
function compactPlaySchema(schema) {
|
|
8646
|
+
if (!schema) return null;
|
|
8647
|
+
const fields = Array.isArray(schema.fields) ? schema.fields.map(
|
|
8648
|
+
(field) => field && typeof field === "object" ? {
|
|
8649
|
+
name: String(field.name ?? ""),
|
|
8650
|
+
type: field.type ?? void 0,
|
|
8651
|
+
required: field.required ?? void 0
|
|
8652
|
+
} : null
|
|
8653
|
+
).filter((field) => Boolean(field?.name)) : [];
|
|
8654
|
+
return fields.length > 0 ? { fields } : schema;
|
|
8655
|
+
}
|
|
8656
|
+
function playSchemaMetadata(schema, key) {
|
|
8657
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return null;
|
|
8658
|
+
const value = schema[key];
|
|
8659
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
8660
|
+
}
|
|
8661
|
+
function playRunCommand(play, options) {
|
|
8662
|
+
const target = play.reference || play.name;
|
|
8663
|
+
if (options?.csvInput) {
|
|
8664
|
+
const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
|
|
8665
|
+
return `deepline plays run ${target} --input '${JSON.stringify({ [inputField]: "leads.csv" })}' --watch`;
|
|
8666
|
+
}
|
|
8667
|
+
return `deepline plays run ${target} --input '{...}' --watch`;
|
|
8668
|
+
}
|
|
8669
|
+
function summarizePlayListItemForCli(play, options) {
|
|
8670
|
+
const aliases = play.aliases?.length ? play.aliases : [play.name];
|
|
8671
|
+
const csvInput = playSchemaMetadata(play.inputSchema, "csvInput");
|
|
8672
|
+
const rowOutputSchema = playSchemaMetadata(play.outputSchema, "rowOutputSchema");
|
|
8673
|
+
const runCommand2 = playRunCommand(play, { csvInput });
|
|
8674
|
+
return {
|
|
8675
|
+
name: play.name,
|
|
8676
|
+
...play.reference ? { reference: play.reference } : {},
|
|
8677
|
+
...play.displayName ? { displayName: play.displayName } : {},
|
|
8678
|
+
origin: play.origin,
|
|
8679
|
+
ownerType: play.ownerType,
|
|
8680
|
+
canEdit: play.canEdit,
|
|
8681
|
+
canClone: play.canClone,
|
|
8682
|
+
aliases,
|
|
8683
|
+
inputSchema: options?.compact ? compactPlaySchema(play.inputSchema) : play.inputSchema ?? null,
|
|
8684
|
+
outputSchema: options?.compact ? compactPlaySchema(play.outputSchema) : play.outputSchema ?? null,
|
|
8685
|
+
...csvInput ? { csvInput } : {},
|
|
8686
|
+
...rowOutputSchema ? { rowOutputSchema } : {},
|
|
8687
|
+
runCommand: runCommand2,
|
|
8688
|
+
examples: [runCommand2],
|
|
8689
|
+
currentPublishedVersion: play.currentPublishedVersion ?? null,
|
|
8690
|
+
isDraftDirty: play.isDraftDirty
|
|
8691
|
+
};
|
|
8692
|
+
}
|
|
8468
8693
|
async function handlePlaySearch(args) {
|
|
8469
8694
|
let options;
|
|
8470
8695
|
try {
|
|
@@ -8486,6 +8711,100 @@ async function handlePlaySearch(args) {
|
|
|
8486
8711
|
}
|
|
8487
8712
|
process.stdout.write(`${plays.length} plays found:
|
|
8488
8713
|
|
|
8714
|
+
`);
|
|
8715
|
+
for (const play of plays) {
|
|
8716
|
+
printPlayDescription(play);
|
|
8717
|
+
console.log("");
|
|
8718
|
+
}
|
|
8719
|
+
return 0;
|
|
8720
|
+
}
|
|
8721
|
+
function normalizePlayGrepText(value) {
|
|
8722
|
+
if (value === null || value === void 0) return "";
|
|
8723
|
+
if (typeof value === "string") return value.toLowerCase();
|
|
8724
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
8725
|
+
return String(value).toLowerCase();
|
|
8726
|
+
}
|
|
8727
|
+
if (Array.isArray(value)) return value.map(normalizePlayGrepText).join(" ");
|
|
8728
|
+
if (typeof value === "object") {
|
|
8729
|
+
return Object.values(value).map(normalizePlayGrepText).join(" ");
|
|
8730
|
+
}
|
|
8731
|
+
return "";
|
|
8732
|
+
}
|
|
8733
|
+
function parsePlayGrepTerms(query, mode) {
|
|
8734
|
+
const trimmed = query.trim().toLowerCase();
|
|
8735
|
+
if (!trimmed) return [];
|
|
8736
|
+
if (mode === "phrase") return [trimmed];
|
|
8737
|
+
const matches = trimmed.match(/"([^"]+)"|'([^']+)'|\S+/g) ?? [];
|
|
8738
|
+
return matches.map((term) => term.replace(/^["']|["']$/g, "").trim()).filter(Boolean);
|
|
8739
|
+
}
|
|
8740
|
+
function matchesPlayGrepQuery(value, query, mode) {
|
|
8741
|
+
const terms = parsePlayGrepTerms(query, mode);
|
|
8742
|
+
if (terms.length === 0) return true;
|
|
8743
|
+
const haystack = normalizePlayGrepText(value);
|
|
8744
|
+
if (mode === "any") return terms.some((term) => haystack.includes(term));
|
|
8745
|
+
return terms.every((term) => haystack.includes(term));
|
|
8746
|
+
}
|
|
8747
|
+
async function handlePlayGrep(args) {
|
|
8748
|
+
const query = args[0]?.trim();
|
|
8749
|
+
if (!query) {
|
|
8750
|
+
console.error("Usage: deepline plays grep <query> [--origin prebuilt|owned] [--compact] [--json]");
|
|
8751
|
+
return 1;
|
|
8752
|
+
}
|
|
8753
|
+
let origin;
|
|
8754
|
+
let mode = "all";
|
|
8755
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
8756
|
+
const arg = args[index];
|
|
8757
|
+
if (arg === "--origin" && args[index + 1]) {
|
|
8758
|
+
const rawOrigin = args[++index].trim().toLowerCase();
|
|
8759
|
+
if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
|
|
8760
|
+
throw new Error(`Invalid value for --origin: ${rawOrigin}`);
|
|
8761
|
+
}
|
|
8762
|
+
origin = rawOrigin;
|
|
8763
|
+
}
|
|
8764
|
+
if (arg === "--mode" && args[index + 1]) {
|
|
8765
|
+
const rawMode = args[++index].trim().toLowerCase();
|
|
8766
|
+
mode = rawMode === "any" || rawMode === "phrase" ? rawMode : "all";
|
|
8767
|
+
}
|
|
8768
|
+
}
|
|
8769
|
+
const compact = args.includes("--compact");
|
|
8770
|
+
const client = new DeeplineClient();
|
|
8771
|
+
const plays = (await client.listPlays({
|
|
8772
|
+
grep: query,
|
|
8773
|
+
grepMode: mode,
|
|
8774
|
+
...origin ? { origin } : {}
|
|
8775
|
+
})).filter((play) => {
|
|
8776
|
+
if (!origin) return true;
|
|
8777
|
+
const isPrebuilt = play.origin === "prebuilt" || play.ownerType === "deepline";
|
|
8778
|
+
return origin === "prebuilt" ? isPrebuilt : !isPrebuilt;
|
|
8779
|
+
}).filter(
|
|
8780
|
+
(play) => matchesPlayGrepQuery(
|
|
8781
|
+
{
|
|
8782
|
+
name: play.name,
|
|
8783
|
+
reference: play.reference,
|
|
8784
|
+
displayName: play.displayName,
|
|
8785
|
+
origin: play.origin,
|
|
8786
|
+
ownerType: play.ownerType,
|
|
8787
|
+
aliases: play.aliases,
|
|
8788
|
+
inputSchema: play.inputSchema,
|
|
8789
|
+
outputSchema: play.outputSchema
|
|
8790
|
+
},
|
|
8791
|
+
query,
|
|
8792
|
+
mode
|
|
8793
|
+
)
|
|
8794
|
+
).map((play) => summarizePlayListItemForCli(play, { compact }));
|
|
8795
|
+
if (argsWantJson(args)) {
|
|
8796
|
+
process.stdout.write(`${JSON.stringify({
|
|
8797
|
+
plays,
|
|
8798
|
+
count: plays.length,
|
|
8799
|
+
query,
|
|
8800
|
+
grep: { mode, terms: parsePlayGrepTerms(query, mode) },
|
|
8801
|
+
filters: { origin: origin ?? null }
|
|
8802
|
+
})}
|
|
8803
|
+
`);
|
|
8804
|
+
return 0;
|
|
8805
|
+
}
|
|
8806
|
+
process.stdout.write(`${plays.length} plays found:
|
|
8807
|
+
|
|
8489
8808
|
`);
|
|
8490
8809
|
for (const play of plays) {
|
|
8491
8810
|
printPlayDescription(play);
|
|
@@ -8501,6 +8820,15 @@ async function handlePlayDescribe(args) {
|
|
|
8501
8820
|
);
|
|
8502
8821
|
return 1;
|
|
8503
8822
|
}
|
|
8823
|
+
if (looksLikeRunId(playName)) {
|
|
8824
|
+
console.error(
|
|
8825
|
+
formatPlayCommandReceivedRunIdError({
|
|
8826
|
+
command: "describe",
|
|
8827
|
+
runId: playName
|
|
8828
|
+
})
|
|
8829
|
+
);
|
|
8830
|
+
return 2;
|
|
8831
|
+
}
|
|
8504
8832
|
const client = new DeeplineClient();
|
|
8505
8833
|
await assertCanonicalNamedPlayReference(client, playName);
|
|
8506
8834
|
const play = await client.describePlay(
|
|
@@ -8657,7 +8985,7 @@ Common commands:
|
|
|
8657
8985
|
deepline plays search email --json
|
|
8658
8986
|
deepline plays describe person-linkedin-to-email --json
|
|
8659
8987
|
deepline plays check my.play.ts
|
|
8660
|
-
deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
|
|
8988
|
+
deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
|
|
8661
8989
|
deepline plays set-live my.play.ts --json
|
|
8662
8990
|
deepline plays get person-linkedin-to-email --json
|
|
8663
8991
|
`
|
|
@@ -8688,8 +9016,10 @@ Notes:
|
|
|
8688
9016
|
Unknown --foo value and --foo.bar value flags are passed into play input.
|
|
8689
9017
|
Example: --limit 5 becomes input.limit = 5.
|
|
8690
9018
|
File args accept local paths; the CLI stages files before submit.
|
|
8691
|
-
|
|
8692
|
-
--wait
|
|
9019
|
+
By default, run waits for completion and prints logs, previews, stats, and
|
|
9020
|
+
next commands. --watch and --wait are accepted compatibility aliases for the
|
|
9021
|
+
default behavior. Use --no-wait only when you intentionally want
|
|
9022
|
+
a fire-and-forget run id.
|
|
8693
9023
|
The play page opens in your browser as soon as the run starts; use --no-open
|
|
8694
9024
|
to only print the URL.
|
|
8695
9025
|
--force supersedes active runs; it does not bypass completed reuse.
|
|
@@ -8717,17 +9047,18 @@ Idempotent execution:
|
|
|
8717
9047
|
.run({ key: 'domain', staleAfterSeconds: 86400 })
|
|
8718
9048
|
|
|
8719
9049
|
Examples:
|
|
8720
|
-
deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
|
|
8721
|
-
deepline plays run my.play.ts --input @input.json --
|
|
8722
|
-
deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}'
|
|
8723
|
-
deepline plays run cto-search.play.ts --limit 5
|
|
9050
|
+
deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
|
|
9051
|
+
deepline plays run my.play.ts --input @input.json --json
|
|
9052
|
+
deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}'
|
|
9053
|
+
deepline plays run cto-search.play.ts --limit 5
|
|
9054
|
+
deepline plays run long-background-play --no-wait
|
|
8724
9055
|
deepline runs export <run-id> --out output.csv
|
|
8725
9056
|
deepline runs get <run-id>
|
|
8726
9057
|
`
|
|
8727
9058
|
).option("--file <path>", "Local play file to run").option("--name <name>", "Saved play name to run").option("-i, --input <json>", "Input JSON object or @file path").option("--live", "Run the current live revision explicitly").option("--latest", "Run the newest saved revision, even if it is not live").option(
|
|
8728
9059
|
"--revision-id <id>",
|
|
8729
9060
|
"Run a specific saved revision instead of the live revision"
|
|
8730
|
-
).option("--watch", "
|
|
9061
|
+
).option("--watch", "Compatibility alias; run waits by default").option("--wait", "Compatibility alias; run waits by default").option("--no-wait", "Start the run and return immediately").option(
|
|
8731
9062
|
"--logs",
|
|
8732
9063
|
"When output is non-interactive, stream play logs to stderr while waiting"
|
|
8733
9064
|
).option("--tail-timeout-ms <ms>", "Timeout while watching the run stream").option("--force", "Supersede any active runs for this play").option("--no-open", "Print the play page URL without opening a browser").option("--json", "Emit JSON output").addHelpText(
|
|
@@ -8760,6 +9091,7 @@ Pass-through input flags:
|
|
|
8760
9091
|
...options.live ? ["--live"] : [],
|
|
8761
9092
|
...options.latest ? ["--latest"] : [],
|
|
8762
9093
|
...options.revisionId ? ["--revision-id", options.revisionId] : [],
|
|
9094
|
+
...options.wait === false ? ["--no-wait"] : [],
|
|
8763
9095
|
...options.watch || options.wait ? ["--watch"] : [],
|
|
8764
9096
|
...options.logs ? ["--logs"] : [],
|
|
8765
9097
|
...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
|
|
@@ -8800,24 +9132,27 @@ Notes:
|
|
|
8800
9132
|
|
|
8801
9133
|
Examples:
|
|
8802
9134
|
deepline plays list
|
|
8803
|
-
deepline plays list --json
|
|
9135
|
+
deepline plays list --origin prebuilt --json
|
|
8804
9136
|
deepline plays search email --origin prebuilt --json
|
|
8805
9137
|
`
|
|
8806
|
-
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
|
|
9138
|
+
).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
|
|
8807
9139
|
process.exitCode = await handlePlayList([
|
|
9140
|
+
...options.origin ? ["--origin", options.origin] : [],
|
|
8808
9141
|
...options.json ? ["--json"] : []
|
|
8809
9142
|
]);
|
|
8810
9143
|
});
|
|
8811
|
-
|
|
9144
|
+
const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").addHelpText(
|
|
8812
9145
|
"after",
|
|
8813
9146
|
`
|
|
8814
9147
|
Notes:
|
|
8815
9148
|
Ranked discovery for workflows. Use --origin prebuilt or --origin owned when
|
|
8816
9149
|
you need to narrow results. Use describe on a result before running it.
|
|
9150
|
+
The grep alias is the same ranked retrieval surface with a more literal name
|
|
9151
|
+
for agents that are filtering the play registry.
|
|
8817
9152
|
|
|
8818
9153
|
Examples:
|
|
8819
9154
|
deepline plays search email
|
|
8820
|
-
deepline plays
|
|
9155
|
+
deepline plays grep "linkedin to email" --origin prebuilt --compact --json
|
|
8821
9156
|
deepline plays describe person-linkedin-to-email --json
|
|
8822
9157
|
`
|
|
8823
9158
|
).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
@@ -8828,6 +9163,30 @@ Examples:
|
|
|
8828
9163
|
...options.json ? ["--json"] : []
|
|
8829
9164
|
]);
|
|
8830
9165
|
});
|
|
9166
|
+
addPlaySearchCommand(play.command("search <query>"));
|
|
9167
|
+
play.command("grep <query>").description("Literal grep over play names, aliases, schemas, and descriptions.").addHelpText(
|
|
9168
|
+
"after",
|
|
9169
|
+
`
|
|
9170
|
+
Notes:
|
|
9171
|
+
Literal registry filtering. Terms are matched case-insensitively against play
|
|
9172
|
+
names, references, display names, aliases, ownership, and schemas. Use
|
|
9173
|
+
--mode phrase for exact phrase matching, --mode any for OR, and the default
|
|
9174
|
+
--mode all for AND.
|
|
9175
|
+
|
|
9176
|
+
Examples:
|
|
9177
|
+
deepline plays grep email --origin prebuilt --json
|
|
9178
|
+
deepline plays grep "company contact" --origin prebuilt --mode all --json
|
|
9179
|
+
deepline plays describe prebuilt/company-to-contact --json
|
|
9180
|
+
`
|
|
9181
|
+
).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--compact", "Emit compact schemas").option("--mode <mode>", "Grep matching mode: all, any, or phrase").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
9182
|
+
process.exitCode = await handlePlayGrep([
|
|
9183
|
+
query,
|
|
9184
|
+
...options.origin ? ["--origin", options.origin] : [],
|
|
9185
|
+
...options.compact ? ["--compact"] : [],
|
|
9186
|
+
...options.mode ? ["--mode", options.mode] : [],
|
|
9187
|
+
...options.json ? ["--json"] : []
|
|
9188
|
+
]);
|
|
9189
|
+
});
|
|
8831
9190
|
play.command("describe <target>").description("Describe a play contract and how to run it.").addHelpText(
|
|
8832
9191
|
"after",
|
|
8833
9192
|
`
|
|
@@ -8856,7 +9215,7 @@ Notes:
|
|
|
8856
9215
|
Examples:
|
|
8857
9216
|
deepline plays versions --name my-play
|
|
8858
9217
|
deepline plays versions --name my-play --json
|
|
8859
|
-
deepline plays run my-play --revision-id <revision-id>
|
|
9218
|
+
deepline plays run my-play --revision-id <revision-id>
|
|
8860
9219
|
`
|
|
8861
9220
|
).option("--name <name>", "Saved play name").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
|
|
8862
9221
|
process.exitCode = await handlePlayVersions([
|
|
@@ -9058,6 +9417,7 @@ Examples:
|
|
|
9058
9417
|
}
|
|
9059
9418
|
|
|
9060
9419
|
// src/cli/commands/tools.ts
|
|
9420
|
+
import { Option } from "commander";
|
|
9061
9421
|
import { chmodSync, mkdtempSync, writeFileSync as writeFileSync8 } from "fs";
|
|
9062
9422
|
import { tmpdir as tmpdir3 } from "os";
|
|
9063
9423
|
import { join as join8 } from "path";
|
|
@@ -9242,9 +9602,42 @@ function toListedTool(tool) {
|
|
|
9242
9602
|
executeCommand: `deepline tools execute ${tool.toolId}`
|
|
9243
9603
|
};
|
|
9244
9604
|
}
|
|
9605
|
+
function normalizeGrepText(value) {
|
|
9606
|
+
if (value === null || value === void 0) return "";
|
|
9607
|
+
if (typeof value === "string") return value.toLowerCase();
|
|
9608
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
9609
|
+
return String(value).toLowerCase();
|
|
9610
|
+
}
|
|
9611
|
+
if (Array.isArray(value)) return value.map(normalizeGrepText).join(" ");
|
|
9612
|
+
if (typeof value === "object") {
|
|
9613
|
+
return Object.values(value).map(normalizeGrepText).join(" ");
|
|
9614
|
+
}
|
|
9615
|
+
return "";
|
|
9616
|
+
}
|
|
9617
|
+
function parseGrepTerms(query, mode) {
|
|
9618
|
+
const trimmed = query.trim().toLowerCase();
|
|
9619
|
+
if (!trimmed) return [];
|
|
9620
|
+
if (mode === "phrase") return [trimmed];
|
|
9621
|
+
const matches = trimmed.match(/"([^"]+)"|'([^']+)'|\S+/g) ?? [];
|
|
9622
|
+
return matches.map((term) => term.replace(/^["']|["']$/g, "").trim()).filter(Boolean);
|
|
9623
|
+
}
|
|
9624
|
+
function matchesGrepQuery(value, query, mode) {
|
|
9625
|
+
const terms = parseGrepTerms(query, mode);
|
|
9626
|
+
if (terms.length === 0) return true;
|
|
9627
|
+
const haystack = normalizeGrepText(value);
|
|
9628
|
+
if (mode === "any") return terms.some((term) => haystack.includes(term));
|
|
9629
|
+
return terms.every((term) => haystack.includes(term));
|
|
9630
|
+
}
|
|
9245
9631
|
async function listTools(args) {
|
|
9246
9632
|
const client = new DeeplineClient();
|
|
9247
|
-
const
|
|
9633
|
+
const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
|
|
9634
|
+
const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
|
|
9635
|
+
const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
9636
|
+
const items = (await client.listTools({
|
|
9637
|
+
...categoryFilter ? { categories: categoryFilter } : {}
|
|
9638
|
+
})).map(toListedTool).filter(
|
|
9639
|
+
(item) => requestedCategories.length === 0 || requestedCategories.some((category) => item.categories.includes(category))
|
|
9640
|
+
);
|
|
9248
9641
|
const render = {
|
|
9249
9642
|
sections: [
|
|
9250
9643
|
{
|
|
@@ -9267,6 +9660,9 @@ async function listTools(args) {
|
|
|
9267
9660
|
{
|
|
9268
9661
|
tools: items,
|
|
9269
9662
|
count: items.length,
|
|
9663
|
+
filters: {
|
|
9664
|
+
categories: requestedCategories
|
|
9665
|
+
},
|
|
9270
9666
|
commandTemplates: TOOL_COMMAND_TEMPLATES,
|
|
9271
9667
|
render
|
|
9272
9668
|
},
|
|
@@ -9293,6 +9689,56 @@ async function searchTools(queryInput, options = {}) {
|
|
|
9293
9689
|
});
|
|
9294
9690
|
return 0;
|
|
9295
9691
|
}
|
|
9692
|
+
async function grepTools(queryInput, options = {}) {
|
|
9693
|
+
const query = queryInput.trim();
|
|
9694
|
+
if (!query) {
|
|
9695
|
+
console.error("Usage: deepline tools grep <query> [--json]");
|
|
9696
|
+
return 1;
|
|
9697
|
+
}
|
|
9698
|
+
const client = new DeeplineClient();
|
|
9699
|
+
const requestedCategories = options.categories ? options.categories.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
9700
|
+
const mode = options.mode ?? "all";
|
|
9701
|
+
const tools = (await client.listTools({
|
|
9702
|
+
grep: query,
|
|
9703
|
+
grepMode: mode,
|
|
9704
|
+
...options.categories ? { categories: options.categories } : {}
|
|
9705
|
+
})).map(toListedTool).filter(
|
|
9706
|
+
(item) => requestedCategories.length === 0 || requestedCategories.some((category) => item.categories.includes(category))
|
|
9707
|
+
).filter(
|
|
9708
|
+
(item) => matchesGrepQuery(
|
|
9709
|
+
{
|
|
9710
|
+
id: item.toolId,
|
|
9711
|
+
toolId: item.toolId,
|
|
9712
|
+
provider: item.provider,
|
|
9713
|
+
displayName: item.displayName,
|
|
9714
|
+
description: item.description,
|
|
9715
|
+
categories: item.categories,
|
|
9716
|
+
operation: item.operation,
|
|
9717
|
+
operationAliases: item.operationAliases,
|
|
9718
|
+
inputFields: item.inputFields
|
|
9719
|
+
},
|
|
9720
|
+
query,
|
|
9721
|
+
mode
|
|
9722
|
+
)
|
|
9723
|
+
);
|
|
9724
|
+
printCommandEnvelope(
|
|
9725
|
+
{
|
|
9726
|
+
tools,
|
|
9727
|
+
count: tools.length,
|
|
9728
|
+
query,
|
|
9729
|
+
grep: {
|
|
9730
|
+
mode,
|
|
9731
|
+
terms: parseGrepTerms(query, mode)
|
|
9732
|
+
},
|
|
9733
|
+
filters: {
|
|
9734
|
+
categories: requestedCategories
|
|
9735
|
+
},
|
|
9736
|
+
commandTemplates: TOOL_COMMAND_TEMPLATES
|
|
9737
|
+
},
|
|
9738
|
+
{ json: options.json || shouldEmitJson() }
|
|
9739
|
+
);
|
|
9740
|
+
return 0;
|
|
9741
|
+
}
|
|
9296
9742
|
function playIdentifiers(play) {
|
|
9297
9743
|
return [play.name, play.reference, ...play.aliases ?? []].filter((value) => Boolean(value?.trim())).map((value) => value.trim());
|
|
9298
9744
|
}
|
|
@@ -9362,24 +9808,27 @@ Notes:
|
|
|
9362
9808
|
|
|
9363
9809
|
Examples:
|
|
9364
9810
|
deepline tools list
|
|
9365
|
-
deepline tools list --json
|
|
9811
|
+
deepline tools list --categories email_finder --json
|
|
9366
9812
|
deepline tools search email --json
|
|
9367
9813
|
`
|
|
9368
|
-
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
|
|
9814
|
+
).option("--categories <categories>", "Comma-separated categories to filter inventory").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
|
|
9369
9815
|
process.exitCode = await listTools([
|
|
9816
|
+
...options.categories ? ["--categories", options.categories] : [],
|
|
9370
9817
|
...options.json ? ["--json"] : []
|
|
9371
9818
|
]);
|
|
9372
9819
|
});
|
|
9373
|
-
|
|
9820
|
+
const addToolSearchCommand = (command) => command.description("Search available tools.").addHelpText(
|
|
9374
9821
|
"after",
|
|
9375
9822
|
`
|
|
9376
9823
|
Notes:
|
|
9377
9824
|
Ranked discovery for atomic provider/API operations. Results include tool ids
|
|
9378
9825
|
that can be passed to deepline tools describe or deepline tools execute.
|
|
9826
|
+
The grep alias is the same ranked retrieval surface with a more literal name
|
|
9827
|
+
for agents that are filtering a registry rather than choosing a workflow.
|
|
9379
9828
|
|
|
9380
9829
|
Examples:
|
|
9381
9830
|
deepline tools search email
|
|
9382
|
-
deepline tools
|
|
9831
|
+
deepline tools grep "company enrichment" --categories enrichment --json
|
|
9383
9832
|
deepline tools search verifier --search-mode v2 --json
|
|
9384
9833
|
`
|
|
9385
9834
|
).option("--categories <categories>", "Comma-separated categories to filter ranked search").option("--search_terms <terms>", "Structured search terms for ranked search").option("--search-terms <terms>", "Structured search terms for ranked search").option("--search-mode <mode>", "Ranked search mode: v1 or v2").option("--include-search-debug", "Include ranked search debug metadata").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
@@ -9391,24 +9840,55 @@ Examples:
|
|
|
9391
9840
|
includeSearchDebug: Boolean(options.includeSearchDebug)
|
|
9392
9841
|
});
|
|
9393
9842
|
});
|
|
9843
|
+
addToolSearchCommand(tools.command("search <query>"));
|
|
9844
|
+
tools.command("grep <query>").description("Literal grep over tool ids, descriptions, categories, and input fields.").addHelpText(
|
|
9845
|
+
"after",
|
|
9846
|
+
`
|
|
9847
|
+
Notes:
|
|
9848
|
+
Literal registry filtering. Terms are matched case-insensitively against tool
|
|
9849
|
+
ids, provider, display name, description, categories, aliases, and input
|
|
9850
|
+
fields. Use --mode phrase for exact phrase matching, --mode any for OR, and
|
|
9851
|
+
the default --mode all for AND.
|
|
9852
|
+
|
|
9853
|
+
Examples:
|
|
9854
|
+
deepline tools grep email --categories email_finder --json
|
|
9855
|
+
deepline tools grep "phone validate" --mode all --json
|
|
9856
|
+
deepline tools grep hunter --mode phrase --json
|
|
9857
|
+
`
|
|
9858
|
+
).option("--categories <categories>", "Comma-separated categories to filter inventory").option("--mode <mode>", "Grep matching mode: all, any, or phrase").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
9859
|
+
const mode = options.mode === "any" || options.mode === "phrase" ? options.mode : "all";
|
|
9860
|
+
process.exitCode = await grepTools(query, {
|
|
9861
|
+
json: options.json,
|
|
9862
|
+
categories: options.categories,
|
|
9863
|
+
mode
|
|
9864
|
+
});
|
|
9865
|
+
});
|
|
9394
9866
|
const addToolMetadataCommand = (command) => command.description("Show metadata for a tool.").addHelpText(
|
|
9395
9867
|
"after",
|
|
9396
9868
|
`
|
|
9397
9869
|
Notes:
|
|
9398
|
-
Shows the
|
|
9399
|
-
|
|
9400
|
-
|
|
9870
|
+
Shows the compact agent contract by default: what the tool does, cost,
|
|
9871
|
+
required inputs, play getters, and a runnable ctx.tools.execute snippet.
|
|
9872
|
+
Use --json for the full metadata/debug payload.
|
|
9401
9873
|
|
|
9402
9874
|
Examples:
|
|
9403
9875
|
deepline tools describe hunter_email_verifier
|
|
9404
|
-
deepline tools describe hunter_email_verifier --
|
|
9876
|
+
deepline tools describe hunter_email_verifier --pricing-only
|
|
9877
|
+
deepline tools describe hunter_email_verifier --schema-only
|
|
9878
|
+
deepline tools describe hunter_email_verifier --examples-only
|
|
9879
|
+
deepline tools describe hunter_email_verifier --json
|
|
9405
9880
|
deepline tools execute hunter_email_verifier --input '{"email":"a@b.com"}'
|
|
9406
9881
|
`
|
|
9407
|
-
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (toolId, options) => {
|
|
9408
|
-
process.exitCode = await getTool(
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9882
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--pricing-only", "Only print pricing and billing semantics").option("--schema-only", "Only print input schema fields").option("--examples-only", "Only print runnable examples and sample payloads").option("--getters-only", "Only print extracted list/value getters").addOption(new Option("--compact", "Compatibility alias for the default compact view").hideHelp()).addOption(new Option("--contract-json", "Compatibility alias for compact contract JSON").hideHelp()).action(async (toolId, options) => {
|
|
9883
|
+
process.exitCode = await getTool(toolId, {
|
|
9884
|
+
json: Boolean(options.json),
|
|
9885
|
+
compact: Boolean(options.compact),
|
|
9886
|
+
contractJson: Boolean(options.contractJson),
|
|
9887
|
+
pricingOnly: Boolean(options.pricingOnly),
|
|
9888
|
+
schemaOnly: Boolean(options.schemaOnly),
|
|
9889
|
+
examplesOnly: Boolean(options.examplesOnly),
|
|
9890
|
+
gettersOnly: Boolean(options.gettersOnly)
|
|
9891
|
+
});
|
|
9412
9892
|
});
|
|
9413
9893
|
addToolMetadataCommand(tools.command("describe <toolId>"));
|
|
9414
9894
|
tools.command("get <toolId>").description("Deprecated. Use tools describe.").addHelpText(
|
|
@@ -9461,8 +9941,7 @@ Examples:
|
|
|
9461
9941
|
process.exitCode = await executeTool(args);
|
|
9462
9942
|
});
|
|
9463
9943
|
}
|
|
9464
|
-
async function getTool(
|
|
9465
|
-
const toolId = args[0];
|
|
9944
|
+
async function getTool(toolId, options = {}) {
|
|
9466
9945
|
if (!toolId) {
|
|
9467
9946
|
console.error("Usage: deepline tools get <toolId> [--json]");
|
|
9468
9947
|
return 1;
|
|
@@ -9479,14 +9958,264 @@ async function getTool(args) {
|
|
|
9479
9958
|
}
|
|
9480
9959
|
throw error;
|
|
9481
9960
|
}
|
|
9482
|
-
if (
|
|
9961
|
+
if (options.contractJson) {
|
|
9962
|
+
process.stdout.write(`${JSON.stringify(toolContractJsonForDescribe(tool, toolId))}
|
|
9963
|
+
`);
|
|
9964
|
+
return 0;
|
|
9965
|
+
}
|
|
9966
|
+
const emitJson = options.json === true;
|
|
9967
|
+
if (emitJson) {
|
|
9483
9968
|
process.stdout.write(`${JSON.stringify(toolMetadataJsonForDescribe(tool, toolId))}
|
|
9484
9969
|
`);
|
|
9485
9970
|
return 0;
|
|
9486
9971
|
}
|
|
9487
|
-
|
|
9972
|
+
const onlyModes = [
|
|
9973
|
+
options.pricingOnly,
|
|
9974
|
+
options.schemaOnly,
|
|
9975
|
+
options.examplesOnly,
|
|
9976
|
+
options.gettersOnly
|
|
9977
|
+
].filter(Boolean).length;
|
|
9978
|
+
if (onlyModes > 1) {
|
|
9979
|
+
console.error("Use only one of --pricing-only, --schema-only, --examples-only, or --getters-only.");
|
|
9980
|
+
return 2;
|
|
9981
|
+
}
|
|
9982
|
+
if (options.pricingOnly) {
|
|
9983
|
+
printToolPricingOnly(tool, toolId);
|
|
9984
|
+
return 0;
|
|
9985
|
+
}
|
|
9986
|
+
if (options.schemaOnly) {
|
|
9987
|
+
printToolSchemaOnly(tool, toolId);
|
|
9988
|
+
return 0;
|
|
9989
|
+
}
|
|
9990
|
+
if (options.examplesOnly) {
|
|
9991
|
+
printToolExamplesOnly(tool, toolId);
|
|
9992
|
+
return 0;
|
|
9993
|
+
}
|
|
9994
|
+
if (options.gettersOnly) {
|
|
9995
|
+
printToolGettersOnly(tool, toolId);
|
|
9996
|
+
return 0;
|
|
9997
|
+
}
|
|
9998
|
+
if (options.compact) {
|
|
9999
|
+
printCompactToolContract(tool, toolId);
|
|
10000
|
+
return 0;
|
|
10001
|
+
}
|
|
10002
|
+
if (shouldEmitJson()) {
|
|
10003
|
+
process.stdout.write(`${JSON.stringify(toolMetadataJsonForDescribe(tool, toolId))}
|
|
10004
|
+
`);
|
|
10005
|
+
return 0;
|
|
10006
|
+
}
|
|
10007
|
+
printCompactToolContract(tool, toolId);
|
|
9488
10008
|
return 0;
|
|
9489
10009
|
}
|
|
10010
|
+
function toolContractJsonForDescribe(tool, requestedToolId) {
|
|
10011
|
+
const toolId = String(tool.toolId || requestedToolId);
|
|
10012
|
+
const inputFields = toolInputFieldsForDisplay(recordField(tool, "inputSchema", "input_schema"));
|
|
10013
|
+
const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
|
|
10014
|
+
const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult", "tool_execution_result");
|
|
10015
|
+
const extractedLists = extractionContractEntries(
|
|
10016
|
+
arrayField(toolExecutionResult, "extractedLists", "extracted_lists")
|
|
10017
|
+
);
|
|
10018
|
+
const extractedValues = extractionContractEntries(
|
|
10019
|
+
arrayField(toolExecutionResult, "extractedValues", "extracted_values")
|
|
10020
|
+
);
|
|
10021
|
+
const cost = recordField(tool, "cost");
|
|
10022
|
+
const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
|
|
10023
|
+
const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
|
|
10024
|
+
return {
|
|
10025
|
+
schemaVersion: 1,
|
|
10026
|
+
toolId,
|
|
10027
|
+
provider: tool.provider,
|
|
10028
|
+
displayName: tool.displayName,
|
|
10029
|
+
description: tool.description,
|
|
10030
|
+
categories: tool.categories,
|
|
10031
|
+
inputFields: inputFields.map((field) => ({
|
|
10032
|
+
name: field.name,
|
|
10033
|
+
type: field.type ?? "unknown",
|
|
10034
|
+
required: Boolean(field.required),
|
|
10035
|
+
...field.description ? { description: field.description } : {},
|
|
10036
|
+
...Object.prototype.hasOwnProperty.call(field, "default") ? { default: field.default } : {}
|
|
10037
|
+
})),
|
|
10038
|
+
cost: {
|
|
10039
|
+
pricingModel: stringField(cost, "pricingModel", "pricing_model") || null,
|
|
10040
|
+
billingMode: stringField(cost, "billingMode", "billing_mode") || null,
|
|
10041
|
+
deeplineCreditsPerPricingUnit: deeplineCredits,
|
|
10042
|
+
deeplineUsdPerPricingUnit
|
|
10043
|
+
},
|
|
10044
|
+
getters: {
|
|
10045
|
+
extractedLists,
|
|
10046
|
+
extractedValues
|
|
10047
|
+
},
|
|
10048
|
+
executeCommand: `deepline tools execute ${toolId} --input '{...}' --json`
|
|
10049
|
+
};
|
|
10050
|
+
}
|
|
10051
|
+
function extractionContractEntries(entries) {
|
|
10052
|
+
return entries.flatMap((entry) => {
|
|
10053
|
+
if (!isRecord4(entry)) return [];
|
|
10054
|
+
const name = stringField(entry, "name");
|
|
10055
|
+
const expression = stringField(entry, "expression");
|
|
10056
|
+
return name && expression ? [{ name, expression }] : [];
|
|
10057
|
+
});
|
|
10058
|
+
}
|
|
10059
|
+
function printCompactToolContract(tool, requestedToolId) {
|
|
10060
|
+
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
10061
|
+
const cost = isRecord4(contract.cost) ? contract.cost : {};
|
|
10062
|
+
const getters = isRecord4(contract.getters) ? contract.getters : {};
|
|
10063
|
+
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
10064
|
+
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
10065
|
+
const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
|
|
10066
|
+
console.log(String(contract.toolId));
|
|
10067
|
+
if (contract.displayName) console.log(`Best for: ${contract.displayName}`);
|
|
10068
|
+
if (typeof contract.description === "string" && contract.description.trim()) {
|
|
10069
|
+
console.log(`Description: ${contract.description.trim()}`);
|
|
10070
|
+
}
|
|
10071
|
+
if (Array.isArray(contract.categories) && contract.categories.length) {
|
|
10072
|
+
console.log(`Tags: ${contract.categories.join(", ")}`);
|
|
10073
|
+
}
|
|
10074
|
+
printToolPricingOnly(tool, requestedToolId, { heading: "Cost" });
|
|
10075
|
+
if (inputFields.length) {
|
|
10076
|
+
console.log("");
|
|
10077
|
+
console.log("Inputs:");
|
|
10078
|
+
for (const field of inputFields) {
|
|
10079
|
+
if (!isRecord4(field)) continue;
|
|
10080
|
+
const name = stringField(field, "name");
|
|
10081
|
+
if (!name) continue;
|
|
10082
|
+
const required = field.required ? "*" : "";
|
|
10083
|
+
const type = stringField(field, "type") || "unknown";
|
|
10084
|
+
const description = stringField(field, "description");
|
|
10085
|
+
console.log(`- ${name}${required}: ${type}${description ? ` - ${description}` : ""}`);
|
|
10086
|
+
}
|
|
10087
|
+
}
|
|
10088
|
+
console.log("");
|
|
10089
|
+
printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
|
|
10090
|
+
if (listGetters.length || valueGetters.length) {
|
|
10091
|
+
console.log("");
|
|
10092
|
+
console.log("Getters:");
|
|
10093
|
+
if (listGetters.length) console.log("Lists:");
|
|
10094
|
+
for (const entry of listGetters) {
|
|
10095
|
+
if (isRecord4(entry)) console.log(`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`);
|
|
10096
|
+
}
|
|
10097
|
+
if (valueGetters.length) console.log("Values:");
|
|
10098
|
+
for (const entry of valueGetters) {
|
|
10099
|
+
if (isRecord4(entry)) console.log(`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`);
|
|
10100
|
+
}
|
|
10101
|
+
}
|
|
10102
|
+
console.log("");
|
|
10103
|
+
console.log(`More: deepline tools describe ${contract.toolId} --pricing-only | --schema-only | --examples-only | --getters-only | --json`);
|
|
10104
|
+
}
|
|
10105
|
+
function printToolPricingOnly(tool, requestedToolId, options = {}) {
|
|
10106
|
+
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
10107
|
+
const cost = isRecord4(contract.cost) ? contract.cost : {};
|
|
10108
|
+
const pricingModel = stringField(cost, "pricingModel") || "unknown";
|
|
10109
|
+
const billingMode = stringField(cost, "billingMode") || "unknown";
|
|
10110
|
+
const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
|
|
10111
|
+
const credits = numberField(cost, "deeplineCreditsPerPricingUnit");
|
|
10112
|
+
const usd = numberField(cost, "deeplineUsdPerPricingUnit");
|
|
10113
|
+
const price = credits !== null ? `${formatDecimal(credits)} Deepline credits${usd !== null ? ` / ${formatUsd(usd)}` : ""} per ${unit}` : "pricing unavailable";
|
|
10114
|
+
console.log(`${options.heading ?? `Pricing: ${contract.toolId}`}: ${price}`);
|
|
10115
|
+
console.log(`Billing: ${billingMode}`);
|
|
10116
|
+
}
|
|
10117
|
+
function printToolSchemaOnly(tool, requestedToolId) {
|
|
10118
|
+
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
10119
|
+
const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
|
|
10120
|
+
console.log(`Schema: ${contract.toolId}`);
|
|
10121
|
+
if (!inputFields.length) {
|
|
10122
|
+
console.log("Inputs: none");
|
|
10123
|
+
return;
|
|
10124
|
+
}
|
|
10125
|
+
console.log("Inputs:");
|
|
10126
|
+
for (const field of inputFields) {
|
|
10127
|
+
if (!isRecord4(field)) continue;
|
|
10128
|
+
const name = stringField(field, "name");
|
|
10129
|
+
if (!name) continue;
|
|
10130
|
+
const required = field.required ? "*" : "";
|
|
10131
|
+
const type = stringField(field, "type") || "unknown";
|
|
10132
|
+
const description = stringField(field, "description");
|
|
10133
|
+
const defaultSuffix = Object.prototype.hasOwnProperty.call(field, "default") ? ` default=${JSON.stringify(field.default)}` : "";
|
|
10134
|
+
console.log(`- ${name}${required}: ${type}${defaultSuffix}${description ? ` - ${description}` : ""}`);
|
|
10135
|
+
}
|
|
10136
|
+
}
|
|
10137
|
+
function printToolExamplesOnly(tool, requestedToolId, options = {}) {
|
|
10138
|
+
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
10139
|
+
const toolId = String(contract.toolId);
|
|
10140
|
+
const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
|
|
10141
|
+
const sampleInput = Object.fromEntries(
|
|
10142
|
+
inputFields.slice(0, 4).flatMap((field) => {
|
|
10143
|
+
if (!isRecord4(field)) return [];
|
|
10144
|
+
const name = stringField(field, "name");
|
|
10145
|
+
if (!name) return [];
|
|
10146
|
+
return [[name, sampleValueForField(field)]];
|
|
10147
|
+
})
|
|
10148
|
+
);
|
|
10149
|
+
console.log(`Use in a play:`);
|
|
10150
|
+
console.log("```ts");
|
|
10151
|
+
console.log("const result = await ctx.tools.execute({");
|
|
10152
|
+
console.log(` id: '${stableStepIdForTool(toolId)}',`);
|
|
10153
|
+
console.log(` tool: '${toolId}',`);
|
|
10154
|
+
console.log(` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`);
|
|
10155
|
+
console.log("});");
|
|
10156
|
+
const getters = isRecord4(contract.getters) ? contract.getters : {};
|
|
10157
|
+
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
10158
|
+
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
10159
|
+
const firstGetter = [...valueGetters, ...listGetters].find(isRecord4);
|
|
10160
|
+
if (firstGetter) {
|
|
10161
|
+
const name = stringField(firstGetter, "name") || "value";
|
|
10162
|
+
const expression = stringField(firstGetter, "expression");
|
|
10163
|
+
if (expression) console.log(`const ${safeIdentifier(name)} = ${expression.replace(/^toolExecutionResult\./, "result.")};`);
|
|
10164
|
+
}
|
|
10165
|
+
console.log("```");
|
|
10166
|
+
if (options.includeSamples !== false) {
|
|
10167
|
+
const samples = recordField(tool, "samples");
|
|
10168
|
+
printSamples(samples);
|
|
10169
|
+
}
|
|
10170
|
+
}
|
|
10171
|
+
function printToolGettersOnly(tool, requestedToolId) {
|
|
10172
|
+
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
10173
|
+
const getters = isRecord4(contract.getters) ? contract.getters : {};
|
|
10174
|
+
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
10175
|
+
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
10176
|
+
console.log(`Getters: ${contract.toolId}`);
|
|
10177
|
+
if (!listGetters.length && !valueGetters.length) {
|
|
10178
|
+
console.log("No generated getters declared. Use --json only if you need raw metadata.");
|
|
10179
|
+
return;
|
|
10180
|
+
}
|
|
10181
|
+
if (listGetters.length) {
|
|
10182
|
+
console.log("Lists:");
|
|
10183
|
+
for (const entry of listGetters) {
|
|
10184
|
+
if (isRecord4(entry)) console.log(`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`);
|
|
10185
|
+
}
|
|
10186
|
+
}
|
|
10187
|
+
if (valueGetters.length) {
|
|
10188
|
+
console.log("Values:");
|
|
10189
|
+
for (const entry of valueGetters) {
|
|
10190
|
+
if (isRecord4(entry)) console.log(`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`);
|
|
10191
|
+
}
|
|
10192
|
+
}
|
|
10193
|
+
}
|
|
10194
|
+
function sampleValueForField(field) {
|
|
10195
|
+
const name = stringField(field, "name").toLowerCase();
|
|
10196
|
+
const type = stringField(field, "type").toLowerCase();
|
|
10197
|
+
if (Object.prototype.hasOwnProperty.call(field, "default")) return field.default;
|
|
10198
|
+
if (name.includes("email")) return "ada@example.com";
|
|
10199
|
+
if (name.includes("domain") || name.includes("website")) return "example.com";
|
|
10200
|
+
if (name.includes("first")) return "Ada";
|
|
10201
|
+
if (name.includes("last")) return "Lovelace";
|
|
10202
|
+
if (name.includes("name")) return "Ada Lovelace";
|
|
10203
|
+
if (type === "integer" || type === "number") return 1;
|
|
10204
|
+
if (type === "boolean") return true;
|
|
10205
|
+
if (type === "array") return [];
|
|
10206
|
+
if (type === "object") return {};
|
|
10207
|
+
return "...";
|
|
10208
|
+
}
|
|
10209
|
+
function stableStepIdForTool(toolId) {
|
|
10210
|
+
return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
|
|
10211
|
+
}
|
|
10212
|
+
function safeIdentifier(name) {
|
|
10213
|
+
const cleaned = name.replace(/[^a-zA-Z0-9_$]+/g, "_").replace(/^[^a-zA-Z_$]+/, "");
|
|
10214
|
+
return cleaned || "value";
|
|
10215
|
+
}
|
|
10216
|
+
function playResultExpression(entry) {
|
|
10217
|
+
return stringField(entry, "expression").replace(/^toolExecutionResult\./, "result.");
|
|
10218
|
+
}
|
|
9490
10219
|
function toolMetadataJsonForDescribe(tool, requestedToolId) {
|
|
9491
10220
|
const toolId = String(tool.toolId || requestedToolId);
|
|
9492
10221
|
const {
|
|
@@ -9517,152 +10246,6 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
|
|
|
9517
10246
|
}
|
|
9518
10247
|
};
|
|
9519
10248
|
}
|
|
9520
|
-
function printToolDetails(tool, requestedToolId) {
|
|
9521
|
-
const toolId = String(tool.toolId || requestedToolId);
|
|
9522
|
-
const operation = typeof tool.operation === "string" ? tool.operation : "";
|
|
9523
|
-
const displayBase = operation && operation.startsWith(`${tool.provider}_`) ? operation.slice(String(tool.provider).length + 1) : operation ? `${tool.provider} ${operation}`.trim() : toolId;
|
|
9524
|
-
const displayName = titleCase(displayBase || String(tool.displayName || toolId));
|
|
9525
|
-
const cost = isRecord4(tool.cost) ? tool.cost : null;
|
|
9526
|
-
const pricing = recordField(tool, "pricing");
|
|
9527
|
-
const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
|
|
9528
|
-
const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
|
|
9529
|
-
const billingSource = stringField(tool, "billingSource", "billing_source");
|
|
9530
|
-
const billingSourceLabel = stringField(tool, "billingSourceLabel", "billing_source_label");
|
|
9531
|
-
const estimatedCreditsRange = stringField(tool, "estimatedCreditsRange", "estimated_credits_range");
|
|
9532
|
-
const estimatedUsdRange = stringField(tool, "estimatedUsdRange", "estimated_usd_range");
|
|
9533
|
-
const estimateModelVersion = stringField(tool, "estimateModelVersion", "estimate_model_version");
|
|
9534
|
-
const estimateBasedOnTools = arrayField(tool, "estimateBasedOnTools", "estimate_based_on_tools").map((item) => String(item).trim()).filter(Boolean);
|
|
9535
|
-
const stepContributions = arrayField(tool, "stepContributions", "step_contributions");
|
|
9536
|
-
const playExpansion = recordField(tool, "playExpansion", "play_expansion");
|
|
9537
|
-
const samples = recordField(tool, "samples");
|
|
9538
|
-
const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
|
|
9539
|
-
console.log(`Tool: ${toolId}`);
|
|
9540
|
-
console.log(" Runtime output help:");
|
|
9541
|
-
console.log(" describe shows declared schema/getters, not an observed provider response");
|
|
9542
|
-
console.log(` observe actual shape: deepline tools execute ${toolId} --input '{...}' --json`);
|
|
9543
|
-
console.log(" for play getter bugs: run the play, then use the db query commands printed by runs get");
|
|
9544
|
-
if (displayName) {
|
|
9545
|
-
console.log(" Display name:");
|
|
9546
|
-
console.log(` ${displayName}`);
|
|
9547
|
-
}
|
|
9548
|
-
if (tool.categories.length > 0) {
|
|
9549
|
-
console.log(" Categories:");
|
|
9550
|
-
console.log(` ${tool.categories.join(", ")}`);
|
|
9551
|
-
}
|
|
9552
|
-
const printedCost = printToolCost({
|
|
9553
|
-
pricing,
|
|
9554
|
-
cost,
|
|
9555
|
-
billingSource,
|
|
9556
|
-
deeplineCredits,
|
|
9557
|
-
deeplineUsdPerPricingUnit
|
|
9558
|
-
});
|
|
9559
|
-
if (!printedCost && ["run_javascript", "call_local_codex", "call_local_claude_code"].includes(toolId)) {
|
|
9560
|
-
console.log(" Cost: free");
|
|
9561
|
-
}
|
|
9562
|
-
if (billingSourceLabel) {
|
|
9563
|
-
console.log(` Billing source: ${billingSourceLabel}`);
|
|
9564
|
-
}
|
|
9565
|
-
if (estimatedCreditsRange) {
|
|
9566
|
-
const usdSuffix = estimatedUsdRange ? ` (~${estimatedUsdRange})` : "";
|
|
9567
|
-
console.log(` Estimated play spend: ${estimatedCreditsRange}${usdSuffix}`);
|
|
9568
|
-
if (estimateModelVersion) console.log(` model: ${estimateModelVersion}`);
|
|
9569
|
-
if (estimateBasedOnTools.length) console.log(` based on: ${estimateBasedOnTools.join(", ")}`);
|
|
9570
|
-
if (stepContributions.length) {
|
|
9571
|
-
console.log(" step contributions:");
|
|
9572
|
-
for (const item of stepContributions) {
|
|
9573
|
-
if (!isRecord4(item)) continue;
|
|
9574
|
-
const stepTool = typeof item.tool === "string" ? item.tool.trim() : "";
|
|
9575
|
-
const low = typeof item.lowCredits === "number" ? item.lowCredits : null;
|
|
9576
|
-
const high = typeof item.highCredits === "number" ? item.highCredits : null;
|
|
9577
|
-
const lowUsd = typeof item.lowUsd === "number" ? item.lowUsd : null;
|
|
9578
|
-
const highUsd = typeof item.highUsd === "number" ? item.highUsd : null;
|
|
9579
|
-
if (!stepTool || low === null || high === null) continue;
|
|
9580
|
-
const stepUsdSuffix = lowUsd !== null && highUsd !== null ? ` (~${formatUsd(lowUsd)}-${formatUsd(highUsd)})` : "";
|
|
9581
|
-
console.log(` - ${stepTool}: ${formatDecimal(low)}-${formatDecimal(high)} credits${stepUsdSuffix}`);
|
|
9582
|
-
}
|
|
9583
|
-
}
|
|
9584
|
-
}
|
|
9585
|
-
if (playExpansion && Object.keys(playExpansion).length > 0) {
|
|
9586
|
-
const group = typeof playExpansion.group === "string" ? playExpansion.group.trim() : "";
|
|
9587
|
-
console.log(" Play expansion:");
|
|
9588
|
-
if (group) console.log(` group: ${group}`);
|
|
9589
|
-
}
|
|
9590
|
-
const fields = toolInputFieldsForDisplay(recordField(tool, "inputSchema", "input_schema"));
|
|
9591
|
-
if (fields.length) {
|
|
9592
|
-
console.log(" Inputs (operation-specific):");
|
|
9593
|
-
for (const field of fields) {
|
|
9594
|
-
const name = String(field.name || "");
|
|
9595
|
-
const typeName = String(field.type || "unknown");
|
|
9596
|
-
const requiredLabel = field.required ? "required" : "optional";
|
|
9597
|
-
const defaultSuffix = Object.prototype.hasOwnProperty.call(field, "default") ? `, default: ${JSON.stringify(field.default)}` : "";
|
|
9598
|
-
const desc = typeof field.description === "string" && field.description.trim() ? ` - ${field.description.trim()}` : "";
|
|
9599
|
-
console.log(` - ${name} (${typeName}, ${requiredLabel}${defaultSuffix})${desc}`);
|
|
9600
|
-
}
|
|
9601
|
-
console.log(" Tip: pass --payload with a JSON object.");
|
|
9602
|
-
}
|
|
9603
|
-
printSamples(samples);
|
|
9604
|
-
printUsageGuidance(usageGuidance);
|
|
9605
|
-
if (isPlayTool(tool)) {
|
|
9606
|
-
console.log(" Play contract:");
|
|
9607
|
-
console.log(" - This is a deepline-native waterfall; the returned rows are extracted by target getters, not by hand-authored payload shape.");
|
|
9608
|
-
if (playExpansion && typeof playExpansion.group === "string" && playExpansion.group.trim()) {
|
|
9609
|
-
console.log(` - Output alias/runtime group is: ${playExpansion.group.trim()}`);
|
|
9610
|
-
}
|
|
9611
|
-
const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult");
|
|
9612
|
-
const extractedValues = arrayField(toolExecutionResult, "extractedValues", "extracted_values");
|
|
9613
|
-
const targets = extractedValues.map((entry) => isRecord4(entry) && typeof entry.name === "string" ? entry.name : "").filter(Boolean).sort();
|
|
9614
|
-
if (targets.length) {
|
|
9615
|
-
console.log(` - Built-in extract targets: ${targets.join(", ")}`);
|
|
9616
|
-
}
|
|
9617
|
-
}
|
|
9618
|
-
console.log("");
|
|
9619
|
-
console.log("Usage:");
|
|
9620
|
-
const requestPayload = samplePayload(samples, "request");
|
|
9621
|
-
if (isPlayTool(tool)) {
|
|
9622
|
-
if (requestPayload !== void 0) {
|
|
9623
|
-
console.log(` deepline enrich --with '${JSON.stringify({ alias: "result", tool: toolId, payload: requestPayload })}'`);
|
|
9624
|
-
} else {
|
|
9625
|
-
console.log(` deepline enrich --with '{"alias":"result","tool":"${toolId}","payload":{...}}'`);
|
|
9626
|
-
}
|
|
9627
|
-
} else if (requestPayload !== void 0) {
|
|
9628
|
-
console.log(` deepline tools execute ${toolId} --payload '${JSON.stringify(requestPayload)}'`);
|
|
9629
|
-
} else {
|
|
9630
|
-
console.log(` deepline tools execute ${toolId} --payload '{...}'`);
|
|
9631
|
-
}
|
|
9632
|
-
console.log(" deepline tools describe <tool_id> --json");
|
|
9633
|
-
}
|
|
9634
|
-
function printUsageGuidance(usageGuidance) {
|
|
9635
|
-
if (Object.keys(usageGuidance).length === 0) return;
|
|
9636
|
-
const execute = stringField(usageGuidance, "execute");
|
|
9637
|
-
const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult", "tool_execution_result");
|
|
9638
|
-
const toolResponse = recordField(toolExecutionResult, "toolResponse", "tool_response");
|
|
9639
|
-
const extractedLists = arrayField(toolExecutionResult, "extractedLists", "extracted_lists");
|
|
9640
|
-
const extractedValues = arrayField(toolExecutionResult, "extractedValues", "extracted_values");
|
|
9641
|
-
console.log(" Usage guidance:");
|
|
9642
|
-
if (execute) console.log(` ${execute}`);
|
|
9643
|
-
const raw = stringField(toolResponse, "raw");
|
|
9644
|
-
const meta = stringField(toolResponse, "meta");
|
|
9645
|
-
if (raw) console.log(` Raw tool response: ${raw}`);
|
|
9646
|
-
if (meta) console.log(` Tool response metadata: ${meta}`);
|
|
9647
|
-
printExtractions("Extracted lists", extractedLists);
|
|
9648
|
-
printExtractions("Extracted values", extractedValues);
|
|
9649
|
-
}
|
|
9650
|
-
function printExtractions(label, entries) {
|
|
9651
|
-
if (!entries.length) return;
|
|
9652
|
-
console.log(` ${label}:`);
|
|
9653
|
-
for (const entry of entries) {
|
|
9654
|
-
if (!isRecord4(entry)) continue;
|
|
9655
|
-
const name = stringField(entry, "name");
|
|
9656
|
-
const expression = stringField(entry, "expression");
|
|
9657
|
-
const details = recordField(entry, "details");
|
|
9658
|
-
const rawToolOutputPaths = arrayField(details, "rawToolOutputPaths", "raw_tool_output_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
|
|
9659
|
-
const candidatePaths = arrayField(details, "candidatePaths", "candidate_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
|
|
9660
|
-
if (!name || !expression) continue;
|
|
9661
|
-
const paths = candidatePaths.length ? candidatePaths : rawToolOutputPaths;
|
|
9662
|
-
const pathSuffix = paths.length ? ` from ${paths.join(", ")}` : "";
|
|
9663
|
-
console.log(` - ${name}: ${expression}${pathSuffix}`);
|
|
9664
|
-
}
|
|
9665
|
-
}
|
|
9666
10249
|
function singleLineText(value, maxLength = 260) {
|
|
9667
10250
|
if (typeof value !== "string") return "";
|
|
9668
10251
|
const text = value.replace(/\s+/g, " ").trim();
|
|
@@ -9679,42 +10262,6 @@ function formatListedToolCost(tool) {
|
|
|
9679
10262
|
const displayText = stringField(pricing, "displayText", "display_text");
|
|
9680
10263
|
return displayText ? `Cost: ${displayText}` : "";
|
|
9681
10264
|
}
|
|
9682
|
-
function printToolCost(input) {
|
|
9683
|
-
const { pricing, cost, billingSource, deeplineCredits, deeplineUsdPerPricingUnit } = input;
|
|
9684
|
-
if (billingSource === "own_provider_credentials") {
|
|
9685
|
-
console.log(" Cost: free through Deepline");
|
|
9686
|
-
return true;
|
|
9687
|
-
}
|
|
9688
|
-
const displayText = stringField(pricing, "displayText", "display_text");
|
|
9689
|
-
if (displayText) {
|
|
9690
|
-
console.log(` Cost: ${displayText}`);
|
|
9691
|
-
const details = arrayField(pricing, "details").map((item) => String(item).trim()).filter(Boolean);
|
|
9692
|
-
if (details.length) {
|
|
9693
|
-
console.log(" notes:");
|
|
9694
|
-
for (const detail of details) console.log(` - ${detail}`);
|
|
9695
|
-
}
|
|
9696
|
-
return true;
|
|
9697
|
-
}
|
|
9698
|
-
const pricingModel = cost ? typeof cost.pricingModel === "string" ? cost.pricingModel : typeof cost.pricing_model === "string" ? cost.pricing_model : "" : "";
|
|
9699
|
-
const billingMode = cost ? typeof cost.billingMode === "string" ? cost.billingMode : typeof cost.billing_mode === "string" ? cost.billing_mode : "" : "";
|
|
9700
|
-
if (deeplineCredits === 0) {
|
|
9701
|
-
console.log(" Cost: Free");
|
|
9702
|
-
return true;
|
|
9703
|
-
}
|
|
9704
|
-
if (pricingModel && deeplineCredits !== null) {
|
|
9705
|
-
const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : "call";
|
|
9706
|
-
const usdText = deeplineUsdPerPricingUnit !== null ? ` / ${formatUsd(deeplineUsdPerPricingUnit)}` : "";
|
|
9707
|
-
const billingSuffix = billingMode ? ` (${billingMode})` : "";
|
|
9708
|
-
console.log(` Cost: ${formatDecimal(deeplineCredits)} Deepline credits${usdText} per ${unit}${billingSuffix}`);
|
|
9709
|
-
return true;
|
|
9710
|
-
}
|
|
9711
|
-
const summary = stringField(pricing, "summary");
|
|
9712
|
-
if (summary) {
|
|
9713
|
-
console.log(` Cost: ${summary}`);
|
|
9714
|
-
return true;
|
|
9715
|
-
}
|
|
9716
|
-
return false;
|
|
9717
|
-
}
|
|
9718
10265
|
function toolInputFieldsForDisplay(inputSchema) {
|
|
9719
10266
|
if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord4);
|
|
9720
10267
|
const jsonSchema = isRecord4(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
|
|
@@ -9765,17 +10312,6 @@ function listExtractorPathsFromUsageGuidance(tool) {
|
|
|
9765
10312
|
).filter(Boolean);
|
|
9766
10313
|
});
|
|
9767
10314
|
}
|
|
9768
|
-
function isPlayTool(tool) {
|
|
9769
|
-
const provider = typeof tool.provider === "string" ? tool.provider : "";
|
|
9770
|
-
return provider === "deepline_native" && Boolean(recordField(tool, "playExpansion", "play_expansion"));
|
|
9771
|
-
}
|
|
9772
|
-
function titleCase(value) {
|
|
9773
|
-
return value.replace(/[_-]+/g, " ").split(" ").filter(Boolean).map((part) => {
|
|
9774
|
-
const lower = part.toLowerCase();
|
|
9775
|
-
const special = { linkedin: "LinkedIn", crm: "CRM", api: "API" };
|
|
9776
|
-
return special[lower] ?? `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`;
|
|
9777
|
-
}).join(" ");
|
|
9778
|
-
}
|
|
9779
10315
|
function formatDecimal(value) {
|
|
9780
10316
|
const text = value.toFixed(12).replace(/0+$/, "").replace(/\.$/, "");
|
|
9781
10317
|
return text || "0";
|
|
@@ -10535,6 +11071,61 @@ function shouldDeferSkillsSyncForCommand() {
|
|
|
10535
11071
|
const subcommand = args[1];
|
|
10536
11072
|
return (command === "play" || command === "plays") && subcommand === "run" && args.includes("--json");
|
|
10537
11073
|
}
|
|
11074
|
+
async function runPlayRunnerHealthCheck() {
|
|
11075
|
+
const dir = await mkdtemp(join11(tmpdir4(), "deepline-health-play-"));
|
|
11076
|
+
const file = join11(dir, "health-check.play.ts");
|
|
11077
|
+
try {
|
|
11078
|
+
await writeFile4(
|
|
11079
|
+
file,
|
|
11080
|
+
[
|
|
11081
|
+
"import { definePlay } from 'deepline';",
|
|
11082
|
+
"",
|
|
11083
|
+
"export default definePlay('health-check', async (ctx) => {",
|
|
11084
|
+
" const rows = await ctx",
|
|
11085
|
+
" .map('health_rows', [{ id: 'a' }, { id: 'b' }])",
|
|
11086
|
+
" .step('echo', (row) => ({ ok: true, id: row.id }))",
|
|
11087
|
+
" .run({ key: 'id' });",
|
|
11088
|
+
" return { ok: true, rows, source: 'deepline health --play-runner' };",
|
|
11089
|
+
"});",
|
|
11090
|
+
""
|
|
11091
|
+
].join("\n"),
|
|
11092
|
+
"utf8"
|
|
11093
|
+
);
|
|
11094
|
+
let capturedOutput = "";
|
|
11095
|
+
const originalWrite = process.stdout.write.bind(process.stdout);
|
|
11096
|
+
process.stdout.write = ((chunk, ...args) => {
|
|
11097
|
+
capturedOutput += typeof chunk === "string" ? chunk : String(chunk);
|
|
11098
|
+
return true;
|
|
11099
|
+
});
|
|
11100
|
+
let exitCode = 1;
|
|
11101
|
+
try {
|
|
11102
|
+
exitCode = await handlePlayRun([
|
|
11103
|
+
file,
|
|
11104
|
+
"--input",
|
|
11105
|
+
"{}",
|
|
11106
|
+
"--watch",
|
|
11107
|
+
"--no-open",
|
|
11108
|
+
"--json"
|
|
11109
|
+
]);
|
|
11110
|
+
} finally {
|
|
11111
|
+
process.stdout.write = originalWrite;
|
|
11112
|
+
}
|
|
11113
|
+
if (exitCode !== 0) {
|
|
11114
|
+
throw new Error(
|
|
11115
|
+
`play runner canary exited ${exitCode}: ${capturedOutput.slice(0, 1e3)}`
|
|
11116
|
+
);
|
|
11117
|
+
}
|
|
11118
|
+
return {
|
|
11119
|
+
status: "ok",
|
|
11120
|
+
playRunner: {
|
|
11121
|
+
status: "ok",
|
|
11122
|
+
check: "no-provider local play run completed"
|
|
11123
|
+
}
|
|
11124
|
+
};
|
|
11125
|
+
} finally {
|
|
11126
|
+
await rm(dir, { recursive: true, force: true });
|
|
11127
|
+
}
|
|
11128
|
+
}
|
|
10538
11129
|
async function main() {
|
|
10539
11130
|
const mainStartedAt = Date.now();
|
|
10540
11131
|
recordCliTrace({
|
|
@@ -10546,7 +11137,7 @@ async function main() {
|
|
|
10546
11137
|
if (printStartupPhase) {
|
|
10547
11138
|
progress?.phase("loading deepline cli");
|
|
10548
11139
|
}
|
|
10549
|
-
const program = new
|
|
11140
|
+
const program = new Command3();
|
|
10550
11141
|
program.name("deepline").description("Deepline CLI (TypeScript SDK)").version(SDK_VERSION, "-v, --version", "Show version").showHelpAfterError().showSuggestionAfterError(true).addHelpText(
|
|
10551
11142
|
"after",
|
|
10552
11143
|
`
|
|
@@ -10555,7 +11146,7 @@ Common commands:
|
|
|
10555
11146
|
deepline auth status --json
|
|
10556
11147
|
deepline plays search email --json
|
|
10557
11148
|
deepline plays describe person-linkedin-to-email --json
|
|
10558
|
-
deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
|
|
11149
|
+
deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
|
|
10559
11150
|
deepline tools execute hunter_email_verifier --input '{"email":"a@b.com"}'
|
|
10560
11151
|
deepline update
|
|
10561
11152
|
|
|
@@ -10611,18 +11202,30 @@ Exit codes:
|
|
|
10611
11202
|
registerDbCommands(program);
|
|
10612
11203
|
registerFeedbackCommands(program);
|
|
10613
11204
|
registerUpdateCommand(program);
|
|
10614
|
-
program.command("health").description("Check server health.").option("--json", "Force JSON output.").
|
|
11205
|
+
program.command("health").description("Check server health.").option("--json", "Force JSON output.").option(
|
|
11206
|
+
"--play-runner",
|
|
11207
|
+
"Run a tiny no-provider play to verify the full play execution plane."
|
|
11208
|
+
).addHelpText(
|
|
10615
11209
|
"after",
|
|
10616
11210
|
`
|
|
10617
11211
|
Notes:
|
|
10618
11212
|
Read-only connectivity check for the configured Deepline host. Prints the raw
|
|
10619
11213
|
server health payload as JSON.
|
|
11214
|
+
Add --play-runner to verify bundling, coordinator dispatch, runtime callbacks,
|
|
11215
|
+
and run streaming with a tiny no-provider play.
|
|
10620
11216
|
|
|
10621
11217
|
Examples:
|
|
10622
11218
|
deepline health
|
|
11219
|
+
deepline health --play-runner
|
|
10623
11220
|
`
|
|
10624
|
-
).action(async () => {
|
|
11221
|
+
).action(async (options) => {
|
|
10625
11222
|
try {
|
|
11223
|
+
if (options.playRunner) {
|
|
11224
|
+
const data2 = await runPlayRunnerHealthCheck();
|
|
11225
|
+
process.stdout.write(`${JSON.stringify(data2, null, 2)}
|
|
11226
|
+
`);
|
|
11227
|
+
return;
|
|
11228
|
+
}
|
|
10626
11229
|
const client = new DeeplineClient();
|
|
10627
11230
|
const data = await client.health();
|
|
10628
11231
|
process.stdout.write(`${JSON.stringify(data, null, 2)}
|