deepline 0.1.77 → 0.1.79
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 +5 -8
- package/dist/cli/index.js +525 -355
- package/dist/cli/index.mjs +538 -368
- package/dist/index.d.mts +31 -2
- package/dist/index.d.ts +31 -2
- package/dist/index.js +3 -2
- package/dist/index.mjs +3 -2
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +273 -83
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +18 -3
- package/dist/repo/apps/play-runner-workers/src/workflow-retry-state.ts +203 -0
- package/dist/repo/sdk/src/client.ts +7 -0
- package/dist/repo/sdk/src/play.ts +1 -1
- package/dist/repo/sdk/src/release.ts +2 -2
- package/dist/repo/sdk/src/types.ts +4 -0
- package/dist/repo/shared_libs/plays/dataset.ts +3 -1
- package/dist/repo/shared_libs/plays/static-pipeline.ts +261 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -229,10 +229,10 @@ var import_node_path2 = require("path");
|
|
|
229
229
|
|
|
230
230
|
// src/release.ts
|
|
231
231
|
var SDK_RELEASE = {
|
|
232
|
-
version: "0.1.
|
|
232
|
+
version: "0.1.79",
|
|
233
233
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
234
234
|
supportPolicy: {
|
|
235
|
-
latest: "0.1.
|
|
235
|
+
latest: "0.1.79",
|
|
236
236
|
minimumSupported: "0.1.53",
|
|
237
237
|
deprecatedBelow: "0.1.53"
|
|
238
238
|
}
|
|
@@ -402,8 +402,8 @@ var HttpClient = class {
|
|
|
402
402
|
if (lastError instanceof DeeplineError) {
|
|
403
403
|
throw lastError;
|
|
404
404
|
}
|
|
405
|
-
const
|
|
406
|
-
throw new DeeplineError(
|
|
405
|
+
const errorMessage3 = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
|
|
406
|
+
throw new DeeplineError(errorMessage3);
|
|
407
407
|
}
|
|
408
408
|
/**
|
|
409
409
|
* Send a GET request.
|
|
@@ -808,6 +808,7 @@ var DeeplineClient = class {
|
|
|
808
808
|
aliases,
|
|
809
809
|
inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
|
|
810
810
|
outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
|
|
811
|
+
staticPipeline: isRecord(play.staticPipeline) ? play.staticPipeline : isRecord(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
|
|
811
812
|
...csvInput ? { csvInput } : {},
|
|
812
813
|
...rowOutputSchema ? { rowOutputSchema } : {},
|
|
813
814
|
runCommand: runCommand2,
|
|
@@ -4033,6 +4034,51 @@ function customerDbRows(result) {
|
|
|
4033
4034
|
function customerDbColumnNames(result) {
|
|
4034
4035
|
return result.columns.map((column) => column.name).filter(Boolean);
|
|
4035
4036
|
}
|
|
4037
|
+
function errorMessage(value) {
|
|
4038
|
+
return value instanceof Error ? value.message : String(value ?? "");
|
|
4039
|
+
}
|
|
4040
|
+
function collectErrorText(value) {
|
|
4041
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
4042
|
+
return errorMessage(value);
|
|
4043
|
+
}
|
|
4044
|
+
const record = value;
|
|
4045
|
+
return [
|
|
4046
|
+
errorMessage(value),
|
|
4047
|
+
typeof record.error === "string" ? record.error : "",
|
|
4048
|
+
typeof record.message === "string" ? record.message : "",
|
|
4049
|
+
typeof record.detail === "string" ? record.detail : "",
|
|
4050
|
+
typeof record.hint === "string" ? record.hint : "",
|
|
4051
|
+
typeof record.code === "string" ? record.code : "",
|
|
4052
|
+
record.response ? collectErrorText(record.response) : "",
|
|
4053
|
+
record.details ? collectErrorText(record.details) : ""
|
|
4054
|
+
].filter(Boolean).join("\n");
|
|
4055
|
+
}
|
|
4056
|
+
function formatDbQueryError(sql, error) {
|
|
4057
|
+
const text = collectErrorText(error);
|
|
4058
|
+
const lower = text.toLowerCase();
|
|
4059
|
+
const referencesStorage = /"storage"\.|storage\./i.test(sql);
|
|
4060
|
+
const runIdColumnMissing = /column\s+"?run_id"?\s+does not exist/i.test(text) || /run_id.*does not exist/i.test(lower);
|
|
4061
|
+
const relationMissing = /relation\s+["']?[^"']*storage[^"']*["']?\s+does not exist/i.test(text) || /42p01/i.test(text);
|
|
4062
|
+
if (referencesStorage && relationMissing) {
|
|
4063
|
+
return [
|
|
4064
|
+
"Customer DB query failed: the referenced storage table does not exist.",
|
|
4065
|
+
"Play map tables are created only when the corresponding ctx.map(...).run(...) call executes. Pilot branches, early returns, and runs that fail before the map do not create that table.",
|
|
4066
|
+
"Use `deepline runs get <run-id> --full --json` to inspect returned dataset handles, then export them with `deepline runs export <run-id> --dataset result.rows --out rows.csv`.",
|
|
4067
|
+
`Original error: ${errorMessage(error)}`
|
|
4068
|
+
].join("\n");
|
|
4069
|
+
}
|
|
4070
|
+
if (referencesStorage && runIdColumnMissing) {
|
|
4071
|
+
return [
|
|
4072
|
+
"Customer DB query failed: storage map tables use `_run_id`, not `run_id`.",
|
|
4073
|
+
"Prefer `deepline runs export <run-id> --dataset result.rows --out rows.csv` unless you are doing deep table debugging.",
|
|
4074
|
+
`Original error: ${errorMessage(error)}`
|
|
4075
|
+
].join("\n");
|
|
4076
|
+
}
|
|
4077
|
+
if (error instanceof DeeplineError) {
|
|
4078
|
+
return error.message;
|
|
4079
|
+
}
|
|
4080
|
+
return errorMessage(error);
|
|
4081
|
+
}
|
|
4036
4082
|
function writeCustomerDbCsv(result, outPath) {
|
|
4037
4083
|
const resolved = (0, import_node_path7.resolve)(outPath);
|
|
4038
4084
|
(0, import_node_fs6.writeFileSync)(
|
|
@@ -4093,7 +4139,13 @@ async function handleDbQuery(args) {
|
|
|
4093
4139
|
const jsonOutput = argsWantJson(args);
|
|
4094
4140
|
const explicitJsonOutput = args.includes("--json");
|
|
4095
4141
|
const client = new DeeplineClient();
|
|
4096
|
-
|
|
4142
|
+
let result;
|
|
4143
|
+
try {
|
|
4144
|
+
result = await client.queryCustomerDb({ sql, maxRows });
|
|
4145
|
+
} catch (error) {
|
|
4146
|
+
console.error(formatDbQueryError(sql, error));
|
|
4147
|
+
return 1;
|
|
4148
|
+
}
|
|
4097
4149
|
const toolCommand = `deepline tools execute query_customer_db --payload ${JSON.stringify(
|
|
4098
4150
|
{
|
|
4099
4151
|
sql,
|
|
@@ -6082,19 +6134,19 @@ function playBootstrapTemplateConfig(template) {
|
|
|
6082
6134
|
function templateExample(template) {
|
|
6083
6135
|
switch (template) {
|
|
6084
6136
|
case "people-list":
|
|
6085
|
-
return "deepline plays bootstrap people-list --from provider:dropleads_search_people
|
|
6137
|
+
return "deepline plays bootstrap people-list --from provider:dropleads_search_people --out people.play.ts";
|
|
6086
6138
|
case "company-list":
|
|
6087
|
-
return "deepline plays bootstrap company-list --from provider:apollo_company_search
|
|
6139
|
+
return "deepline plays bootstrap company-list --from provider:apollo_company_search --out companies.play.ts";
|
|
6088
6140
|
case "people-email":
|
|
6089
|
-
return "deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall
|
|
6141
|
+
return "deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --out email-flow.play.ts";
|
|
6090
6142
|
case "people-phone":
|
|
6091
|
-
return "deepline plays bootstrap people-phone --from csv:data/vp_contacts.csv --using play:prebuilt/person-to-phone
|
|
6143
|
+
return "deepline plays bootstrap people-phone --from csv:data/vp_contacts.csv --using play:prebuilt/person-to-phone --out phone-flow.play.ts";
|
|
6092
6144
|
case "company-people":
|
|
6093
|
-
return "deepline plays bootstrap company-people --from provider:apollo_company_search --using play:prebuilt/company-to-contact
|
|
6145
|
+
return "deepline plays bootstrap company-people --from provider:apollo_company_search --using play:prebuilt/company-to-contact --out company-people.play.ts";
|
|
6094
6146
|
case "company-people-email":
|
|
6095
|
-
return "deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email providers:hunter_email_finder,leadmagic_email_finder
|
|
6147
|
+
return "deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email providers:hunter_email_finder,leadmagic_email_finder --out account-emails.play.ts";
|
|
6096
6148
|
case "company-people-phone":
|
|
6097
|
-
return "deepline plays bootstrap company-people-phone --from provider:apollo_company_search --people play:prebuilt/company-to-contact --phone providers:ai_ark_mobile_phone_finder
|
|
6149
|
+
return "deepline plays bootstrap company-people-phone --from provider:apollo_company_search --people play:prebuilt/company-to-contact --phone providers:ai_ark_mobile_phone_finder --out account-phones.play.ts";
|
|
6098
6150
|
}
|
|
6099
6151
|
}
|
|
6100
6152
|
var PLAY_BOOTSTRAP_STAGE_NAMES = [
|
|
@@ -6103,19 +6155,22 @@ var PLAY_BOOTSTRAP_STAGE_NAMES = [
|
|
|
6103
6155
|
"phone"
|
|
6104
6156
|
];
|
|
6105
6157
|
function playBootstrapUsageLine() {
|
|
6106
|
-
return `Usage: deepline plays bootstrap <${formatPlayBootstrapTemplates()}> --from <csv:PATH|play:REF|provider:ID|providers:ID,ID> [--using <play:REF|providers:ID,ID>] [--people play:REF] [--email <play:REF|providers:ID,ID>] [--phone <play:REF|providers:ID,ID>] [--limit 5]
|
|
6158
|
+
return `Usage: deepline plays bootstrap <${formatPlayBootstrapTemplates()}> --from <csv:PATH|play:REF|provider:ID|providers:ID,ID> [--using <play:REF|providers:ID,ID>] [--people play:REF] [--email <play:REF|providers:ID,ID>] [--phone <play:REF|providers:ID,ID>] [--limit 5] [--out flow.play.ts]`;
|
|
6107
6159
|
}
|
|
6108
6160
|
function requireBootstrapTemplate(rawTemplate) {
|
|
6109
6161
|
if (!rawTemplate) {
|
|
6110
6162
|
throw new PlayBootstrapUsageError(
|
|
6111
6163
|
`plays bootstrap needs a route template: ${formatPlayBootstrapTemplates()}.
|
|
6112
|
-
Example: deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall
|
|
6164
|
+
Example: deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --out email-flow.play.ts`
|
|
6113
6165
|
);
|
|
6114
6166
|
}
|
|
6115
6167
|
if (!isPlayBootstrapTemplate(rawTemplate)) {
|
|
6168
|
+
const looksLikePlayReference = rawTemplate.includes("/") || rawTemplate.endsWith(".play.ts");
|
|
6116
6169
|
throw new PlayBootstrapUsageError(
|
|
6117
6170
|
`Unknown plays bootstrap template: ${rawTemplate}
|
|
6118
|
-
Supported templates: ${formatPlayBootstrapTemplates()}`
|
|
6171
|
+
Supported templates: ${formatPlayBootstrapTemplates()}` + (looksLikePlayReference ? `
|
|
6172
|
+
|
|
6173
|
+
"${rawTemplate}" looks like an existing play reference or file, not a bootstrap template. Do not run plays bootstrap on prebuilt/<play-name>. Use "deepline plays run ${rawTemplate} --input '{...}' --watch" to run it directly, "deepline plays describe ${rawTemplate} --json" to inspect its contract, or "deepline plays get ${rawTemplate} --source --out scratchpad.play.ts" to clone/edit it.` : "")
|
|
6119
6174
|
);
|
|
6120
6175
|
}
|
|
6121
6176
|
return rawTemplate;
|
|
@@ -6257,7 +6312,8 @@ function parsePlayBootstrapOptions(args) {
|
|
|
6257
6312
|
people: null,
|
|
6258
6313
|
email: null,
|
|
6259
6314
|
phone: null,
|
|
6260
|
-
limit: 5
|
|
6315
|
+
limit: 5,
|
|
6316
|
+
out: null
|
|
6261
6317
|
};
|
|
6262
6318
|
for (let index = 0; index < rest.length; index += 1) {
|
|
6263
6319
|
const arg = rest[index];
|
|
@@ -6291,6 +6347,10 @@ function parsePlayBootstrapOptions(args) {
|
|
|
6291
6347
|
options.limit = parsePositiveInteger2(value(), "--limit");
|
|
6292
6348
|
index += 1;
|
|
6293
6349
|
break;
|
|
6350
|
+
case "--out":
|
|
6351
|
+
options.out = value();
|
|
6352
|
+
index += 1;
|
|
6353
|
+
break;
|
|
6294
6354
|
default:
|
|
6295
6355
|
throw new PlayBootstrapUsageError(
|
|
6296
6356
|
`Unknown plays bootstrap option: ${arg}
|
|
@@ -6408,7 +6468,7 @@ function packagedCsvPathForPlay(csvPath) {
|
|
|
6408
6468
|
const relativePath = (0, import_node_path11.relative)(playDir, absoluteCsvPath);
|
|
6409
6469
|
if (relativePath === "" || relativePath.startsWith("..") || (0, import_node_path11.isAbsolute)(relativePath)) {
|
|
6410
6470
|
throw new PlayBootstrapUsageError(
|
|
6411
|
-
`--from csv:${csvPath} must point to a file inside the directory where you run plays bootstrap. Run bootstrap from the intended play directory
|
|
6471
|
+
`--from csv:${csvPath} must point to a file inside the directory where you run plays bootstrap. Run bootstrap from the intended play directory and write the play with --out there.`
|
|
6412
6472
|
);
|
|
6413
6473
|
}
|
|
6414
6474
|
const portablePath = relativePath.split("\\").join("/");
|
|
@@ -6815,8 +6875,18 @@ function validateBootstrapRoutes(input2) {
|
|
|
6815
6875
|
"Company-to-people bootstrap only accepts --people play:<play-ref> for now. Providers are too task-specific; choose or create a people play so the generated file can show the mapping contract."
|
|
6816
6876
|
);
|
|
6817
6877
|
}
|
|
6878
|
+
assertComposablePlayRoute({
|
|
6879
|
+
stageLabel: "--people",
|
|
6880
|
+
playRef: stagePlayRef(input2.options.people),
|
|
6881
|
+
play: input2.peoplePlay
|
|
6882
|
+
});
|
|
6818
6883
|
for (const finder of ["email_finder", "phone_finder"]) {
|
|
6819
6884
|
const requiredCategory = PLAY_BOOTSTRAP_PROVIDER_CATEGORY_BY_FINDER[finder];
|
|
6885
|
+
assertComposablePlayRoute({
|
|
6886
|
+
stageLabel: finder === "email_finder" ? "--email/--using" : "--phone/--using",
|
|
6887
|
+
playRef: stagePlayRef(finderStage(input2.options, finder)),
|
|
6888
|
+
play: input2.finderPlays[finder] ?? null
|
|
6889
|
+
});
|
|
6820
6890
|
for (const tool of input2.finderTools[finder] ?? []) {
|
|
6821
6891
|
if (!tool.categories.includes(requiredCategory)) {
|
|
6822
6892
|
throw new PlayBootstrapValidationError(
|
|
@@ -6831,6 +6901,38 @@ function validateBootstrapRoutes(input2) {
|
|
|
6831
6901
|
}
|
|
6832
6902
|
}
|
|
6833
6903
|
}
|
|
6904
|
+
function staticPipelineSubsteps(pipeline) {
|
|
6905
|
+
if (!isRecord3(pipeline)) return [];
|
|
6906
|
+
return [
|
|
6907
|
+
...extractionEntries(pipeline.stages),
|
|
6908
|
+
...extractionEntries(pipeline.substeps)
|
|
6909
|
+
];
|
|
6910
|
+
}
|
|
6911
|
+
function playUsesMapBackedRuntime(play) {
|
|
6912
|
+
const pipeline = play?.staticPipeline;
|
|
6913
|
+
if (!isRecord3(pipeline)) return false;
|
|
6914
|
+
if (stringValue(pipeline.tableNamespace)) return true;
|
|
6915
|
+
return staticPipelineSubsteps(pipeline).some((substep) => {
|
|
6916
|
+
if (stringValue(substep.type) === "map") return true;
|
|
6917
|
+
return playUsesMapBackedRuntime({
|
|
6918
|
+
name: play?.name ?? "child",
|
|
6919
|
+
aliases: [],
|
|
6920
|
+
runCommand: "",
|
|
6921
|
+
examples: [],
|
|
6922
|
+
staticPipeline: isRecord3(substep.pipeline) ? substep.pipeline : null
|
|
6923
|
+
});
|
|
6924
|
+
});
|
|
6925
|
+
}
|
|
6926
|
+
function assertComposablePlayRoute(input2) {
|
|
6927
|
+
if (!input2.playRef || !playUsesMapBackedRuntime(input2.play)) return;
|
|
6928
|
+
const runCommand2 = input2.play?.runCommand?.trim() || `deepline plays run ${input2.playRef} --input '{...}' --watch`;
|
|
6929
|
+
throw new PlayBootstrapValidationError(
|
|
6930
|
+
`Cannot use ${input2.stageLabel} play:${input2.playRef} in plays bootstrap composition: the selected play is map-backed/direct-run-only. Child plays that use ctx.map() own durable table state and must be run directly, exported, or validated as their own play instead of wrapped with ctx.runPlay. Run it directly first: ${runCommand2}`
|
|
6931
|
+
);
|
|
6932
|
+
}
|
|
6933
|
+
function sourcePlayNeedsExportFirst(input2) {
|
|
6934
|
+
return input2.source.kind === "play" && playUsesMapBackedRuntime(input2.sourcePlay);
|
|
6935
|
+
}
|
|
6834
6936
|
function sourceCollectionName(entity) {
|
|
6835
6937
|
switch (entity) {
|
|
6836
6938
|
case "company":
|
|
@@ -6860,6 +6962,22 @@ function generateCsvSourceRowsBlock(input2) {
|
|
|
6860
6962
|
const ${input2.collection}: ${input2.collectionType}[] = await sourceDataset.peek(limit);`;
|
|
6861
6963
|
}
|
|
6862
6964
|
function generatePlaySourceRowsBlock(input2) {
|
|
6965
|
+
if (sourcePlayNeedsExportFirst({
|
|
6966
|
+
source: input2.source,
|
|
6967
|
+
sourcePlay: input2.sourcePlay
|
|
6968
|
+
})) {
|
|
6969
|
+
const sourcePlay = input2.sourcePlay;
|
|
6970
|
+
const runCommand2 = sourcePlay?.runCommand?.trim() || `deepline plays run ${input2.source.value} --input '{...}' --watch`;
|
|
6971
|
+
return `// Source play ${input2.source.value} is map-backed/direct-run-only, so this generated play is stage 2.
|
|
6972
|
+
// Stage 1:
|
|
6973
|
+
// ${runCommand2}
|
|
6974
|
+
// Stage 2:
|
|
6975
|
+
// deepline runs export <stage-1-run-id> --dataset 'result.rows' --out source-export.csv
|
|
6976
|
+
// Stage 3:
|
|
6977
|
+
// deepline plays run <this-file.play.ts> --input '{"sourceCsv":"source-export.csv","limit":${input2.sourcePlay ? "5" : "5"}}' --watch
|
|
6978
|
+
const sourceDataset = await ctx.csv<${input2.collectionType}>(input.sourceCsv ?? './source-export.csv');
|
|
6979
|
+
const ${input2.collection}: ${input2.collectionType}[] = await sourceDataset.peek(limit);`;
|
|
6980
|
+
}
|
|
6863
6981
|
const playInput = generatePlayInputObject({
|
|
6864
6982
|
schema: input2.sourcePlay?.inputSchema,
|
|
6865
6983
|
indent: " ",
|
|
@@ -7144,6 +7262,7 @@ ${typeDefinitions}
|
|
|
7144
7262
|
|
|
7145
7263
|
type Input = {
|
|
7146
7264
|
limit?: number;
|
|
7265
|
+
sourceCsv?: string;
|
|
7147
7266
|
};
|
|
7148
7267
|
|
|
7149
7268
|
export default definePlay(${jsString(input2.options.name)}, async (ctx, input: Input = {}) => {
|
|
@@ -7210,6 +7329,7 @@ async function loadBootstrapContracts(client, options) {
|
|
|
7210
7329
|
validateBootstrapRoutes({
|
|
7211
7330
|
options,
|
|
7212
7331
|
sourceTools: contracts.sourceTools,
|
|
7332
|
+
sourcePlay: contracts.sourcePlay,
|
|
7213
7333
|
peoplePlay: contracts.peoplePlay,
|
|
7214
7334
|
finderTools: contracts.finderTools,
|
|
7215
7335
|
finderPlays: contracts.finderPlays
|
|
@@ -7231,11 +7351,11 @@ function loadCsvContext(source) {
|
|
|
7231
7351
|
};
|
|
7232
7352
|
}
|
|
7233
7353
|
}
|
|
7234
|
-
function
|
|
7354
|
+
function errorMessage2(error) {
|
|
7235
7355
|
return error instanceof Error ? error.message : String(error);
|
|
7236
7356
|
}
|
|
7237
7357
|
function renderPlayBootstrapError(error) {
|
|
7238
|
-
console.error(
|
|
7358
|
+
console.error(errorMessage2(error));
|
|
7239
7359
|
return error instanceof PlayBootstrapError ? error.exitCode : 1;
|
|
7240
7360
|
}
|
|
7241
7361
|
async function runPlayBootstrap(args) {
|
|
@@ -7248,6 +7368,12 @@ async function runPlayBootstrap(args) {
|
|
|
7248
7368
|
...contracts,
|
|
7249
7369
|
...csvContext
|
|
7250
7370
|
});
|
|
7371
|
+
if (options.out) {
|
|
7372
|
+
(0, import_node_fs9.writeFileSync)((0, import_node_path11.resolve)(options.out), source, "utf-8");
|
|
7373
|
+
process.stdout.write(`Wrote ${(0, import_node_path11.resolve)(options.out)}
|
|
7374
|
+
`);
|
|
7375
|
+
return 0;
|
|
7376
|
+
}
|
|
7251
7377
|
process.stdout.write(source);
|
|
7252
7378
|
return 0;
|
|
7253
7379
|
}
|
|
@@ -7260,7 +7386,7 @@ function registerPlayBootstrapCommand(play) {
|
|
|
7260
7386
|
`
|
|
7261
7387
|
Notes:
|
|
7262
7388
|
Cloud-validated play generator for agents. Pick the JTBD as the positional
|
|
7263
|
-
template, bind resources with typed refs,
|
|
7389
|
+
template, bind resources with typed refs, write the .play.ts file with --out,
|
|
7264
7390
|
then edit the generated TODO mapping comments. Multiple finder providers are
|
|
7265
7391
|
generated as a waterfall in the order you pass them.
|
|
7266
7392
|
|
|
@@ -7268,9 +7394,8 @@ Notes:
|
|
|
7268
7394
|
Deepline. It prints TypeScript source only and does not run paid tools; the
|
|
7269
7395
|
generated play may spend credits later when you run it.
|
|
7270
7396
|
|
|
7271
|
-
stdout is the generated .play.ts source.
|
|
7272
|
-
|
|
7273
|
-
deepline plays bootstrap ... > scratchpad.play.ts
|
|
7397
|
+
By default stdout is the generated .play.ts source. Use --out to write a file
|
|
7398
|
+
directly. Errors and diagnostics go to stderr. There is no JSON mode.
|
|
7274
7399
|
|
|
7275
7400
|
Templates:
|
|
7276
7401
|
people-list start from people/contact rows
|
|
@@ -7294,7 +7419,7 @@ Notes:
|
|
|
7294
7419
|
email/phone finder providers must match their category and expose value getters
|
|
7295
7420
|
finder plays/providers must match the route; generated code leaves input mapping explicit
|
|
7296
7421
|
business-specific provider inputs and company -> people persona fields are TODOs in code
|
|
7297
|
-
csv: paths are resolved from the directory where you run bootstrap;
|
|
7422
|
+
csv: paths are resolved from the directory where you run bootstrap; write the play file there too
|
|
7298
7423
|
|
|
7299
7424
|
Exit codes:
|
|
7300
7425
|
0 success
|
|
@@ -7302,13 +7427,13 @@ Notes:
|
|
|
7302
7427
|
7 route validation failed
|
|
7303
7428
|
|
|
7304
7429
|
Examples:
|
|
7305
|
-
deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --limit 5
|
|
7430
|
+
deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --limit 5 --out email-flow.play.ts
|
|
7306
7431
|
deepline plays check email-flow.play.ts
|
|
7307
7432
|
deepline plays run email-flow.play.ts --input '{"limit":5}' --watch
|
|
7308
7433
|
|
|
7309
|
-
deepline plays bootstrap people-email --from provider:dropleads_search_people --using providers:hunter_email_finder,leadmagic_email_finder --limit 5
|
|
7310
|
-
deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email play:prebuilt/name-and-domain-to-email-waterfall --limit 5
|
|
7311
|
-
deepline plays bootstrap company-list --from provider:apollo_company_search --limit 5
|
|
7434
|
+
deepline plays bootstrap people-email --from provider:dropleads_search_people --using providers:hunter_email_finder,leadmagic_email_finder --limit 5 --out prospecting.play.ts
|
|
7435
|
+
deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email play:prebuilt/name-and-domain-to-email-waterfall --limit 5 --out account-contacts.play.ts
|
|
7436
|
+
deepline plays bootstrap company-list --from provider:apollo_company_search --limit 5 --out companies.play.ts
|
|
7312
7437
|
`
|
|
7313
7438
|
).option("--name <name>", "Generated play name").option(
|
|
7314
7439
|
"--from <ref>",
|
|
@@ -7325,7 +7450,7 @@ Examples:
|
|
|
7325
7450
|
).option(
|
|
7326
7451
|
"--phone <ref>",
|
|
7327
7452
|
"Phone finder stage: play:REF, provider:ID, or providers:ID,ID"
|
|
7328
|
-
).option("--limit <n>", "Maximum rows to fan out in the generated play").action(async (template, options) => {
|
|
7453
|
+
).option("--limit <n>", "Maximum rows to fan out in the generated play").option("--out <path>", "Write generated play source to a file").action(async (template, options) => {
|
|
7329
7454
|
process.exitCode = await handlePlayBootstrap([
|
|
7330
7455
|
template,
|
|
7331
7456
|
...options.name ? ["--name", options.name] : [],
|
|
@@ -7334,7 +7459,8 @@ Examples:
|
|
|
7334
7459
|
...options.people ? ["--people", options.people] : [],
|
|
7335
7460
|
...options.email ? ["--email", options.email] : [],
|
|
7336
7461
|
...options.phone ? ["--phone", options.phone] : [],
|
|
7337
|
-
...options.limit ? ["--limit", options.limit] : []
|
|
7462
|
+
...options.limit ? ["--limit", options.limit] : [],
|
|
7463
|
+
...options.out ? ["--out", options.out] : []
|
|
7338
7464
|
]);
|
|
7339
7465
|
});
|
|
7340
7466
|
}
|
|
@@ -7484,204 +7610,6 @@ function createCliProgress(enabled) {
|
|
|
7484
7610
|
return progress;
|
|
7485
7611
|
}
|
|
7486
7612
|
|
|
7487
|
-
// ../shared_libs/plays/row-identity.ts
|
|
7488
|
-
var POSTGRES_IDENTIFIER_MAX_LENGTH = 63;
|
|
7489
|
-
var PLAY_NAME_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
|
|
7490
|
-
var MAP_KEY_NAMESPACE_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
|
|
7491
|
-
var SHA256_INITIAL_HASH = [
|
|
7492
|
-
1779033703,
|
|
7493
|
-
3144134277,
|
|
7494
|
-
1013904242,
|
|
7495
|
-
2773480762,
|
|
7496
|
-
1359893119,
|
|
7497
|
-
2600822924,
|
|
7498
|
-
528734635,
|
|
7499
|
-
1541459225
|
|
7500
|
-
];
|
|
7501
|
-
var SHA256_ROUND_CONSTANTS = [
|
|
7502
|
-
1116352408,
|
|
7503
|
-
1899447441,
|
|
7504
|
-
3049323471,
|
|
7505
|
-
3921009573,
|
|
7506
|
-
961987163,
|
|
7507
|
-
1508970993,
|
|
7508
|
-
2453635748,
|
|
7509
|
-
2870763221,
|
|
7510
|
-
3624381080,
|
|
7511
|
-
310598401,
|
|
7512
|
-
607225278,
|
|
7513
|
-
1426881987,
|
|
7514
|
-
1925078388,
|
|
7515
|
-
2162078206,
|
|
7516
|
-
2614888103,
|
|
7517
|
-
3248222580,
|
|
7518
|
-
3835390401,
|
|
7519
|
-
4022224774,
|
|
7520
|
-
264347078,
|
|
7521
|
-
604807628,
|
|
7522
|
-
770255983,
|
|
7523
|
-
1249150122,
|
|
7524
|
-
1555081692,
|
|
7525
|
-
1996064986,
|
|
7526
|
-
2554220882,
|
|
7527
|
-
2821834349,
|
|
7528
|
-
2952996808,
|
|
7529
|
-
3210313671,
|
|
7530
|
-
3336571891,
|
|
7531
|
-
3584528711,
|
|
7532
|
-
113926993,
|
|
7533
|
-
338241895,
|
|
7534
|
-
666307205,
|
|
7535
|
-
773529912,
|
|
7536
|
-
1294757372,
|
|
7537
|
-
1396182291,
|
|
7538
|
-
1695183700,
|
|
7539
|
-
1986661051,
|
|
7540
|
-
2177026350,
|
|
7541
|
-
2456956037,
|
|
7542
|
-
2730485921,
|
|
7543
|
-
2820302411,
|
|
7544
|
-
3259730800,
|
|
7545
|
-
3345764771,
|
|
7546
|
-
3516065817,
|
|
7547
|
-
3600352804,
|
|
7548
|
-
4094571909,
|
|
7549
|
-
275423344,
|
|
7550
|
-
430227734,
|
|
7551
|
-
506948616,
|
|
7552
|
-
659060556,
|
|
7553
|
-
883997877,
|
|
7554
|
-
958139571,
|
|
7555
|
-
1322822218,
|
|
7556
|
-
1537002063,
|
|
7557
|
-
1747873779,
|
|
7558
|
-
1955562222,
|
|
7559
|
-
2024104815,
|
|
7560
|
-
2227730452,
|
|
7561
|
-
2361852424,
|
|
7562
|
-
2428436474,
|
|
7563
|
-
2756734187,
|
|
7564
|
-
3204031479,
|
|
7565
|
-
3329325298
|
|
7566
|
-
];
|
|
7567
|
-
function rightRotate32(value, bits) {
|
|
7568
|
-
return value >>> bits | value << 32 - bits;
|
|
7569
|
-
}
|
|
7570
|
-
function sha256Hex(input2) {
|
|
7571
|
-
const bytes = Array.from(new TextEncoder().encode(input2));
|
|
7572
|
-
const bitLength = bytes.length * 8;
|
|
7573
|
-
bytes.push(128);
|
|
7574
|
-
while (bytes.length % 64 !== 56) {
|
|
7575
|
-
bytes.push(0);
|
|
7576
|
-
}
|
|
7577
|
-
const highBits = Math.floor(bitLength / 4294967296);
|
|
7578
|
-
const lowBits = bitLength >>> 0;
|
|
7579
|
-
bytes.push(
|
|
7580
|
-
highBits >>> 24 & 255,
|
|
7581
|
-
highBits >>> 16 & 255,
|
|
7582
|
-
highBits >>> 8 & 255,
|
|
7583
|
-
highBits & 255,
|
|
7584
|
-
lowBits >>> 24 & 255,
|
|
7585
|
-
lowBits >>> 16 & 255,
|
|
7586
|
-
lowBits >>> 8 & 255,
|
|
7587
|
-
lowBits & 255
|
|
7588
|
-
);
|
|
7589
|
-
const hash = [...SHA256_INITIAL_HASH];
|
|
7590
|
-
const words = new Array(64).fill(0);
|
|
7591
|
-
for (let offset = 0; offset < bytes.length; offset += 64) {
|
|
7592
|
-
for (let index = 0; index < 16; index += 1) {
|
|
7593
|
-
const wordOffset = offset + index * 4;
|
|
7594
|
-
words[index] = (bytes[wordOffset] ?? 0) << 24 | (bytes[wordOffset + 1] ?? 0) << 16 | (bytes[wordOffset + 2] ?? 0) << 8 | (bytes[wordOffset + 3] ?? 0);
|
|
7595
|
-
}
|
|
7596
|
-
for (let index = 16; index < 64; index += 1) {
|
|
7597
|
-
const s0 = rightRotate32(words[index - 15], 7) ^ rightRotate32(words[index - 15], 18) ^ words[index - 15] >>> 3;
|
|
7598
|
-
const s1 = rightRotate32(words[index - 2], 17) ^ rightRotate32(words[index - 2], 19) ^ words[index - 2] >>> 10;
|
|
7599
|
-
words[index] = words[index - 16] + s0 + words[index - 7] + s1 >>> 0;
|
|
7600
|
-
}
|
|
7601
|
-
let [a, b, c, d, e, f, g, h] = hash;
|
|
7602
|
-
for (let index = 0; index < 64; index += 1) {
|
|
7603
|
-
const s1 = rightRotate32(e, 6) ^ rightRotate32(e, 11) ^ rightRotate32(e, 25);
|
|
7604
|
-
const ch = e & f ^ ~e & g;
|
|
7605
|
-
const temp1 = h + s1 + ch + SHA256_ROUND_CONSTANTS[index] + words[index] >>> 0;
|
|
7606
|
-
const s0 = rightRotate32(a, 2) ^ rightRotate32(a, 13) ^ rightRotate32(a, 22);
|
|
7607
|
-
const maj = a & b ^ a & c ^ b & c;
|
|
7608
|
-
const temp2 = s0 + maj >>> 0;
|
|
7609
|
-
h = g;
|
|
7610
|
-
g = f;
|
|
7611
|
-
f = e;
|
|
7612
|
-
e = d + temp1 >>> 0;
|
|
7613
|
-
d = c;
|
|
7614
|
-
c = b;
|
|
7615
|
-
b = a;
|
|
7616
|
-
a = temp1 + temp2 >>> 0;
|
|
7617
|
-
}
|
|
7618
|
-
hash[0] = hash[0] + a >>> 0;
|
|
7619
|
-
hash[1] = hash[1] + b >>> 0;
|
|
7620
|
-
hash[2] = hash[2] + c >>> 0;
|
|
7621
|
-
hash[3] = hash[3] + d >>> 0;
|
|
7622
|
-
hash[4] = hash[4] + e >>> 0;
|
|
7623
|
-
hash[5] = hash[5] + f >>> 0;
|
|
7624
|
-
hash[6] = hash[6] + g >>> 0;
|
|
7625
|
-
hash[7] = hash[7] + h >>> 0;
|
|
7626
|
-
}
|
|
7627
|
-
return hash.map((word) => word.toString(16).padStart(8, "0")).join("");
|
|
7628
|
-
}
|
|
7629
|
-
function sanitizeIdentifierPart(value) {
|
|
7630
|
-
return value.trim().replace(/[^a-z0-9]+/gi, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
7631
|
-
}
|
|
7632
|
-
function validateIdentifierPart(rawValue, label, maxLength) {
|
|
7633
|
-
const sanitized = sanitizeIdentifierPart(rawValue);
|
|
7634
|
-
if (!sanitized) {
|
|
7635
|
-
throw new Error(
|
|
7636
|
-
`${label} must contain at least one letter or number after normalization. Use only letters, numbers, underscores, or hyphens.`
|
|
7637
|
-
);
|
|
7638
|
-
}
|
|
7639
|
-
if (sanitized.length > maxLength) {
|
|
7640
|
-
throw new Error(
|
|
7641
|
-
`${label} is too long after normalization (${sanitized.length}/${maxLength}). Shorten it to ${maxLength} characters or fewer. Normalized value: "${sanitized}".`
|
|
7642
|
-
);
|
|
7643
|
-
}
|
|
7644
|
-
return sanitized;
|
|
7645
|
-
}
|
|
7646
|
-
function normalizePlayName(value) {
|
|
7647
|
-
if (value.includes("/")) {
|
|
7648
|
-
throw new Error(
|
|
7649
|
-
'Play name cannot contain "/". Slash is reserved for qualified play references like "prebuilt/example" or "self/example".'
|
|
7650
|
-
);
|
|
7651
|
-
}
|
|
7652
|
-
return validateIdentifierPart(value, "Play name", PLAY_NAME_MAX_LENGTH);
|
|
7653
|
-
}
|
|
7654
|
-
function normalizePlayNameForSheet(value) {
|
|
7655
|
-
if (!value.includes("/")) {
|
|
7656
|
-
return normalizePlayName(value);
|
|
7657
|
-
}
|
|
7658
|
-
const digest = sha256Hex(value).slice(0, 12);
|
|
7659
|
-
const normalizedReference = sanitizeIdentifierPart(
|
|
7660
|
-
value.replace(/\//g, "__")
|
|
7661
|
-
);
|
|
7662
|
-
const prefixLength = Math.max(1, PLAY_NAME_MAX_LENGTH - digest.length - 1);
|
|
7663
|
-
const prefix = normalizedReference.slice(0, prefixLength).replace(/_+$/g, "") || "qualified_play";
|
|
7664
|
-
return `${prefix}_${digest}`;
|
|
7665
|
-
}
|
|
7666
|
-
function normalizeTableNamespace(value) {
|
|
7667
|
-
return validateIdentifierPart(
|
|
7668
|
-
value,
|
|
7669
|
-
"ctx.dataset() key",
|
|
7670
|
-
MAP_KEY_NAMESPACE_MAX_LENGTH
|
|
7671
|
-
);
|
|
7672
|
-
}
|
|
7673
|
-
function validatePlaySheetTableName(playName, tableNamespace) {
|
|
7674
|
-
const playSegment = normalizePlayNameForSheet(playName);
|
|
7675
|
-
const keySegment = normalizeTableNamespace(tableNamespace);
|
|
7676
|
-
const resolved = `${playSegment}_${keySegment}`;
|
|
7677
|
-
if (resolved.length > POSTGRES_IDENTIFIER_MAX_LENGTH) {
|
|
7678
|
-
throw new Error(
|
|
7679
|
-
`Play sheet table name is too long after normalization (${resolved.length}/63). Shorten the play name or ctx.dataset() key. Resolved table name: "${resolved}".`
|
|
7680
|
-
);
|
|
7681
|
-
}
|
|
7682
|
-
return resolved;
|
|
7683
|
-
}
|
|
7684
|
-
|
|
7685
7613
|
// src/cli/trace.ts
|
|
7686
7614
|
var cliTraceStartedAt = Date.now();
|
|
7687
7615
|
function isTruthyEnv(value) {
|
|
@@ -7732,6 +7660,11 @@ async function traceCliSpan(phase, fields, run) {
|
|
|
7732
7660
|
|
|
7733
7661
|
// src/cli/play-check-hints.ts
|
|
7734
7662
|
var EXTRACTED_GETTER_ERROR_HINT = "Deepline hint: extractedValues/extractedLists .get() only works for declared Deepline getters listed by `deepline tools describe <tool> --json`. Use `toolExecutionResult.toolResponse.raw` for provider/tool-specific fields.";
|
|
7663
|
+
var DATASET_API_HINT = "Deepline hint: PlayDataset is lazy and durable. Use `.peek(n)` for a small preview or `.materialize()` when you intentionally need rows in memory; do not use `.rows`, `.toArray()`, or array methods directly on the dataset handle.";
|
|
7664
|
+
var ROW_PROPERTY_HINT = "Deepline hint: this row type only contains fields produced by the CSV/schema and previous map steps. Check source column casing and the exact output field names from earlier steps before scaling.";
|
|
7665
|
+
var TOOLS_EXECUTE_SIGNATURE_HINT = "Deepline hint: ctx.tools.execute requires a request object: `ctx.tools.execute({ id, tool, input, description })`. The stable `id` is required for replay-safe receipts.";
|
|
7666
|
+
var RUN_PLAY_SIGNATURE_HINT = "Deepline hint: ctx.runPlay uses a stable key plus a composable child play reference. Direct-run-only or map-backed batch plays must be run directly, exported, then consumed by a separate play.";
|
|
7667
|
+
var MAP_BACKED_CHILD_HINT = "Deepline hint: map-backed child plays own durable table state and cannot be called from another play. Run that play directly, export its dataset, then pass the CSV to the next play.";
|
|
7735
7668
|
function sourceLineForError(sourceCode, error) {
|
|
7736
7669
|
const match = error.match(/:(\d+):(\d+)\s/);
|
|
7737
7670
|
const lineNumber = match?.[1] ? Number(match[1]) : NaN;
|
|
@@ -7742,21 +7675,65 @@ function looksLikeInvalidExtractedGetter(error, sourceLine) {
|
|
|
7742
7675
|
if (!/Property '[^']+' does not exist on type/.test(error)) return false;
|
|
7743
7676
|
return /\bextracted(?:Values|Lists)\s*\./.test(sourceLine);
|
|
7744
7677
|
}
|
|
7678
|
+
function looksLikeDatasetApiMisuse(error, sourceLine) {
|
|
7679
|
+
return /Property '(?:rows|toArray|forEach|map|filter|reduce)' does not exist on type '[^']*PlayDataset/.test(
|
|
7680
|
+
error
|
|
7681
|
+
) || /\b(?:\.rows|\.toArray\(\)|\.forEach\(|\.map\(|\.filter\(|\.reduce\()/.test(
|
|
7682
|
+
sourceLine
|
|
7683
|
+
);
|
|
7684
|
+
}
|
|
7685
|
+
function looksLikeRowPropertyMismatch(error, sourceLine) {
|
|
7686
|
+
if (!/Property '[^']+' does not exist on type/.test(error)) return false;
|
|
7687
|
+
if (looksLikeInvalidExtractedGetter(error, sourceLine)) return false;
|
|
7688
|
+
return /\brow\.[A-Za-z_][A-Za-z0-9_]*/.test(sourceLine);
|
|
7689
|
+
}
|
|
7690
|
+
function looksLikeToolsExecuteSignature(error, sourceLine) {
|
|
7691
|
+
return /ctx\.tools\.execute requires a request object/i.test(error) || /Expected 1 arguments?, but got [2-9]/.test(error) && /\btools\.execute\(/.test(sourceLine);
|
|
7692
|
+
}
|
|
7693
|
+
function looksLikeRunPlaySignature(error, sourceLine) {
|
|
7694
|
+
return /ctx\.runPlay/i.test(error) || /(?:Expected|Argument of type|No overload matches)/.test(error) && /\brunPlay\(/.test(sourceLine);
|
|
7695
|
+
}
|
|
7696
|
+
function looksLikeMapBackedChild(error) {
|
|
7697
|
+
return /map-backed child play|direct-run-only|cannot call a map-backed|own durable table/i.test(
|
|
7698
|
+
error
|
|
7699
|
+
);
|
|
7700
|
+
}
|
|
7701
|
+
function hintForError(error, sourceLine) {
|
|
7702
|
+
if (looksLikeInvalidExtractedGetter(error, sourceLine)) {
|
|
7703
|
+
return EXTRACTED_GETTER_ERROR_HINT;
|
|
7704
|
+
}
|
|
7705
|
+
if (looksLikeDatasetApiMisuse(error, sourceLine)) {
|
|
7706
|
+
return DATASET_API_HINT;
|
|
7707
|
+
}
|
|
7708
|
+
if (looksLikeToolsExecuteSignature(error, sourceLine)) {
|
|
7709
|
+
return TOOLS_EXECUTE_SIGNATURE_HINT;
|
|
7710
|
+
}
|
|
7711
|
+
if (looksLikeMapBackedChild(error)) {
|
|
7712
|
+
return MAP_BACKED_CHILD_HINT;
|
|
7713
|
+
}
|
|
7714
|
+
if (looksLikeRunPlaySignature(error, sourceLine)) {
|
|
7715
|
+
return RUN_PLAY_SIGNATURE_HINT;
|
|
7716
|
+
}
|
|
7717
|
+
if (looksLikeRowPropertyMismatch(error, sourceLine)) {
|
|
7718
|
+
return ROW_PROPERTY_HINT;
|
|
7719
|
+
}
|
|
7720
|
+
return null;
|
|
7721
|
+
}
|
|
7745
7722
|
function addPlayCheckRepairHints(input2) {
|
|
7746
|
-
|
|
7723
|
+
const addedHints = /* @__PURE__ */ new Set();
|
|
7747
7724
|
return input2.errors.map((error) => {
|
|
7748
7725
|
const line = sourceLineForError(input2.sourceCode, error);
|
|
7749
|
-
|
|
7726
|
+
const hint = hintForError(error, line);
|
|
7727
|
+
if (!hint || addedHints.has(hint) || error.includes(hint)) {
|
|
7750
7728
|
return error;
|
|
7751
7729
|
}
|
|
7752
|
-
|
|
7730
|
+
addedHints.add(hint);
|
|
7753
7731
|
return `${error}
|
|
7754
|
-
${
|
|
7732
|
+
${hint}`;
|
|
7755
7733
|
});
|
|
7756
7734
|
}
|
|
7757
7735
|
|
|
7758
7736
|
// src/cli/commands/play.ts
|
|
7759
|
-
var PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS = 2500;
|
|
7760
7737
|
var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
7761
7738
|
"--json",
|
|
7762
7739
|
"--wait",
|
|
@@ -8308,8 +8285,6 @@ var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
|
|
|
8308
8285
|
"cancelled"
|
|
8309
8286
|
]);
|
|
8310
8287
|
var PLAY_START_TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
|
|
8311
|
-
var PLAY_CUSTOMER_STORAGE_SCHEMA_NAME = "storage";
|
|
8312
|
-
var PLAY_INTERNAL_STEP_RECEIPT_TABLE = "_deepline_step_receipts";
|
|
8313
8288
|
function getEventPayload(event) {
|
|
8314
8289
|
return event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
8315
8290
|
}
|
|
@@ -8363,36 +8338,8 @@ function getLogLinesFromLiveEvent(event) {
|
|
|
8363
8338
|
const lines = getEventPayload(event).lines;
|
|
8364
8339
|
return Array.isArray(lines) ? lines.filter((line) => typeof line === "string") : [];
|
|
8365
8340
|
}
|
|
8366
|
-
function
|
|
8367
|
-
return `
|
|
8368
|
-
}
|
|
8369
|
-
function quoteSqlLiteral(value) {
|
|
8370
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
8371
|
-
}
|
|
8372
|
-
function buildDebugDbQueryCommand(sql) {
|
|
8373
|
-
return `deepline db query --sql ${shellSingleQuote(sql)} --max-rows 20 --json`;
|
|
8374
|
-
}
|
|
8375
|
-
function buildStepReceiptsDebugCommand(runId) {
|
|
8376
|
-
const table = `${quoteSqlIdentifier(
|
|
8377
|
-
PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
|
|
8378
|
-
)}.${quoteSqlIdentifier(PLAY_INTERNAL_STEP_RECEIPT_TABLE)}`;
|
|
8379
|
-
const sql = `select convert_from(k, 'UTF8') as receipt_key, case status when 0 then 'pending' when 1 then 'running' when 2 then 'completed' when 3 then 'failed' when 4 then 'skipped' else status::text end as status, output, error, updated_at from ${table} where run_id = ${quoteSqlLiteral(runId)} order by updated_at asc, receipt_key asc limit 20`;
|
|
8380
|
-
return buildDebugDbQueryCommand(sql);
|
|
8381
|
-
}
|
|
8382
|
-
function buildMapTableDebugCommand(input2) {
|
|
8383
|
-
try {
|
|
8384
|
-
const tableName = validatePlaySheetTableName(
|
|
8385
|
-
input2.playName,
|
|
8386
|
-
input2.tableNamespace
|
|
8387
|
-
);
|
|
8388
|
-
const table = `${quoteSqlIdentifier(
|
|
8389
|
-
PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
|
|
8390
|
-
)}.${quoteSqlIdentifier(tableName)}`;
|
|
8391
|
-
const sql = `select * from ${table} where _run_id = ${quoteSqlLiteral(input2.runId)} limit 20`;
|
|
8392
|
-
return buildDebugDbQueryCommand(sql);
|
|
8393
|
-
} catch {
|
|
8394
|
-
return null;
|
|
8395
|
-
}
|
|
8341
|
+
function buildRunInspectCommand(runId) {
|
|
8342
|
+
return `deepline runs get ${runId} --full --json`;
|
|
8396
8343
|
}
|
|
8397
8344
|
function extractTableNamespaceFromLiveEvent(event) {
|
|
8398
8345
|
const payload = getEventPayload(event);
|
|
@@ -8417,7 +8364,7 @@ function emitLiveDebugTableHints(input2) {
|
|
|
8417
8364
|
if (!input2.state.emittedDebugKeys.has(receiptsKey)) {
|
|
8418
8365
|
input2.state.emittedDebugKeys.add(receiptsKey);
|
|
8419
8366
|
input2.progress.writeLine(
|
|
8420
|
-
`
|
|
8367
|
+
`Inspect run output: ${buildRunInspectCommand(input2.runId)}`,
|
|
8421
8368
|
process.stdout
|
|
8422
8369
|
);
|
|
8423
8370
|
}
|
|
@@ -8429,17 +8376,9 @@ function emitLiveDebugTableHints(input2) {
|
|
|
8429
8376
|
if (input2.state.emittedDebugKeys.has(tableKey)) {
|
|
8430
8377
|
return;
|
|
8431
8378
|
}
|
|
8432
|
-
const command = buildMapTableDebugCommand({
|
|
8433
|
-
playName: input2.playName,
|
|
8434
|
-
runId: input2.runId,
|
|
8435
|
-
tableNamespace
|
|
8436
|
-
});
|
|
8437
|
-
if (!command) {
|
|
8438
|
-
return;
|
|
8439
|
-
}
|
|
8440
8379
|
input2.state.emittedDebugKeys.add(tableKey);
|
|
8441
8380
|
input2.progress.writeLine(
|
|
8442
|
-
`
|
|
8381
|
+
`Possible map table ${tableNamespace}: created only after this ctx.map(...).run(...) executes. Inspect returned datasets with ${buildRunInspectCommand(input2.runId)}`,
|
|
8443
8382
|
process.stdout
|
|
8444
8383
|
);
|
|
8445
8384
|
}
|
|
@@ -8643,7 +8582,7 @@ async function waitForPlayCompletionByStream(input2) {
|
|
|
8643
8582
|
}
|
|
8644
8583
|
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
8645
8584
|
throw new DeeplineError(
|
|
8646
|
-
`Play
|
|
8585
|
+
`Play watch stream ended before the run reached a terminal status. runId=${input2.workflowId}${phaseSuffix}. Inspect the current run with 'deepline runs get ${input2.workflowId} --full --json' or continue watching with 'deepline runs tail ${input2.workflowId}'.`,
|
|
8647
8586
|
void 0,
|
|
8648
8587
|
"PLAY_LIVE_STREAM_ENDED",
|
|
8649
8588
|
{
|
|
@@ -8699,10 +8638,6 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
8699
8638
|
let eventCount = 0;
|
|
8700
8639
|
let firstRunIdMs = null;
|
|
8701
8640
|
let lastPhase = null;
|
|
8702
|
-
const startRequest = {
|
|
8703
|
-
...input2.request,
|
|
8704
|
-
waitForCompletionMs: typeof input2.request.waitForCompletionMs === "number" ? input2.request.waitForCompletionMs : PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS
|
|
8705
|
-
};
|
|
8706
8641
|
const timeout = input2.waitTimeoutMs === null ? null : setTimeout(
|
|
8707
8642
|
() => {
|
|
8708
8643
|
timedOut = true;
|
|
@@ -8711,7 +8646,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
8711
8646
|
Math.max(1, input2.waitTimeoutMs)
|
|
8712
8647
|
);
|
|
8713
8648
|
try {
|
|
8714
|
-
for await (const event of input2.client.startPlayRunStream(
|
|
8649
|
+
for await (const event of input2.client.startPlayRunStream(input2.request, {
|
|
8715
8650
|
signal: controller.signal
|
|
8716
8651
|
})) {
|
|
8717
8652
|
eventCount += 1;
|
|
@@ -8804,7 +8739,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
8804
8739
|
const reason = error instanceof Error ? error.message : String(error);
|
|
8805
8740
|
if (!input2.jsonOutput) {
|
|
8806
8741
|
process.stderr.write(
|
|
8807
|
-
`[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to run stream (${reason})
|
|
8742
|
+
`[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to canonical run stream (${reason})
|
|
8808
8743
|
`
|
|
8809
8744
|
);
|
|
8810
8745
|
}
|
|
@@ -8841,7 +8776,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
8841
8776
|
if (lastKnownWorkflowId) {
|
|
8842
8777
|
if (!input2.jsonOutput) {
|
|
8843
8778
|
input2.progress.writeLine(
|
|
8844
|
-
`[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to run stream`
|
|
8779
|
+
`[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to canonical run stream`
|
|
8845
8780
|
);
|
|
8846
8781
|
}
|
|
8847
8782
|
recordCliTrace({
|
|
@@ -8871,7 +8806,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
8871
8806
|
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
8872
8807
|
const idSuffix = lastKnownWorkflowId ? ` runId=${lastKnownWorkflowId}` : "";
|
|
8873
8808
|
throw new DeeplineError(
|
|
8874
|
-
`Play start stream ended before
|
|
8809
|
+
`Play start stream ended before a terminal status was observed${idSuffix}${phaseSuffix}. If a run id was printed, inspect it with 'deepline runs get <run-id> --full --json' or continue watching with 'deepline runs tail <run-id>'.`,
|
|
8875
8810
|
void 0,
|
|
8876
8811
|
"PLAY_START_STREAM_ENDED",
|
|
8877
8812
|
{
|
|
@@ -9219,6 +9154,32 @@ function formatPlayErrorForDisplay(status, error) {
|
|
|
9219
9154
|
}
|
|
9220
9155
|
return error;
|
|
9221
9156
|
}
|
|
9157
|
+
function isGenericInternalServerError(error) {
|
|
9158
|
+
if (!error) return false;
|
|
9159
|
+
return /^(?:internalservererror|internal server error|http 500|500)$/i.test(
|
|
9160
|
+
error.trim()
|
|
9161
|
+
);
|
|
9162
|
+
}
|
|
9163
|
+
function selectRunErrorForDisplay(status) {
|
|
9164
|
+
const progressError = getStringField(status.progress, "error");
|
|
9165
|
+
if (!isGenericInternalServerError(progressError)) {
|
|
9166
|
+
return progressError;
|
|
9167
|
+
}
|
|
9168
|
+
const directErrors = getRecordField(status, "errors");
|
|
9169
|
+
if (Array.isArray(directErrors)) {
|
|
9170
|
+
for (const entry of directErrors) {
|
|
9171
|
+
const message = getStringField(entry, "message");
|
|
9172
|
+
if (message && !isGenericInternalServerError(message)) {
|
|
9173
|
+
return message;
|
|
9174
|
+
}
|
|
9175
|
+
}
|
|
9176
|
+
}
|
|
9177
|
+
const directError = getStringField(status, "error");
|
|
9178
|
+
if (directError && !isGenericInternalServerError(directError)) {
|
|
9179
|
+
return directError;
|
|
9180
|
+
}
|
|
9181
|
+
return progressError;
|
|
9182
|
+
}
|
|
9222
9183
|
function normalizeRunStatusForEnvelope(status) {
|
|
9223
9184
|
const run = status.run ?? null;
|
|
9224
9185
|
return {
|
|
@@ -9356,8 +9317,7 @@ function compactPlayStatus(status) {
|
|
|
9356
9317
|
const billing = status && typeof status === "object" ? stripProviderSpendFromBilling(
|
|
9357
9318
|
status.billing
|
|
9358
9319
|
) : null;
|
|
9359
|
-
const
|
|
9360
|
-
const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
|
|
9320
|
+
const error = selectRunErrorForDisplay(status) ?? (typeof status.error === "string" ? String(status.error) : null);
|
|
9361
9321
|
const displayError = formatPlayErrorForDisplay(status, error);
|
|
9362
9322
|
return {
|
|
9363
9323
|
runId: status.runId,
|
|
@@ -9612,8 +9572,8 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
9612
9572
|
for (const warning of warnings) {
|
|
9613
9573
|
lines.push(` warning: ${warning}`);
|
|
9614
9574
|
}
|
|
9615
|
-
const progressError = status
|
|
9616
|
-
if (progressError
|
|
9575
|
+
const progressError = selectRunErrorForDisplay(status);
|
|
9576
|
+
if (progressError) {
|
|
9617
9577
|
const billing = extractBillingForStatus(status, progressError);
|
|
9618
9578
|
if (isInsufficientCreditsBilling(billing)) {
|
|
9619
9579
|
lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
|
|
@@ -9665,9 +9625,6 @@ var RUN_EXPORT_PAGE_SIZE = 5e3;
|
|
|
9665
9625
|
function shellSingleQuote(value) {
|
|
9666
9626
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
9667
9627
|
}
|
|
9668
|
-
function sqlStringLiteral(value) {
|
|
9669
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
9670
|
-
}
|
|
9671
9628
|
function runExportRetryCommand(runId, outPath, datasetPath) {
|
|
9672
9629
|
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote((0, import_node_path12.resolve)(outPath))}`;
|
|
9673
9630
|
}
|
|
@@ -9686,26 +9643,6 @@ function extractRunPlayName(status) {
|
|
|
9686
9643
|
}
|
|
9687
9644
|
return null;
|
|
9688
9645
|
}
|
|
9689
|
-
function normalizeCustomerDbIdentifier(value) {
|
|
9690
|
-
return value.split("/").pop().replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
9691
|
-
}
|
|
9692
|
-
function buildCustomerDbQueryPlan(input2) {
|
|
9693
|
-
const playName = extractRunPlayName(input2.status);
|
|
9694
|
-
const tableNamespace = input2.rowsInfo.tableNamespace?.trim();
|
|
9695
|
-
if (!playName || !tableNamespace || input2.rowsInfo.totalRows <= 0) {
|
|
9696
|
-
return null;
|
|
9697
|
-
}
|
|
9698
|
-
const tableName = `${normalizeCustomerDbIdentifier(playName)}_${normalizeCustomerDbIdentifier(
|
|
9699
|
-
tableNamespace
|
|
9700
|
-
)}`;
|
|
9701
|
-
const sql = `select * from "storage"."${tableName}" where _run_id = ${sqlStringLiteral(input2.status.runId)} limit ${input2.rowsInfo.totalRows}`;
|
|
9702
|
-
const base = `deepline customer-db query --sql ${shellSingleQuote(sql)} --max-rows ${input2.rowsInfo.totalRows}`;
|
|
9703
|
-
return {
|
|
9704
|
-
sql,
|
|
9705
|
-
json: `${base} --json`,
|
|
9706
|
-
csv: `${base} --format csv --out ${shellSingleQuote((0, import_node_path12.resolve)(input2.outPath))}`
|
|
9707
|
-
};
|
|
9708
|
-
}
|
|
9709
9646
|
function exportableSheetRow(row) {
|
|
9710
9647
|
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
9711
9648
|
return null;
|
|
@@ -10052,8 +9989,12 @@ function renderServerResultView(value) {
|
|
|
10052
9989
|
lines.push(
|
|
10053
9990
|
` ${String(table.tableNamespace ?? "table")}${lineLabel}: ${rowLabel}${details.join(" ")}`
|
|
10054
9991
|
);
|
|
10055
|
-
if (typeof table.queryDatasetCommand === "string") {
|
|
10056
|
-
lines.push(`
|
|
9992
|
+
if (typeof table.queryDatasetCommand === "string" && typeof table.rowCount === "number" && table.rowCount > 0) {
|
|
9993
|
+
lines.push(` debug backing table: ${table.queryDatasetCommand}`);
|
|
9994
|
+
} else if (typeof table.queryDatasetCommand === "string") {
|
|
9995
|
+
lines.push(
|
|
9996
|
+
" no rows observed for this run; backing table is created only if this map ran"
|
|
9997
|
+
);
|
|
10057
9998
|
}
|
|
10058
9999
|
if (table.debugHelp && typeof table.debugHelp === "object" && !Array.isArray(table.debugHelp)) {
|
|
10059
10000
|
const debugHelp = table.debugHelp;
|
|
@@ -10399,9 +10340,51 @@ function printToolGetterHints(hints) {
|
|
|
10399
10340
|
async function handlePlayCheck(args) {
|
|
10400
10341
|
const options = parsePlayCheckOptions(args);
|
|
10401
10342
|
if (!isFileTarget(options.target)) {
|
|
10402
|
-
const
|
|
10403
|
-
|
|
10404
|
-
|
|
10343
|
+
const client2 = new DeeplineClient();
|
|
10344
|
+
try {
|
|
10345
|
+
await assertCanonicalNamedPlayReference(client2, options.target);
|
|
10346
|
+
const play = await client2.describePlay(
|
|
10347
|
+
parseReferencedPlayTarget2(options.target).playName,
|
|
10348
|
+
{ compact: true }
|
|
10349
|
+
);
|
|
10350
|
+
const result2 = {
|
|
10351
|
+
valid: true,
|
|
10352
|
+
target: options.target,
|
|
10353
|
+
name: play.name,
|
|
10354
|
+
reference: play.reference ?? options.target,
|
|
10355
|
+
origin: play.origin ?? null,
|
|
10356
|
+
ownerType: play.ownerType ?? null,
|
|
10357
|
+
inputSchema: play.inputSchema ?? null,
|
|
10358
|
+
outputSchema: play.outputSchema ?? null,
|
|
10359
|
+
staticPipeline: play.staticPipeline ?? null,
|
|
10360
|
+
note: "Named/prebuilt play contract is available. No run was started."
|
|
10361
|
+
};
|
|
10362
|
+
if (options.jsonOutput) {
|
|
10363
|
+
process.stdout.write(`${JSON.stringify(result2)}
|
|
10364
|
+
`);
|
|
10365
|
+
} else {
|
|
10366
|
+
console.log(`\u2713 ${result2.reference} passed named play contract check`);
|
|
10367
|
+
console.log(" no run started; no Deepline credits spent");
|
|
10368
|
+
if (play.runCommand) console.log(` run: ${play.runCommand}`);
|
|
10369
|
+
}
|
|
10370
|
+
return 0;
|
|
10371
|
+
} catch (error) {
|
|
10372
|
+
const resolved = (0, import_node_path12.resolve)(options.target);
|
|
10373
|
+
const message = error instanceof Error && error.message ? error.message : `File not found: ${resolved}`;
|
|
10374
|
+
if (options.jsonOutput) {
|
|
10375
|
+
process.stdout.write(
|
|
10376
|
+
`${JSON.stringify({
|
|
10377
|
+
valid: false,
|
|
10378
|
+
target: options.target,
|
|
10379
|
+
errors: [message]
|
|
10380
|
+
})}
|
|
10381
|
+
`
|
|
10382
|
+
);
|
|
10383
|
+
} else {
|
|
10384
|
+
console.error(message);
|
|
10385
|
+
}
|
|
10386
|
+
return 1;
|
|
10387
|
+
}
|
|
10405
10388
|
}
|
|
10406
10389
|
const absolutePlayPath = (0, import_node_path12.resolve)(options.target);
|
|
10407
10390
|
const sourceCode = (0, import_node_fs10.readFileSync)(absolutePlayPath, "utf-8");
|
|
@@ -11048,18 +11031,9 @@ async function handleRunExport(args) {
|
|
|
11048
11031
|
datasetPath
|
|
11049
11032
|
});
|
|
11050
11033
|
const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
|
|
11051
|
-
const queryPlan = exportResult && outPath ? buildCustomerDbQueryPlan({
|
|
11052
|
-
status,
|
|
11053
|
-
rowsInfo: exportResult.rowsInfo,
|
|
11054
|
-
outPath
|
|
11055
|
-
}) : null;
|
|
11056
11034
|
const next = {
|
|
11057
11035
|
...buildRunNextCommands(status),
|
|
11058
|
-
export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source)
|
|
11059
|
-
...queryPlan ? {
|
|
11060
|
-
queryJson: queryPlan.json,
|
|
11061
|
-
queryCsv: queryPlan.csv
|
|
11062
|
-
} : {}
|
|
11036
|
+
export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source)
|
|
11063
11037
|
};
|
|
11064
11038
|
const payload = {
|
|
11065
11039
|
runId: status.runId,
|
|
@@ -11068,13 +11042,6 @@ async function handleRunExport(args) {
|
|
|
11068
11042
|
source,
|
|
11069
11043
|
rowCount: exportResult?.rowsInfo.totalRows ?? null,
|
|
11070
11044
|
columns: exportResult?.rowsInfo.columns ?? [],
|
|
11071
|
-
...queryPlan ? {
|
|
11072
|
-
query: {
|
|
11073
|
-
sql: queryPlan.sql,
|
|
11074
|
-
json: queryPlan.json,
|
|
11075
|
-
csv: queryPlan.csv
|
|
11076
|
-
}
|
|
11077
|
-
} : {},
|
|
11078
11045
|
...metadataOutPath ? { metadata_path: metadataOutPath } : {},
|
|
11079
11046
|
local: { csv_path: exportResult?.path ?? null },
|
|
11080
11047
|
next,
|
|
@@ -11084,8 +11051,7 @@ async function handleRunExport(args) {
|
|
|
11084
11051
|
title: "run export",
|
|
11085
11052
|
lines: [
|
|
11086
11053
|
`Exported ${status.runId} to ${exportResult?.path ?? outPath}`,
|
|
11087
|
-
...source ? [`source=${source}`] : []
|
|
11088
|
-
...queryPlan ? [`query=${queryPlan.json}`] : []
|
|
11054
|
+
...source ? [`source=${source}`] : []
|
|
11089
11055
|
]
|
|
11090
11056
|
}
|
|
11091
11057
|
]
|
|
@@ -11265,7 +11231,8 @@ function parsePlaySearchOptions(args) {
|
|
|
11265
11231
|
return {
|
|
11266
11232
|
query,
|
|
11267
11233
|
jsonOutput: argsWantJson(args),
|
|
11268
|
-
compact: args.includes("--compact")
|
|
11234
|
+
compact: args.includes("--compact"),
|
|
11235
|
+
prebuiltOnly: args.includes("--prebuilt")
|
|
11269
11236
|
};
|
|
11270
11237
|
}
|
|
11271
11238
|
function printPlayDescription(play) {
|
|
@@ -11313,6 +11280,7 @@ function printPlayDescription(play) {
|
|
|
11313
11280
|
console.log(` ${line}`);
|
|
11314
11281
|
}
|
|
11315
11282
|
}
|
|
11283
|
+
console.log(` Describe: deepline plays describe ${reference} --json`);
|
|
11316
11284
|
console.log(` Run: ${play.runCommand}`);
|
|
11317
11285
|
const cloneEditStarter = play.cloneEditStarter ?? buildCloneEditStarter(play);
|
|
11318
11286
|
if (cloneEditStarter) {
|
|
@@ -11320,6 +11288,31 @@ function printPlayDescription(play) {
|
|
|
11320
11288
|
console.log(` Check starter: ${cloneEditStarter.checkCommand}`);
|
|
11321
11289
|
}
|
|
11322
11290
|
}
|
|
11291
|
+
function inputFieldNames(schema) {
|
|
11292
|
+
const fields = Array.isArray(schema?.fields) ? schema.fields : [];
|
|
11293
|
+
return fields.map(
|
|
11294
|
+
(field) => field && typeof field === "object" ? String(field.name ?? "").trim() : ""
|
|
11295
|
+
).filter(Boolean);
|
|
11296
|
+
}
|
|
11297
|
+
function printCompactPlaySearchResult(play) {
|
|
11298
|
+
const reference = formatPlayListReference(play);
|
|
11299
|
+
const aliases = play.aliases.slice(0, 6).join(", ");
|
|
11300
|
+
console.log(`Play: ${reference}`);
|
|
11301
|
+
if (play.displayName && play.displayName !== play.name) {
|
|
11302
|
+
console.log(` Display name: ${play.displayName}`);
|
|
11303
|
+
}
|
|
11304
|
+
if (aliases) {
|
|
11305
|
+
console.log(` Aliases: ${aliases}`);
|
|
11306
|
+
}
|
|
11307
|
+
const fields = inputFieldNames(play.inputSchema);
|
|
11308
|
+
if (fields.length > 0) {
|
|
11309
|
+
console.log(` Inputs: ${fields.join(", ")}`);
|
|
11310
|
+
} else if (play.inputSchema) {
|
|
11311
|
+
console.log(" Inputs: see describe");
|
|
11312
|
+
}
|
|
11313
|
+
console.log(` Describe: deepline plays describe ${reference} --json`);
|
|
11314
|
+
console.log(` Run: ${play.runCommand}`);
|
|
11315
|
+
}
|
|
11323
11316
|
function compactPlaySchema(schema) {
|
|
11324
11317
|
if (!schema) return null;
|
|
11325
11318
|
const fields = Array.isArray(schema.fields) ? schema.fields.map(
|
|
@@ -11383,22 +11376,44 @@ async function handlePlaySearch(args) {
|
|
|
11383
11376
|
return 1;
|
|
11384
11377
|
}
|
|
11385
11378
|
const client = new DeeplineClient();
|
|
11386
|
-
const plays = await client.searchPlays({
|
|
11379
|
+
const plays = (await client.searchPlays({
|
|
11387
11380
|
query: options.query,
|
|
11388
11381
|
compact: options.compact
|
|
11389
|
-
})
|
|
11382
|
+
})).filter(
|
|
11383
|
+
(play) => options.prebuiltOnly ? play.origin === "prebuilt" || play.ownerType === "deepline" : true
|
|
11384
|
+
);
|
|
11390
11385
|
if (options.jsonOutput) {
|
|
11391
|
-
|
|
11392
|
-
|
|
11386
|
+
const jsonPlays = options.prebuiltOnly ? plays.map((play) => ({
|
|
11387
|
+
...play,
|
|
11388
|
+
inputSchema: compactPlaySchema(play.inputSchema)
|
|
11389
|
+
})) : plays;
|
|
11390
|
+
process.stdout.write(
|
|
11391
|
+
`${JSON.stringify({
|
|
11392
|
+
plays: jsonPlays,
|
|
11393
|
+
total: jsonPlays.length,
|
|
11394
|
+
truncated: false
|
|
11395
|
+
})}
|
|
11396
|
+
`
|
|
11397
|
+
);
|
|
11393
11398
|
return 0;
|
|
11394
11399
|
}
|
|
11395
|
-
|
|
11400
|
+
const displayPlays = options.prebuiltOnly ? plays.slice(0, 5) : plays;
|
|
11401
|
+
process.stdout.write(
|
|
11402
|
+
`${plays.length} plays found${options.prebuiltOnly && plays.length > displayPlays.length ? `; showing top ${displayPlays.length}` : ""}:
|
|
11396
11403
|
|
|
11397
|
-
`
|
|
11398
|
-
|
|
11399
|
-
|
|
11404
|
+
`
|
|
11405
|
+
);
|
|
11406
|
+
for (const play of displayPlays) {
|
|
11407
|
+
if (options.prebuiltOnly) {
|
|
11408
|
+
printCompactPlaySearchResult(play);
|
|
11409
|
+
} else {
|
|
11410
|
+
printPlayDescription(play);
|
|
11411
|
+
}
|
|
11400
11412
|
console.log("");
|
|
11401
11413
|
}
|
|
11414
|
+
if (options.prebuiltOnly && plays.length > displayPlays.length) {
|
|
11415
|
+
console.log("Use --json for the full machine-readable result set.");
|
|
11416
|
+
}
|
|
11402
11417
|
return 0;
|
|
11403
11418
|
}
|
|
11404
11419
|
function normalizePlayGrepText(value) {
|
|
@@ -11665,15 +11680,18 @@ Common commands:
|
|
|
11665
11680
|
deepline plays get person-linkedin-to-email --json
|
|
11666
11681
|
`
|
|
11667
11682
|
);
|
|
11668
|
-
play.command("check <target>").description("
|
|
11683
|
+
play.command("check <target>").description("Check a local play file or named/prebuilt play contract.").addHelpText(
|
|
11669
11684
|
"after",
|
|
11670
11685
|
`
|
|
11671
11686
|
Notes:
|
|
11672
11687
|
Validates a local play without storing it, promoting it, or starting a run.
|
|
11673
11688
|
This uses the authoritative cloud preflight path.
|
|
11689
|
+
For named or prebuilt plays, validates that the contract is discoverable
|
|
11690
|
+
without starting a run or spending Deepline credits.
|
|
11674
11691
|
|
|
11675
11692
|
Examples:
|
|
11676
11693
|
deepline plays check my.play.ts
|
|
11694
|
+
deepline plays check prebuilt/name-and-domain-to-email-waterfall-batch
|
|
11677
11695
|
deepline plays check my.play.ts --json
|
|
11678
11696
|
`
|
|
11679
11697
|
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
|
|
@@ -11823,11 +11841,13 @@ Examples:
|
|
|
11823
11841
|
...options.json ? ["--json"] : []
|
|
11824
11842
|
]);
|
|
11825
11843
|
});
|
|
11826
|
-
const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").addHelpText(
|
|
11844
|
+
const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").option("--prebuilt", "Only show Deepline-managed prebuilt plays").addHelpText(
|
|
11827
11845
|
"after",
|
|
11828
11846
|
`
|
|
11829
11847
|
Notes:
|
|
11830
11848
|
Ranked discovery for workflows. Use describe on a result before running it.
|
|
11849
|
+
Prefer --prebuilt for new GTM tasks so old workspace scratchpads do not
|
|
11850
|
+
outrank Deepline-managed routes unless the user names one explicitly.
|
|
11831
11851
|
The grep alias is the same ranked retrieval surface with a more literal name
|
|
11832
11852
|
for agents that are filtering the play registry.
|
|
11833
11853
|
|
|
@@ -11839,6 +11859,7 @@ Examples:
|
|
|
11839
11859
|
).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
11840
11860
|
process.exitCode = await handlePlaySearch([
|
|
11841
11861
|
query,
|
|
11862
|
+
...options.prebuilt ? ["--prebuilt"] : [],
|
|
11842
11863
|
...options.compact ? ["--compact"] : [],
|
|
11843
11864
|
...options.json ? ["--json"] : []
|
|
11844
11865
|
]);
|
|
@@ -12086,7 +12107,7 @@ Notes:
|
|
|
12086
12107
|
Writes a returned dataset handle to the requested local CSV path. Use runs get
|
|
12087
12108
|
first to inspect dataset paths like result.rows or result.nested.contacts.
|
|
12088
12109
|
--metadata-out writes the same export metadata object returned by --json,
|
|
12089
|
-
including source
|
|
12110
|
+
including the source dataset path and row/column metadata.
|
|
12090
12111
|
|
|
12091
12112
|
Examples:
|
|
12092
12113
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
@@ -14017,7 +14038,7 @@ async function listTools(args) {
|
|
|
14017
14038
|
const client = new DeeplineClient();
|
|
14018
14039
|
const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
|
|
14019
14040
|
const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
|
|
14020
|
-
const compact = args.includes("--compact");
|
|
14041
|
+
const compact = args.includes("--compact") || !args.includes("--json");
|
|
14021
14042
|
const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
14022
14043
|
const items = (await client.listTools({
|
|
14023
14044
|
...categoryFilter ? { categories: categoryFilter } : {}
|
|
@@ -14073,9 +14094,10 @@ async function searchTools(queryInput, options = {}) {
|
|
|
14073
14094
|
searchMode: options.searchMode,
|
|
14074
14095
|
includeSearchDebug: options.includeSearchDebug
|
|
14075
14096
|
});
|
|
14076
|
-
const
|
|
14097
|
+
const shouldCompact = options.compact || !options.json;
|
|
14098
|
+
const payload = shouldCompact && Array.isArray(result.tools) ? {
|
|
14077
14099
|
...result,
|
|
14078
|
-
tools: result.tools.map(compactTool)
|
|
14100
|
+
tools: result.tools.slice(0, 8).map(compactTool)
|
|
14079
14101
|
} : result;
|
|
14080
14102
|
printCommandEnvelope(payload, {
|
|
14081
14103
|
json: options.json || shouldEmitJson()
|
|
@@ -14116,7 +14138,8 @@ async function grepTools(queryInput, options = {}) {
|
|
|
14116
14138
|
mode
|
|
14117
14139
|
)
|
|
14118
14140
|
);
|
|
14119
|
-
const
|
|
14141
|
+
const shouldCompact = options.compact || !options.json;
|
|
14142
|
+
const outputTools = shouldCompact ? tools.slice(0, 8).map(compactTool) : tools;
|
|
14120
14143
|
printCommandEnvelope(
|
|
14121
14144
|
{
|
|
14122
14145
|
tools: outputTools,
|
|
@@ -14135,11 +14158,19 @@ async function grepTools(queryInput, options = {}) {
|
|
|
14135
14158
|
);
|
|
14136
14159
|
return 0;
|
|
14137
14160
|
}
|
|
14161
|
+
function numericToolField(tool, field) {
|
|
14162
|
+
const value = tool[field];
|
|
14163
|
+
return typeof value === "number" ? value : void 0;
|
|
14164
|
+
}
|
|
14138
14165
|
function compactTool(tool) {
|
|
14139
14166
|
const listed = toListedTool(tool);
|
|
14167
|
+
const searchScore = numericToolField(tool, "searchScore");
|
|
14168
|
+
const search_score = numericToolField(tool, "search_score");
|
|
14140
14169
|
return {
|
|
14141
14170
|
id: listed.id,
|
|
14142
14171
|
toolId: listed.toolId,
|
|
14172
|
+
...search_score !== void 0 ? { search_score } : {},
|
|
14173
|
+
...searchScore !== void 0 ? { searchScore } : {},
|
|
14143
14174
|
provider: listed.provider,
|
|
14144
14175
|
displayName: listed.displayName,
|
|
14145
14176
|
description: listed.description,
|
|
@@ -14460,7 +14491,7 @@ async function getTool(toolId, options = {}) {
|
|
|
14460
14491
|
}
|
|
14461
14492
|
if (shouldEmitJson()) {
|
|
14462
14493
|
process.stdout.write(
|
|
14463
|
-
`${JSON.stringify(
|
|
14494
|
+
`${JSON.stringify(toolContractJsonForDescribe(tool, toolId))}
|
|
14464
14495
|
`
|
|
14465
14496
|
);
|
|
14466
14497
|
return 0;
|
|
@@ -14785,8 +14816,8 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
|
|
|
14785
14816
|
invalidGetterHint: "If TypeScript says an extractedValues/extractedLists property does not exist, that field is not a declared Deepline getter.",
|
|
14786
14817
|
observeActualShape: `deepline tools execute ${toolId} --input '{...}' --json`,
|
|
14787
14818
|
observedOutput: `deepline tools execute ${toolId} --input '{...}' --json`,
|
|
14788
|
-
forPlayGetterBugs: "Run
|
|
14789
|
-
executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run
|
|
14819
|
+
forPlayGetterBugs: "Run a tiny play, inspect `deepline runs get <run-id> --full --json`, and export returned dataset handles with `deepline runs export`. Backing tables exist only for ctx.map(...).run(...) stages that actually executed.",
|
|
14820
|
+
executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run output and returned dataset handles."
|
|
14790
14821
|
},
|
|
14791
14822
|
starterScript: {
|
|
14792
14823
|
path: starterScript.path,
|
|
@@ -15803,6 +15834,7 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
|
15803
15834
|
}
|
|
15804
15835
|
|
|
15805
15836
|
// src/cli/index.ts
|
|
15837
|
+
var PREFLIGHT_TIMEOUT_MS = 3e3;
|
|
15806
15838
|
function asCommanderError(error) {
|
|
15807
15839
|
if (!(error instanceof Error) || !("code" in error)) {
|
|
15808
15840
|
return null;
|
|
@@ -15883,6 +15915,124 @@ async function runPlayRunnerHealthCheck() {
|
|
|
15883
15915
|
await (0, import_promises6.rm)(dir, { recursive: true, force: true });
|
|
15884
15916
|
}
|
|
15885
15917
|
}
|
|
15918
|
+
function pickString(value, ...keys) {
|
|
15919
|
+
for (const key of keys) {
|
|
15920
|
+
const candidate = value[key];
|
|
15921
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
15922
|
+
return candidate;
|
|
15923
|
+
}
|
|
15924
|
+
}
|
|
15925
|
+
return null;
|
|
15926
|
+
}
|
|
15927
|
+
function preflightErrorMessage(error) {
|
|
15928
|
+
return error instanceof Error ? error.message : String(error);
|
|
15929
|
+
}
|
|
15930
|
+
async function runPreflightCheck() {
|
|
15931
|
+
const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
|
|
15932
|
+
const healthController = new AbortController();
|
|
15933
|
+
const healthTimeout = setTimeout(
|
|
15934
|
+
() => healthController.abort(),
|
|
15935
|
+
PREFLIGHT_TIMEOUT_MS
|
|
15936
|
+
);
|
|
15937
|
+
const health = await fetch(new URL("/api/v2/health", baseUrl), {
|
|
15938
|
+
signal: healthController.signal
|
|
15939
|
+
}).then(async (response) => {
|
|
15940
|
+
const payload = await response.json().catch(() => ({}));
|
|
15941
|
+
return {
|
|
15942
|
+
status: response.ok ? pickString(payload, "status") ?? "ok" : "unreachable",
|
|
15943
|
+
version: pickString(payload, "version")
|
|
15944
|
+
};
|
|
15945
|
+
}).catch(() => ({ status: "unreachable", version: null })).finally(() => clearTimeout(healthTimeout));
|
|
15946
|
+
const apiKey = resolveApiKeyForBaseUrl(baseUrl);
|
|
15947
|
+
const http = apiKey ? new HttpClient(
|
|
15948
|
+
resolveConfig({
|
|
15949
|
+
baseUrl,
|
|
15950
|
+
apiKey,
|
|
15951
|
+
timeout: PREFLIGHT_TIMEOUT_MS,
|
|
15952
|
+
maxRetries: 0
|
|
15953
|
+
})
|
|
15954
|
+
) : null;
|
|
15955
|
+
const [auth, billing] = http ? await Promise.all([
|
|
15956
|
+
http.post("/api/v2/auth/cli/status", {
|
|
15957
|
+
api_key: apiKey,
|
|
15958
|
+
reveal: false
|
|
15959
|
+
}).catch((error) => ({
|
|
15960
|
+
status: "not_connected",
|
|
15961
|
+
connected: false,
|
|
15962
|
+
error: preflightErrorMessage(error)
|
|
15963
|
+
})),
|
|
15964
|
+
http.get("/api/v2/billing/balance").catch(
|
|
15965
|
+
(error) => ({
|
|
15966
|
+
balance: null,
|
|
15967
|
+
balance_display: "unavailable",
|
|
15968
|
+
balance_status: "unknown",
|
|
15969
|
+
error: preflightErrorMessage(error)
|
|
15970
|
+
})
|
|
15971
|
+
)
|
|
15972
|
+
]) : [
|
|
15973
|
+
{
|
|
15974
|
+
status: "not_connected",
|
|
15975
|
+
connected: false,
|
|
15976
|
+
error: "No API key found. Run: deepline auth register"
|
|
15977
|
+
},
|
|
15978
|
+
{
|
|
15979
|
+
balance: null,
|
|
15980
|
+
balance_display: "unavailable until authenticated",
|
|
15981
|
+
balance_status: "unknown"
|
|
15982
|
+
}
|
|
15983
|
+
];
|
|
15984
|
+
const authStatus = pickString(auth, "status") ?? "unknown";
|
|
15985
|
+
const billingRecord = billing;
|
|
15986
|
+
const balanceDisplay = pickString(billing, "balance_display", "balanceDisplay") ?? `${String(billingRecord.balance ?? 0)} Deepline Credits`;
|
|
15987
|
+
const balanceStatus = pickString(billing, "balance_status", "balanceStatus") ?? "unknown";
|
|
15988
|
+
return {
|
|
15989
|
+
status: health.status === "ok" && (authStatus === "connected" || authStatus === "claimed") ? "ok" : "check",
|
|
15990
|
+
host: baseUrl,
|
|
15991
|
+
health: {
|
|
15992
|
+
status: health.status,
|
|
15993
|
+
version: health.version ?? null
|
|
15994
|
+
},
|
|
15995
|
+
auth: {
|
|
15996
|
+
status: authStatus,
|
|
15997
|
+
connected: authStatus === "connected" || authStatus === "claimed",
|
|
15998
|
+
org_id: pickString(auth, "org_id", "orgId"),
|
|
15999
|
+
org_name: pickString(auth, "org_name", "orgName"),
|
|
16000
|
+
rate_limit_tier: pickString(auth, "rate_limit_tier", "rateLimitTier"),
|
|
16001
|
+
error: pickString(auth, "error")
|
|
16002
|
+
},
|
|
16003
|
+
billing: {
|
|
16004
|
+
balance: billingRecord.balance ?? null,
|
|
16005
|
+
balance_display: balanceDisplay,
|
|
16006
|
+
rough_usd_balance: billingRecord.rough_usd_balance ?? billingRecord.roughUsdBalance ?? null,
|
|
16007
|
+
balance_status: balanceStatus,
|
|
16008
|
+
error: pickString(billing, "error")
|
|
16009
|
+
},
|
|
16010
|
+
render: {
|
|
16011
|
+
sections: [
|
|
16012
|
+
{
|
|
16013
|
+
title: "preflight",
|
|
16014
|
+
lines: [
|
|
16015
|
+
`host: ${baseUrl}`,
|
|
16016
|
+
`health: ${health.status}`,
|
|
16017
|
+
`auth: ${authStatus}`,
|
|
16018
|
+
`billing: ${balanceDisplay} (${balanceStatus})`
|
|
16019
|
+
]
|
|
16020
|
+
}
|
|
16021
|
+
]
|
|
16022
|
+
}
|
|
16023
|
+
};
|
|
16024
|
+
}
|
|
16025
|
+
function printPreflightHuman(data) {
|
|
16026
|
+
const render = data.render;
|
|
16027
|
+
const lines = render?.sections?.flatMap((section) => section.lines ?? []);
|
|
16028
|
+
if (lines?.length) {
|
|
16029
|
+
process.stdout.write(`${lines.join("\n")}
|
|
16030
|
+
`);
|
|
16031
|
+
return;
|
|
16032
|
+
}
|
|
16033
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
16034
|
+
`);
|
|
16035
|
+
}
|
|
15886
16036
|
async function main() {
|
|
15887
16037
|
const mainStartedAt = Date.now();
|
|
15888
16038
|
recordCliTrace({
|
|
@@ -15899,6 +16049,7 @@ async function main() {
|
|
|
15899
16049
|
"after",
|
|
15900
16050
|
`
|
|
15901
16051
|
Common commands:
|
|
16052
|
+
deepline preflight
|
|
15902
16053
|
deepline health
|
|
15903
16054
|
deepline auth status --json
|
|
15904
16055
|
deepline plays search email --json
|
|
@@ -15920,7 +16071,6 @@ Output:
|
|
|
15920
16071
|
|
|
15921
16072
|
Safety:
|
|
15922
16073
|
Commands that mutate state, open a browser, write files, stop work, or spend credits say so in their help.
|
|
15923
|
-
Use --no-open where available for CI and agent runs.
|
|
15924
16074
|
|
|
15925
16075
|
Exit codes:
|
|
15926
16076
|
0 success; 2 usage/local input error; 3 auth/permission error; 4 not found;
|
|
@@ -15962,6 +16112,26 @@ Exit codes:
|
|
|
15962
16112
|
registerDbCommands(program);
|
|
15963
16113
|
registerFeedbackCommands(program);
|
|
15964
16114
|
registerUpdateCommand(program);
|
|
16115
|
+
program.command("preflight").description("Run compact health, auth, and Deepline billing checks.").option("--json", "Force JSON output.").addHelpText(
|
|
16116
|
+
"after",
|
|
16117
|
+
`
|
|
16118
|
+
Notes:
|
|
16119
|
+
Read-only setup check for the configured Deepline host. Shows server health,
|
|
16120
|
+
auth connection, and customer-visible Deepline balance in one compact command.
|
|
16121
|
+
|
|
16122
|
+
Examples:
|
|
16123
|
+
deepline preflight
|
|
16124
|
+
deepline preflight --json
|
|
16125
|
+
`
|
|
16126
|
+
).action(async (options) => {
|
|
16127
|
+
const data = await runPreflightCheck();
|
|
16128
|
+
if (shouldEmitJson(options.json)) {
|
|
16129
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
16130
|
+
`);
|
|
16131
|
+
} else {
|
|
16132
|
+
printPreflightHuman(data);
|
|
16133
|
+
}
|
|
16134
|
+
});
|
|
15965
16135
|
program.command("health").description("Check server health.").option("--json", "Force JSON output.").option(
|
|
15966
16136
|
"--play-runner",
|
|
15967
16137
|
"Run a tiny no-provider play to verify the full play execution plane."
|