deepline 0.1.11 → 0.1.19
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 +18 -10
- package/dist/cli/index.js +1795 -1052
- package/dist/cli/index.mjs +1795 -1053
- package/dist/index.d.mts +427 -308
- package/dist/index.d.ts +427 -308
- package/dist/index.js +391 -326
- package/dist/index.mjs +391 -325
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +88 -22
- package/dist/repo/apps/play-runner-workers/src/entry.ts +804 -1253
- package/dist/repo/sdk/src/client.ts +287 -47
- package/dist/repo/sdk/src/config.ts +125 -8
- package/dist/repo/sdk/src/http.ts +10 -2
- package/dist/repo/sdk/src/index.ts +7 -16
- package/dist/repo/sdk/src/play.ts +105 -140
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +23 -6
- package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
- package/dist/repo/sdk/src/tool-output.ts +0 -146
- package/dist/repo/sdk/src/types.ts +27 -0
- package/dist/repo/sdk/src/version.ts +2 -2
- package/dist/repo/sdk/src/worker-play-entry.ts +3 -0
- package/dist/repo/shared_libs/play-runtime/csv-rename.ts +180 -0
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +250 -133
- package/dist/repo/shared_libs/plays/bundling/index.ts +274 -234
- package/dist/repo/shared_libs/plays/dataset.ts +29 -1
- package/package.json +5 -4
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
- package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
- package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +0 -184
- package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
- package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
- package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
- package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
- package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
- package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
- package/dist/repo/sdk/src/cli/commands/play.ts +0 -3307
- package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
- package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -341
- package/dist/repo/sdk/src/cli/index.ts +0 -148
- package/dist/repo/sdk/src/cli/progress.ts +0 -149
- package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
- package/dist/repo/sdk/src/cli/trace.ts +0 -61
- package/dist/repo/sdk/src/cli/utils.ts +0 -145
- package/dist/repo/sdk/src/compat.ts +0 -77
- package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
- package/dist/repo/shared_libs/observability/tracing.ts +0 -98
- package/dist/repo/shared_libs/play-runtime/context.ts +0 -3999
- package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -713
- package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
- package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
- package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
- package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
- package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
- package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
- package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
- package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
- package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
- package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
- package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
- package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
- package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
- package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
- package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
- package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
- package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
- package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
- package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
- package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
- package/dist/repo/shared_libs/plays/definition.ts +0 -264
- package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
- package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
- package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
- package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -415
- package/dist/repo/shared_libs/temporal/constants.ts +0 -39
- package/dist/repo/shared_libs/temporal/preview-config.ts +0 -153
package/dist/cli/index.js
CHANGED
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli/index.ts
|
|
27
|
-
var
|
|
27
|
+
var import_commander = require("commander");
|
|
28
28
|
|
|
29
29
|
// src/config.ts
|
|
30
30
|
var import_node_fs = require("fs");
|
|
@@ -70,6 +70,16 @@ var ConfigError = class extends DeeplineError {
|
|
|
70
70
|
var PROD_URL = "https://code.deepline.com";
|
|
71
71
|
var DEFAULT_TIMEOUT = 6e4;
|
|
72
72
|
var DEFAULT_MAX_RETRIES = 3;
|
|
73
|
+
var ACTIVE_DEEPLINE_ENV_FILE = ".env.deepline";
|
|
74
|
+
function isProdBaseUrl(baseUrl) {
|
|
75
|
+
return baseUrl.trim().replace(/\/$/, "") === PROD_URL;
|
|
76
|
+
}
|
|
77
|
+
function profileNameForBaseUrl(baseUrl) {
|
|
78
|
+
return isProdBaseUrl(baseUrl) ? "prod" : "dev";
|
|
79
|
+
}
|
|
80
|
+
function projectEnvStartDir() {
|
|
81
|
+
return process.env.DEEPLINE_PROJECT_ENV_DIR?.trim() || process.cwd();
|
|
82
|
+
}
|
|
73
83
|
function baseUrlSlug(baseUrl) {
|
|
74
84
|
let url;
|
|
75
85
|
try {
|
|
@@ -105,16 +115,52 @@ function parseEnvFile(filePath) {
|
|
|
105
115
|
}
|
|
106
116
|
return env;
|
|
107
117
|
}
|
|
108
|
-
function
|
|
118
|
+
function findNearestEnvFile(names, startDir = process.cwd()) {
|
|
109
119
|
let current = (0, import_node_path.resolve)(startDir);
|
|
110
120
|
while (true) {
|
|
111
|
-
const
|
|
112
|
-
|
|
121
|
+
for (const name of names) {
|
|
122
|
+
const filePath = (0, import_node_path.join)(current, name);
|
|
123
|
+
if ((0, import_node_fs.existsSync)(filePath)) return filePath;
|
|
124
|
+
}
|
|
113
125
|
const parent = (0, import_node_path.dirname)(current);
|
|
114
|
-
if (parent === current) return
|
|
126
|
+
if (parent === current) return null;
|
|
115
127
|
current = parent;
|
|
116
128
|
}
|
|
117
129
|
}
|
|
130
|
+
function findNearestEnv(names, startDir = process.cwd()) {
|
|
131
|
+
const filePath = findNearestEnvFile(names, startDir);
|
|
132
|
+
return filePath ? parseEnvFile(filePath) : {};
|
|
133
|
+
}
|
|
134
|
+
function findNearestWorktreeEnv(startDir = process.cwd()) {
|
|
135
|
+
return findNearestEnv([".env.worktree"], startDir);
|
|
136
|
+
}
|
|
137
|
+
function resolveProfileEnvFileNames() {
|
|
138
|
+
const explicitProfile = process.env.DEEPLINE_ENV_PROFILE?.trim() || process.env.DEEPLINE_PROFILE?.trim() || "";
|
|
139
|
+
const names = [];
|
|
140
|
+
if (explicitProfile) names.push(`.env.deepline.${explicitProfile}`);
|
|
141
|
+
const nodeEnv = process.env.NODE_ENV?.trim();
|
|
142
|
+
if (nodeEnv === "production") names.push(".env.deepline.prod");
|
|
143
|
+
else if (nodeEnv === "staging") names.push(".env.deepline.staging");
|
|
144
|
+
names.push(ACTIVE_DEEPLINE_ENV_FILE);
|
|
145
|
+
return names;
|
|
146
|
+
}
|
|
147
|
+
function resolveProjectAppEnvFileNames() {
|
|
148
|
+
const nodeEnv = process.env.NODE_ENV?.trim();
|
|
149
|
+
const names = [];
|
|
150
|
+
if (nodeEnv === "production") names.push(".env.prod");
|
|
151
|
+
if (nodeEnv === "staging") names.push(".env.staging");
|
|
152
|
+
names.push(".env.local", ".env");
|
|
153
|
+
return names;
|
|
154
|
+
}
|
|
155
|
+
function resolveBaseUrlFromEnvValues(env) {
|
|
156
|
+
return env.DEEPLINE_ORIGIN_URL?.trim() || env.DEEPLINE_API_BASE_URL?.trim() || "";
|
|
157
|
+
}
|
|
158
|
+
function loadProjectDeeplineEnv() {
|
|
159
|
+
return findNearestEnv(resolveProfileEnvFileNames(), projectEnvStartDir());
|
|
160
|
+
}
|
|
161
|
+
function loadProjectAppEnv() {
|
|
162
|
+
return findNearestEnv(resolveProjectAppEnvFileNames(), projectEnvStartDir());
|
|
163
|
+
}
|
|
118
164
|
function normalizeWorktreeBaseUrl(baseUrl, worktreeEnv = findNearestWorktreeEnv()) {
|
|
119
165
|
const trimmed = baseUrl.trim().replace(/\/$/, "");
|
|
120
166
|
if (!trimmed) return trimmed;
|
|
@@ -166,6 +212,10 @@ function autoDetectBaseUrl() {
|
|
|
166
212
|
if (envOrigin) return normalizeWorktreeBaseUrl(envOrigin);
|
|
167
213
|
const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
|
|
168
214
|
if (envBase) return normalizeWorktreeBaseUrl(envBase);
|
|
215
|
+
const projectDeeplineBaseUrl = resolveBaseUrlFromEnvValues(loadProjectDeeplineEnv());
|
|
216
|
+
if (projectDeeplineBaseUrl) return normalizeWorktreeBaseUrl(projectDeeplineBaseUrl);
|
|
217
|
+
const projectAppBaseUrl = resolveBaseUrlFromEnvValues(loadProjectAppEnv());
|
|
218
|
+
if (projectAppBaseUrl) return normalizeWorktreeBaseUrl(projectAppBaseUrl);
|
|
169
219
|
const worktreeBaseUrl = resolveWorktreeBaseUrl();
|
|
170
220
|
if (worktreeBaseUrl) return worktreeBaseUrl;
|
|
171
221
|
const globalEnv = loadGlobalCliEnv();
|
|
@@ -177,7 +227,9 @@ function resolveConfig(options) {
|
|
|
177
227
|
const requestedBaseUrl = options?.baseUrl?.trim() || autoDetectBaseUrl();
|
|
178
228
|
const baseUrl = normalizeWorktreeBaseUrl(requestedBaseUrl);
|
|
179
229
|
const cliEnv = loadCliEnv(baseUrl);
|
|
180
|
-
const
|
|
230
|
+
const projectDeeplineEnv = loadProjectDeeplineEnv();
|
|
231
|
+
const projectAppEnv = loadProjectAppEnv();
|
|
232
|
+
const apiKey = options?.apiKey?.trim() || process.env.DEEPLINE_API_KEY?.trim() || projectDeeplineEnv.DEEPLINE_API_KEY || projectAppEnv.DEEPLINE_API_KEY || cliEnv.DEEPLINE_API_KEY || "";
|
|
181
233
|
if (!apiKey) {
|
|
182
234
|
throw new ConfigError(
|
|
183
235
|
`No API key found. Set DEEPLINE_API_KEY env var, pass apiKey option, or run: deepline auth register`
|
|
@@ -190,10 +242,32 @@ function resolveConfig(options) {
|
|
|
190
242
|
maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
|
|
191
243
|
};
|
|
192
244
|
}
|
|
245
|
+
function mergeEnvFile(filePath, values) {
|
|
246
|
+
const existing = (0, import_node_fs.existsSync)(filePath) ? parseEnvFile(filePath) : {};
|
|
247
|
+
const merged = { ...existing, ...values };
|
|
248
|
+
const dir = (0, import_node_path.dirname)(filePath);
|
|
249
|
+
if (!(0, import_node_fs.existsSync)(dir)) (0, import_node_fs.mkdirSync)(dir, { recursive: true });
|
|
250
|
+
const lines = Object.entries(merged).filter(([, value]) => value !== "").map(([key, value]) => `${key}=${value}`);
|
|
251
|
+
(0, import_node_fs.writeFileSync)(filePath, `${lines.join("\n")}
|
|
252
|
+
`, "utf-8");
|
|
253
|
+
}
|
|
254
|
+
function saveProjectDeeplineEnvValues(baseUrl, values, startDir = projectEnvStartDir()) {
|
|
255
|
+
const root = (0, import_node_path.resolve)(startDir);
|
|
256
|
+
const profile = profileNameForBaseUrl(baseUrl);
|
|
257
|
+
const files = [
|
|
258
|
+
(0, import_node_path.join)(root, ACTIVE_DEEPLINE_ENV_FILE),
|
|
259
|
+
(0, import_node_path.join)(root, `.env.deepline.${profile}`)
|
|
260
|
+
];
|
|
261
|
+
if (profile === "dev") files.push((0, import_node_path.join)(root, ".env"));
|
|
262
|
+
for (const filePath of files) {
|
|
263
|
+
mergeEnvFile(filePath, values);
|
|
264
|
+
}
|
|
265
|
+
return files;
|
|
266
|
+
}
|
|
193
267
|
|
|
194
268
|
// src/version.ts
|
|
195
|
-
var SDK_VERSION = "0.1.
|
|
196
|
-
var SDK_API_CONTRACT = "2026-
|
|
269
|
+
var SDK_VERSION = "0.1.19";
|
|
270
|
+
var SDK_API_CONTRACT = "2026-05-runs-v2";
|
|
197
271
|
|
|
198
272
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
199
273
|
var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
|
|
@@ -386,8 +460,12 @@ var HttpClient = class {
|
|
|
386
460
|
* @param path - API path
|
|
387
461
|
* @param body - Request body (will be JSON-serialized)
|
|
388
462
|
*/
|
|
389
|
-
async post(path, body) {
|
|
390
|
-
return this.request(path, {
|
|
463
|
+
async post(path, body, headers) {
|
|
464
|
+
return this.request(path, {
|
|
465
|
+
method: "POST",
|
|
466
|
+
body,
|
|
467
|
+
headers
|
|
468
|
+
});
|
|
391
469
|
}
|
|
392
470
|
/**
|
|
393
471
|
* Send a DELETE request.
|
|
@@ -467,6 +545,10 @@ function sleep(ms) {
|
|
|
467
545
|
|
|
468
546
|
// src/client.ts
|
|
469
547
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
548
|
+
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
549
|
+
function isRecord(value) {
|
|
550
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
551
|
+
}
|
|
470
552
|
function normalizePlayStatus(raw) {
|
|
471
553
|
const status = typeof raw.status === "string" ? raw.status : typeof raw.temporalStatus === "string" ? mapLegacyTemporalStatus(raw.temporalStatus) : "running";
|
|
472
554
|
const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
|
|
@@ -496,6 +578,7 @@ function mapLegacyTemporalStatus(status) {
|
|
|
496
578
|
var DeeplineClient = class {
|
|
497
579
|
http;
|
|
498
580
|
config;
|
|
581
|
+
runs;
|
|
499
582
|
/**
|
|
500
583
|
* @param options - Optional overrides for API key, base URL, timeout, and retries.
|
|
501
584
|
* @throws {@link ConfigError} if no API key can be resolved from any source.
|
|
@@ -503,6 +586,13 @@ var DeeplineClient = class {
|
|
|
503
586
|
constructor(options) {
|
|
504
587
|
this.config = resolveConfig(options);
|
|
505
588
|
this.http = new HttpClient(this.config);
|
|
589
|
+
this.runs = {
|
|
590
|
+
get: (runId) => this.getRunStatus(runId),
|
|
591
|
+
list: (options2) => this.listRuns(options2),
|
|
592
|
+
tail: (runId, options2) => this.tailRun(runId, options2),
|
|
593
|
+
logs: (runId, options2) => this.getRunLogs(runId, options2),
|
|
594
|
+
stop: (runId, options2) => this.stopRun(runId, options2)
|
|
595
|
+
};
|
|
506
596
|
}
|
|
507
597
|
/** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
|
|
508
598
|
get baseUrl() {
|
|
@@ -519,12 +609,27 @@ var DeeplineClient = class {
|
|
|
519
609
|
).filter((field) => Boolean(field?.name)) : [];
|
|
520
610
|
return fields.length > 0 ? { fields } : schema;
|
|
521
611
|
}
|
|
522
|
-
|
|
523
|
-
|
|
612
|
+
schemaMetadata(schema, key) {
|
|
613
|
+
if (!isRecord(schema)) return null;
|
|
614
|
+
const value = schema[key];
|
|
615
|
+
return isRecord(value) ? value : null;
|
|
616
|
+
}
|
|
617
|
+
playRunCommand(play, options) {
|
|
618
|
+
const target = play.reference || play.name;
|
|
619
|
+
if (options?.csvInput) {
|
|
620
|
+
const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
|
|
621
|
+
return `deepline plays run ${target} --${inputField} leads.csv --watch`;
|
|
622
|
+
}
|
|
623
|
+
return `deepline plays run ${target} --input '{...}' --watch`;
|
|
524
624
|
}
|
|
525
625
|
summarizePlayListItem(play, options) {
|
|
526
626
|
const aliases = play.aliases?.length ? play.aliases : [play.name];
|
|
527
|
-
const
|
|
627
|
+
const csvInput = this.schemaMetadata(play.inputSchema, "csvInput");
|
|
628
|
+
const rowOutputSchema = this.schemaMetadata(
|
|
629
|
+
play.outputSchema,
|
|
630
|
+
"rowOutputSchema"
|
|
631
|
+
);
|
|
632
|
+
const runCommand = this.playRunCommand(play, { csvInput });
|
|
528
633
|
return {
|
|
529
634
|
name: play.name,
|
|
530
635
|
...play.reference ? { reference: play.reference } : {},
|
|
@@ -536,6 +641,8 @@ var DeeplineClient = class {
|
|
|
536
641
|
aliases,
|
|
537
642
|
inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
|
|
538
643
|
outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
|
|
644
|
+
...csvInput ? { csvInput } : {},
|
|
645
|
+
...rowOutputSchema ? { rowOutputSchema } : {},
|
|
539
646
|
runCommand,
|
|
540
647
|
examples: [runCommand],
|
|
541
648
|
currentPublishedVersion: play.currentPublishedVersion ?? null,
|
|
@@ -574,6 +681,31 @@ var DeeplineClient = class {
|
|
|
574
681
|
);
|
|
575
682
|
return res.tools;
|
|
576
683
|
}
|
|
684
|
+
/**
|
|
685
|
+
* Search available tools using Deepline's ranked backend search.
|
|
686
|
+
*
|
|
687
|
+
* This is the same discovery surface used by the legacy CLI: it ranks across
|
|
688
|
+
* tool metadata, categories, agent guidance, and input schema fields.
|
|
689
|
+
*/
|
|
690
|
+
async searchTools(options = {}) {
|
|
691
|
+
const params = new URLSearchParams();
|
|
692
|
+
const query = options.query?.trim() ?? "";
|
|
693
|
+
params.set("q", query);
|
|
694
|
+
params.set(
|
|
695
|
+
"include_search_debug",
|
|
696
|
+
options.includeSearchDebug ? "true" : "false"
|
|
697
|
+
);
|
|
698
|
+
params.set("search_mode", options.searchMode ?? "v2");
|
|
699
|
+
if (options.categories?.trim()) {
|
|
700
|
+
params.set("categories", options.categories.trim());
|
|
701
|
+
}
|
|
702
|
+
if (options.searchTerms?.trim()) {
|
|
703
|
+
params.set("search_terms", options.searchTerms.trim());
|
|
704
|
+
}
|
|
705
|
+
return this.http.get(
|
|
706
|
+
`/api/v2/integrations/list?${params.toString()}`
|
|
707
|
+
);
|
|
708
|
+
}
|
|
577
709
|
/**
|
|
578
710
|
* Get detailed metadata for a single tool.
|
|
579
711
|
*
|
|
@@ -602,55 +734,24 @@ var DeeplineClient = class {
|
|
|
602
734
|
);
|
|
603
735
|
}
|
|
604
736
|
/**
|
|
605
|
-
* Execute a tool and return the
|
|
606
|
-
*
|
|
607
|
-
* Sends the input payload to the tool and returns the `.result` field from the
|
|
608
|
-
* response. For the full response envelope (including job_id, credits, etc.),
|
|
609
|
-
* use {@link executeToolRaw}.
|
|
610
|
-
*
|
|
611
|
-
* @param toolId - Tool identifier (e.g. `"test_company_search"`)
|
|
612
|
-
* @param input - Tool-specific input parameters
|
|
613
|
-
* @returns The tool's output (shape varies by tool)
|
|
614
|
-
* @throws {@link DeeplineError} if the tool execution fails
|
|
615
|
-
*
|
|
616
|
-
* @example
|
|
617
|
-
* ```typescript
|
|
618
|
-
* const company = await client.executeTool('test_company_search', {
|
|
619
|
-
* domain: 'stripe.com',
|
|
620
|
-
* });
|
|
621
|
-
* console.log(company); // { name: "Stripe", industry: "Financial Services", ... }
|
|
622
|
-
* ```
|
|
623
|
-
*/
|
|
624
|
-
async executeTool(toolId, input) {
|
|
625
|
-
const res = await this.http.post(
|
|
626
|
-
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
627
|
-
{ payload: input }
|
|
628
|
-
);
|
|
629
|
-
return res.result ?? res;
|
|
630
|
-
}
|
|
631
|
-
/**
|
|
632
|
-
* Execute a tool and return the full response envelope.
|
|
633
|
-
*
|
|
634
|
-
* Unlike {@link executeTool}, this returns the complete API response including
|
|
635
|
-
* `job_id`, `status`, `credits`, and the raw `result` object.
|
|
737
|
+
* Execute a tool and return the standard execution envelope.
|
|
636
738
|
*
|
|
637
|
-
*
|
|
638
|
-
*
|
|
639
|
-
*
|
|
640
|
-
*
|
|
641
|
-
* @example
|
|
642
|
-
* ```typescript
|
|
643
|
-
* const raw = await client.executeToolRaw('test_company_search', { domain: 'stripe.com' });
|
|
644
|
-
* console.log(`Job: ${raw.job_id}, Credits: ${raw.credits}`);
|
|
645
|
-
* console.log(`Result:`, raw.result);
|
|
646
|
-
* ```
|
|
739
|
+
* The `result.data` field contains the provider payload. `result.meta`
|
|
740
|
+
* contains provider/upstream metadata such as HTTP status or paging details.
|
|
741
|
+
* Top-level fields such as `status`, `job_id`, and `billing` describe the
|
|
742
|
+
* Deepline execution.
|
|
647
743
|
*/
|
|
648
|
-
async
|
|
744
|
+
async executeTool(toolId, input, options) {
|
|
745
|
+
const headers = options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : void 0;
|
|
649
746
|
return this.http.post(
|
|
650
747
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
651
|
-
{ payload: input }
|
|
748
|
+
{ payload: input },
|
|
749
|
+
headers
|
|
652
750
|
);
|
|
653
751
|
}
|
|
752
|
+
async executeToolRaw(toolId, input, options) {
|
|
753
|
+
return this.executeTool(toolId, input, options);
|
|
754
|
+
}
|
|
654
755
|
async queryCustomerDb(input) {
|
|
655
756
|
return this.http.post("/api/v2/db/query", {
|
|
656
757
|
sql: input.sql,
|
|
@@ -699,6 +800,7 @@ var DeeplineClient = class {
|
|
|
699
800
|
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
700
801
|
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
701
802
|
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
803
|
+
...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
|
|
702
804
|
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
703
805
|
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
704
806
|
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
@@ -723,6 +825,7 @@ var DeeplineClient = class {
|
|
|
723
825
|
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
724
826
|
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
725
827
|
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
828
|
+
...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
|
|
726
829
|
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
727
830
|
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
728
831
|
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
@@ -759,6 +862,7 @@ var DeeplineClient = class {
|
|
|
759
862
|
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
760
863
|
name: input.name,
|
|
761
864
|
sourceCode: input.sourceCode,
|
|
865
|
+
sourceFiles: input.sourceFiles,
|
|
762
866
|
artifact: input.artifact
|
|
763
867
|
});
|
|
764
868
|
return this.http.post("/api/v2/plays/artifacts", {
|
|
@@ -773,6 +877,7 @@ var DeeplineClient = class {
|
|
|
773
877
|
compilerManifest: artifact.compilerManifest ?? await this.compilePlayManifest({
|
|
774
878
|
name: artifact.name,
|
|
775
879
|
sourceCode: artifact.sourceCode,
|
|
880
|
+
sourceFiles: artifact.sourceFiles,
|
|
776
881
|
artifact: artifact.artifact
|
|
777
882
|
})
|
|
778
883
|
}))
|
|
@@ -799,11 +904,13 @@ var DeeplineClient = class {
|
|
|
799
904
|
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
800
905
|
name: input.name,
|
|
801
906
|
sourceCode: input.sourceCode,
|
|
907
|
+
sourceFiles: input.sourceFiles,
|
|
802
908
|
artifact: input.artifact
|
|
803
909
|
});
|
|
804
910
|
const registeredArtifact = await this.registerPlayArtifact({
|
|
805
911
|
name: input.name,
|
|
806
912
|
sourceCode: input.sourceCode,
|
|
913
|
+
sourceFiles: input.sourceFiles,
|
|
807
914
|
artifact: input.artifact,
|
|
808
915
|
compilerManifest,
|
|
809
916
|
publish: false
|
|
@@ -863,11 +970,13 @@ var DeeplineClient = class {
|
|
|
863
970
|
const compilerManifest = options?.compilerManifest ?? await this.compilePlayManifest({
|
|
864
971
|
name,
|
|
865
972
|
sourceCode,
|
|
973
|
+
sourceFiles: options?.sourceFiles,
|
|
866
974
|
artifact
|
|
867
975
|
});
|
|
868
976
|
const registeredArtifact = await this.registerPlayArtifact({
|
|
869
977
|
name,
|
|
870
978
|
sourceCode,
|
|
979
|
+
sourceFiles: options?.sourceFiles,
|
|
871
980
|
artifact,
|
|
872
981
|
compilerManifest,
|
|
873
982
|
publish: false
|
|
@@ -1051,6 +1160,112 @@ var DeeplineClient = class {
|
|
|
1051
1160
|
);
|
|
1052
1161
|
return response.runs ?? [];
|
|
1053
1162
|
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Get a run by id using the public runs resource model.
|
|
1165
|
+
*
|
|
1166
|
+
* This is the SDK equivalent of:
|
|
1167
|
+
*
|
|
1168
|
+
* ```bash
|
|
1169
|
+
* deepline runs get <run-id> --json
|
|
1170
|
+
* ```
|
|
1171
|
+
*/
|
|
1172
|
+
async getRunStatus(runId) {
|
|
1173
|
+
const response = await this.http.get(
|
|
1174
|
+
`/api/v2/runs/${encodeURIComponent(runId)}`
|
|
1175
|
+
);
|
|
1176
|
+
return normalizePlayStatus(response);
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* List play runs using the public runs resource model.
|
|
1180
|
+
*
|
|
1181
|
+
* This is the SDK equivalent of:
|
|
1182
|
+
*
|
|
1183
|
+
* ```bash
|
|
1184
|
+
* deepline runs list --play <play-name> --status failed --json
|
|
1185
|
+
* ```
|
|
1186
|
+
*/
|
|
1187
|
+
async listRuns(options) {
|
|
1188
|
+
const playName = options.play.trim();
|
|
1189
|
+
if (!playName) {
|
|
1190
|
+
throw new Error("runs.list requires options.play.");
|
|
1191
|
+
}
|
|
1192
|
+
const params = new URLSearchParams({ play: playName });
|
|
1193
|
+
const status = options.status?.trim();
|
|
1194
|
+
if (status) {
|
|
1195
|
+
params.set("status", status);
|
|
1196
|
+
}
|
|
1197
|
+
const response = await this.http.get(
|
|
1198
|
+
`/api/v2/runs?${params.toString()}`
|
|
1199
|
+
);
|
|
1200
|
+
return response.runs ?? [];
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Fetch the lightweight tail status for a run using the public runs resource model.
|
|
1204
|
+
*
|
|
1205
|
+
* This is the SDK equivalent of:
|
|
1206
|
+
*
|
|
1207
|
+
* ```bash
|
|
1208
|
+
* deepline runs tail <run-id> --json
|
|
1209
|
+
* ```
|
|
1210
|
+
*/
|
|
1211
|
+
async tailRun(runId, options) {
|
|
1212
|
+
const afterLogIndex = typeof options?.afterLogIndex === "number" ? options.afterLogIndex : typeof options?.cursor === "number" ? options.cursor : typeof options?.cursor === "string" && options.cursor.trim() ? Number(options.cursor) : void 0;
|
|
1213
|
+
const params = new URLSearchParams();
|
|
1214
|
+
if (Number.isFinite(afterLogIndex)) {
|
|
1215
|
+
params.set("afterLogIndex", String(Number(afterLogIndex)));
|
|
1216
|
+
}
|
|
1217
|
+
if (typeof options?.waitMs === "number") {
|
|
1218
|
+
params.set("waitMs", String(options.waitMs));
|
|
1219
|
+
}
|
|
1220
|
+
if (options?.terminalOnly) {
|
|
1221
|
+
params.set("terminalOnly", "true");
|
|
1222
|
+
}
|
|
1223
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
1224
|
+
const response = await this.http.get(
|
|
1225
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/tail${suffix}`
|
|
1226
|
+
);
|
|
1227
|
+
return normalizePlayStatus(response);
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Fetch persisted logs for a run using the public runs resource model.
|
|
1231
|
+
*
|
|
1232
|
+
* This is the SDK equivalent of:
|
|
1233
|
+
*
|
|
1234
|
+
* ```bash
|
|
1235
|
+
* deepline runs logs <run-id> --limit 200 --json
|
|
1236
|
+
* ```
|
|
1237
|
+
*/
|
|
1238
|
+
async getRunLogs(runId, options) {
|
|
1239
|
+
const status = await this.getRunStatus(runId);
|
|
1240
|
+
const logs = status.progress?.logs ?? [];
|
|
1241
|
+
const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
|
|
1242
|
+
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
1243
|
+
return {
|
|
1244
|
+
runId: status.runId,
|
|
1245
|
+
totalCount: logs.length,
|
|
1246
|
+
returnedCount: entries.length,
|
|
1247
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
1248
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
1249
|
+
truncated: logs.length > entries.length,
|
|
1250
|
+
hasMore: logs.length > entries.length,
|
|
1251
|
+
entries
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Stop a run by id using the public runs resource model.
|
|
1256
|
+
*
|
|
1257
|
+
* This is the SDK equivalent of:
|
|
1258
|
+
*
|
|
1259
|
+
* ```bash
|
|
1260
|
+
* deepline runs stop <run-id> --reason "stale lock" --json
|
|
1261
|
+
* ```
|
|
1262
|
+
*/
|
|
1263
|
+
async stopRun(runId, options) {
|
|
1264
|
+
return this.http.post(
|
|
1265
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/stop`,
|
|
1266
|
+
options?.reason ? { reason: options.reason } : {}
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1054
1269
|
async listPlays() {
|
|
1055
1270
|
const response = await this.http.get(
|
|
1056
1271
|
"/api/v2/plays"
|
|
@@ -1437,6 +1652,7 @@ function saveEnvValues(values, baseUrl) {
|
|
|
1437
1652
|
const merged = { ...existing, ...values };
|
|
1438
1653
|
const lines = Object.entries(merged).filter(([, v]) => v !== "").map(([k, v]) => `${k}=${v}`);
|
|
1439
1654
|
(0, import_node_fs3.writeFileSync)(filePath, lines.join("\n") + "\n", "utf-8");
|
|
1655
|
+
saveProjectDeeplineEnvValues(baseUrl, values);
|
|
1440
1656
|
}
|
|
1441
1657
|
async function httpJson(method, url, apiKey, body) {
|
|
1442
1658
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -1976,24 +2192,70 @@ function registerBillingCommands(program) {
|
|
|
1976
2192
|
// src/cli/dataset-stats.ts
|
|
1977
2193
|
var import_node_fs4 = require("fs");
|
|
1978
2194
|
var import_node_path4 = require("path");
|
|
1979
|
-
|
|
2195
|
+
var CSV_PROJECTED_FIELDS_KEY = "__deeplineCsvProjectedFields";
|
|
2196
|
+
function csvProjectedFields(row) {
|
|
2197
|
+
const serialized = row[CSV_PROJECTED_FIELDS_KEY];
|
|
2198
|
+
if (!Array.isArray(serialized)) {
|
|
2199
|
+
return /* @__PURE__ */ new Set();
|
|
2200
|
+
}
|
|
2201
|
+
return new Set(
|
|
2202
|
+
serialized.filter((field) => typeof field === "string")
|
|
2203
|
+
);
|
|
2204
|
+
}
|
|
2205
|
+
function stripCsvProjectionFields(row) {
|
|
2206
|
+
const projectedFields = csvProjectedFields(row);
|
|
2207
|
+
if (projectedFields.size === 0 && !(CSV_PROJECTED_FIELDS_KEY in row)) {
|
|
2208
|
+
return row;
|
|
2209
|
+
}
|
|
2210
|
+
const stripped = { ...row };
|
|
2211
|
+
for (const field of projectedFields) {
|
|
2212
|
+
delete stripped[field];
|
|
2213
|
+
}
|
|
2214
|
+
delete stripped[CSV_PROJECTED_FIELDS_KEY];
|
|
2215
|
+
return stripped;
|
|
2216
|
+
}
|
|
2217
|
+
function stripCsvProjectionColumns(columns, rows) {
|
|
2218
|
+
const projectedFields = /* @__PURE__ */ new Set();
|
|
2219
|
+
let hasProjectionMetadata = false;
|
|
2220
|
+
for (const row of rows) {
|
|
2221
|
+
for (const field of csvProjectedFields(row)) {
|
|
2222
|
+
projectedFields.add(field);
|
|
2223
|
+
}
|
|
2224
|
+
hasProjectionMetadata ||= CSV_PROJECTED_FIELDS_KEY in row;
|
|
2225
|
+
}
|
|
2226
|
+
if (!hasProjectionMetadata && projectedFields.size === 0) {
|
|
2227
|
+
return columns;
|
|
2228
|
+
}
|
|
2229
|
+
return columns.filter(
|
|
2230
|
+
(column) => column !== CSV_PROJECTED_FIELDS_KEY && !projectedFields.has(column)
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
function sanitizeCsvProjectionInfo(input) {
|
|
2234
|
+
const columns = stripCsvProjectionColumns(input.columns, input.rows);
|
|
2235
|
+
const rows = input.rows.map(stripCsvProjectionFields);
|
|
2236
|
+
return { rows, columns };
|
|
2237
|
+
}
|
|
2238
|
+
function isRecord2(value) {
|
|
1980
2239
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1981
2240
|
}
|
|
1982
2241
|
function isSerializedDataset(value) {
|
|
1983
|
-
return
|
|
2242
|
+
return isRecord2(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
|
|
1984
2243
|
}
|
|
1985
2244
|
function rowArray(value) {
|
|
1986
2245
|
if (!Array.isArray(value)) {
|
|
1987
2246
|
return null;
|
|
1988
2247
|
}
|
|
1989
2248
|
const rows = value.filter(
|
|
1990
|
-
(row) =>
|
|
2249
|
+
(row) => isRecord2(row)
|
|
1991
2250
|
);
|
|
1992
2251
|
return rows.length === value.length ? rows : null;
|
|
1993
2252
|
}
|
|
1994
2253
|
function readNumber(value) {
|
|
1995
2254
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
|
|
1996
2255
|
}
|
|
2256
|
+
function numericStat(value) {
|
|
2257
|
+
return readNumber(value) ?? 0;
|
|
2258
|
+
}
|
|
1997
2259
|
function inferColumns(rows) {
|
|
1998
2260
|
const columns = [];
|
|
1999
2261
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2011,12 +2273,12 @@ function inferColumns(rows) {
|
|
|
2011
2273
|
return columns;
|
|
2012
2274
|
}
|
|
2013
2275
|
function extractCanonicalRowsInfo(statusOrResult) {
|
|
2014
|
-
const root =
|
|
2015
|
-
const result =
|
|
2276
|
+
const root = isRecord2(statusOrResult) ? statusOrResult : null;
|
|
2277
|
+
const result = isRecord2(root?.result) ? root.result : root;
|
|
2016
2278
|
if (!result) {
|
|
2017
2279
|
return null;
|
|
2018
2280
|
}
|
|
2019
|
-
const metadata =
|
|
2281
|
+
const metadata = isRecord2(result._metadata) ? result._metadata : null;
|
|
2020
2282
|
const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
|
|
2021
2283
|
const candidates = [
|
|
2022
2284
|
{ source: "result.contacts", value: result.contacts, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
|
|
@@ -2024,8 +2286,8 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2024
2286
|
{ source: "result.rows", value: result.rows, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
|
|
2025
2287
|
{ source: "result.results", value: result.results, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count }
|
|
2026
2288
|
];
|
|
2027
|
-
if (
|
|
2028
|
-
const outputMetadata =
|
|
2289
|
+
if (isRecord2(result.output)) {
|
|
2290
|
+
const outputMetadata = isRecord2(result.output._metadata) ? result.output._metadata : null;
|
|
2029
2291
|
const outputTotalFromMetadata = outputMetadata?.totalRows ?? outputMetadata?.rowCount ?? outputMetadata?.count;
|
|
2030
2292
|
candidates.push(
|
|
2031
2293
|
{ source: "result.output.contacts", value: result.output.contacts, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count },
|
|
@@ -2036,12 +2298,17 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2036
2298
|
}
|
|
2037
2299
|
for (const candidate of candidates) {
|
|
2038
2300
|
if (isSerializedDataset(candidate.value)) {
|
|
2039
|
-
const
|
|
2040
|
-
const totalRows2 = readNumber(candidate.value.count) ??
|
|
2301
|
+
const rawRows = rowArray(candidate.value.preview) ?? [];
|
|
2302
|
+
const totalRows2 = readNumber(candidate.value.count) ?? rawRows.length;
|
|
2303
|
+
const rawColumns = Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string") ? candidate.value.columns : inferColumns(rawRows);
|
|
2304
|
+
const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
|
|
2305
|
+
rows: rawRows,
|
|
2306
|
+
columns: rawColumns
|
|
2307
|
+
});
|
|
2041
2308
|
return {
|
|
2042
2309
|
rows: rows2,
|
|
2043
2310
|
totalRows: totalRows2,
|
|
2044
|
-
columns
|
|
2311
|
+
columns,
|
|
2045
2312
|
complete: rows2.length === totalRows2,
|
|
2046
2313
|
source: candidate.source
|
|
2047
2314
|
};
|
|
@@ -2051,10 +2318,14 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2051
2318
|
continue;
|
|
2052
2319
|
}
|
|
2053
2320
|
const totalRows = readNumber(candidate.total) ?? rows.length;
|
|
2054
|
-
|
|
2321
|
+
const sanitized = sanitizeCsvProjectionInfo({
|
|
2055
2322
|
rows,
|
|
2323
|
+
columns: inferColumns(rows)
|
|
2324
|
+
});
|
|
2325
|
+
return {
|
|
2326
|
+
rows: sanitized.rows,
|
|
2056
2327
|
totalRows,
|
|
2057
|
-
columns:
|
|
2328
|
+
columns: sanitized.columns,
|
|
2058
2329
|
complete: rows.length === totalRows,
|
|
2059
2330
|
source: candidate.source
|
|
2060
2331
|
};
|
|
@@ -2064,6 +2335,31 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2064
2335
|
function percentText(numerator, denominator) {
|
|
2065
2336
|
return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
|
|
2066
2337
|
}
|
|
2338
|
+
function isDatasetExecutionStatsInput(value) {
|
|
2339
|
+
return isRecord2(value) && isRecord2(value.columnStats) && Object.values(value.columnStats).every(isRecord2);
|
|
2340
|
+
}
|
|
2341
|
+
function extractDatasetExecutionStats(statusOrResult) {
|
|
2342
|
+
if (!isRecord2(statusOrResult)) {
|
|
2343
|
+
return null;
|
|
2344
|
+
}
|
|
2345
|
+
const direct = statusOrResult.dataset_execution_stats;
|
|
2346
|
+
if (isDatasetExecutionStatsInput(direct)) {
|
|
2347
|
+
return direct;
|
|
2348
|
+
}
|
|
2349
|
+
const nested = isRecord2(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
|
|
2350
|
+
return isDatasetExecutionStatsInput(nested) ? nested : null;
|
|
2351
|
+
}
|
|
2352
|
+
function formatExecutionStats(raw, denominator) {
|
|
2353
|
+
return {
|
|
2354
|
+
queued: percentText(numericStat(raw.queued), denominator),
|
|
2355
|
+
running: percentText(numericStat(raw.running), denominator),
|
|
2356
|
+
"completed:executed": percentText(numericStat(raw.completed), denominator),
|
|
2357
|
+
"completed:reused": percentText(numericStat(raw.cached), denominator),
|
|
2358
|
+
"skipped:condition": percentText(numericStat(raw.skipped), denominator),
|
|
2359
|
+
"skipped:missed": percentText(numericStat(raw.missed), denominator),
|
|
2360
|
+
failed: percentText(numericStat(raw.failed), denominator)
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2067
2363
|
function countPercentText(count, denominator) {
|
|
2068
2364
|
return denominator > 0 ? `${count} (${Math.round(100 * count / denominator)}%)` : "0 (0%)";
|
|
2069
2365
|
}
|
|
@@ -2102,13 +2398,13 @@ function summarizeSampleValue(value, depth = 0) {
|
|
|
2102
2398
|
if (typeof parsed === "number" || typeof parsed === "boolean") return parsed;
|
|
2103
2399
|
if (depth >= 3) {
|
|
2104
2400
|
if (Array.isArray(parsed)) return [];
|
|
2105
|
-
if (
|
|
2401
|
+
if (isRecord2(parsed)) return {};
|
|
2106
2402
|
return compactScalar(parsed);
|
|
2107
2403
|
}
|
|
2108
2404
|
if (Array.isArray(parsed)) {
|
|
2109
2405
|
return parsed.slice(0, 3).map((item) => summarizeSampleValue(item, depth + 1));
|
|
2110
2406
|
}
|
|
2111
|
-
if (
|
|
2407
|
+
if (isRecord2(parsed)) {
|
|
2112
2408
|
const out = {};
|
|
2113
2409
|
for (const [key, nested] of Object.entries(parsed)) {
|
|
2114
2410
|
if (["__dl", "meta", "metadata"].includes(key)) {
|
|
@@ -2138,7 +2434,7 @@ function compactCell(value) {
|
|
|
2138
2434
|
}
|
|
2139
2435
|
return `[${parsed.length} items]`;
|
|
2140
2436
|
}
|
|
2141
|
-
if (
|
|
2437
|
+
if (isRecord2(parsed)) {
|
|
2142
2438
|
for (const key of ["matched_result", "output"]) {
|
|
2143
2439
|
if (parsed[key] !== null && parsed[key] !== void 0 && parsed[key] !== "") {
|
|
2144
2440
|
return compactCell(parsed[key]);
|
|
@@ -2156,15 +2452,16 @@ function compactCell(value) {
|
|
|
2156
2452
|
}
|
|
2157
2453
|
return compactScalar(parsed, 120);
|
|
2158
2454
|
}
|
|
2159
|
-
function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows)) {
|
|
2455
|
+
function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows), executionStats) {
|
|
2456
|
+
const sanitized = sanitizeCsvProjectionInfo({ rows, columns });
|
|
2160
2457
|
const columnStats = {};
|
|
2161
|
-
for (const column of columns) {
|
|
2458
|
+
for (const column of sanitized.columns) {
|
|
2162
2459
|
let nonEmpty = 0;
|
|
2163
2460
|
let empty = 0;
|
|
2164
2461
|
let sampleValue;
|
|
2165
2462
|
let sampleValueType = null;
|
|
2166
2463
|
const valueCounts = /* @__PURE__ */ new Map();
|
|
2167
|
-
for (const row of rows) {
|
|
2464
|
+
for (const row of sanitized.rows) {
|
|
2168
2465
|
const raw = row[column];
|
|
2169
2466
|
const value = compactCell(raw);
|
|
2170
2467
|
if (value) {
|
|
@@ -2183,6 +2480,10 @@ function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns
|
|
|
2183
2480
|
non_empty: percentText(nonEmpty, denominator),
|
|
2184
2481
|
unique: valueCounts.size
|
|
2185
2482
|
};
|
|
2483
|
+
const rawExecutionStats = executionStats?.columnStats[column];
|
|
2484
|
+
if (rawExecutionStats) {
|
|
2485
|
+
stat3.execution = formatExecutionStats(rawExecutionStats, totalRows);
|
|
2486
|
+
}
|
|
2186
2487
|
if (sampleValue !== void 0 && sampleValueType) {
|
|
2187
2488
|
stat3.sample_value = sampleValue;
|
|
2188
2489
|
stat3.sample_type = sampleValueType;
|
|
@@ -2216,10 +2517,14 @@ function writeCanonicalRowsCsv(rowsInfo, outPath) {
|
|
|
2216
2517
|
`Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; cannot export a complete CSV from this status payload yet.`
|
|
2217
2518
|
);
|
|
2218
2519
|
}
|
|
2520
|
+
const sanitized = sanitizeCsvProjectionInfo({
|
|
2521
|
+
rows: rowsInfo.rows,
|
|
2522
|
+
columns: rowsInfo.columns
|
|
2523
|
+
});
|
|
2219
2524
|
const resolved = (0, import_node_path4.resolve)(outPath);
|
|
2220
2525
|
(0, import_node_fs4.writeFileSync)(
|
|
2221
2526
|
resolved,
|
|
2222
|
-
csvStringFromRows(
|
|
2527
|
+
csvStringFromRows(sanitized.rows, sanitized.columns),
|
|
2223
2528
|
"utf-8"
|
|
2224
2529
|
);
|
|
2225
2530
|
return resolved;
|
|
@@ -2511,7 +2816,6 @@ function registerOrgCommands(program) {
|
|
|
2511
2816
|
var import_node_crypto3 = require("crypto");
|
|
2512
2817
|
var import_node_fs6 = require("fs");
|
|
2513
2818
|
var import_node_path8 = require("path");
|
|
2514
|
-
var import_commander = require("commander");
|
|
2515
2819
|
|
|
2516
2820
|
// src/plays/bundle-play-file.ts
|
|
2517
2821
|
var import_node_os5 = require("os");
|
|
@@ -2526,7 +2830,6 @@ var import_node_os4 = require("os");
|
|
|
2526
2830
|
var import_node_path5 = require("path");
|
|
2527
2831
|
var import_node_module = require("module");
|
|
2528
2832
|
var import_esbuild = require("esbuild");
|
|
2529
|
-
var import_typescript = __toESM(require("typescript"));
|
|
2530
2833
|
|
|
2531
2834
|
// ../shared_libs/play-runtime/backend.ts
|
|
2532
2835
|
var PLAY_RUNTIME_BACKENDS = {
|
|
@@ -2596,6 +2899,14 @@ var PLAY_SOURCE_FILE_PATTERN = /\.play\.(?:[cm]?[jt]sx?)$/i;
|
|
|
2596
2899
|
var NODE_BUILTIN_SET = new Set(
|
|
2597
2900
|
import_node_module.builtinModules.flatMap((name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`])
|
|
2598
2901
|
);
|
|
2902
|
+
function assertValidExportName(exportName) {
|
|
2903
|
+
if (exportName === "default") return;
|
|
2904
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(exportName)) {
|
|
2905
|
+
throw new Error(
|
|
2906
|
+
`Invalid play export name "${exportName}". Named prebuilt exports must be valid JavaScript identifiers.`
|
|
2907
|
+
);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2599
2910
|
function sha256(value) {
|
|
2600
2911
|
return (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
|
|
2601
2912
|
}
|
|
@@ -2603,56 +2914,6 @@ function formatEsbuildMessage(message) {
|
|
|
2603
2914
|
const location = message.location ? `${message.location.file}:${message.location.line}:${message.location.column}` : null;
|
|
2604
2915
|
return location ? `${location} ${message.text}` : message.text;
|
|
2605
2916
|
}
|
|
2606
|
-
function formatTypeScriptDiagnostic(diagnostic) {
|
|
2607
|
-
if (!diagnostic.file) {
|
|
2608
|
-
const message2 = import_typescript.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n").trim();
|
|
2609
|
-
return message2 || null;
|
|
2610
|
-
}
|
|
2611
|
-
const start = diagnostic.start ?? 0;
|
|
2612
|
-
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(start);
|
|
2613
|
-
const message = import_typescript.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n").trim();
|
|
2614
|
-
if (!message) {
|
|
2615
|
-
return null;
|
|
2616
|
-
}
|
|
2617
|
-
return `${diagnostic.file.fileName}:${line + 1}:${character + 1} ${message}`;
|
|
2618
|
-
}
|
|
2619
|
-
function resolveBundledTypeRoots() {
|
|
2620
|
-
try {
|
|
2621
|
-
return [(0, import_node_path5.dirname)((0, import_node_path5.dirname)(playArtifactRequire.resolve("@types/node/package.json")))];
|
|
2622
|
-
} catch {
|
|
2623
|
-
return [];
|
|
2624
|
-
}
|
|
2625
|
-
}
|
|
2626
|
-
function typecheckPlaySource(input, adapter) {
|
|
2627
|
-
const rootNames = Array.from(
|
|
2628
|
-
/* @__PURE__ */ new Set([
|
|
2629
|
-
...input.importPolicy.localFiles,
|
|
2630
|
-
...input.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
2631
|
-
])
|
|
2632
|
-
);
|
|
2633
|
-
const sdkTypesPath = adapter.sdkTypesEntryFile ?? adapter.sdkEntryFile;
|
|
2634
|
-
const program = import_typescript.default.createProgram(rootNames, {
|
|
2635
|
-
target: import_typescript.default.ScriptTarget.ES2023,
|
|
2636
|
-
// SDK source uses fetch/RequestInit/URL and node-aware config helpers.
|
|
2637
|
-
// The play runtime import policy below still bans Node modules from play
|
|
2638
|
-
// source for workers_edge bundles.
|
|
2639
|
-
lib: ["lib.es2023.d.ts", "lib.dom.d.ts"],
|
|
2640
|
-
module: import_typescript.default.ModuleKind.ESNext,
|
|
2641
|
-
moduleResolution: import_typescript.default.ModuleResolutionKind.Bundler,
|
|
2642
|
-
paths: { deepline: [sdkTypesPath] },
|
|
2643
|
-
strict: true,
|
|
2644
|
-
skipLibCheck: true,
|
|
2645
|
-
noEmit: true,
|
|
2646
|
-
esModuleInterop: true,
|
|
2647
|
-
allowSyntheticDefaultImports: true,
|
|
2648
|
-
allowImportingTsExtensions: true,
|
|
2649
|
-
allowJs: true,
|
|
2650
|
-
resolveJsonModule: true,
|
|
2651
|
-
types: ["node"],
|
|
2652
|
-
typeRoots: resolveBundledTypeRoots()
|
|
2653
|
-
});
|
|
2654
|
-
return import_typescript.default.getPreEmitDiagnostics(program).map(formatTypeScriptDiagnostic).filter((message) => Boolean(message));
|
|
2655
|
-
}
|
|
2656
2917
|
function isLocalSpecifier(specifier) {
|
|
2657
2918
|
return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/") || specifier.startsWith("file:");
|
|
2658
2919
|
}
|
|
@@ -2676,11 +2937,8 @@ function assertWithinPlayWorkspace(input) {
|
|
|
2676
2937
|
if (isPathInsideDirectory(input.resolvedPath, input.workspace.rootDir)) {
|
|
2677
2938
|
return;
|
|
2678
2939
|
}
|
|
2679
|
-
const position = input.sourceFile.getLineAndCharacterOfPosition(
|
|
2680
|
-
input.node.getStart(input.sourceFile)
|
|
2681
|
-
);
|
|
2682
2940
|
throw new Error(
|
|
2683
|
-
`${input.importer}:${
|
|
2941
|
+
`${input.importer}:${input.line}:${input.column} Local play imports must stay inside the play workspace (${input.workspace.rootDir}). Import "${input.specifier}" resolved to ${input.resolvedPath}, which crosses into app/backend code. Use the public SDK/API surface or move shared helpers into the play workspace.`
|
|
2684
2942
|
);
|
|
2685
2943
|
}
|
|
2686
2944
|
function getPackageName(specifier) {
|
|
@@ -2690,72 +2948,135 @@ function getPackageName(specifier) {
|
|
|
2690
2948
|
}
|
|
2691
2949
|
return specifier.split("/")[0] ?? specifier;
|
|
2692
2950
|
}
|
|
2693
|
-
function scriptKindForFile(filePath) {
|
|
2694
|
-
const extension = (0, import_node_path5.extname)(filePath).toLowerCase();
|
|
2695
|
-
switch (extension) {
|
|
2696
|
-
case ".tsx":
|
|
2697
|
-
return import_typescript.default.ScriptKind.TSX;
|
|
2698
|
-
case ".jsx":
|
|
2699
|
-
return import_typescript.default.ScriptKind.JSX;
|
|
2700
|
-
case ".js":
|
|
2701
|
-
case ".mjs":
|
|
2702
|
-
case ".cjs":
|
|
2703
|
-
return import_typescript.default.ScriptKind.JS;
|
|
2704
|
-
case ".json":
|
|
2705
|
-
return import_typescript.default.ScriptKind.JSON;
|
|
2706
|
-
default:
|
|
2707
|
-
return import_typescript.default.ScriptKind.TS;
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
2951
|
function isPlaySourceFile(filePath) {
|
|
2711
2952
|
return PLAY_SOURCE_FILE_PATTERN.test(filePath);
|
|
2712
2953
|
}
|
|
2713
|
-
function
|
|
2714
|
-
|
|
2715
|
-
|
|
2954
|
+
function stripCommentsToSpaces(source) {
|
|
2955
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
|
|
2956
|
+
/(^|[^:])\/\/.*$/gm,
|
|
2957
|
+
(match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
|
|
2958
|
+
);
|
|
2959
|
+
}
|
|
2960
|
+
function lineAndColumnAt(source, index) {
|
|
2961
|
+
const prefix = source.slice(0, index);
|
|
2962
|
+
const lines = prefix.split("\n");
|
|
2963
|
+
return { line: lines.length, column: lines[lines.length - 1].length + 1 };
|
|
2964
|
+
}
|
|
2965
|
+
function findSourceImportReferences(sourceCode) {
|
|
2966
|
+
const source = stripCommentsToSpaces(sourceCode);
|
|
2967
|
+
const references = [];
|
|
2968
|
+
const addReference = (specifier, specifierIndex, kind) => {
|
|
2969
|
+
if (!specifier) return;
|
|
2970
|
+
const position = lineAndColumnAt(sourceCode, specifierIndex);
|
|
2971
|
+
references.push({
|
|
2972
|
+
specifier,
|
|
2973
|
+
line: position.line,
|
|
2974
|
+
column: position.column,
|
|
2975
|
+
kind
|
|
2976
|
+
});
|
|
2977
|
+
};
|
|
2978
|
+
const staticImportPattern = /\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?(['"])([^'"\n]+)\1/g;
|
|
2979
|
+
for (const match of source.matchAll(staticImportPattern)) {
|
|
2980
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "static");
|
|
2981
|
+
}
|
|
2982
|
+
const dynamicImportPattern = /\bimport\s*\(\s*(['"])([^'"\n]+)\1/g;
|
|
2983
|
+
for (const match of source.matchAll(dynamicImportPattern)) {
|
|
2984
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "dynamic-import");
|
|
2985
|
+
}
|
|
2986
|
+
const requirePattern = /\brequire\s*\(\s*(['"])([^'"\n]+)\1/g;
|
|
2987
|
+
for (const match of source.matchAll(requirePattern)) {
|
|
2988
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "require");
|
|
2989
|
+
}
|
|
2990
|
+
const literalDynamicImportIndexes = new Set(
|
|
2991
|
+
[...source.matchAll(dynamicImportPattern)].map((match) => match.index)
|
|
2992
|
+
);
|
|
2993
|
+
for (const match of source.matchAll(/\bimport\s*\(/g)) {
|
|
2994
|
+
if (literalDynamicImportIndexes.has(match.index)) continue;
|
|
2995
|
+
const position = lineAndColumnAt(sourceCode, match.index);
|
|
2996
|
+
throw new Error(
|
|
2997
|
+
`:${position.line}:${position.column} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
2998
|
+
);
|
|
2999
|
+
}
|
|
3000
|
+
const literalRequireIndexes = new Set(
|
|
3001
|
+
[...source.matchAll(requirePattern)].map((match) => match.index)
|
|
3002
|
+
);
|
|
3003
|
+
for (const match of source.matchAll(/\brequire\s*\(/g)) {
|
|
3004
|
+
if (literalRequireIndexes.has(match.index)) continue;
|
|
3005
|
+
const position = lineAndColumnAt(sourceCode, match.index);
|
|
3006
|
+
throw new Error(
|
|
3007
|
+
`:${position.line}:${position.column} Dynamic require() is not allowed in plays. Use static imports or require("literal") only.`
|
|
3008
|
+
);
|
|
3009
|
+
}
|
|
3010
|
+
return references.sort(
|
|
3011
|
+
(left, right) => left.line === right.line ? left.column - right.column : left.line - right.line
|
|
3012
|
+
);
|
|
3013
|
+
}
|
|
3014
|
+
function unquoteStringLiteral(literal) {
|
|
3015
|
+
const trimmed = literal.trim();
|
|
3016
|
+
const quote = trimmed[0];
|
|
3017
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
3018
|
+
return null;
|
|
3019
|
+
}
|
|
3020
|
+
try {
|
|
3021
|
+
return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
|
|
3022
|
+
} catch {
|
|
3023
|
+
return trimmed.slice(1, -1);
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
function findMatchingBrace(source, openIndex) {
|
|
3027
|
+
let depth = 0;
|
|
3028
|
+
let quote = null;
|
|
3029
|
+
let escaped = false;
|
|
3030
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
3031
|
+
const char = source[index];
|
|
3032
|
+
if (quote) {
|
|
3033
|
+
if (escaped) {
|
|
3034
|
+
escaped = false;
|
|
3035
|
+
} else if (char === "\\") {
|
|
3036
|
+
escaped = true;
|
|
3037
|
+
} else if (char === quote) {
|
|
3038
|
+
quote = null;
|
|
3039
|
+
}
|
|
2716
3040
|
continue;
|
|
2717
3041
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
if (!matches) {
|
|
3042
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3043
|
+
quote = char;
|
|
2721
3044
|
continue;
|
|
2722
3045
|
}
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
)
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
const
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
3046
|
+
if (char === "{") depth += 1;
|
|
3047
|
+
if (char === "}") {
|
|
3048
|
+
depth -= 1;
|
|
3049
|
+
if (depth === 0) return index;
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
return -1;
|
|
3053
|
+
}
|
|
3054
|
+
function extractDefinedPlayName(sourceCode, _filePath) {
|
|
3055
|
+
const source = stripCommentsToSpaces(sourceCode);
|
|
3056
|
+
const callPattern = /(?:\b[A-Za-z_$][\w$]*\s*\.\s*)?\b(?:definePlay|defineWorkflow)\s*\(/g;
|
|
3057
|
+
for (const match of source.matchAll(callPattern)) {
|
|
3058
|
+
const openParen = match.index + match[0].length - 1;
|
|
3059
|
+
const firstArgStart = openParen + 1;
|
|
3060
|
+
const firstNonSpace = source.slice(firstArgStart).search(/\S/);
|
|
3061
|
+
if (firstNonSpace < 0) continue;
|
|
3062
|
+
const argIndex = firstArgStart + firstNonSpace;
|
|
3063
|
+
const quote = source[argIndex];
|
|
3064
|
+
if (quote === '"' || quote === "'") {
|
|
3065
|
+
const literalMatch = source.slice(argIndex).match(/^(['"])(?:\\.|(?!\1)[\s\S])*\1/);
|
|
3066
|
+
const value = literalMatch ? unquoteStringLiteral(literalMatch[0]) : null;
|
|
3067
|
+
if (value?.trim()) return value.trim();
|
|
3068
|
+
}
|
|
3069
|
+
if (quote === "{") {
|
|
3070
|
+
const closeBrace = findMatchingBrace(source, argIndex);
|
|
3071
|
+
if (closeBrace < 0) continue;
|
|
3072
|
+
const objectSource = source.slice(argIndex + 1, closeBrace);
|
|
3073
|
+
const idMatch = objectSource.match(/(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/);
|
|
3074
|
+
if (idMatch?.[2]?.trim()) {
|
|
3075
|
+
return idMatch[2].trim();
|
|
2753
3076
|
}
|
|
2754
3077
|
}
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
visit(sourceFile);
|
|
2758
|
-
return detectedPlayName;
|
|
3078
|
+
}
|
|
3079
|
+
return null;
|
|
2759
3080
|
}
|
|
2760
3081
|
function getPackageRequireCandidates(fromFile) {
|
|
2761
3082
|
const candidates = [
|
|
@@ -2790,6 +3111,29 @@ function workersPlayEntryAliasPlugin(playFilePath) {
|
|
|
2790
3111
|
}
|
|
2791
3112
|
};
|
|
2792
3113
|
}
|
|
3114
|
+
function workersNamedPlayEntryAliasPlugin(playFilePath, exportName) {
|
|
3115
|
+
return {
|
|
3116
|
+
name: "deepline-workers-named-play-entry-alias",
|
|
3117
|
+
setup(buildContext) {
|
|
3118
|
+
buildContext.onResolve(
|
|
3119
|
+
{ filter: new RegExp(`^${WORKERS_PLAY_ENTRY_VIRTUAL}$`) },
|
|
3120
|
+
() => ({
|
|
3121
|
+
path: `${playFilePath}.${exportName}.entry.ts`,
|
|
3122
|
+
namespace: "deepline-named-play-entry"
|
|
3123
|
+
})
|
|
3124
|
+
);
|
|
3125
|
+
buildContext.onLoad(
|
|
3126
|
+
{ filter: /.*/, namespace: "deepline-named-play-entry" },
|
|
3127
|
+
() => ({
|
|
3128
|
+
contents: `export { ${exportName} as default } from ${JSON.stringify(playFilePath)};
|
|
3129
|
+
`,
|
|
3130
|
+
loader: "ts",
|
|
3131
|
+
resolveDir: (0, import_node_path5.dirname)(playFilePath)
|
|
3132
|
+
})
|
|
3133
|
+
);
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
}
|
|
2793
3137
|
function workersNodeBuiltinStubPlugin() {
|
|
2794
3138
|
const UNSUPPORTED = /* @__PURE__ */ new Set(["node:fs", "node:fs/promises", "node:os", "node:child_process"]);
|
|
2795
3139
|
return {
|
|
@@ -3042,18 +3386,10 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3042
3386
|
if ((0, import_node_path5.extname)(absolutePath).toLowerCase() === ".json") {
|
|
3043
3387
|
return;
|
|
3044
3388
|
}
|
|
3045
|
-
const
|
|
3046
|
-
absolutePath,
|
|
3047
|
-
sourceCode2,
|
|
3048
|
-
import_typescript.default.ScriptTarget.Latest,
|
|
3049
|
-
true,
|
|
3050
|
-
scriptKindForFile(absolutePath)
|
|
3051
|
-
);
|
|
3052
|
-
const handleSpecifier = async (specifier, node, kind) => {
|
|
3389
|
+
const handleSpecifier = async (specifier, line, column, kind) => {
|
|
3053
3390
|
if (kind === "dynamic-import") {
|
|
3054
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3055
3391
|
throw new Error(
|
|
3056
|
-
`${absolutePath}:${
|
|
3392
|
+
`${absolutePath}:${line}:${column} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
3057
3393
|
);
|
|
3058
3394
|
}
|
|
3059
3395
|
if (NODE_BUILTIN_SET.has(specifier)) {
|
|
@@ -3067,16 +3403,15 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3067
3403
|
specifier,
|
|
3068
3404
|
resolvedPath: resolved,
|
|
3069
3405
|
workspace,
|
|
3070
|
-
|
|
3071
|
-
|
|
3406
|
+
line,
|
|
3407
|
+
column
|
|
3072
3408
|
});
|
|
3073
3409
|
if (resolved !== absoluteEntryFile && isPlaySourceFile(resolved)) {
|
|
3074
3410
|
const importedSource = await (0, import_promises2.readFile)(resolved, "utf-8");
|
|
3075
3411
|
const importedPlayName = extractDefinedPlayName(importedSource, resolved);
|
|
3076
3412
|
if (!importedPlayName) {
|
|
3077
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3078
3413
|
throw new Error(
|
|
3079
|
-
`${absolutePath}:${
|
|
3414
|
+
`${absolutePath}:${line}:${column} Imported play file "${specifier}" must export definePlay(...) so it can be runtime-composed.`
|
|
3080
3415
|
);
|
|
3081
3416
|
}
|
|
3082
3417
|
importedPlayDependencies.set(resolved, {
|
|
@@ -3089,44 +3424,28 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3089
3424
|
return;
|
|
3090
3425
|
}
|
|
3091
3426
|
if (specifier.includes(":")) {
|
|
3092
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3093
3427
|
throw new Error(
|
|
3094
|
-
`${absolutePath}:${
|
|
3428
|
+
`${absolutePath}:${line}:${column} Unsupported import specifier "${specifier}". Allowed imports are relative files, Node builtins, and installed packages.`
|
|
3095
3429
|
);
|
|
3096
3430
|
}
|
|
3097
3431
|
const packageImport = resolvePackageImport(specifier, absolutePath, adapter);
|
|
3098
3432
|
packages.set(packageImport.name, packageImport.version);
|
|
3099
3433
|
};
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
await handleSpecifier(
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
throw new Error(
|
|
3109
|
-
`${absolutePath}:${position.line + 1}:${position.character + 1} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
3110
|
-
);
|
|
3111
|
-
}
|
|
3112
|
-
await handleSpecifier(node.arguments[0].text, node, "dynamic-import");
|
|
3113
|
-
}
|
|
3114
|
-
if (import_typescript.default.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
3115
|
-
const firstArgument = node.arguments[0];
|
|
3116
|
-
if (node.arguments.length !== 1 || !firstArgument || !import_typescript.default.isStringLiteralLike(firstArgument)) {
|
|
3117
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3118
|
-
throw new Error(
|
|
3119
|
-
`${absolutePath}:${position.line + 1}:${position.character + 1} Dynamic require() is not allowed in plays. Use static imports or require("literal") only.`
|
|
3120
|
-
);
|
|
3121
|
-
}
|
|
3122
|
-
await handleSpecifier(firstArgument.text, node, "require");
|
|
3123
|
-
}
|
|
3434
|
+
try {
|
|
3435
|
+
for (const reference of findSourceImportReferences(sourceCode2)) {
|
|
3436
|
+
await handleSpecifier(
|
|
3437
|
+
reference.specifier,
|
|
3438
|
+
reference.line,
|
|
3439
|
+
reference.column,
|
|
3440
|
+
reference.kind
|
|
3441
|
+
);
|
|
3124
3442
|
}
|
|
3125
|
-
|
|
3126
|
-
|
|
3443
|
+
} catch (error) {
|
|
3444
|
+
if (error instanceof Error && error.message.startsWith(":")) {
|
|
3445
|
+
throw new Error(`${absolutePath}${error.message}`);
|
|
3127
3446
|
}
|
|
3128
|
-
|
|
3129
|
-
|
|
3447
|
+
throw error;
|
|
3448
|
+
}
|
|
3130
3449
|
};
|
|
3131
3450
|
await visitFile(absoluteEntryFile);
|
|
3132
3451
|
const sourceCode = localFiles.get(absoluteEntryFile) ?? "";
|
|
@@ -3146,6 +3465,11 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3146
3465
|
const playName = extractDefinedPlayName(sourceCode, absoluteEntryFile);
|
|
3147
3466
|
return {
|
|
3148
3467
|
sourceCode,
|
|
3468
|
+
sourceFiles: Object.fromEntries(
|
|
3469
|
+
[...localFiles.entries()].sort(
|
|
3470
|
+
(left, right) => left[0].localeCompare(right[0])
|
|
3471
|
+
)
|
|
3472
|
+
),
|
|
3149
3473
|
sourceHash,
|
|
3150
3474
|
graphHash,
|
|
3151
3475
|
importPolicy: {
|
|
@@ -3209,8 +3533,7 @@ function normalizeSourceMapForRuntime(sourceMapText) {
|
|
|
3209
3533
|
parsed.sourceRoot = void 0;
|
|
3210
3534
|
return JSON.stringify(parsed);
|
|
3211
3535
|
}
|
|
3212
|
-
function
|
|
3213
|
-
const bundleBytes = Buffer.byteLength(bundledCode, "utf8");
|
|
3536
|
+
function getBundleSizeErrorForBytes(filePath, bundleBytes, artifactKind) {
|
|
3214
3537
|
if (bundleBytes > MAX_PLAY_BUNDLE_BYTES) {
|
|
3215
3538
|
return `${filePath} Play bundle exceeds the 30 MiB limit (${bundleBytes} bytes > ${MAX_PLAY_BUNDLE_BYTES} bytes).`;
|
|
3216
3539
|
}
|
|
@@ -3221,11 +3544,27 @@ function getBundleSizeError(filePath, bundledCode, artifactKind) {
|
|
|
3221
3544
|
}
|
|
3222
3545
|
return null;
|
|
3223
3546
|
}
|
|
3224
|
-
|
|
3547
|
+
function getBundleSizeError(filePath, bundledCode, artifactKind) {
|
|
3548
|
+
return getBundleSizeErrorForBytes(
|
|
3549
|
+
filePath,
|
|
3550
|
+
Buffer.byteLength(bundledCode, "utf8"),
|
|
3551
|
+
artifactKind
|
|
3552
|
+
);
|
|
3553
|
+
}
|
|
3554
|
+
async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter, exportName) {
|
|
3225
3555
|
const sdkAliasPlugin = localSdkAliasPlugin(adapter);
|
|
3226
3556
|
const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
|
|
3557
|
+
const namedExportShim = exportName === "default" ? null : `export { ${exportName} as default } from ${JSON.stringify(entryFile)};
|
|
3558
|
+
`;
|
|
3227
3559
|
const result = await (0, import_esbuild.build)({
|
|
3228
|
-
|
|
3560
|
+
...namedExportShim ? {
|
|
3561
|
+
stdin: {
|
|
3562
|
+
contents: namedExportShim,
|
|
3563
|
+
resolveDir: (0, import_node_path5.dirname)(entryFile),
|
|
3564
|
+
sourcefile: `${(0, import_node_path5.basename)(entryFile)}.${exportName}.entry.ts`,
|
|
3565
|
+
loader: "ts"
|
|
3566
|
+
}
|
|
3567
|
+
} : { entryPoints: [entryFile] },
|
|
3229
3568
|
absWorkingDir: adapter.projectRoot,
|
|
3230
3569
|
bundle: true,
|
|
3231
3570
|
format: "cjs",
|
|
@@ -3253,10 +3592,10 @@ async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter
|
|
|
3253
3592
|
outputExtension: "cjs"
|
|
3254
3593
|
};
|
|
3255
3594
|
}
|
|
3256
|
-
async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter) {
|
|
3595
|
+
async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter, exportName) {
|
|
3257
3596
|
const sdkAliasPlugin = localSdkAliasPlugin(adapter, { workersRuntime: true });
|
|
3258
3597
|
const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
|
|
3259
|
-
const playEntryAlias = workersPlayEntryAliasPlugin(playEntryFile);
|
|
3598
|
+
const playEntryAlias = exportName === "default" ? workersPlayEntryAliasPlugin(playEntryFile) : workersNamedPlayEntryAliasPlugin(playEntryFile, exportName);
|
|
3260
3599
|
const result = await (0, import_esbuild.build)({
|
|
3261
3600
|
// Entry is the Workers harness; it imports the play via the virtual
|
|
3262
3601
|
// `deepline-play-entry` alias resolved by workersPlayEntryAliasPlugin.
|
|
@@ -3327,10 +3666,16 @@ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies,
|
|
|
3327
3666
|
async function bundlePlayFile(filePath, options) {
|
|
3328
3667
|
const adapter = options.adapter;
|
|
3329
3668
|
const target = options.target ?? PLAY_ARTIFACT_KINDS.cjsNode20;
|
|
3669
|
+
const exportName = options.exportName?.trim() || "default";
|
|
3670
|
+
assertValidExportName(exportName);
|
|
3330
3671
|
const absolutePath = await normalizeLocalPath(filePath);
|
|
3331
3672
|
adapter.warnAboutNonDevelopmentBundling?.(absolutePath);
|
|
3332
3673
|
try {
|
|
3333
3674
|
const analysis = await analyzeSourceGraph(absolutePath, adapter);
|
|
3675
|
+
analysis.graphHash = sha256(
|
|
3676
|
+
`${analysis.graphHash}
|
|
3677
|
+
entry-export:${exportName}`
|
|
3678
|
+
);
|
|
3334
3679
|
if (target === PLAY_ARTIFACT_KINDS.esmWorkers) {
|
|
3335
3680
|
const harnessFingerprint = await computeWorkersHarnessFingerprintWithAdapter(adapter);
|
|
3336
3681
|
analysis.graphHash = sha256(
|
|
@@ -3338,6 +3683,23 @@ async function bundlePlayFile(filePath, options) {
|
|
|
3338
3683
|
workers-harness:${harnessFingerprint}`
|
|
3339
3684
|
);
|
|
3340
3685
|
}
|
|
3686
|
+
const typecheckErrors = [
|
|
3687
|
+
...await adapter.typecheckPlaySource?.({
|
|
3688
|
+
sourceCode: analysis.sourceCode,
|
|
3689
|
+
sourcePath: absolutePath,
|
|
3690
|
+
importedFilePaths: [
|
|
3691
|
+
...analysis.importPolicy.localFiles,
|
|
3692
|
+
...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
3693
|
+
]
|
|
3694
|
+
}) ?? []
|
|
3695
|
+
];
|
|
3696
|
+
if (typecheckErrors.length > 0) {
|
|
3697
|
+
return {
|
|
3698
|
+
success: false,
|
|
3699
|
+
filePath: absolutePath,
|
|
3700
|
+
errors: typecheckErrors
|
|
3701
|
+
};
|
|
3702
|
+
}
|
|
3341
3703
|
const cachedArtifact = await readArtifactCache(analysis.graphHash, target, adapter);
|
|
3342
3704
|
const discoveredFiles = await adapter.discoverPackagedLocalFiles(absolutePath);
|
|
3343
3705
|
if (cachedArtifact) {
|
|
@@ -3357,6 +3719,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3357
3719
|
success: true,
|
|
3358
3720
|
artifact: { ...cachedArtifact, cacheHit: true },
|
|
3359
3721
|
sourceCode: analysis.sourceCode,
|
|
3722
|
+
sourceFiles: analysis.sourceFiles,
|
|
3360
3723
|
filePath: absolutePath,
|
|
3361
3724
|
playName: analysis.playName,
|
|
3362
3725
|
packagedFiles: discoveredFiles.files,
|
|
@@ -3364,25 +3727,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3364
3727
|
importedPlayDependencies: analysis.importedPlayDependencies
|
|
3365
3728
|
};
|
|
3366
3729
|
}
|
|
3367
|
-
const
|
|
3368
|
-
...adapter.typecheckSdkTypes === false ? [] : typecheckPlaySource(analysis, adapter),
|
|
3369
|
-
...await adapter.typecheckPlaySource?.({
|
|
3370
|
-
sourceCode: analysis.sourceCode,
|
|
3371
|
-
sourcePath: absolutePath,
|
|
3372
|
-
importedFilePaths: [
|
|
3373
|
-
...analysis.importPolicy.localFiles,
|
|
3374
|
-
...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
3375
|
-
]
|
|
3376
|
-
}) ?? []
|
|
3377
|
-
];
|
|
3378
|
-
if (typecheckErrors.length > 0) {
|
|
3379
|
-
return {
|
|
3380
|
-
success: false,
|
|
3381
|
-
filePath: absolutePath,
|
|
3382
|
-
errors: typecheckErrors
|
|
3383
|
-
};
|
|
3384
|
-
}
|
|
3385
|
-
const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter);
|
|
3730
|
+
const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
|
|
3386
3731
|
if (Array.isArray(buildOutcome)) {
|
|
3387
3732
|
return {
|
|
3388
3733
|
success: false,
|
|
@@ -3392,7 +3737,8 @@ workers-harness:${harnessFingerprint}`
|
|
|
3392
3737
|
}
|
|
3393
3738
|
const { bundledCode, sourceMapText, outputExtension } = buildOutcome;
|
|
3394
3739
|
const normalizedSourceMap = normalizeSourceMapForRuntime(sourceMapText);
|
|
3395
|
-
const
|
|
3740
|
+
const virtualBaseName = exportName === "default" ? (0, import_node_path5.basename)(absolutePath).replace(/\.[^.]+$/, "") : `${(0, import_node_path5.basename)(absolutePath).replace(/\.[^.]+$/, "")}.${exportName}`;
|
|
3741
|
+
const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${virtualBaseName}.${outputExtension}`;
|
|
3396
3742
|
const executableCode = `${bundledCode}
|
|
3397
3743
|
//# sourceMappingURL=${(0, import_node_path5.basename)(virtualFilename)}.map
|
|
3398
3744
|
`;
|
|
@@ -3430,6 +3776,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3430
3776
|
success: true,
|
|
3431
3777
|
artifact,
|
|
3432
3778
|
sourceCode: analysis.sourceCode,
|
|
3779
|
+
sourceFiles: analysis.sourceFiles,
|
|
3433
3780
|
filePath: absolutePath,
|
|
3434
3781
|
playName: analysis.playName,
|
|
3435
3782
|
packagedFiles: discoveredFiles.files,
|
|
@@ -3511,7 +3858,6 @@ function resolveExecutionProfile(override) {
|
|
|
3511
3858
|
var import_node_crypto2 = require("crypto");
|
|
3512
3859
|
var import_promises3 = require("fs/promises");
|
|
3513
3860
|
var import_node_path6 = require("path");
|
|
3514
|
-
var import_typescript2 = __toESM(require("typescript"));
|
|
3515
3861
|
var SOURCE_EXTENSIONS2 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
|
|
3516
3862
|
function sha2562(buffer) {
|
|
3517
3863
|
return (0, import_node_crypto2.createHash)("sha256").update(buffer).digest("hex");
|
|
@@ -3523,94 +3869,181 @@ function contentTypeForFile(filePath) {
|
|
|
3523
3869
|
if (extension === ".txt") return "text/plain";
|
|
3524
3870
|
return "application/octet-stream";
|
|
3525
3871
|
}
|
|
3526
|
-
function
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
return import_typescript2.default.isIdentifier(target) && (target.text === "ctx" || target.text.endsWith("Ctx")) && node.expression.name.text === "csv";
|
|
3532
|
-
}
|
|
3533
|
-
function extractSourceFragment(source, node) {
|
|
3534
|
-
return source.slice(node.getStart(), node.getEnd()).trim();
|
|
3535
|
-
}
|
|
3536
|
-
function referencesInputIdentifier(node) {
|
|
3537
|
-
if (import_typescript2.default.isIdentifier(node) && node.text === "input") {
|
|
3538
|
-
return true;
|
|
3539
|
-
}
|
|
3540
|
-
return node.getChildren().some((child) => referencesInputIdentifier(child));
|
|
3872
|
+
function stripCommentsToSpaces2(source) {
|
|
3873
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
|
|
3874
|
+
/(^|[^:])\/\/.*$/gm,
|
|
3875
|
+
(match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
|
|
3876
|
+
);
|
|
3541
3877
|
}
|
|
3542
|
-
function
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
return import_typescript2.default.isIdentifier(node.expression) && node.expression.text === "input";
|
|
3548
|
-
}
|
|
3549
|
-
if (import_typescript2.default.isIdentifier(node)) {
|
|
3550
|
-
return node.text === "input";
|
|
3551
|
-
}
|
|
3552
|
-
if (import_typescript2.default.isParenthesizedExpression(node)) {
|
|
3553
|
-
return isRuntimeInputExpression(node.expression);
|
|
3878
|
+
function unquoteStringLiteral2(literal) {
|
|
3879
|
+
const trimmed = literal.trim();
|
|
3880
|
+
const quote = trimmed[0];
|
|
3881
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
3882
|
+
return null;
|
|
3554
3883
|
}
|
|
3555
|
-
|
|
3556
|
-
return
|
|
3884
|
+
try {
|
|
3885
|
+
return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
|
|
3886
|
+
} catch {
|
|
3887
|
+
return trimmed.slice(1, -1);
|
|
3557
3888
|
}
|
|
3558
|
-
|
|
3559
|
-
|
|
3889
|
+
}
|
|
3890
|
+
function splitTopLevelPlus(expression) {
|
|
3891
|
+
const parts = [];
|
|
3892
|
+
let start = 0;
|
|
3893
|
+
let depth = 0;
|
|
3894
|
+
let quote = null;
|
|
3895
|
+
let escaped = false;
|
|
3896
|
+
for (let index = 0; index < expression.length; index += 1) {
|
|
3897
|
+
const char = expression[index];
|
|
3898
|
+
if (quote) {
|
|
3899
|
+
if (escaped) {
|
|
3900
|
+
escaped = false;
|
|
3901
|
+
} else if (char === "\\") {
|
|
3902
|
+
escaped = true;
|
|
3903
|
+
} else if (char === quote) {
|
|
3904
|
+
quote = null;
|
|
3905
|
+
}
|
|
3906
|
+
continue;
|
|
3907
|
+
}
|
|
3908
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3909
|
+
quote = char;
|
|
3910
|
+
continue;
|
|
3911
|
+
}
|
|
3912
|
+
if (char === "(" || char === "[" || char === "{") depth += 1;
|
|
3913
|
+
if (char === ")" || char === "]" || char === "}") depth -= 1;
|
|
3914
|
+
if (char === "+" && depth === 0) {
|
|
3915
|
+
parts.push(expression.slice(start, index));
|
|
3916
|
+
start = index + 1;
|
|
3917
|
+
}
|
|
3560
3918
|
}
|
|
3561
|
-
|
|
3919
|
+
if (parts.length === 0) return null;
|
|
3920
|
+
parts.push(expression.slice(start));
|
|
3921
|
+
return parts;
|
|
3562
3922
|
}
|
|
3563
|
-
function
|
|
3564
|
-
|
|
3565
|
-
|
|
3923
|
+
function stripOuterParens(expression) {
|
|
3924
|
+
let value = expression.trim();
|
|
3925
|
+
while (value.startsWith("(") && value.endsWith(")")) {
|
|
3926
|
+
value = value.slice(1, -1).trim();
|
|
3566
3927
|
}
|
|
3567
|
-
|
|
3568
|
-
|
|
3928
|
+
return value;
|
|
3929
|
+
}
|
|
3930
|
+
function isRuntimeInputExpression(expression) {
|
|
3931
|
+
return /(^|[^\w$])input([^\w$]|$)/.test(expression);
|
|
3932
|
+
}
|
|
3933
|
+
function resolveStringExpression(expression, constants) {
|
|
3934
|
+
const value = stripOuterParens(expression);
|
|
3935
|
+
if (/^(['"])(?:\\.|(?!\1)[\s\S])*\1$/.test(value)) {
|
|
3936
|
+
return unquoteStringLiteral2(value);
|
|
3569
3937
|
}
|
|
3570
|
-
if (
|
|
3571
|
-
return
|
|
3938
|
+
if (/^`(?:\\.|[^`$]|\$(?!\{))*`$/.test(value)) {
|
|
3939
|
+
return value.slice(1, -1);
|
|
3572
3940
|
}
|
|
3573
|
-
if (
|
|
3574
|
-
|
|
3575
|
-
for (const span of node.templateSpans) {
|
|
3576
|
-
const resolved = resolveStringExpression(span.expression, constants);
|
|
3577
|
-
if (resolved == null) {
|
|
3578
|
-
return null;
|
|
3579
|
-
}
|
|
3580
|
-
value += resolved + span.literal.text;
|
|
3581
|
-
}
|
|
3582
|
-
return value;
|
|
3941
|
+
if (/^[A-Za-z_$][\w$]*$/.test(value)) {
|
|
3942
|
+
return constants.get(value) ?? null;
|
|
3583
3943
|
}
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
const
|
|
3587
|
-
|
|
3588
|
-
return null;
|
|
3589
|
-
}
|
|
3590
|
-
return left + right;
|
|
3944
|
+
const parts = splitTopLevelPlus(value);
|
|
3945
|
+
if (parts) {
|
|
3946
|
+
const resolved = parts.map((part) => resolveStringExpression(part, constants));
|
|
3947
|
+
return resolved.every((part) => part != null) ? resolved.join("") : null;
|
|
3591
3948
|
}
|
|
3592
3949
|
return null;
|
|
3593
3950
|
}
|
|
3594
|
-
function collectTopLevelStringConstants(
|
|
3951
|
+
function collectTopLevelStringConstants(sourceCode) {
|
|
3595
3952
|
const constants = /* @__PURE__ */ new Map();
|
|
3596
|
-
|
|
3597
|
-
|
|
3953
|
+
const source = stripCommentsToSpaces2(sourceCode);
|
|
3954
|
+
for (const match of source.matchAll(/(?:^|\n)\s*const\s+([A-Za-z_$][\w$]*)\s*=\s*([^;\n]+)/g)) {
|
|
3955
|
+
const resolved = resolveStringExpression(match[2], constants);
|
|
3956
|
+
if (resolved != null) {
|
|
3957
|
+
constants.set(match[1], resolved);
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
return constants;
|
|
3961
|
+
}
|
|
3962
|
+
function findMatchingGenericEnd(source, openIndex) {
|
|
3963
|
+
let depth = 0;
|
|
3964
|
+
let quote = null;
|
|
3965
|
+
let escaped = false;
|
|
3966
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
3967
|
+
const char = source[index];
|
|
3968
|
+
if (quote) {
|
|
3969
|
+
if (escaped) {
|
|
3970
|
+
escaped = false;
|
|
3971
|
+
} else if (char === "\\") {
|
|
3972
|
+
escaped = true;
|
|
3973
|
+
} else if (char === quote) {
|
|
3974
|
+
quote = null;
|
|
3975
|
+
}
|
|
3598
3976
|
continue;
|
|
3599
3977
|
}
|
|
3600
|
-
if (
|
|
3978
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3979
|
+
quote = char;
|
|
3601
3980
|
continue;
|
|
3602
3981
|
}
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3982
|
+
if (char === "<") depth += 1;
|
|
3983
|
+
if (char === ">") {
|
|
3984
|
+
depth -= 1;
|
|
3985
|
+
if (depth === 0) return index;
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
return -1;
|
|
3989
|
+
}
|
|
3990
|
+
function findCallOpenParen(source, afterCsvIndex) {
|
|
3991
|
+
let index = afterCsvIndex;
|
|
3992
|
+
while (/\s/.test(source[index] ?? "")) index += 1;
|
|
3993
|
+
if (source[index] === "<") {
|
|
3994
|
+
const genericEnd = findMatchingGenericEnd(source, index);
|
|
3995
|
+
if (genericEnd < 0) return -1;
|
|
3996
|
+
index = genericEnd + 1;
|
|
3997
|
+
while (/\s/.test(source[index] ?? "")) index += 1;
|
|
3998
|
+
}
|
|
3999
|
+
return source[index] === "(" ? index : -1;
|
|
4000
|
+
}
|
|
4001
|
+
function firstCallArgument(source, openParen) {
|
|
4002
|
+
let depth = 0;
|
|
4003
|
+
let quote = null;
|
|
4004
|
+
let escaped = false;
|
|
4005
|
+
const start = openParen + 1;
|
|
4006
|
+
for (let index = start; index < source.length; index += 1) {
|
|
4007
|
+
const char = source[index];
|
|
4008
|
+
if (quote) {
|
|
4009
|
+
if (escaped) {
|
|
4010
|
+
escaped = false;
|
|
4011
|
+
} else if (char === "\\") {
|
|
4012
|
+
escaped = true;
|
|
4013
|
+
} else if (char === quote) {
|
|
4014
|
+
quote = null;
|
|
3610
4015
|
}
|
|
4016
|
+
continue;
|
|
4017
|
+
}
|
|
4018
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
4019
|
+
quote = char;
|
|
4020
|
+
continue;
|
|
4021
|
+
}
|
|
4022
|
+
if (char === "(" || char === "[" || char === "{") depth += 1;
|
|
4023
|
+
if (char === ")" && depth === 0) {
|
|
4024
|
+
const text = source.slice(start, index).trim();
|
|
4025
|
+
return text ? { text, start, end: index } : null;
|
|
3611
4026
|
}
|
|
4027
|
+
if (char === "," && depth === 0) {
|
|
4028
|
+
const text = source.slice(start, index).trim();
|
|
4029
|
+
return text ? { text, start, end: index } : null;
|
|
4030
|
+
}
|
|
4031
|
+
if (char === ")" || char === "]" || char === "}") depth -= 1;
|
|
3612
4032
|
}
|
|
3613
|
-
return
|
|
4033
|
+
return null;
|
|
4034
|
+
}
|
|
4035
|
+
function localImportSpecifiers(sourceCode) {
|
|
4036
|
+
const source = stripCommentsToSpaces2(sourceCode);
|
|
4037
|
+
const specifiers = [];
|
|
4038
|
+
for (const match of source.matchAll(
|
|
4039
|
+
/\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?['"]([^'"]+)['"]/g
|
|
4040
|
+
)) {
|
|
4041
|
+
if (match[1]?.startsWith(".")) specifiers.push(match[1]);
|
|
4042
|
+
}
|
|
4043
|
+
for (const match of source.matchAll(/\brequire\s*\(\s*(['"])(\.[^'"]*)\1\s*\)/g)) {
|
|
4044
|
+
specifiers.push(match[2]);
|
|
4045
|
+
}
|
|
4046
|
+
return specifiers;
|
|
3614
4047
|
}
|
|
3615
4048
|
async function fileExists2(filePath) {
|
|
3616
4049
|
try {
|
|
@@ -3655,69 +4088,60 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
3655
4088
|
}
|
|
3656
4089
|
visitedFiles.add(absolutePath);
|
|
3657
4090
|
const sourceCode = await (0, import_promises3.readFile)(absolutePath, "utf-8");
|
|
3658
|
-
const
|
|
3659
|
-
|
|
3660
|
-
sourceCode,
|
|
3661
|
-
import_typescript2.default.ScriptTarget.Latest,
|
|
3662
|
-
true,
|
|
3663
|
-
import_typescript2.default.ScriptKind.TS
|
|
3664
|
-
);
|
|
3665
|
-
const constants = collectTopLevelStringConstants(sourceFile);
|
|
4091
|
+
const scanSource = stripCommentsToSpaces2(sourceCode);
|
|
4092
|
+
const constants = collectTopLevelStringConstants(sourceCode);
|
|
3666
4093
|
const childVisits = [];
|
|
3667
|
-
const
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
4094
|
+
for (const match of scanSource.matchAll(/\b([A-Za-z_$][\w$]*)\s*\.\s*csv\b/g)) {
|
|
4095
|
+
const target = match[1];
|
|
4096
|
+
if (target !== "ctx" && !target.endsWith("Ctx")) {
|
|
4097
|
+
continue;
|
|
4098
|
+
}
|
|
4099
|
+
const openParen = findCallOpenParen(scanSource, match.index + match[0].length);
|
|
4100
|
+
if (openParen < 0) {
|
|
4101
|
+
continue;
|
|
4102
|
+
}
|
|
4103
|
+
const argument = firstCallArgument(scanSource, openParen);
|
|
4104
|
+
if (!argument) {
|
|
4105
|
+
unresolved.push({
|
|
4106
|
+
sourceFragment: "ctx.csv()",
|
|
4107
|
+
message: "ctx.csv() requires a file path string or input reference."
|
|
4108
|
+
});
|
|
4109
|
+
} else if (!isRuntimeInputExpression(argument.text)) {
|
|
4110
|
+
const resolvedPath = resolveStringExpression(argument.text, constants);
|
|
4111
|
+
if (resolvedPath == null) {
|
|
3671
4112
|
unresolved.push({
|
|
3672
|
-
sourceFragment:
|
|
3673
|
-
message: "ctx.csv()
|
|
4113
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4114
|
+
message: "Could not resolve this ctx.csv(...) path at submit time. Use a string literal, a top-level const string, or pass a runtime input like input.file."
|
|
3674
4115
|
});
|
|
3675
|
-
} else
|
|
3676
|
-
const
|
|
3677
|
-
if (resolvedPath
|
|
4116
|
+
} else {
|
|
4117
|
+
const absoluteCsvPath = (0, import_node_path6.resolve)((0, import_node_path6.dirname)(absolutePath), resolvedPath);
|
|
4118
|
+
if ((0, import_node_path6.isAbsolute)(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
|
|
3678
4119
|
unresolved.push({
|
|
3679
|
-
sourceFragment:
|
|
3680
|
-
message: "
|
|
3681
|
-
});
|
|
3682
|
-
} else {
|
|
3683
|
-
const absoluteCsvPath = (0, import_node_path6.resolve)((0, import_node_path6.dirname)(absolutePath), resolvedPath);
|
|
3684
|
-
if ((0, import_node_path6.isAbsolute)(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
|
|
3685
|
-
unresolved.push({
|
|
3686
|
-
sourceFragment: extractSourceFragment(sourceCode, argument),
|
|
3687
|
-
message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
|
|
3688
|
-
});
|
|
3689
|
-
return;
|
|
3690
|
-
}
|
|
3691
|
-
const buffer = await (0, import_promises3.readFile)(absoluteCsvPath);
|
|
3692
|
-
const stats = await (0, import_promises3.stat)(absoluteCsvPath);
|
|
3693
|
-
files.set(absoluteCsvPath, {
|
|
3694
|
-
sourceFragment: extractSourceFragment(sourceCode, argument),
|
|
3695
|
-
logicalPath: resolvedPath,
|
|
3696
|
-
absolutePath: absoluteCsvPath,
|
|
3697
|
-
bytes: stats.size,
|
|
3698
|
-
contentHash: sha2562(buffer),
|
|
3699
|
-
contentType: contentTypeForFile(absoluteCsvPath)
|
|
4120
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4121
|
+
message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
|
|
3700
4122
|
});
|
|
4123
|
+
continue;
|
|
3701
4124
|
}
|
|
4125
|
+
const buffer = await (0, import_promises3.readFile)(absoluteCsvPath);
|
|
4126
|
+
const stats = await (0, import_promises3.stat)(absoluteCsvPath);
|
|
4127
|
+
files.set(absoluteCsvPath, {
|
|
4128
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4129
|
+
logicalPath: resolvedPath,
|
|
4130
|
+
absolutePath: absoluteCsvPath,
|
|
4131
|
+
bytes: stats.size,
|
|
4132
|
+
contentHash: sha2562(buffer),
|
|
4133
|
+
contentType: contentTypeForFile(absoluteCsvPath)
|
|
4134
|
+
});
|
|
3702
4135
|
}
|
|
3703
4136
|
}
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
)
|
|
3709
|
-
)
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
childVisits.push(
|
|
3713
|
-
resolveLocalImport2(absolutePath, node.arguments[0].text).then(
|
|
3714
|
-
(resolvedImport) => visitSourceFile(resolvedImport)
|
|
3715
|
-
)
|
|
3716
|
-
);
|
|
3717
|
-
}
|
|
3718
|
-
await Promise.all(node.getChildren(sourceFile).map((child) => visitNode(child)));
|
|
3719
|
-
};
|
|
3720
|
-
await visitNode(sourceFile);
|
|
4137
|
+
}
|
|
4138
|
+
for (const specifier of localImportSpecifiers(sourceCode)) {
|
|
4139
|
+
childVisits.push(
|
|
4140
|
+
resolveLocalImport2(absolutePath, specifier).then(
|
|
4141
|
+
(resolvedImport) => visitSourceFile(resolvedImport)
|
|
4142
|
+
)
|
|
4143
|
+
);
|
|
4144
|
+
}
|
|
3721
4145
|
await Promise.all(childVisits);
|
|
3722
4146
|
};
|
|
3723
4147
|
await visitSourceFile(absoluteEntryFile);
|
|
@@ -3729,7 +4153,7 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
3729
4153
|
|
|
3730
4154
|
// src/plays/bundle-play-file.ts
|
|
3731
4155
|
var import_meta2 = {};
|
|
3732
|
-
var PLAY_BUNDLE_CACHE_VERSION2 =
|
|
4156
|
+
var PLAY_BUNDLE_CACHE_VERSION2 = 26;
|
|
3733
4157
|
var MODULE_DIR = (0, import_node_path7.dirname)((0, import_node_url.fileURLToPath)(import_meta2.url));
|
|
3734
4158
|
var SDK_PACKAGE_ROOT = (0, import_node_path7.resolve)(MODULE_DIR, "..", "..");
|
|
3735
4159
|
var SOURCE_REPO_ROOT = (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "..");
|
|
@@ -3744,7 +4168,7 @@ var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED
|
|
|
3744
4168
|
var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? (0, import_node_path7.resolve)(SOURCE_REPO_ROOT, "sdk", "src") : HAS_PACKAGED_BUNDLING_SOURCES ? (0, import_node_path7.resolve)(PACKAGED_REPO_ROOT, "sdk", "src") : (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "src");
|
|
3745
4169
|
var SDK_PACKAGE_JSON = (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "package.json");
|
|
3746
4170
|
var SDK_ENTRY_FILE = (0, import_node_path7.resolve)(SDK_SOURCE_ROOT, "index.ts");
|
|
3747
|
-
var SDK_TYPES_ENTRY_FILE = (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
4171
|
+
var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : (0, import_node_path7.resolve)(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
3748
4172
|
var SDK_WORKERS_ENTRY_FILE = (0, import_node_path7.resolve)(SDK_SOURCE_ROOT, "worker-play-entry.ts");
|
|
3749
4173
|
var WORKERS_HARNESS_ENTRY_FILE = (0, import_node_path7.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
|
|
3750
4174
|
var WORKERS_HARNESS_FILES_DIR = (0, import_node_path7.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src");
|
|
@@ -3776,7 +4200,7 @@ function createSdkPlayBundlingAdapter() {
|
|
|
3776
4200
|
sdkSourceRoot: SDK_SOURCE_ROOT,
|
|
3777
4201
|
sdkPackageJson: SDK_PACKAGE_JSON,
|
|
3778
4202
|
sdkEntryFile: SDK_ENTRY_FILE,
|
|
3779
|
-
sdkTypesEntryFile: (0, import_node_fs5.existsSync)(SDK_TYPES_ENTRY_FILE) ?
|
|
4203
|
+
sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES || !(0, import_node_fs5.existsSync)(SDK_TYPES_ENTRY_FILE) ? SDK_ENTRY_FILE : SDK_TYPES_ENTRY_FILE,
|
|
3780
4204
|
sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
|
|
3781
4205
|
workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
|
|
3782
4206
|
workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
|
|
@@ -3787,6 +4211,7 @@ function createSdkPlayBundlingAdapter() {
|
|
|
3787
4211
|
async function bundlePlayFile2(filePath, options = {}) {
|
|
3788
4212
|
return bundlePlayFile(filePath, {
|
|
3789
4213
|
target: options.target ?? defaultPlayBundleTarget(),
|
|
4214
|
+
exportName: options.exportName,
|
|
3790
4215
|
adapter: createSdkPlayBundlingAdapter()
|
|
3791
4216
|
});
|
|
3792
4217
|
}
|
|
@@ -3936,54 +4361,6 @@ function createCliProgress(enabled) {
|
|
|
3936
4361
|
return progress;
|
|
3937
4362
|
}
|
|
3938
4363
|
|
|
3939
|
-
// src/cli/trace.ts
|
|
3940
|
-
var cliTraceStartedAt = Date.now();
|
|
3941
|
-
function isTruthyEnv(value) {
|
|
3942
|
-
return value === "1" || value === "true" || value === "yes";
|
|
3943
|
-
}
|
|
3944
|
-
function isCliTraceEnabled() {
|
|
3945
|
-
return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
|
|
3946
|
-
}
|
|
3947
|
-
function recordCliTrace(event) {
|
|
3948
|
-
if (!isCliTraceEnabled()) {
|
|
3949
|
-
return;
|
|
3950
|
-
}
|
|
3951
|
-
const now = Date.now();
|
|
3952
|
-
const payload = {
|
|
3953
|
-
ts: now,
|
|
3954
|
-
source: "cli",
|
|
3955
|
-
sinceStartMs: now - cliTraceStartedAt,
|
|
3956
|
-
...event
|
|
3957
|
-
};
|
|
3958
|
-
process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
|
|
3959
|
-
`);
|
|
3960
|
-
}
|
|
3961
|
-
async function traceCliSpan(phase, fields, run) {
|
|
3962
|
-
if (!isCliTraceEnabled()) {
|
|
3963
|
-
return run();
|
|
3964
|
-
}
|
|
3965
|
-
const startedAt = Date.now();
|
|
3966
|
-
try {
|
|
3967
|
-
const result = await run();
|
|
3968
|
-
recordCliTrace({
|
|
3969
|
-
phase,
|
|
3970
|
-
ms: Date.now() - startedAt,
|
|
3971
|
-
ok: true,
|
|
3972
|
-
...fields
|
|
3973
|
-
});
|
|
3974
|
-
return result;
|
|
3975
|
-
} catch (error) {
|
|
3976
|
-
recordCliTrace({
|
|
3977
|
-
phase,
|
|
3978
|
-
ms: Date.now() - startedAt,
|
|
3979
|
-
ok: false,
|
|
3980
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3981
|
-
...fields
|
|
3982
|
-
});
|
|
3983
|
-
throw error;
|
|
3984
|
-
}
|
|
3985
|
-
}
|
|
3986
|
-
|
|
3987
4364
|
// src/cli/commands/play.ts
|
|
3988
4365
|
function parseReferencedPlayTarget(target) {
|
|
3989
4366
|
const trimmed = target.trim();
|
|
@@ -4031,67 +4408,6 @@ function defaultMaterializedPlayPath(reference) {
|
|
|
4031
4408
|
const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
4032
4409
|
return (0, import_node_path8.resolve)(`${safeName || "play"}.play.ts`);
|
|
4033
4410
|
}
|
|
4034
|
-
function sanitizeGeneratedPlayName(value) {
|
|
4035
|
-
return value.trim().toLowerCase().replace(/^prebuilt\//, "").replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "play";
|
|
4036
|
-
}
|
|
4037
|
-
function buildGeneratedCsvWrapperSource(input) {
|
|
4038
|
-
return `import { definePlay } from 'deepline';
|
|
4039
|
-
|
|
4040
|
-
export default definePlay(
|
|
4041
|
-
${JSON.stringify(input.wrapperName)},
|
|
4042
|
-
async (ctx, input: Record<string, unknown> & { file: string }) => {
|
|
4043
|
-
const rows = await ctx.csv<Record<string, unknown>>(input.file);
|
|
4044
|
-
const constants = Object.fromEntries(
|
|
4045
|
-
Object.entries(input).filter(([key]) => key !== 'file'),
|
|
4046
|
-
);
|
|
4047
|
-
|
|
4048
|
-
const mappedRows = await ctx
|
|
4049
|
-
.map('csv_rows', rows, {
|
|
4050
|
-
key: (row, index) =>
|
|
4051
|
-
String(
|
|
4052
|
-
row.id ??
|
|
4053
|
-
row.lead_id ??
|
|
4054
|
-
row.email ??
|
|
4055
|
-
row.linkedin_url ??
|
|
4056
|
-
row.domain ??
|
|
4057
|
-
index,
|
|
4058
|
-
),
|
|
4059
|
-
})
|
|
4060
|
-
.step('result', (row, rowCtx) =>
|
|
4061
|
-
rowCtx.runPlay(
|
|
4062
|
-
'row_play',
|
|
4063
|
-
${JSON.stringify(input.playRef)},
|
|
4064
|
-
{
|
|
4065
|
-
...constants,
|
|
4066
|
-
...row,
|
|
4067
|
-
},
|
|
4068
|
-
{
|
|
4069
|
-
description: 'Run the source play for this CSV row.',
|
|
4070
|
-
},
|
|
4071
|
-
),
|
|
4072
|
-
)
|
|
4073
|
-
.run({ description: 'Run the source play once per CSV row.' });
|
|
4074
|
-
|
|
4075
|
-
return { rows: mappedRows };
|
|
4076
|
-
},
|
|
4077
|
-
);
|
|
4078
|
-
`;
|
|
4079
|
-
}
|
|
4080
|
-
function writeGeneratedCsvWrapperPlay(playRef) {
|
|
4081
|
-
const baseName = sanitizeGeneratedPlayName(
|
|
4082
|
-
parseReferencedPlayTarget(playRef).unqualifiedPlayName
|
|
4083
|
-
);
|
|
4084
|
-
const wrapperName = `${baseName}-csv`;
|
|
4085
|
-
const outputDir = (0, import_node_path8.resolve)(".deepline", "generated");
|
|
4086
|
-
const outputPath = (0, import_node_path8.join)(outputDir, `${wrapperName}.play.ts`);
|
|
4087
|
-
(0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
|
|
4088
|
-
(0, import_node_fs6.writeFileSync)(
|
|
4089
|
-
outputPath,
|
|
4090
|
-
buildGeneratedCsvWrapperSource({ wrapperName, playRef }),
|
|
4091
|
-
"utf-8"
|
|
4092
|
-
);
|
|
4093
|
-
return outputPath;
|
|
4094
|
-
}
|
|
4095
4411
|
function materializeRemotePlaySource(input) {
|
|
4096
4412
|
if (isFileTarget(input.target)) {
|
|
4097
4413
|
return null;
|
|
@@ -4121,13 +4437,15 @@ function formatLoadedPlayMessage(materializedFile) {
|
|
|
4121
4437
|
return `Loaded play here: ${materializedFile.path}`;
|
|
4122
4438
|
}
|
|
4123
4439
|
function buildReadonlyPrebuiltPlayError(reference) {
|
|
4440
|
+
const localName = reference.split("/").slice(1).join("/") || "custom-play";
|
|
4124
4441
|
return new Error(
|
|
4125
4442
|
`Cannot edit or push ${reference} because Deepline prebuilt plays are read-only.
|
|
4126
4443
|
To make your own version:
|
|
4127
|
-
1.
|
|
4128
|
-
2. Change definePlay('${
|
|
4129
|
-
3. Run: deepline plays
|
|
4130
|
-
4.
|
|
4444
|
+
1. Run: deepline plays get ${reference} --source --out ./${localName}.play.ts
|
|
4445
|
+
2. Change definePlay('${localName}', ...) to a new play name you own.
|
|
4446
|
+
3. Run: deepline plays check ./${localName}.play.ts
|
|
4447
|
+
4. Run: deepline plays publish ./${localName}.play.ts
|
|
4448
|
+
5. Your play will then live under your workspace namespace.`
|
|
4131
4449
|
);
|
|
4132
4450
|
}
|
|
4133
4451
|
async function ensureEditableRemotePlay(client, target) {
|
|
@@ -4157,7 +4475,10 @@ function looksLikeFilePath(target) {
|
|
|
4157
4475
|
if (target.trim().toLowerCase().startsWith("prebuilt/")) {
|
|
4158
4476
|
return false;
|
|
4159
4477
|
}
|
|
4160
|
-
|
|
4478
|
+
if (target.startsWith("./") || target.startsWith("../") || target.startsWith("/") || target.startsWith("~/")) {
|
|
4479
|
+
return true;
|
|
4480
|
+
}
|
|
4481
|
+
return target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
|
|
4161
4482
|
}
|
|
4162
4483
|
function parsePositiveInteger2(value, flagName) {
|
|
4163
4484
|
const parsed = Number.parseInt(value, 10);
|
|
@@ -4174,6 +4495,142 @@ function parseJsonInput(raw) {
|
|
|
4174
4495
|
}
|
|
4175
4496
|
return parsed;
|
|
4176
4497
|
}
|
|
4498
|
+
function parseInputFieldFlag(rawFlag, nextArg) {
|
|
4499
|
+
const flag = rawFlag.slice(2);
|
|
4500
|
+
const equalsIndex = flag.indexOf("=");
|
|
4501
|
+
if (equalsIndex > 0) {
|
|
4502
|
+
const path = flag.slice(0, equalsIndex).trim();
|
|
4503
|
+
const value = flag.slice(equalsIndex + 1);
|
|
4504
|
+
if (!path) {
|
|
4505
|
+
throw new Error(`Invalid play input flag: ${rawFlag}`);
|
|
4506
|
+
}
|
|
4507
|
+
return { path, value };
|
|
4508
|
+
}
|
|
4509
|
+
if (!nextArg || nextArg.startsWith("--")) {
|
|
4510
|
+
throw new Error(`Play input flag ${rawFlag} requires a value.`);
|
|
4511
|
+
}
|
|
4512
|
+
return { path: flag, value: nextArg };
|
|
4513
|
+
}
|
|
4514
|
+
function parseInputFlagValue(raw) {
|
|
4515
|
+
const trimmed = raw.trim();
|
|
4516
|
+
if (!trimmed) return "";
|
|
4517
|
+
if (trimmed === "true" || trimmed === "false" || trimmed === "null" || trimmed.startsWith("{") || trimmed.startsWith("[") || /^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
4518
|
+
try {
|
|
4519
|
+
return JSON.parse(trimmed);
|
|
4520
|
+
} catch {
|
|
4521
|
+
return raw;
|
|
4522
|
+
}
|
|
4523
|
+
}
|
|
4524
|
+
return raw;
|
|
4525
|
+
}
|
|
4526
|
+
function getDottedInputValue(input, path) {
|
|
4527
|
+
const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
4528
|
+
let cursor = input;
|
|
4529
|
+
for (const part of parts) {
|
|
4530
|
+
if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
|
|
4531
|
+
return void 0;
|
|
4532
|
+
}
|
|
4533
|
+
cursor = cursor[part];
|
|
4534
|
+
}
|
|
4535
|
+
return cursor;
|
|
4536
|
+
}
|
|
4537
|
+
function setDottedInputValue(input, path, value) {
|
|
4538
|
+
const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
4539
|
+
if (parts.length === 0) {
|
|
4540
|
+
throw new Error(`Invalid play input flag path: ${path}`);
|
|
4541
|
+
}
|
|
4542
|
+
let cursor = input;
|
|
4543
|
+
for (const part of parts.slice(0, -1)) {
|
|
4544
|
+
const existing = cursor[part];
|
|
4545
|
+
if (existing !== void 0 && (!existing || typeof existing !== "object" || Array.isArray(existing))) {
|
|
4546
|
+
throw new Error(
|
|
4547
|
+
`Cannot set --${path}; input.${part} is already a non-object value.`
|
|
4548
|
+
);
|
|
4549
|
+
}
|
|
4550
|
+
if (!existing) {
|
|
4551
|
+
cursor[part] = {};
|
|
4552
|
+
}
|
|
4553
|
+
cursor = cursor[part];
|
|
4554
|
+
}
|
|
4555
|
+
cursor[parts[parts.length - 1]] = value;
|
|
4556
|
+
}
|
|
4557
|
+
function schemaMetadata(schema, key) {
|
|
4558
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return null;
|
|
4559
|
+
const value = schema[key];
|
|
4560
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4561
|
+
}
|
|
4562
|
+
function stringMetadata(metadata, key) {
|
|
4563
|
+
const value = metadata?.[key];
|
|
4564
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4565
|
+
}
|
|
4566
|
+
function inputFieldFromCsvArg(csvArg) {
|
|
4567
|
+
if (typeof csvArg !== "string") return null;
|
|
4568
|
+
const match = /^input\.([A-Za-z_$][\w$]*)$/.exec(csvArg.trim());
|
|
4569
|
+
return match?.[1] ?? null;
|
|
4570
|
+
}
|
|
4571
|
+
function fileInputBindingsFromPlaySchema(inputSchema) {
|
|
4572
|
+
const csvInput = schemaMetadata(inputSchema, "csvInput");
|
|
4573
|
+
if (!csvInput) return [];
|
|
4574
|
+
return [
|
|
4575
|
+
{
|
|
4576
|
+
inputPath: stringMetadata(csvInput, "inputField") ?? "csv"
|
|
4577
|
+
}
|
|
4578
|
+
];
|
|
4579
|
+
}
|
|
4580
|
+
function fileInputBindingsFromStaticPipeline(staticPipeline) {
|
|
4581
|
+
if (!staticPipeline || typeof staticPipeline !== "object" || Array.isArray(staticPipeline)) {
|
|
4582
|
+
return [];
|
|
4583
|
+
}
|
|
4584
|
+
const inputField = inputFieldFromCsvArg(
|
|
4585
|
+
staticPipeline.csvArg
|
|
4586
|
+
);
|
|
4587
|
+
return inputField ? [{ inputPath: inputField }] : [];
|
|
4588
|
+
}
|
|
4589
|
+
function applyCsvShortcutInput(input) {
|
|
4590
|
+
const csvValue = getDottedInputValue(input.runtimeInput, "csv");
|
|
4591
|
+
if (csvValue == null || csvValue === "") return;
|
|
4592
|
+
const candidate = input.bindings.find((binding) => binding.inputPath !== "csv")?.inputPath ?? input.fallbackInputPath ?? null;
|
|
4593
|
+
if (!candidate || candidate === "csv") return;
|
|
4594
|
+
const existing = getDottedInputValue(input.runtimeInput, candidate);
|
|
4595
|
+
if (existing != null && existing !== "") return;
|
|
4596
|
+
setDottedInputValue(input.runtimeInput, candidate, csvValue);
|
|
4597
|
+
}
|
|
4598
|
+
function isLocalFilePathValue(value) {
|
|
4599
|
+
if (typeof value !== "string" || !value.trim()) return false;
|
|
4600
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
4601
|
+
return (0, import_node_fs6.existsSync)((0, import_node_path8.resolve)(value));
|
|
4602
|
+
}
|
|
4603
|
+
async function stageFileInputArgs(input) {
|
|
4604
|
+
const uniqueBindings = [
|
|
4605
|
+
...new Map(input.bindings.map((binding) => [binding.inputPath, binding])).values()
|
|
4606
|
+
];
|
|
4607
|
+
const localFiles = uniqueBindings.flatMap((binding) => {
|
|
4608
|
+
const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
|
|
4609
|
+
if (!isLocalFilePathValue(value)) return [];
|
|
4610
|
+
const absolutePath = (0, import_node_path8.resolve)(value);
|
|
4611
|
+
return [{ binding, absolutePath, logicalPath: (0, import_node_path8.basename)(absolutePath) }];
|
|
4612
|
+
});
|
|
4613
|
+
if (localFiles.length === 0) {
|
|
4614
|
+
return { inputFile: null, packagedFiles: [] };
|
|
4615
|
+
}
|
|
4616
|
+
input.progress.phase(
|
|
4617
|
+
localFiles.length === 1 ? "staging input file" : "staging input files"
|
|
4618
|
+
);
|
|
4619
|
+
const staged = await input.client.stagePlayFiles(
|
|
4620
|
+
localFiles.map((file) => stageFile(file.logicalPath, file.absolutePath))
|
|
4621
|
+
);
|
|
4622
|
+
for (const [index, file] of localFiles.entries()) {
|
|
4623
|
+
setDottedInputValue(input.runtimeInput, file.binding.inputPath, file.logicalPath);
|
|
4624
|
+
const stagedFile = staged[index];
|
|
4625
|
+
if (stagedFile && stagedFile.logicalPath !== file.logicalPath) {
|
|
4626
|
+
setDottedInputValue(input.runtimeInput, file.binding.inputPath, stagedFile.logicalPath);
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
4629
|
+
return {
|
|
4630
|
+
inputFile: staged[0] ?? null,
|
|
4631
|
+
packagedFiles: staged.slice(1)
|
|
4632
|
+
};
|
|
4633
|
+
}
|
|
4177
4634
|
function stageFile(logicalPath, absolutePath) {
|
|
4178
4635
|
const buffer = (0, import_node_fs6.readFileSync)(absolutePath);
|
|
4179
4636
|
return {
|
|
@@ -4261,6 +4718,7 @@ async function compileBundledPlayGraphManifests(client, graph) {
|
|
|
4261
4718
|
node.compilerManifest = await client.compilePlayManifest({
|
|
4262
4719
|
name,
|
|
4263
4720
|
sourceCode: node.sourceCode,
|
|
4721
|
+
sourceFiles: node.sourceFiles,
|
|
4264
4722
|
artifact: node.artifact,
|
|
4265
4723
|
importedPlayDependencies: node.importedPlayDependencies.map(
|
|
4266
4724
|
(dependency) => {
|
|
@@ -4310,6 +4768,7 @@ async function publishImportedPlayDependencies(client, graph) {
|
|
|
4310
4768
|
await client.registerPlayArtifact({
|
|
4311
4769
|
name: node.playName,
|
|
4312
4770
|
sourceCode: node.sourceCode,
|
|
4771
|
+
sourceFiles: node.sourceFiles,
|
|
4313
4772
|
artifact: node.artifact,
|
|
4314
4773
|
compilerManifest: requireCompilerManifest(node),
|
|
4315
4774
|
publish: true
|
|
@@ -4327,67 +4786,6 @@ function formatTimestamp(value) {
|
|
|
4327
4786
|
function formatRunLine(run) {
|
|
4328
4787
|
return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)}`;
|
|
4329
4788
|
}
|
|
4330
|
-
function parsePlayRunTarget(input) {
|
|
4331
|
-
const { args, usage } = input;
|
|
4332
|
-
let runId = null;
|
|
4333
|
-
let playName = null;
|
|
4334
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
4335
|
-
const arg = args[index];
|
|
4336
|
-
if (arg === "--json") {
|
|
4337
|
-
continue;
|
|
4338
|
-
}
|
|
4339
|
-
if (arg === "--reason" || arg === "--interval-ms" || arg === "--poll-interval-ms") {
|
|
4340
|
-
index += 1;
|
|
4341
|
-
continue;
|
|
4342
|
-
}
|
|
4343
|
-
if (arg === "--run-id" && args[index + 1]) {
|
|
4344
|
-
runId = args[++index].trim();
|
|
4345
|
-
continue;
|
|
4346
|
-
}
|
|
4347
|
-
if (arg === "--name" && args[index + 1] && input.allowName) {
|
|
4348
|
-
playName = parseReferencedPlayTarget(args[++index]).playName;
|
|
4349
|
-
continue;
|
|
4350
|
-
}
|
|
4351
|
-
if (arg.startsWith("--")) {
|
|
4352
|
-
continue;
|
|
4353
|
-
}
|
|
4354
|
-
throw new DeeplineError(
|
|
4355
|
-
`Unexpected positional target "${arg}". Use --run-id for run ids.
|
|
4356
|
-
${usage}`
|
|
4357
|
-
);
|
|
4358
|
-
}
|
|
4359
|
-
const explicitTargets = [runId, playName].filter(Boolean).length;
|
|
4360
|
-
if (explicitTargets > 1) {
|
|
4361
|
-
throw new DeeplineError(`Choose exactly one play run target.
|
|
4362
|
-
${usage}`);
|
|
4363
|
-
}
|
|
4364
|
-
if (runId) {
|
|
4365
|
-
return { kind: "run", runId };
|
|
4366
|
-
}
|
|
4367
|
-
if (playName) {
|
|
4368
|
-
return { kind: "name", name: playName };
|
|
4369
|
-
}
|
|
4370
|
-
throw new DeeplineError(usage);
|
|
4371
|
-
}
|
|
4372
|
-
async function resolvePlayRunId(client, target) {
|
|
4373
|
-
if (target.kind === "run") {
|
|
4374
|
-
try {
|
|
4375
|
-
const status = await client.getPlayStatus(target.runId);
|
|
4376
|
-
return status.runId;
|
|
4377
|
-
} catch (error) {
|
|
4378
|
-
if (!(error instanceof DeeplineError) || error.statusCode !== 404) {
|
|
4379
|
-
throw error;
|
|
4380
|
-
}
|
|
4381
|
-
throw new DeeplineError(`No play run found for run id: ${target.runId}`);
|
|
4382
|
-
}
|
|
4383
|
-
}
|
|
4384
|
-
const runs = await client.listPlayRuns(target.name);
|
|
4385
|
-
const workflowId = runs[0]?.workflowId ?? "";
|
|
4386
|
-
if (!workflowId) {
|
|
4387
|
-
throw new DeeplineError(`No runs found for play: ${target.name}`);
|
|
4388
|
-
}
|
|
4389
|
-
return workflowId;
|
|
4390
|
-
}
|
|
4391
4789
|
function isTransientPlayStatusPollError(error) {
|
|
4392
4790
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
4393
4791
|
return error.statusCode >= 500 && error.statusCode < 600;
|
|
@@ -4491,7 +4889,7 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
4491
4889
|
if (input.waitTimeoutMs !== null && Date.now() - input.startedAt >= input.waitTimeoutMs) {
|
|
4492
4890
|
const hasRealRunId = input.workflowId.length > 0 && input.workflowId !== "pending";
|
|
4493
4891
|
const phaseSuffix = input.lastPhase && input.lastPhase.trim() ? ` (last observed phase: ${input.lastPhase.trim()})` : "";
|
|
4494
|
-
const tailHint = hasRealRunId ? ` Run 'deepline
|
|
4892
|
+
const tailHint = hasRealRunId ? ` Run 'deepline runs tail ${input.workflowId} --json' to inspect it, or rerun with a larger --tail-timeout-ms.` : ` The run never reported a workflow id \u2014 the start request likely failed before reaching the scheduler. Check server logs and rerun with a larger --tail-timeout-ms.`;
|
|
4495
4893
|
throw new DeeplineError(
|
|
4496
4894
|
`Timed out waiting for play ${hasRealRunId ? input.workflowId : "<no run id>"} after ${Math.ceil(input.waitTimeoutMs / 1e3)}s${phaseSuffix}.${tailHint}`,
|
|
4497
4895
|
void 0,
|
|
@@ -4504,66 +4902,6 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
4504
4902
|
);
|
|
4505
4903
|
}
|
|
4506
4904
|
}
|
|
4507
|
-
async function waitForPlayCompletionByStream(input) {
|
|
4508
|
-
const controller = new AbortController();
|
|
4509
|
-
let timedOut = false;
|
|
4510
|
-
let lastPhase = null;
|
|
4511
|
-
const timeout = input.waitTimeoutMs === null ? null : setTimeout(
|
|
4512
|
-
() => {
|
|
4513
|
-
timedOut = true;
|
|
4514
|
-
controller.abort();
|
|
4515
|
-
},
|
|
4516
|
-
Math.max(1, input.waitTimeoutMs - (Date.now() - input.startedAt))
|
|
4517
|
-
);
|
|
4518
|
-
try {
|
|
4519
|
-
for await (const event of input.client.streamPlayRunEvents(
|
|
4520
|
-
input.workflowId,
|
|
4521
|
-
{ signal: controller.signal }
|
|
4522
|
-
)) {
|
|
4523
|
-
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
4524
|
-
const phase = describeLiveEventPhase(event);
|
|
4525
|
-
if (phase) {
|
|
4526
|
-
lastPhase = phase;
|
|
4527
|
-
input.progress.phase(phase);
|
|
4528
|
-
}
|
|
4529
|
-
printPlayLogLines({
|
|
4530
|
-
lines: getLogLinesFromLiveEvent(event),
|
|
4531
|
-
status: null,
|
|
4532
|
-
jsonOutput: input.jsonOutput,
|
|
4533
|
-
emitLogs: input.emitLogs,
|
|
4534
|
-
state: input.state,
|
|
4535
|
-
progress: input.progress
|
|
4536
|
-
});
|
|
4537
|
-
const status = getStatusFromLiveEvent(event);
|
|
4538
|
-
if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
|
|
4539
|
-
const finalStatus = await input.client.getPlayStatus(input.workflowId);
|
|
4540
|
-
if (TERMINAL_PLAY_STATUSES2.has(finalStatus.status)) {
|
|
4541
|
-
return finalStatus;
|
|
4542
|
-
}
|
|
4543
|
-
}
|
|
4544
|
-
}
|
|
4545
|
-
} catch (error) {
|
|
4546
|
-
if (timedOut) {
|
|
4547
|
-
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
4548
|
-
}
|
|
4549
|
-
throw error;
|
|
4550
|
-
} finally {
|
|
4551
|
-
if (timeout) {
|
|
4552
|
-
clearTimeout(timeout);
|
|
4553
|
-
}
|
|
4554
|
-
}
|
|
4555
|
-
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
4556
|
-
throw new DeeplineError(
|
|
4557
|
-
`Play live stream ended before the run reached a terminal state runId=${input.workflowId}${phaseSuffix}.`,
|
|
4558
|
-
void 0,
|
|
4559
|
-
"PLAY_LIVE_STREAM_ENDED",
|
|
4560
|
-
{
|
|
4561
|
-
runId: input.workflowId,
|
|
4562
|
-
workflowId: input.workflowId,
|
|
4563
|
-
...lastPhase ? { phase: lastPhase } : {}
|
|
4564
|
-
}
|
|
4565
|
-
);
|
|
4566
|
-
}
|
|
4567
4905
|
async function startAndWaitForPlayCompletionByStream(input) {
|
|
4568
4906
|
const startedAt = Date.now();
|
|
4569
4907
|
const state = {
|
|
@@ -4582,24 +4920,10 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4582
4920
|
},
|
|
4583
4921
|
Math.max(1, input.waitTimeoutMs)
|
|
4584
4922
|
);
|
|
4585
|
-
recordCliTrace({
|
|
4586
|
-
phase: "cli.start_stream_request",
|
|
4587
|
-
playName: input.playName
|
|
4588
|
-
});
|
|
4589
4923
|
try {
|
|
4590
|
-
let eventCount = 0;
|
|
4591
4924
|
for await (const event of input.client.startPlayRunStream(input.request, {
|
|
4592
4925
|
signal: controller.signal
|
|
4593
|
-
})) {
|
|
4594
|
-
eventCount += 1;
|
|
4595
|
-
if (eventCount === 1) {
|
|
4596
|
-
recordCliTrace({
|
|
4597
|
-
phase: "cli.start_stream_first_event",
|
|
4598
|
-
ms: Date.now() - startedAt,
|
|
4599
|
-
playName: input.playName,
|
|
4600
|
-
eventType: event.type
|
|
4601
|
-
});
|
|
4602
|
-
}
|
|
4926
|
+
})) {
|
|
4603
4927
|
const eventRunId = getEventPayload(event).runId;
|
|
4604
4928
|
if (typeof eventRunId === "string" && eventRunId && eventRunId !== "pending") {
|
|
4605
4929
|
lastKnownWorkflowId = eventRunId;
|
|
@@ -4640,14 +4964,6 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4640
4964
|
});
|
|
4641
4965
|
const finalStatus = getFinalStatusFromLiveEvent(event);
|
|
4642
4966
|
if (finalStatus) {
|
|
4643
|
-
recordCliTrace({
|
|
4644
|
-
phase: "cli.start_stream_final_event",
|
|
4645
|
-
ms: Date.now() - startedAt,
|
|
4646
|
-
playName: input.playName,
|
|
4647
|
-
runId: finalStatus.runId,
|
|
4648
|
-
status: finalStatus.status,
|
|
4649
|
-
eventCount
|
|
4650
|
-
});
|
|
4651
4967
|
return finalStatus;
|
|
4652
4968
|
}
|
|
4653
4969
|
}
|
|
@@ -4665,10 +4981,12 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4665
4981
|
clearTimeout(timeout);
|
|
4666
4982
|
}
|
|
4667
4983
|
const reason = error instanceof Error ? error.message : String(error);
|
|
4668
|
-
|
|
4669
|
-
|
|
4984
|
+
if (!input.jsonOutput) {
|
|
4985
|
+
process.stderr.write(
|
|
4986
|
+
`[play watch] start stream failed after run ${lastKnownWorkflowId}; falling back to polling (${reason})
|
|
4670
4987
|
`
|
|
4671
|
-
|
|
4988
|
+
);
|
|
4989
|
+
}
|
|
4672
4990
|
return waitForPlayCompletionByPolling({
|
|
4673
4991
|
client: input.client,
|
|
4674
4992
|
workflowId: lastKnownWorkflowId,
|
|
@@ -4687,6 +5005,24 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4687
5005
|
clearTimeout(timeout);
|
|
4688
5006
|
}
|
|
4689
5007
|
}
|
|
5008
|
+
if (lastKnownWorkflowId) {
|
|
5009
|
+
if (!input.jsonOutput) {
|
|
5010
|
+
input.progress.writeLine(
|
|
5011
|
+
`[play watch] start stream ended after run ${lastKnownWorkflowId}; falling back to polling`
|
|
5012
|
+
);
|
|
5013
|
+
}
|
|
5014
|
+
return waitForPlayCompletionByPolling({
|
|
5015
|
+
client: input.client,
|
|
5016
|
+
workflowId: lastKnownWorkflowId,
|
|
5017
|
+
pollIntervalMs: 500,
|
|
5018
|
+
jsonOutput: input.jsonOutput,
|
|
5019
|
+
emitLogs: input.emitLogs,
|
|
5020
|
+
waitTimeoutMs: input.waitTimeoutMs,
|
|
5021
|
+
startedAt,
|
|
5022
|
+
state,
|
|
5023
|
+
progress: input.progress
|
|
5024
|
+
});
|
|
5025
|
+
}
|
|
4690
5026
|
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
4691
5027
|
const idSuffix = lastKnownWorkflowId ? ` runId=${lastKnownWorkflowId}` : "";
|
|
4692
5028
|
throw new DeeplineError(
|
|
@@ -4764,38 +5100,6 @@ async function waitForPlayCompletionByPolling(input) {
|
|
|
4764
5100
|
}
|
|
4765
5101
|
}
|
|
4766
5102
|
}
|
|
4767
|
-
async function waitForPlayCompletion(input) {
|
|
4768
|
-
const startedAt = Date.now();
|
|
4769
|
-
const state = {
|
|
4770
|
-
lastLogIndex: 0,
|
|
4771
|
-
emittedRunnerStarted: false
|
|
4772
|
-
};
|
|
4773
|
-
try {
|
|
4774
|
-
return await waitForPlayCompletionByStream({
|
|
4775
|
-
...input,
|
|
4776
|
-
startedAt,
|
|
4777
|
-
state,
|
|
4778
|
-
progress: input.progress
|
|
4779
|
-
});
|
|
4780
|
-
} catch (error) {
|
|
4781
|
-
assertPlayWaitNotTimedOut({
|
|
4782
|
-
workflowId: input.workflowId,
|
|
4783
|
-
startedAt,
|
|
4784
|
-
waitTimeoutMs: input.waitTimeoutMs
|
|
4785
|
-
});
|
|
4786
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
4787
|
-
process.stderr.write(
|
|
4788
|
-
`[play watch] SSE stream failed; falling back to polling (${reason})
|
|
4789
|
-
`
|
|
4790
|
-
);
|
|
4791
|
-
return waitForPlayCompletionByPolling({
|
|
4792
|
-
...input,
|
|
4793
|
-
startedAt,
|
|
4794
|
-
state,
|
|
4795
|
-
progress: input.progress
|
|
4796
|
-
});
|
|
4797
|
-
}
|
|
4798
|
-
}
|
|
4799
5103
|
function formatInteger(value) {
|
|
4800
5104
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
|
|
4801
5105
|
}
|
|
@@ -4924,18 +5228,153 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
4924
5228
|
}
|
|
4925
5229
|
function buildRunNextCommands(runId) {
|
|
4926
5230
|
return {
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
logs: `deepline runs logs ${runId}
|
|
5231
|
+
get: `deepline runs get ${runId} --json`,
|
|
5232
|
+
tail: `deepline runs tail ${runId} --json`,
|
|
5233
|
+
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
5234
|
+
logs: `deepline runs logs ${runId} --out run.log --json`,
|
|
5235
|
+
exportCsv: `deepline runs export ${runId} --out output.csv`
|
|
5236
|
+
};
|
|
5237
|
+
}
|
|
5238
|
+
var RUN_LOG_PREVIEW_LIMIT = 20;
|
|
5239
|
+
function getRecordField(value, key) {
|
|
5240
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value[key] : void 0;
|
|
5241
|
+
}
|
|
5242
|
+
function getNumericField(value, key) {
|
|
5243
|
+
const field = getRecordField(value, key);
|
|
5244
|
+
return typeof field === "number" && Number.isFinite(field) ? field : null;
|
|
5245
|
+
}
|
|
5246
|
+
function getStringField(value, key) {
|
|
5247
|
+
const field = getRecordField(value, key);
|
|
5248
|
+
return typeof field === "string" && field.trim() ? field : null;
|
|
5249
|
+
}
|
|
5250
|
+
function normalizeRunStatusForEnvelope(status) {
|
|
5251
|
+
const run = status.run ?? null;
|
|
5252
|
+
return {
|
|
5253
|
+
id: status.runId,
|
|
5254
|
+
playName: status.playName ?? status.name ?? getStringField(run, "playName") ?? null,
|
|
5255
|
+
status: status.status,
|
|
5256
|
+
runtime: getStringField(status, "runtime") ?? getStringField(status, "runtimeBackend") ?? getStringField(run, "runtime") ?? null,
|
|
5257
|
+
startedAt: getStringField(run, "startTime") ?? getStringField(run, "startedAt") ?? null,
|
|
5258
|
+
updatedAt: getStringField(status, "updatedAt") ?? getStringField(run, "updatedAt") ?? null,
|
|
5259
|
+
finishedAt: getStringField(run, "closeTime") ?? getStringField(run, "finishedAt") ?? null,
|
|
5260
|
+
source: getRecordField(status, "source") ?? getRecordField(status, "artifact") ?? null
|
|
5261
|
+
};
|
|
5262
|
+
}
|
|
5263
|
+
function normalizeProgressForEnvelope(status, rowsInfo) {
|
|
5264
|
+
const progress = status.progress;
|
|
5265
|
+
const total = getNumericField(progress, "totalRows") ?? getNumericField(progress, "total") ?? rowsInfo?.totalRows ?? null;
|
|
5266
|
+
const failed = getNumericField(progress, "failed") ?? getNumericField(progress, "failedRows") ?? null;
|
|
5267
|
+
const completed = getNumericField(progress, "completed") ?? getNumericField(progress, "completedRows") ?? (status.status === "completed" ? total : null);
|
|
5268
|
+
const pending = getNumericField(progress, "pending") ?? (typeof total === "number" && typeof completed === "number" && typeof failed === "number" ? Math.max(0, total - completed - failed) : null);
|
|
5269
|
+
return {
|
|
5270
|
+
total,
|
|
5271
|
+
completed,
|
|
5272
|
+
pending,
|
|
5273
|
+
failed,
|
|
5274
|
+
executed: getNumericField(progress, "executed"),
|
|
5275
|
+
reused: getNumericField(progress, "reused"),
|
|
5276
|
+
skipped: getNumericField(progress, "skipped"),
|
|
5277
|
+
retried: getNumericField(progress, "retried"),
|
|
5278
|
+
degraded: typeof getRecordField(progress, "degraded") === "boolean" ? getRecordField(progress, "degraded") : null,
|
|
5279
|
+
duplicates: getRecordField(progress, "duplicates") ?? null,
|
|
5280
|
+
active: getStringField(progress, "status") ?? getStringField(status, "activeStep") ?? getStringField(status, "activeNodeId") ?? null,
|
|
5281
|
+
wait: status.wait ?? null
|
|
5282
|
+
};
|
|
5283
|
+
}
|
|
5284
|
+
function normalizeOutputsForEnvelope(rowsInfo, exportedPath) {
|
|
5285
|
+
if (!rowsInfo) {
|
|
5286
|
+
return exportedPath ? [{ name: "output", kind: "file", path: exportedPath }] : [];
|
|
5287
|
+
}
|
|
5288
|
+
return [
|
|
5289
|
+
{
|
|
5290
|
+
name: "rows",
|
|
5291
|
+
kind: "dataset",
|
|
5292
|
+
rowCount: rowsInfo.totalRows,
|
|
5293
|
+
columns: rowsInfo.columns,
|
|
5294
|
+
preview: rowsInfo.rows.slice(0, 5),
|
|
5295
|
+
previewRowCount: Math.min(rowsInfo.rows.length, 5),
|
|
5296
|
+
previewLimit: 5,
|
|
5297
|
+
complete: rowsInfo.complete,
|
|
5298
|
+
source: rowsInfo.source,
|
|
5299
|
+
...exportedPath ? { csv_path: exportedPath } : {}
|
|
5300
|
+
}
|
|
5301
|
+
];
|
|
5302
|
+
}
|
|
5303
|
+
function normalizeStepsForEnvelope(status) {
|
|
5304
|
+
const directSteps = getRecordField(status, "steps");
|
|
5305
|
+
if (Array.isArray(directSteps)) {
|
|
5306
|
+
return directSteps;
|
|
5307
|
+
}
|
|
5308
|
+
const timeline = getRecordField(status, "timeline");
|
|
5309
|
+
if (Array.isArray(timeline)) {
|
|
5310
|
+
return timeline;
|
|
5311
|
+
}
|
|
5312
|
+
return [];
|
|
5313
|
+
}
|
|
5314
|
+
function normalizeErrorsForEnvelope(status, error) {
|
|
5315
|
+
const directErrors = getRecordField(status, "errors");
|
|
5316
|
+
if (Array.isArray(directErrors)) {
|
|
5317
|
+
return directErrors.filter(
|
|
5318
|
+
(entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)
|
|
5319
|
+
);
|
|
5320
|
+
}
|
|
5321
|
+
if (!error) {
|
|
5322
|
+
return [];
|
|
5323
|
+
}
|
|
5324
|
+
return [
|
|
5325
|
+
{
|
|
5326
|
+
code: getStringField(status, "errorCode") ?? "RUN_FAILED",
|
|
5327
|
+
phase: getStringField(status, "errorPhase") ?? "runtime",
|
|
5328
|
+
message: error,
|
|
5329
|
+
retryable: typeof getRecordField(status, "retryable") === "boolean" ? getRecordField(status, "retryable") : null,
|
|
5330
|
+
nextAction: `deepline runs get ${status.runId} --json`
|
|
5331
|
+
}
|
|
5332
|
+
];
|
|
5333
|
+
}
|
|
5334
|
+
function normalizeLogsForEnvelope(status) {
|
|
5335
|
+
const logs = Array.isArray(status.progress?.logs) ? status.progress.logs : [];
|
|
5336
|
+
const offset = typeof status.progress?.logOffset === "number" && Number.isFinite(status.progress.logOffset) ? Math.max(0, Math.trunc(status.progress.logOffset)) : 0;
|
|
5337
|
+
const totalCount = offset + logs.length;
|
|
5338
|
+
const entries = logs.slice(Math.max(0, logs.length - RUN_LOG_PREVIEW_LIMIT));
|
|
5339
|
+
const firstSequence = entries.length === 0 ? null : offset + logs.length - entries.length + 1;
|
|
5340
|
+
const lastSequence = totalCount === 0 ? null : totalCount;
|
|
5341
|
+
return {
|
|
5342
|
+
totalCount,
|
|
5343
|
+
returnedCount: entries.length,
|
|
5344
|
+
firstSequence,
|
|
5345
|
+
lastSequence,
|
|
5346
|
+
truncated: totalCount > entries.length,
|
|
5347
|
+
hasMore: totalCount > entries.length,
|
|
5348
|
+
entries,
|
|
5349
|
+
nextCursor: lastSequence
|
|
4931
5350
|
};
|
|
4932
5351
|
}
|
|
5352
|
+
function stripProviderSpendFromBilling(value) {
|
|
5353
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5354
|
+
return value;
|
|
5355
|
+
}
|
|
5356
|
+
const next = {};
|
|
5357
|
+
for (const [key, item] of Object.entries(value)) {
|
|
5358
|
+
if (key === "providerCostUsd" || key === "totalProviderCostUsd") {
|
|
5359
|
+
continue;
|
|
5360
|
+
}
|
|
5361
|
+
next[key] = item;
|
|
5362
|
+
}
|
|
5363
|
+
return next;
|
|
5364
|
+
}
|
|
4933
5365
|
function compactPlayStatus(status, options) {
|
|
4934
5366
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
4935
5367
|
const result = status && typeof status === "object" ? status.result : null;
|
|
4936
5368
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
4937
|
-
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
4938
|
-
|
|
5369
|
+
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5370
|
+
rowsInfo.rows,
|
|
5371
|
+
rowsInfo.totalRows,
|
|
5372
|
+
rowsInfo.columns,
|
|
5373
|
+
extractDatasetExecutionStats(status)
|
|
5374
|
+
) : null;
|
|
5375
|
+
const billing = status && typeof status === "object" ? stripProviderSpendFromBilling(
|
|
5376
|
+
status.billing
|
|
5377
|
+
) : null;
|
|
4939
5378
|
const progressError = status.progress?.error;
|
|
4940
5379
|
const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
|
|
4941
5380
|
return {
|
|
@@ -4944,18 +5383,38 @@ function compactPlayStatus(status, options) {
|
|
|
4944
5383
|
...typeof status.name === "string" ? { name: status.name } : {},
|
|
4945
5384
|
...typeof status.playName === "string" ? { playName: status.playName } : {},
|
|
4946
5385
|
status: status.status,
|
|
5386
|
+
run: normalizeRunStatusForEnvelope(status),
|
|
5387
|
+
progress: normalizeProgressForEnvelope(status, rowsInfo),
|
|
5388
|
+
outputs: normalizeOutputsForEnvelope(rowsInfo, options?.exportedPath),
|
|
5389
|
+
steps: normalizeStepsForEnvelope(status),
|
|
5390
|
+
errors: normalizeErrorsForEnvelope(status, error),
|
|
5391
|
+
logs: normalizeLogsForEnvelope(status),
|
|
4947
5392
|
...error ? { error } : {},
|
|
4948
5393
|
...warnings.length > 0 ? { warnings } : {},
|
|
4949
5394
|
output: buildOutputSummary(rowsInfo, options?.exportedPath) ?? result ?? null,
|
|
4950
5395
|
...result !== void 0 ? { result } : {},
|
|
4951
5396
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
4952
5397
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
4953
|
-
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0,
|
|
5398
|
+
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
|
|
4954
5399
|
...billing ? { billing } : {},
|
|
4955
|
-
...status.run ? { run: status.run } : {},
|
|
4956
5400
|
next: buildRunNextCommands(status.runId)
|
|
4957
5401
|
};
|
|
4958
5402
|
}
|
|
5403
|
+
function enrichPlayStatusWithDatasetStats(status) {
|
|
5404
|
+
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
5405
|
+
if (!rowsInfo?.complete) {
|
|
5406
|
+
return status;
|
|
5407
|
+
}
|
|
5408
|
+
return {
|
|
5409
|
+
...status,
|
|
5410
|
+
dataset_stats: buildDatasetStats(
|
|
5411
|
+
rowsInfo.rows,
|
|
5412
|
+
rowsInfo.totalRows,
|
|
5413
|
+
rowsInfo.columns,
|
|
5414
|
+
extractDatasetExecutionStats(status)
|
|
5415
|
+
)
|
|
5416
|
+
};
|
|
5417
|
+
}
|
|
4959
5418
|
function formatDatasetStatsLines(datasetStats) {
|
|
4960
5419
|
if (!datasetStats) {
|
|
4961
5420
|
return [];
|
|
@@ -4965,10 +5424,11 @@ function formatDatasetStatsLines(datasetStats) {
|
|
|
4965
5424
|
0,
|
|
4966
5425
|
12
|
|
4967
5426
|
)) {
|
|
4968
|
-
const topValues = stat3.top_values ? `,
|
|
4969
|
-
const sample = stat3.sample_value !== void 0 ? `,
|
|
5427
|
+
const topValues = stat3.top_values ? `, top_values=${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
|
|
5428
|
+
const sample = stat3.sample_value !== void 0 ? `, sample_value=${JSON.stringify(stat3.sample_value)}` : "";
|
|
5429
|
+
const execution = stat3.execution ? `, execution=${Object.entries(stat3.execution).map(([bucket, count]) => `${bucket}=${count}`).join(", ")}` : "";
|
|
4970
5430
|
lines.push(
|
|
4971
|
-
` ${column}:
|
|
5431
|
+
` ${column}: non_empty=${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}${execution}`
|
|
4972
5432
|
);
|
|
4973
5433
|
}
|
|
4974
5434
|
return lines;
|
|
@@ -4977,7 +5437,7 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
4977
5437
|
if (jsonOutput) {
|
|
4978
5438
|
process.stdout.write(
|
|
4979
5439
|
`${JSON.stringify(
|
|
4980
|
-
options?.fullJson ? status : compactPlayStatus(status, options)
|
|
5440
|
+
options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status, options)
|
|
4981
5441
|
)}
|
|
4982
5442
|
`
|
|
4983
5443
|
);
|
|
@@ -4991,7 +5451,12 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
4991
5451
|
lines.push(`${success ? "\u2713" : "\u2717"} ${publicStatus} ${runId}`);
|
|
4992
5452
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
4993
5453
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
4994
|
-
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5454
|
+
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5455
|
+
rowsInfo.rows,
|
|
5456
|
+
rowsInfo.totalRows,
|
|
5457
|
+
rowsInfo.columns,
|
|
5458
|
+
extractDatasetExecutionStats(status)
|
|
5459
|
+
) : null;
|
|
4995
5460
|
const outputSummary = buildOutputSummary(rowsInfo, options?.exportedPath);
|
|
4996
5461
|
if (outputSummary) {
|
|
4997
5462
|
const columns = Array.isArray(outputSummary.columns) ? outputSummary.columns.length : 0;
|
|
@@ -5110,10 +5575,10 @@ function writeStartedPlayRun(input) {
|
|
|
5110
5575
|
const lines = [
|
|
5111
5576
|
`Started ${input.playName}`,
|
|
5112
5577
|
` run id: ${input.runId}`,
|
|
5113
|
-
`
|
|
5114
|
-
` tail logs: deepline
|
|
5115
|
-
` stop run: deepline
|
|
5116
|
-
` result JSON: deepline
|
|
5578
|
+
` get status: deepline runs get ${input.runId} --json`,
|
|
5579
|
+
` tail logs: deepline runs tail ${input.runId} --json`,
|
|
5580
|
+
` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`,
|
|
5581
|
+
` result JSON: deepline runs get ${input.runId} --json`
|
|
5117
5582
|
];
|
|
5118
5583
|
if (input.dashboardUrl) {
|
|
5119
5584
|
lines.push(` play page: ${input.dashboardUrl}`);
|
|
@@ -5126,10 +5591,9 @@ function writeStartedPlayRun(input) {
|
|
|
5126
5591
|
console.log(output);
|
|
5127
5592
|
}
|
|
5128
5593
|
function parsePlayRunOptions(args) {
|
|
5129
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--
|
|
5594
|
+
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--json] [--<input> value]";
|
|
5130
5595
|
let filePath = null;
|
|
5131
5596
|
let playName = null;
|
|
5132
|
-
let csvPath = null;
|
|
5133
5597
|
let input = null;
|
|
5134
5598
|
let revisionId = null;
|
|
5135
5599
|
let revisionSelector = null;
|
|
@@ -5150,10 +5614,6 @@ function parsePlayRunOptions(args) {
|
|
|
5150
5614
|
playName = parseReferencedPlayTarget(args[++index]).playName;
|
|
5151
5615
|
continue;
|
|
5152
5616
|
}
|
|
5153
|
-
if (arg === "--csv" && args[index + 1]) {
|
|
5154
|
-
csvPath = (0, import_node_path8.resolve)(args[++index]);
|
|
5155
|
-
continue;
|
|
5156
|
-
}
|
|
5157
5617
|
if ((arg === "--input" || arg === "-i") && args[index + 1]) {
|
|
5158
5618
|
input = parseJsonInput(args[++index]);
|
|
5159
5619
|
continue;
|
|
@@ -5203,8 +5663,13 @@ function parsePlayRunOptions(args) {
|
|
|
5203
5663
|
continue;
|
|
5204
5664
|
}
|
|
5205
5665
|
if (arg.startsWith("--")) {
|
|
5206
|
-
|
|
5207
|
-
|
|
5666
|
+
const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
|
|
5667
|
+
input ??= {};
|
|
5668
|
+
setDottedInputValue(input, path, parseInputFlagValue(value));
|
|
5669
|
+
if (!arg.includes("=")) {
|
|
5670
|
+
index += 1;
|
|
5671
|
+
}
|
|
5672
|
+
continue;
|
|
5208
5673
|
}
|
|
5209
5674
|
if (!arg.startsWith("--") && !filePath && !playName) {
|
|
5210
5675
|
if (isFileTarget(arg) || looksLikeFilePath(arg)) {
|
|
@@ -5240,7 +5705,6 @@ ${usage}`);
|
|
|
5240
5705
|
}
|
|
5241
5706
|
return {
|
|
5242
5707
|
target: filePath ? { kind: "file", path: filePath } : { kind: "name", name: playName },
|
|
5243
|
-
csvPath,
|
|
5244
5708
|
input,
|
|
5245
5709
|
revisionId,
|
|
5246
5710
|
revisionSelector,
|
|
@@ -5261,6 +5725,10 @@ function parsePlayCheckOptions(args) {
|
|
|
5261
5725
|
const jsonOutput = argsWantJson(args);
|
|
5262
5726
|
return { target, jsonOutput };
|
|
5263
5727
|
}
|
|
5728
|
+
function shouldUseLocalOnlyPlayCheck() {
|
|
5729
|
+
const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
|
|
5730
|
+
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
5731
|
+
}
|
|
5264
5732
|
async function handlePlayCheck(args) {
|
|
5265
5733
|
const options = parsePlayCheckOptions(args);
|
|
5266
5734
|
if (!isFileTarget(options.target)) {
|
|
@@ -5286,10 +5754,28 @@ async function handlePlayCheck(args) {
|
|
|
5286
5754
|
return 1;
|
|
5287
5755
|
}
|
|
5288
5756
|
const playName = graph.root.playName ?? extractPlayName(sourceCode, absolutePlayPath);
|
|
5757
|
+
if (shouldUseLocalOnlyPlayCheck()) {
|
|
5758
|
+
const result2 = {
|
|
5759
|
+
valid: true,
|
|
5760
|
+
errors: [],
|
|
5761
|
+
staticPipeline: graph.root.compilerManifest?.staticPipeline ?? null,
|
|
5762
|
+
artifactHash: graph.root.artifact.artifactHash,
|
|
5763
|
+
graphHash: graph.root.artifact.graphHash
|
|
5764
|
+
};
|
|
5765
|
+
if (options.jsonOutput) {
|
|
5766
|
+
process.stdout.write(`${JSON.stringify({ name: playName, ...result2 })}
|
|
5767
|
+
`);
|
|
5768
|
+
} else {
|
|
5769
|
+
console.log(`\u2713 ${playName} passed local play check`);
|
|
5770
|
+
console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
|
|
5771
|
+
}
|
|
5772
|
+
return 0;
|
|
5773
|
+
}
|
|
5289
5774
|
const client = new DeeplineClient();
|
|
5290
5775
|
const result = await client.checkPlayArtifact({
|
|
5291
5776
|
name: playName,
|
|
5292
5777
|
sourceCode: graph.root.sourceCode,
|
|
5778
|
+
sourceFiles: graph.root.sourceFiles,
|
|
5293
5779
|
artifact: graph.root.artifact
|
|
5294
5780
|
});
|
|
5295
5781
|
if (options.jsonOutput) {
|
|
@@ -5315,34 +5801,12 @@ async function handleFileBackedRun(options) {
|
|
|
5315
5801
|
const client = new DeeplineClient();
|
|
5316
5802
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
5317
5803
|
const absolutePlayPath = (0, import_node_path8.resolve)(options.target.path);
|
|
5318
|
-
recordCliTrace({
|
|
5319
|
-
phase: "cli.play_run_file_start",
|
|
5320
|
-
playPath: absolutePlayPath,
|
|
5321
|
-
watch: options.watch,
|
|
5322
|
-
hasCsv: Boolean(options.csvPath),
|
|
5323
|
-
force: options.force
|
|
5324
|
-
});
|
|
5325
5804
|
progress.phase("compiling play");
|
|
5326
|
-
const readSourceStartedAt = Date.now();
|
|
5327
5805
|
const sourceCode = (0, import_node_fs6.readFileSync)(absolutePlayPath, "utf-8");
|
|
5328
|
-
recordCliTrace({
|
|
5329
|
-
phase: "cli.read_play_source",
|
|
5330
|
-
ms: Date.now() - readSourceStartedAt,
|
|
5331
|
-
bytes: sourceCode.length,
|
|
5332
|
-
playPath: absolutePlayPath
|
|
5333
|
-
});
|
|
5334
5806
|
let graph;
|
|
5335
5807
|
try {
|
|
5336
|
-
graph = await
|
|
5337
|
-
|
|
5338
|
-
{ playPath: absolutePlayPath },
|
|
5339
|
-
() => collectBundledPlayGraph(absolutePlayPath)
|
|
5340
|
-
);
|
|
5341
|
-
await traceCliSpan(
|
|
5342
|
-
"cli.compile_play_manifest",
|
|
5343
|
-
{ playPath: absolutePlayPath, nodeCount: graph.nodes.size },
|
|
5344
|
-
() => compileBundledPlayGraphManifests(client, graph)
|
|
5345
|
-
);
|
|
5808
|
+
graph = await collectBundledPlayGraph(absolutePlayPath);
|
|
5809
|
+
await compileBundledPlayGraphManifests(client, graph);
|
|
5346
5810
|
progress.phase("compiled play");
|
|
5347
5811
|
} catch (error) {
|
|
5348
5812
|
progress.fail();
|
|
@@ -5353,87 +5817,65 @@ async function handleFileBackedRun(options) {
|
|
|
5353
5817
|
const playName = bundleResult.playName ?? extractPlayName(sourceCode, absolutePlayPath);
|
|
5354
5818
|
try {
|
|
5355
5819
|
progress.phase("publishing imported plays");
|
|
5356
|
-
await
|
|
5357
|
-
"cli.publish_imported_plays",
|
|
5358
|
-
{ playName, nodeCount: graph.nodes.size },
|
|
5359
|
-
() => publishImportedPlayDependencies(client, graph)
|
|
5360
|
-
);
|
|
5820
|
+
await publishImportedPlayDependencies(client, graph);
|
|
5361
5821
|
} catch (error) {
|
|
5362
5822
|
progress.fail();
|
|
5363
5823
|
console.error(error instanceof Error ? error.message : String(error));
|
|
5364
5824
|
return 1;
|
|
5365
5825
|
}
|
|
5366
5826
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5367
|
-
const prepareFilesStartedAt = Date.now();
|
|
5368
5827
|
const packagedFileUploads = bundleResult.packagedFiles.map(
|
|
5369
5828
|
(file) => stageFile(file.logicalPath, file.absolutePath)
|
|
5370
5829
|
);
|
|
5371
|
-
const
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5830
|
+
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
5831
|
+
requireCompilerManifest(bundleResult).staticPipeline
|
|
5832
|
+
);
|
|
5833
|
+
applyCsvShortcutInput({
|
|
5834
|
+
runtimeInput,
|
|
5835
|
+
bindings: fileInputBindings,
|
|
5836
|
+
fallbackInputPath: "file"
|
|
5837
|
+
});
|
|
5838
|
+
const stagedFileInputs = await stageFileInputArgs({
|
|
5839
|
+
client,
|
|
5840
|
+
runtimeInput,
|
|
5841
|
+
bindings: fileInputBindings,
|
|
5842
|
+
progress
|
|
5381
5843
|
});
|
|
5382
5844
|
const startRequest = {
|
|
5383
5845
|
name: playName,
|
|
5384
5846
|
sourceCode: bundleResult.sourceCode,
|
|
5847
|
+
sourceFiles: bundleResult.sourceFiles,
|
|
5385
5848
|
runtimeArtifact: bundleResult.artifact,
|
|
5386
5849
|
compilerManifest: requireCompilerManifest(bundleResult),
|
|
5387
|
-
inputFileUpload,
|
|
5388
5850
|
packagedFileUploads,
|
|
5389
5851
|
...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
|
|
5852
|
+
...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
|
|
5853
|
+
...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
|
|
5390
5854
|
...options.force ? { force: true } : {}
|
|
5391
5855
|
};
|
|
5392
5856
|
if (options.watch) {
|
|
5393
5857
|
progress.phase("starting run");
|
|
5394
|
-
const finalStatus = await
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
() => startAndWaitForPlayCompletionByStream({
|
|
5398
|
-
client,
|
|
5399
|
-
request: startRequest,
|
|
5400
|
-
playName,
|
|
5401
|
-
jsonOutput: options.jsonOutput,
|
|
5402
|
-
emitLogs: options.emitLogs,
|
|
5403
|
-
waitTimeoutMs: options.waitTimeoutMs,
|
|
5404
|
-
progress
|
|
5405
|
-
})
|
|
5406
|
-
);
|
|
5407
|
-
const exportStartedAt = Date.now();
|
|
5408
|
-
const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
|
|
5409
|
-
recordCliTrace({
|
|
5410
|
-
phase: "cli.export_rows",
|
|
5411
|
-
ms: Date.now() - exportStartedAt,
|
|
5858
|
+
const finalStatus = await startAndWaitForPlayCompletionByStream({
|
|
5859
|
+
client,
|
|
5860
|
+
request: startRequest,
|
|
5412
5861
|
playName,
|
|
5413
|
-
|
|
5862
|
+
jsonOutput: options.jsonOutput,
|
|
5863
|
+
emitLogs: options.emitLogs,
|
|
5864
|
+
waitTimeoutMs: options.waitTimeoutMs,
|
|
5865
|
+
progress
|
|
5414
5866
|
});
|
|
5867
|
+
const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
|
|
5415
5868
|
if (finalStatus.status === "completed") {
|
|
5416
5869
|
progress.complete();
|
|
5417
5870
|
} else {
|
|
5418
5871
|
progress.fail();
|
|
5419
5872
|
}
|
|
5420
|
-
recordCliTrace({
|
|
5421
|
-
phase: "cli.write_play_result",
|
|
5422
|
-
playName,
|
|
5423
|
-
status: finalStatus.status,
|
|
5424
|
-
runId: finalStatus.runId
|
|
5425
|
-
});
|
|
5426
5873
|
writePlayResult(finalStatus, options.jsonOutput, { exportedPath });
|
|
5427
5874
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
5428
5875
|
}
|
|
5429
5876
|
progress.phase("starting run");
|
|
5430
|
-
const started = await
|
|
5431
|
-
|
|
5432
|
-
{ playName },
|
|
5433
|
-
() => client.startPlayRun(startRequest)
|
|
5434
|
-
);
|
|
5435
|
-
const fallbackDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
5436
|
-
const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
|
|
5877
|
+
const started = await client.startPlayRun(startRequest);
|
|
5878
|
+
const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
5437
5879
|
progress.phase(`loading play on ${dashboardUrl}`);
|
|
5438
5880
|
progress.complete();
|
|
5439
5881
|
writeStartedPlayRun({
|
|
@@ -5441,7 +5883,7 @@ async function handleFileBackedRun(options) {
|
|
|
5441
5883
|
playName,
|
|
5442
5884
|
status: started.status,
|
|
5443
5885
|
statusUrl: started.statusUrl,
|
|
5444
|
-
dashboardUrl,
|
|
5886
|
+
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
5445
5887
|
jsonOutput: options.jsonOutput,
|
|
5446
5888
|
progress
|
|
5447
5889
|
});
|
|
@@ -5467,9 +5909,8 @@ async function handleNamedRun(options) {
|
|
|
5467
5909
|
}
|
|
5468
5910
|
const client = new DeeplineClient();
|
|
5469
5911
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
5470
|
-
let stagedInputFile = null;
|
|
5471
5912
|
progress.phase("loading play definition");
|
|
5472
|
-
await assertCanonicalNamedPlayReference(client, options.target.name);
|
|
5913
|
+
const playDetail = await assertCanonicalNamedPlayReference(client, options.target.name);
|
|
5473
5914
|
progress.phase("selecting revision");
|
|
5474
5915
|
const selectedRevisionId = await resolveNamedRunRevisionId({
|
|
5475
5916
|
client,
|
|
@@ -5477,19 +5918,27 @@ async function handleNamedRun(options) {
|
|
|
5477
5918
|
revisionId: options.revisionId,
|
|
5478
5919
|
selector: options.revisionSelector
|
|
5479
5920
|
});
|
|
5480
|
-
if (options.csvPath) {
|
|
5481
|
-
progress.phase("staging input file");
|
|
5482
|
-
const [staged] = await client.stagePlayFiles([
|
|
5483
|
-
stageFile((0, import_node_path8.basename)(options.csvPath), options.csvPath)
|
|
5484
|
-
]);
|
|
5485
|
-
stagedInputFile = staged ?? null;
|
|
5486
|
-
}
|
|
5487
5921
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5922
|
+
const fileInputBindings = [
|
|
5923
|
+
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
5924
|
+
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
5925
|
+
];
|
|
5926
|
+
applyCsvShortcutInput({
|
|
5927
|
+
runtimeInput,
|
|
5928
|
+
bindings: fileInputBindings
|
|
5929
|
+
});
|
|
5930
|
+
const stagedFileInputs = await stageFileInputArgs({
|
|
5931
|
+
client,
|
|
5932
|
+
runtimeInput,
|
|
5933
|
+
bindings: fileInputBindings,
|
|
5934
|
+
progress
|
|
5935
|
+
});
|
|
5488
5936
|
const startRequest = {
|
|
5489
5937
|
name: options.target.name,
|
|
5490
5938
|
...selectedRevisionId ? { revisionId: selectedRevisionId } : {},
|
|
5491
5939
|
...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
|
|
5492
|
-
...
|
|
5940
|
+
...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
|
|
5941
|
+
...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
|
|
5493
5942
|
...options.force ? { force: true } : {}
|
|
5494
5943
|
};
|
|
5495
5944
|
if (options.watch) {
|
|
@@ -5503,22 +5952,6 @@ async function handleNamedRun(options) {
|
|
|
5503
5952
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
5504
5953
|
progress
|
|
5505
5954
|
});
|
|
5506
|
-
if (finalStatus.status !== "completed" && options.csvPath) {
|
|
5507
|
-
progress.phase("generating csv wrapper play");
|
|
5508
|
-
const generatedPlayPath = writeGeneratedCsvWrapperPlay(
|
|
5509
|
-
options.target.name
|
|
5510
|
-
);
|
|
5511
|
-
progress.writeLogLine(
|
|
5512
|
-
`Generated CSV wrapper play: ${generatedPlayPath}`
|
|
5513
|
-
);
|
|
5514
|
-
progress.phase("running generated csv wrapper play");
|
|
5515
|
-
return handleFileBackedRun({
|
|
5516
|
-
...options,
|
|
5517
|
-
target: { kind: "file", path: generatedPlayPath },
|
|
5518
|
-
revisionId: null,
|
|
5519
|
-
revisionSelector: null
|
|
5520
|
-
});
|
|
5521
|
-
}
|
|
5522
5955
|
const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
|
|
5523
5956
|
if (finalStatus.status === "completed") {
|
|
5524
5957
|
progress.complete();
|
|
@@ -5530,11 +5963,10 @@ async function handleNamedRun(options) {
|
|
|
5530
5963
|
}
|
|
5531
5964
|
progress.phase("starting run");
|
|
5532
5965
|
const started = await client.startPlayRun(startRequest);
|
|
5533
|
-
const
|
|
5966
|
+
const dashboardUrl = buildPlayDashboardUrl(
|
|
5534
5967
|
client.baseUrl,
|
|
5535
5968
|
options.target.name
|
|
5536
5969
|
);
|
|
5537
|
-
const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
|
|
5538
5970
|
progress.phase(`loading play on ${dashboardUrl}`);
|
|
5539
5971
|
progress.complete();
|
|
5540
5972
|
writeStartedPlayRun({
|
|
@@ -5542,7 +5974,7 @@ async function handleNamedRun(options) {
|
|
|
5542
5974
|
playName: started.name ?? options.target.name,
|
|
5543
5975
|
status: started.status,
|
|
5544
5976
|
statusUrl: started.statusUrl,
|
|
5545
|
-
dashboardUrl,
|
|
5977
|
+
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
5546
5978
|
jsonOutput: options.jsonOutput,
|
|
5547
5979
|
progress
|
|
5548
5980
|
});
|
|
@@ -5576,80 +6008,101 @@ async function handlePlayRun(args) {
|
|
|
5576
6008
|
}
|
|
5577
6009
|
return handleNamedRun(options);
|
|
5578
6010
|
}
|
|
5579
|
-
|
|
5580
|
-
const usage = "Usage: deepline play tail --run-id <run-id> [--interval-ms 1000] [--json]\n deepline play tail --name <name> [--interval-ms 1000] [--json]";
|
|
5581
|
-
let target;
|
|
5582
|
-
try {
|
|
5583
|
-
target = parsePlayRunTarget({ args, usage, allowName: true });
|
|
5584
|
-
} catch (error) {
|
|
5585
|
-
console.error(error instanceof Error ? error.message : usage);
|
|
5586
|
-
return 1;
|
|
5587
|
-
}
|
|
5588
|
-
const client = new DeeplineClient();
|
|
5589
|
-
const jsonOutput = argsWantJson(args);
|
|
5590
|
-
const emitLogs = !jsonOutput || args.includes("--logs");
|
|
5591
|
-
let intervalMs = 500;
|
|
6011
|
+
function parseRunIdPositional(args, usage) {
|
|
5592
6012
|
for (let index = 0; index < args.length; index += 1) {
|
|
5593
6013
|
const arg = args[index];
|
|
5594
|
-
if (
|
|
5595
|
-
|
|
6014
|
+
if (arg === "--json" || arg === "--full" || arg === "--logs" || arg === "--compact" || arg === "--limit") {
|
|
6015
|
+
if (arg === "--limit" && args[index + 1]) {
|
|
6016
|
+
index += 1;
|
|
6017
|
+
}
|
|
6018
|
+
continue;
|
|
6019
|
+
}
|
|
6020
|
+
if ((arg === "--out" || arg === "--cursor" || arg === "--reason" || arg === "--interval-ms" || arg === "--poll-interval-ms") && args[index + 1]) {
|
|
6021
|
+
index += 1;
|
|
6022
|
+
continue;
|
|
6023
|
+
}
|
|
6024
|
+
if (!arg.startsWith("--")) {
|
|
6025
|
+
return arg;
|
|
5596
6026
|
}
|
|
5597
6027
|
}
|
|
5598
|
-
|
|
5599
|
-
const progress = getActiveCliProgress() ?? createCliProgress(!jsonOutput);
|
|
5600
|
-
progress.phase(`tailing ${workflowId}`);
|
|
5601
|
-
const finalStatus = await waitForPlayCompletion({
|
|
5602
|
-
client,
|
|
5603
|
-
workflowId,
|
|
5604
|
-
pollIntervalMs: intervalMs,
|
|
5605
|
-
jsonOutput,
|
|
5606
|
-
emitLogs,
|
|
5607
|
-
waitTimeoutMs: null,
|
|
5608
|
-
progress
|
|
5609
|
-
});
|
|
5610
|
-
if (finalStatus.status === "completed") {
|
|
5611
|
-
progress.complete();
|
|
5612
|
-
} else {
|
|
5613
|
-
progress.fail();
|
|
5614
|
-
}
|
|
5615
|
-
writePlayResult(finalStatus, jsonOutput);
|
|
5616
|
-
return finalStatus.status === "completed" ? 0 : 1;
|
|
6028
|
+
throw new DeeplineError(usage);
|
|
5617
6029
|
}
|
|
5618
|
-
async function
|
|
5619
|
-
const usage = "Usage: deepline
|
|
5620
|
-
let
|
|
6030
|
+
async function handleRunGet(args) {
|
|
6031
|
+
const usage = "Usage: deepline runs get <run-id> [--json] [--full]";
|
|
6032
|
+
let runId;
|
|
5621
6033
|
try {
|
|
5622
|
-
|
|
6034
|
+
runId = parseRunIdPositional(args, usage);
|
|
5623
6035
|
} catch (error) {
|
|
5624
6036
|
console.error(error instanceof Error ? error.message : usage);
|
|
5625
6037
|
return 1;
|
|
5626
6038
|
}
|
|
5627
6039
|
const client = new DeeplineClient();
|
|
5628
|
-
const
|
|
5629
|
-
const status = await client.getPlayStatus(workflowId);
|
|
6040
|
+
const status = await client.runs.get(runId);
|
|
5630
6041
|
writePlayResult(status, argsWantJson(args), {
|
|
5631
6042
|
fullJson: args.includes("--full")
|
|
5632
6043
|
});
|
|
5633
6044
|
return 0;
|
|
5634
6045
|
}
|
|
5635
|
-
function
|
|
6046
|
+
async function handleRunsList(args) {
|
|
6047
|
+
const usage = "Usage: deepline runs list --play <play-name> [--status <status>] [--json]";
|
|
6048
|
+
let playName = null;
|
|
6049
|
+
let statusFilter = null;
|
|
5636
6050
|
for (let index = 0; index < args.length; index += 1) {
|
|
5637
6051
|
const arg = args[index];
|
|
5638
|
-
if (arg === "--
|
|
6052
|
+
if ((arg === "--play" || arg === "--name") && args[index + 1]) {
|
|
6053
|
+
playName = parseReferencedPlayTarget(args[++index]).playName;
|
|
5639
6054
|
continue;
|
|
5640
6055
|
}
|
|
5641
|
-
if (arg === "--
|
|
5642
|
-
|
|
6056
|
+
if (arg === "--status" && args[index + 1]) {
|
|
6057
|
+
statusFilter = args[++index].trim().toLowerCase();
|
|
5643
6058
|
continue;
|
|
5644
6059
|
}
|
|
5645
|
-
if (
|
|
5646
|
-
|
|
6060
|
+
if (arg === "--json" || arg === "--compact") {
|
|
6061
|
+
continue;
|
|
5647
6062
|
}
|
|
5648
6063
|
}
|
|
5649
|
-
|
|
6064
|
+
if (!playName) {
|
|
6065
|
+
console.error(usage);
|
|
6066
|
+
return 1;
|
|
6067
|
+
}
|
|
6068
|
+
const client = new DeeplineClient();
|
|
6069
|
+
const runs = (await client.runs.list({
|
|
6070
|
+
play: playName,
|
|
6071
|
+
...statusFilter ? { status: statusFilter } : {}
|
|
6072
|
+
})).map((run) => ({
|
|
6073
|
+
runId: run.workflowId,
|
|
6074
|
+
workflowId: run.workflowId,
|
|
6075
|
+
temporalRunId: run.runId,
|
|
6076
|
+
status: String(run.status ?? "").toLowerCase(),
|
|
6077
|
+
startedAt: run.startTime,
|
|
6078
|
+
finishedAt: run.closeTime,
|
|
6079
|
+
executionTime: run.executionTime,
|
|
6080
|
+
playName: run.memo?.playName ?? playName
|
|
6081
|
+
}));
|
|
6082
|
+
if (argsWantJson(args)) {
|
|
6083
|
+
process.stdout.write(
|
|
6084
|
+
`${JSON.stringify({
|
|
6085
|
+
runs,
|
|
6086
|
+
count: runs.length,
|
|
6087
|
+
next: {
|
|
6088
|
+
get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
|
|
6089
|
+
}
|
|
6090
|
+
})}
|
|
6091
|
+
`
|
|
6092
|
+
);
|
|
6093
|
+
} else {
|
|
6094
|
+
if (runs.length === 0) {
|
|
6095
|
+
console.log(`No runs found for ${playName}.`);
|
|
6096
|
+
} else {
|
|
6097
|
+
for (const run of runs) {
|
|
6098
|
+
console.log(`${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
return 0;
|
|
5650
6103
|
}
|
|
5651
|
-
async function
|
|
5652
|
-
const usage = "Usage: deepline runs
|
|
6104
|
+
async function handleRunTail(args) {
|
|
6105
|
+
const usage = "Usage: deepline runs tail <run-id> [--json] [--compact] [--cursor <cursor>]";
|
|
5653
6106
|
let runId;
|
|
5654
6107
|
try {
|
|
5655
6108
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -5658,14 +6111,24 @@ async function handleRunStatus(args) {
|
|
|
5658
6111
|
return 1;
|
|
5659
6112
|
}
|
|
5660
6113
|
const client = new DeeplineClient();
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
6114
|
+
let afterLogIndex;
|
|
6115
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6116
|
+
const arg = args[index];
|
|
6117
|
+
if (arg === "--cursor" && args[index + 1]) {
|
|
6118
|
+
const parsed = Number(args[++index]);
|
|
6119
|
+
if (Number.isInteger(parsed) && parsed >= 0) {
|
|
6120
|
+
afterLogIndex = parsed;
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
6124
|
+
const status = await client.runs.tail(runId, {
|
|
6125
|
+
...afterLogIndex !== void 0 ? { afterLogIndex } : {}
|
|
5664
6126
|
});
|
|
5665
|
-
|
|
6127
|
+
writePlayResult(status, argsWantJson(args));
|
|
6128
|
+
return status.status === "failed" ? 1 : 0;
|
|
5666
6129
|
}
|
|
5667
6130
|
async function handleRunLogs(args) {
|
|
5668
|
-
const usage = "Usage: deepline runs logs <run-id> [--json]";
|
|
6131
|
+
const usage = "Usage: deepline runs logs <run-id> [--limit 200] [--out run.log] [--json]";
|
|
5669
6132
|
let runId;
|
|
5670
6133
|
try {
|
|
5671
6134
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -5673,14 +6136,86 @@ async function handleRunLogs(args) {
|
|
|
5673
6136
|
console.error(error instanceof Error ? error.message : usage);
|
|
5674
6137
|
return 1;
|
|
5675
6138
|
}
|
|
6139
|
+
let limit = 200;
|
|
6140
|
+
let outPath = null;
|
|
6141
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6142
|
+
const arg = args[index];
|
|
6143
|
+
if (arg === "--limit" && args[index + 1]) {
|
|
6144
|
+
limit = parsePositiveInteger2(args[++index], "--limit");
|
|
6145
|
+
continue;
|
|
6146
|
+
}
|
|
6147
|
+
if (arg === "--out" && args[index + 1]) {
|
|
6148
|
+
outPath = (0, import_node_path8.resolve)(args[++index]);
|
|
6149
|
+
}
|
|
6150
|
+
}
|
|
5676
6151
|
const client = new DeeplineClient();
|
|
5677
|
-
const status = await client.
|
|
6152
|
+
const status = await client.runs.get(runId);
|
|
5678
6153
|
const logs = status.progress?.logs ?? [];
|
|
6154
|
+
if (outPath) {
|
|
6155
|
+
(0, import_node_fs6.writeFileSync)(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
6156
|
+
if (argsWantJson(args)) {
|
|
6157
|
+
process.stdout.write(
|
|
6158
|
+
`${JSON.stringify({
|
|
6159
|
+
runId: status.runId,
|
|
6160
|
+
log_path: outPath,
|
|
6161
|
+
lineCount: logs.length
|
|
6162
|
+
})}
|
|
6163
|
+
`
|
|
6164
|
+
);
|
|
6165
|
+
} else {
|
|
6166
|
+
console.log(`Wrote ${logs.length} log lines to ${outPath}`);
|
|
6167
|
+
}
|
|
6168
|
+
return 0;
|
|
6169
|
+
}
|
|
6170
|
+
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
5679
6171
|
if (argsWantJson(args)) {
|
|
5680
|
-
process.stdout.write(
|
|
6172
|
+
process.stdout.write(
|
|
6173
|
+
`${JSON.stringify({
|
|
6174
|
+
runId: status.runId,
|
|
6175
|
+
totalCount: logs.length,
|
|
6176
|
+
returnedCount: entries.length,
|
|
6177
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
6178
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
6179
|
+
truncated: logs.length > entries.length,
|
|
6180
|
+
hasMore: logs.length > entries.length,
|
|
6181
|
+
entries,
|
|
6182
|
+
next: {
|
|
6183
|
+
export: `deepline runs logs ${status.runId} --out run.log --json`
|
|
6184
|
+
}
|
|
6185
|
+
})}
|
|
6186
|
+
`
|
|
6187
|
+
);
|
|
6188
|
+
} else {
|
|
6189
|
+
process.stdout.write(`${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`);
|
|
6190
|
+
}
|
|
6191
|
+
return 0;
|
|
6192
|
+
}
|
|
6193
|
+
async function handleRunStop(args) {
|
|
6194
|
+
const usage = 'Usage: deepline runs stop <run-id> [--reason "text"] [--json]';
|
|
6195
|
+
let runId;
|
|
6196
|
+
try {
|
|
6197
|
+
runId = parseRunIdPositional(args, usage);
|
|
6198
|
+
} catch (error) {
|
|
6199
|
+
console.error(error instanceof Error ? error.message : usage);
|
|
6200
|
+
return 1;
|
|
6201
|
+
}
|
|
6202
|
+
let reason;
|
|
6203
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6204
|
+
const arg = args[index];
|
|
6205
|
+
if (arg === "--reason" && args[index + 1]) {
|
|
6206
|
+
reason = args[++index];
|
|
6207
|
+
}
|
|
6208
|
+
}
|
|
6209
|
+
const client = new DeeplineClient();
|
|
6210
|
+
const result = await client.runs.stop(runId, { reason });
|
|
6211
|
+
if (argsWantJson(args)) {
|
|
6212
|
+
process.stdout.write(`${JSON.stringify(result)}
|
|
5681
6213
|
`);
|
|
5682
6214
|
} else {
|
|
5683
|
-
|
|
6215
|
+
console.log(`Stopped ${result.runId}`);
|
|
6216
|
+
if (result.hitlCancelledCount > 0) {
|
|
6217
|
+
console.log(` cancelled HITL waits: ${result.hitlCancelledCount}`);
|
|
6218
|
+
}
|
|
5684
6219
|
}
|
|
5685
6220
|
return 0;
|
|
5686
6221
|
}
|
|
@@ -5723,37 +6258,6 @@ async function handleRunExport(args) {
|
|
|
5723
6258
|
}
|
|
5724
6259
|
return 0;
|
|
5725
6260
|
}
|
|
5726
|
-
async function handlePlayStop(args) {
|
|
5727
|
-
const usage = 'Usage: deepline play stop --run-id <run-id> [--reason "text"] [--json]';
|
|
5728
|
-
let target;
|
|
5729
|
-
try {
|
|
5730
|
-
target = parsePlayRunTarget({ args, usage, allowName: false });
|
|
5731
|
-
} catch (error) {
|
|
5732
|
-
console.error(error instanceof Error ? error.message : usage);
|
|
5733
|
-
return 1;
|
|
5734
|
-
}
|
|
5735
|
-
const client = new DeeplineClient();
|
|
5736
|
-
const jsonOutput = argsWantJson(args);
|
|
5737
|
-
let reason;
|
|
5738
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
5739
|
-
const arg = args[index];
|
|
5740
|
-
if (arg === "--reason" && args[index + 1]) {
|
|
5741
|
-
reason = args[++index];
|
|
5742
|
-
}
|
|
5743
|
-
}
|
|
5744
|
-
const workflowId = await resolvePlayRunId(client, target);
|
|
5745
|
-
const result = await client.stopPlay(workflowId, { reason });
|
|
5746
|
-
if (jsonOutput) {
|
|
5747
|
-
process.stdout.write(`${JSON.stringify(result)}
|
|
5748
|
-
`);
|
|
5749
|
-
} else {
|
|
5750
|
-
console.log(`Stopped ${result.runId}`);
|
|
5751
|
-
if (result.hitlCancelledCount > 0) {
|
|
5752
|
-
console.log(` cancelled HITL waits: ${result.hitlCancelledCount}`);
|
|
5753
|
-
}
|
|
5754
|
-
}
|
|
5755
|
-
return 0;
|
|
5756
|
-
}
|
|
5757
6261
|
async function handlePlayGet(args) {
|
|
5758
6262
|
const target = args[0];
|
|
5759
6263
|
if (!target) {
|
|
@@ -5761,8 +6265,9 @@ async function handlePlayGet(args) {
|
|
|
5761
6265
|
return 1;
|
|
5762
6266
|
}
|
|
5763
6267
|
const client = new DeeplineClient();
|
|
5764
|
-
const
|
|
6268
|
+
const explicitJson = args.includes("--json");
|
|
5765
6269
|
const sourceOutput = args.includes("--source");
|
|
6270
|
+
const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
|
|
5766
6271
|
let outPath = null;
|
|
5767
6272
|
for (let index = 1; index < args.length; index += 1) {
|
|
5768
6273
|
const arg = args[index];
|
|
@@ -5773,7 +6278,7 @@ async function handlePlayGet(args) {
|
|
|
5773
6278
|
const playName = isFileTarget(target) ? extractPlayName((0, import_node_fs6.readFileSync)((0, import_node_path8.resolve)(target), "utf-8"), (0, import_node_path8.resolve)(target)) : parseReferencedPlayTarget(target).playName;
|
|
5774
6279
|
const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
|
|
5775
6280
|
const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
|
|
5776
|
-
const materializedFile =
|
|
6281
|
+
const materializedFile = outPath ? materializeRemotePlaySource({
|
|
5777
6282
|
target,
|
|
5778
6283
|
playName,
|
|
5779
6284
|
sourceCode: resolvedSource,
|
|
@@ -5813,6 +6318,10 @@ async function handlePlayGet(args) {
|
|
|
5813
6318
|
}
|
|
5814
6319
|
return 0;
|
|
5815
6320
|
}
|
|
6321
|
+
if (outPath && loadedMessage) {
|
|
6322
|
+
console.log(loadedMessage);
|
|
6323
|
+
return 0;
|
|
6324
|
+
}
|
|
5816
6325
|
console.log(`Play: ${formatPlayReference(detail.play)}`);
|
|
5817
6326
|
console.log(
|
|
5818
6327
|
`Working version: ${detail.play.workingRevision?.version ?? "\u2014"}`
|
|
@@ -5836,33 +6345,6 @@ async function handlePlayGet(args) {
|
|
|
5836
6345
|
}
|
|
5837
6346
|
return 0;
|
|
5838
6347
|
}
|
|
5839
|
-
async function handlePlayRuns(args) {
|
|
5840
|
-
const nameIndex = args.indexOf("--name");
|
|
5841
|
-
const name = nameIndex >= 0 ? args[nameIndex + 1] : void 0;
|
|
5842
|
-
if (!name) {
|
|
5843
|
-
console.error("Usage: deepline play runs --name <name> [--json]");
|
|
5844
|
-
return 1;
|
|
5845
|
-
}
|
|
5846
|
-
const client = new DeeplineClient();
|
|
5847
|
-
const jsonOutput = argsWantJson(args);
|
|
5848
|
-
await assertCanonicalNamedPlayReference(client, name);
|
|
5849
|
-
const runs = await client.listPlayRuns(
|
|
5850
|
-
parseReferencedPlayTarget(name).playName
|
|
5851
|
-
);
|
|
5852
|
-
if (jsonOutput) {
|
|
5853
|
-
process.stdout.write(`${JSON.stringify({ runs })}
|
|
5854
|
-
`);
|
|
5855
|
-
return 0;
|
|
5856
|
-
}
|
|
5857
|
-
if (runs.length === 0) {
|
|
5858
|
-
console.log(`No runs found for ${name}.`);
|
|
5859
|
-
return 0;
|
|
5860
|
-
}
|
|
5861
|
-
for (const run of runs) {
|
|
5862
|
-
console.log(formatRunLine(run));
|
|
5863
|
-
}
|
|
5864
|
-
return 0;
|
|
5865
|
-
}
|
|
5866
6348
|
function formatVersionLine(version) {
|
|
5867
6349
|
const revisionLabel = version.artifactHash?.slice(0, 12) ?? "unknown-revision";
|
|
5868
6350
|
return `v${version.version} ${revisionLabel} ${formatTimestamp(version.createdAt)}`;
|
|
@@ -5979,7 +6461,26 @@ function printPlayDescription(play) {
|
|
|
5979
6461
|
console.log(` ${line}`);
|
|
5980
6462
|
}
|
|
5981
6463
|
}
|
|
6464
|
+
if (play.csvInput) {
|
|
6465
|
+
console.log(" CSV input:");
|
|
6466
|
+
const rendered = JSON.stringify(play.csvInput, null, 2);
|
|
6467
|
+
for (const line of rendered.split("\n")) {
|
|
6468
|
+
console.log(` ${line}`);
|
|
6469
|
+
}
|
|
6470
|
+
}
|
|
6471
|
+
if (play.rowOutputSchema) {
|
|
6472
|
+
console.log(" Row output schema:");
|
|
6473
|
+
const rendered = JSON.stringify(play.rowOutputSchema, null, 2);
|
|
6474
|
+
for (const line of rendered.split("\n")) {
|
|
6475
|
+
console.log(` ${line}`);
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
5982
6478
|
console.log(` Run: ${play.runCommand}`);
|
|
6479
|
+
if (play.origin === "prebuilt" && !play.canEdit) {
|
|
6480
|
+
console.log(
|
|
6481
|
+
` Customize: deepline plays get ${reference} --source --out ./my-play.play.ts`
|
|
6482
|
+
);
|
|
6483
|
+
}
|
|
5983
6484
|
}
|
|
5984
6485
|
async function handlePlaySearch(args) {
|
|
5985
6486
|
let options;
|
|
@@ -6077,6 +6578,7 @@ async function handlePlayPublish(args) {
|
|
|
6077
6578
|
const published = await client.registerPlayArtifact({
|
|
6078
6579
|
name: rootPlayName,
|
|
6079
6580
|
sourceCode: graph.root.sourceCode,
|
|
6581
|
+
sourceFiles: graph.root.sourceFiles,
|
|
6080
6582
|
artifact: graph.root.artifact,
|
|
6081
6583
|
compilerManifest: requireCompilerManifest(graph.root),
|
|
6082
6584
|
publish: true
|
|
@@ -6118,6 +6620,45 @@ async function handlePlayPublish(args) {
|
|
|
6118
6620
|
`);
|
|
6119
6621
|
return result.success ? 0 : 1;
|
|
6120
6622
|
}
|
|
6623
|
+
async function handlePlayDelete(args) {
|
|
6624
|
+
const playName = args[0];
|
|
6625
|
+
if (!playName) {
|
|
6626
|
+
console.error("Usage: deepline plays delete <play-name> --yes [--json]");
|
|
6627
|
+
return 1;
|
|
6628
|
+
}
|
|
6629
|
+
const confirmed = args.includes("--yes") || args.includes("-y") || args.includes("--force");
|
|
6630
|
+
if (!confirmed) {
|
|
6631
|
+
console.error(
|
|
6632
|
+
"Refusing to delete without --yes. This deletes the org-owned play, its revisions, trigger bindings, and local run records."
|
|
6633
|
+
);
|
|
6634
|
+
return 1;
|
|
6635
|
+
}
|
|
6636
|
+
const client = new DeeplineClient();
|
|
6637
|
+
let detail;
|
|
6638
|
+
try {
|
|
6639
|
+
detail = await client.getPlay(parseReferencedPlayTarget(playName).playName);
|
|
6640
|
+
} catch (error) {
|
|
6641
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
6642
|
+
return 1;
|
|
6643
|
+
}
|
|
6644
|
+
if (detail.play.ownerType === "deepline" || detail.play.origin === "prebuilt") {
|
|
6645
|
+
console.error(`Cannot delete prebuilt play: ${formatPlayReference(detail.play)}`);
|
|
6646
|
+
return 1;
|
|
6647
|
+
}
|
|
6648
|
+
const result = await client.deletePlay(
|
|
6649
|
+
parseReferencedPlayTarget(formatPlayReference(detail.play)).playName
|
|
6650
|
+
);
|
|
6651
|
+
if (argsWantJson(args)) {
|
|
6652
|
+
process.stdout.write(`${JSON.stringify(result)}
|
|
6653
|
+
`);
|
|
6654
|
+
return result.deleted ? 0 : 1;
|
|
6655
|
+
}
|
|
6656
|
+
process.stdout.write(
|
|
6657
|
+
`Deleted ${result.name}: revisions=${result.deletedRevisionCount}, bindings=${result.deletedBindingCount}, runs=${result.deletedRunCount}
|
|
6658
|
+
`
|
|
6659
|
+
);
|
|
6660
|
+
return result.deleted ? 0 : 1;
|
|
6661
|
+
}
|
|
6121
6662
|
function registerPlayCommands(program) {
|
|
6122
6663
|
const play = program.command("plays").alias("play").description("Search, validate, run, and manage cloud plays.").addHelpText(
|
|
6123
6664
|
"after",
|
|
@@ -6151,21 +6692,23 @@ Examples:
|
|
|
6151
6692
|
...options.json ? ["--json"] : []
|
|
6152
6693
|
]);
|
|
6153
6694
|
});
|
|
6154
|
-
play.command("run [target]").description("Run a play file or named play.").addHelpText(
|
|
6695
|
+
play.command("run [target]").description("Run a play file or named play.").allowUnknownOption(true).allowExcessArguments(true).addHelpText(
|
|
6155
6696
|
"after",
|
|
6156
6697
|
`
|
|
6157
6698
|
Notes:
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6699
|
+
Local play files are bundled locally, then validated and executed in Deepline cloud.
|
|
6700
|
+
Named plays run the stored live cloud revision.
|
|
6701
|
+
Unknown --foo and --foo.bar flags are treated as play input args.
|
|
6702
|
+
File-like input args accept local paths; the CLI stages those files before submit.
|
|
6703
|
+
Run performs server preflight automatically. Use \`deepline plays check <file>\`
|
|
6704
|
+
to validate without starting a run.
|
|
6162
6705
|
|
|
6163
6706
|
Examples:
|
|
6164
6707
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
|
|
6165
6708
|
deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
|
|
6166
6709
|
deepline plays run enrich.play.ts --csv leads.csv --watch --out leads-enriched.csv
|
|
6167
6710
|
`
|
|
6168
|
-
).option("--file <path>", "Local play file to run").option("--name <name>", "Saved play name to run").option("
|
|
6711
|
+
).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(
|
|
6169
6712
|
"--revision-id <id>",
|
|
6170
6713
|
"Run a specific saved revision instead of the live revision"
|
|
6171
6714
|
).option(
|
|
@@ -6174,12 +6717,21 @@ Examples:
|
|
|
6174
6717
|
).option("--watch", "Stream logs until completion").option(
|
|
6175
6718
|
"--logs",
|
|
6176
6719
|
"When output is non-interactive, stream play logs to stderr while waiting"
|
|
6177
|
-
).option("--poll-interval-ms <ms>", "Polling interval while tailing").option("--tail-timeout-ms <ms>", "Timeout while tailing").option("--force", "Supersede any active runs for this play").option("--json", "Emit JSON output").action(async (target, options) => {
|
|
6720
|
+
).option("--poll-interval-ms <ms>", "Polling interval while tailing").option("--tail-timeout-ms <ms>", "Timeout while tailing").option("--force", "Supersede any active runs for this play").option("--json", "Emit JSON output").action(async (target, options, command) => {
|
|
6721
|
+
const passthroughArgs = [...command.args];
|
|
6722
|
+
const explicitTarget = options.file || options.name;
|
|
6723
|
+
const targetIsInputFlag = typeof target === "string" && target.startsWith("--");
|
|
6724
|
+
const effectiveTarget = explicitTarget || targetIsInputFlag ? null : target;
|
|
6725
|
+
if (explicitTarget && typeof target === "string" && !targetIsInputFlag && !passthroughArgs.includes(target)) {
|
|
6726
|
+
passthroughArgs.push(target);
|
|
6727
|
+
}
|
|
6728
|
+
if (effectiveTarget && passthroughArgs[0] === effectiveTarget) {
|
|
6729
|
+
passthroughArgs.shift();
|
|
6730
|
+
}
|
|
6178
6731
|
process.exitCode = await handlePlayRun([
|
|
6179
|
-
...
|
|
6732
|
+
...effectiveTarget ? [effectiveTarget] : [],
|
|
6180
6733
|
...options.file ? ["--file", options.file] : [],
|
|
6181
6734
|
...options.name ? ["--name", options.name] : [],
|
|
6182
|
-
...options.csv ? ["--csv", options.csv] : [],
|
|
6183
6735
|
...options.input ? ["--input", options.input] : [],
|
|
6184
6736
|
...options.live ? ["--live"] : [],
|
|
6185
6737
|
...options.latest ? ["--latest"] : [],
|
|
@@ -6190,7 +6742,8 @@ Examples:
|
|
|
6190
6742
|
...options.pollIntervalMs ? ["--poll-interval-ms", options.pollIntervalMs] : [],
|
|
6191
6743
|
...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
|
|
6192
6744
|
...options.force ? ["--force"] : [],
|
|
6193
|
-
...options.json ? ["--json"] : []
|
|
6745
|
+
...options.json ? ["--json"] : [],
|
|
6746
|
+
...passthroughArgs
|
|
6194
6747
|
]);
|
|
6195
6748
|
});
|
|
6196
6749
|
play.command("get <target>").description("Fetch full play details.").addHelpText(
|
|
@@ -6204,12 +6757,10 @@ Notes:
|
|
|
6204
6757
|
Examples:
|
|
6205
6758
|
deepline plays get person-linkedin-to-email
|
|
6206
6759
|
deepline plays get person-linkedin-to-email --json | jq '.play.liveRevision'
|
|
6760
|
+
deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source > email-waterfall.play.ts
|
|
6761
|
+
deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source --out ./email-waterfall.play.ts
|
|
6207
6762
|
`
|
|
6208
|
-
).option("--json", "Emit JSON output. Also automatic when stdout is piped").
|
|
6209
|
-
new import_commander.Option("--source", "Materialize or print the source code").hideHelp()
|
|
6210
|
-
).addOption(
|
|
6211
|
-
new import_commander.Option("--out <path>", "Write source to a specific path").hideHelp()
|
|
6212
|
-
).action(async (target, options) => {
|
|
6763
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--source", "Print raw source code; combine with --out to write a file").option("--out <path>", "Write source to a specific path").action(async (target, options) => {
|
|
6213
6764
|
process.exitCode = await handlePlayGet([
|
|
6214
6765
|
target,
|
|
6215
6766
|
...options.json ? ["--json"] : [],
|
|
@@ -6248,50 +6799,12 @@ Examples:
|
|
|
6248
6799
|
...options.json ? ["--json"] : []
|
|
6249
6800
|
]);
|
|
6250
6801
|
});
|
|
6251
|
-
play.command("runs").description("List runs for a named play.").option("--name <name>", "Saved play name").option("--json", "Emit JSON output").action(async (options) => {
|
|
6252
|
-
process.exitCode = await handlePlayRuns([
|
|
6253
|
-
...options.name ? ["--name", options.name] : [],
|
|
6254
|
-
...options.json ? ["--json"] : []
|
|
6255
|
-
]);
|
|
6256
|
-
});
|
|
6257
6802
|
play.command("versions").description("List revisions for a named play.").option("--name <name>", "Saved play name").option("--json", "Emit JSON output").action(async (options) => {
|
|
6258
6803
|
process.exitCode = await handlePlayVersions([
|
|
6259
6804
|
...options.name ? ["--name", options.name] : [],
|
|
6260
6805
|
...options.json ? ["--json"] : []
|
|
6261
6806
|
]);
|
|
6262
6807
|
});
|
|
6263
|
-
play.command("tail").description("Tail events for a play run.").option("--run-id <runId>", "Run id to tail").option("--name <name>", "Tail the latest run for a named play").option("--interval-ms <ms>", "Polling interval while tailing").option("--logs", "With --json, stream play logs to stderr while waiting").option("--json", "Emit JSON output").action(async (options) => {
|
|
6264
|
-
process.exitCode = await handlePlayTail([
|
|
6265
|
-
...options.runId ? ["--run-id", options.runId] : [],
|
|
6266
|
-
...options.name ? ["--name", options.name] : [],
|
|
6267
|
-
...options.intervalMs ? ["--interval-ms", options.intervalMs] : [],
|
|
6268
|
-
...options.logs ? ["--logs"] : [],
|
|
6269
|
-
...options.json ? ["--json"] : []
|
|
6270
|
-
]);
|
|
6271
|
-
});
|
|
6272
|
-
play.command("status").description("Show status for a play run.").option("--run-id <runId>", "Run id to inspect").option("--name <name>", "Inspect the latest run for a named play").option("--json", "Emit JSON output").option("--full", "With --json, emit the full raw status payload").action(async (options) => {
|
|
6273
|
-
process.exitCode = await handlePlayStatus([
|
|
6274
|
-
...options.runId ? ["--run-id", options.runId] : [],
|
|
6275
|
-
...options.name ? ["--name", options.name] : [],
|
|
6276
|
-
...options.json ? ["--json"] : [],
|
|
6277
|
-
...options.full ? ["--full"] : []
|
|
6278
|
-
]);
|
|
6279
|
-
});
|
|
6280
|
-
play.command("export").description("Export a completed play run to CSV.").requiredOption("--run-id <runId>", "Run id to export").requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
|
|
6281
|
-
process.exitCode = await handleRunExport([
|
|
6282
|
-
options.runId,
|
|
6283
|
-
"--out",
|
|
6284
|
-
options.out,
|
|
6285
|
-
...options.json ? ["--json"] : []
|
|
6286
|
-
]);
|
|
6287
|
-
});
|
|
6288
|
-
play.command("stop").description("Stop a play run.").option("--run-id <runId>", "Run id to stop").option("--reason <text>", "Reason to include with the stop request").option("--json", "Emit JSON output").action(async (options) => {
|
|
6289
|
-
process.exitCode = await handlePlayStop([
|
|
6290
|
-
...options.runId ? ["--run-id", options.runId] : [],
|
|
6291
|
-
...options.reason ? ["--reason", options.reason] : [],
|
|
6292
|
-
...options.json ? ["--json"] : []
|
|
6293
|
-
]);
|
|
6294
|
-
});
|
|
6295
6808
|
play.command("publish <target>").description("Bundle, validate, save, and publish a play.").option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option("--json", "Emit JSON output").action(async (target, options) => {
|
|
6296
6809
|
process.exitCode = await handlePlayPublish([
|
|
6297
6810
|
target,
|
|
@@ -6300,38 +6813,79 @@ Examples:
|
|
|
6300
6813
|
...options.json ? ["--json"] : []
|
|
6301
6814
|
]);
|
|
6302
6815
|
});
|
|
6303
|
-
|
|
6816
|
+
play.command("delete <target>").description("Delete an org-owned play and its saved revisions/runs.").option("-y, --yes", "Confirm deletion").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
|
|
6817
|
+
process.exitCode = await handlePlayDelete([
|
|
6818
|
+
target,
|
|
6819
|
+
...options.yes ? ["--yes"] : [],
|
|
6820
|
+
...options.json ? ["--json"] : []
|
|
6821
|
+
]);
|
|
6822
|
+
});
|
|
6823
|
+
const runs = program.command("runs").description("Inspect, tail, stop, and export play runs.").addHelpText(
|
|
6304
6824
|
"after",
|
|
6305
6825
|
`
|
|
6306
6826
|
Examples:
|
|
6307
|
-
deepline runs
|
|
6827
|
+
deepline runs get play/my-play/run/20260501t000000-000 --json
|
|
6828
|
+
deepline runs tail play/my-play/run/20260501t000000-000
|
|
6829
|
+
deepline runs logs play/my-play/run/20260501t000000-000 --out run.log --json
|
|
6830
|
+
deepline runs list --play my-play --status failed --json
|
|
6831
|
+
deepline runs stop play/my-play/run/20260501t000000-000 --reason "stale lock" --json
|
|
6308
6832
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
6309
|
-
deepline runs logs play/my-play/run/20260501t000000-000
|
|
6310
6833
|
`
|
|
6311
6834
|
);
|
|
6312
|
-
runs.command("
|
|
6313
|
-
process.exitCode = await
|
|
6835
|
+
runs.command("get <runId>").description("Get status, progress, outputs, errors, and recovery metadata for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--full", "Debug only: with --json, emit the raw status payload").action(async (runId, options) => {
|
|
6836
|
+
process.exitCode = await handleRunGet([
|
|
6314
6837
|
runId,
|
|
6315
6838
|
...options.json ? ["--json"] : [],
|
|
6316
6839
|
...options.full ? ["--full"] : []
|
|
6317
6840
|
]);
|
|
6318
6841
|
});
|
|
6319
|
-
runs.command("
|
|
6320
|
-
process.exitCode = await
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
options.
|
|
6842
|
+
runs.command("list").description("List play runs.").requiredOption("--play <name>", "Play name to filter runs").option("--status <status>", "Filter by run status").option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
|
|
6843
|
+
process.exitCode = await handleRunsList([
|
|
6844
|
+
"--play",
|
|
6845
|
+
options.play,
|
|
6846
|
+
...options.status ? ["--status", options.status] : [],
|
|
6847
|
+
...options.compact ? ["--compact"] : [],
|
|
6324
6848
|
...options.json ? ["--json"] : []
|
|
6325
6849
|
]);
|
|
6326
6850
|
});
|
|
6327
|
-
runs.command("
|
|
6851
|
+
runs.command("tail <runId>").description("Tail or fetch recent live events for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--compact", "Drop verbose fields from JSON output").option("--cursor <cursor>", "Resume after a previously returned event cursor").action(async (runId, options) => {
|
|
6852
|
+
process.exitCode = await handleRunTail([
|
|
6853
|
+
runId,
|
|
6854
|
+
...options.json ? ["--json"] : [],
|
|
6855
|
+
...options.compact ? ["--compact"] : [],
|
|
6856
|
+
...options.cursor ? ["--cursor", options.cursor] : []
|
|
6857
|
+
]);
|
|
6858
|
+
});
|
|
6859
|
+
runs.command("logs <runId>").description("Fetch persisted logs for a play run.").option("--limit <count>", "Maximum recent log lines to print without --out", "200").option("--out <path>", "Write the full persisted log stream to a file").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
6328
6860
|
process.exitCode = await handleRunLogs([
|
|
6329
6861
|
runId,
|
|
6862
|
+
...options.limit ? ["--limit", options.limit] : [],
|
|
6863
|
+
...options.out ? ["--out", options.out] : [],
|
|
6864
|
+
...options.json ? ["--json"] : []
|
|
6865
|
+
]);
|
|
6866
|
+
});
|
|
6867
|
+
runs.command("stop <runId>").description("Stop a play run.").option("--reason <text>", "Reason to include with the stop request").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
6868
|
+
process.exitCode = await handleRunStop([
|
|
6869
|
+
runId,
|
|
6870
|
+
...options.reason ? ["--reason", options.reason] : [],
|
|
6871
|
+
...options.json ? ["--json"] : []
|
|
6872
|
+
]);
|
|
6873
|
+
});
|
|
6874
|
+
runs.command("export <runId>").description("Export the completed row output for a play run to CSV.").requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
6875
|
+
process.exitCode = await handleRunExport([
|
|
6876
|
+
runId,
|
|
6877
|
+
"--out",
|
|
6878
|
+
options.out,
|
|
6330
6879
|
...options.json ? ["--json"] : []
|
|
6331
6880
|
]);
|
|
6332
6881
|
});
|
|
6333
6882
|
}
|
|
6334
6883
|
|
|
6884
|
+
// src/cli/commands/tools.ts
|
|
6885
|
+
var import_node_fs8 = require("fs");
|
|
6886
|
+
var import_node_os7 = require("os");
|
|
6887
|
+
var import_node_path10 = require("path");
|
|
6888
|
+
|
|
6335
6889
|
// src/tool-output.ts
|
|
6336
6890
|
var import_node_fs7 = require("fs");
|
|
6337
6891
|
var import_node_os6 = require("os");
|
|
@@ -6500,31 +7054,23 @@ async function listTools(args) {
|
|
|
6500
7054
|
}
|
|
6501
7055
|
return 0;
|
|
6502
7056
|
}
|
|
6503
|
-
function
|
|
6504
|
-
const
|
|
6505
|
-
tool.toolId,
|
|
6506
|
-
tool.provider,
|
|
6507
|
-
tool.displayName,
|
|
6508
|
-
tool.description,
|
|
6509
|
-
tool.operation,
|
|
6510
|
-
tool.operationId,
|
|
6511
|
-
...tool.operationAliases ?? [],
|
|
6512
|
-
...tool.categories ?? [],
|
|
6513
|
-
tool.inputSchema ? JSON.stringify(tool.inputSchema) : ""
|
|
6514
|
-
].filter(Boolean).join(" ").toLowerCase();
|
|
6515
|
-
return terms.every((term) => haystack.includes(term));
|
|
6516
|
-
}
|
|
6517
|
-
async function searchTools(args) {
|
|
6518
|
-
const query = args[0]?.trim();
|
|
7057
|
+
async function searchTools(queryInput, options = {}) {
|
|
7058
|
+
const query = queryInput.trim();
|
|
6519
7059
|
if (!query) {
|
|
6520
7060
|
console.error("Usage: deepline tools search <query> [--json]");
|
|
6521
7061
|
return 1;
|
|
6522
7062
|
}
|
|
6523
|
-
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
6524
7063
|
const client = new DeeplineClient();
|
|
6525
|
-
const
|
|
6526
|
-
|
|
6527
|
-
|
|
7064
|
+
const result = await client.searchTools({
|
|
7065
|
+
query,
|
|
7066
|
+
categories: options.categories,
|
|
7067
|
+
searchTerms: options.searchTerms,
|
|
7068
|
+
searchMode: options.searchMode,
|
|
7069
|
+
includeSearchDebug: options.includeSearchDebug
|
|
7070
|
+
});
|
|
7071
|
+
const items = result.tools.map(toListedTool);
|
|
7072
|
+
if (options.json || shouldEmitJson()) {
|
|
7073
|
+
process.stdout.write(`${JSON.stringify({ ...result, tools: items })}
|
|
6528
7074
|
`);
|
|
6529
7075
|
return 0;
|
|
6530
7076
|
}
|
|
@@ -6578,11 +7124,14 @@ Common commands:
|
|
|
6578
7124
|
...options.json ? ["--json"] : []
|
|
6579
7125
|
]);
|
|
6580
7126
|
});
|
|
6581
|
-
tools.command("search <query>").description("Search available tools.").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
6582
|
-
process.exitCode = await searchTools(
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
7127
|
+
tools.command("search <query>").description("Search available tools.").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) => {
|
|
7128
|
+
process.exitCode = await searchTools(query, {
|
|
7129
|
+
json: options.json,
|
|
7130
|
+
categories: options.categories,
|
|
7131
|
+
searchTerms: options.searchTerms ?? options.search_terms,
|
|
7132
|
+
searchMode: options.searchMode === "v1" || options.searchMode === "v2" ? options.searchMode : void 0,
|
|
7133
|
+
includeSearchDebug: Boolean(options.includeSearchDebug)
|
|
7134
|
+
});
|
|
6586
7135
|
});
|
|
6587
7136
|
tools.command("get <toolId>").alias("describe").description("Show metadata for a tool.").addHelpText(
|
|
6588
7137
|
"after",
|
|
@@ -6664,7 +7213,7 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
6664
7213
|
const operation = typeof tool.operation === "string" ? tool.operation : "";
|
|
6665
7214
|
const displayBase = operation && operation.startsWith(`${tool.provider}_`) ? operation.slice(String(tool.provider).length + 1) : operation ? `${tool.provider} ${operation}`.trim() : toolId;
|
|
6666
7215
|
const displayName = titleCase(displayBase || String(tool.displayName || toolId));
|
|
6667
|
-
const cost =
|
|
7216
|
+
const cost = isRecord3(tool.cost) ? tool.cost : null;
|
|
6668
7217
|
const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
|
|
6669
7218
|
const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
|
|
6670
7219
|
const deeplineUsdPerCredit = numberField(tool, "deeplineUsdPerCredit", "deepline_usd_per_credit");
|
|
@@ -6709,7 +7258,7 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
6709
7258
|
if (stepContributions.length) {
|
|
6710
7259
|
console.log(" step contributions:");
|
|
6711
7260
|
for (const item of stepContributions) {
|
|
6712
|
-
if (!
|
|
7261
|
+
if (!isRecord3(item)) continue;
|
|
6713
7262
|
const stepTool = typeof item.tool === "string" ? item.tool.trim() : "";
|
|
6714
7263
|
const low = typeof item.lowCredits === "number" ? item.lowCredits : null;
|
|
6715
7264
|
const high = typeof item.highCredits === "number" ? item.highCredits : null;
|
|
@@ -6796,12 +7345,12 @@ function printToolCost(input) {
|
|
|
6796
7345
|
return false;
|
|
6797
7346
|
}
|
|
6798
7347
|
function toolInputFieldsForDisplay(inputSchema) {
|
|
6799
|
-
if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(
|
|
6800
|
-
const jsonSchema =
|
|
6801
|
-
const properties =
|
|
7348
|
+
if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord3);
|
|
7349
|
+
const jsonSchema = isRecord3(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
|
|
7350
|
+
const properties = isRecord3(jsonSchema.properties) ? jsonSchema.properties : {};
|
|
6802
7351
|
const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
|
|
6803
7352
|
return Object.entries(properties).map(([name, value]) => {
|
|
6804
|
-
const property =
|
|
7353
|
+
const property = isRecord3(value) ? value : {};
|
|
6805
7354
|
return {
|
|
6806
7355
|
name,
|
|
6807
7356
|
type: typeof property.type === "string" ? property.type : "unknown",
|
|
@@ -6828,7 +7377,7 @@ function printJsonPreview(label, payload) {
|
|
|
6828
7377
|
}
|
|
6829
7378
|
function samplePayload(samples, key) {
|
|
6830
7379
|
const entry = samples[key];
|
|
6831
|
-
if (!
|
|
7380
|
+
if (!isRecord3(entry)) return void 0;
|
|
6832
7381
|
return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
|
|
6833
7382
|
}
|
|
6834
7383
|
function isPlayTool(tool) {
|
|
@@ -6849,7 +7398,7 @@ function formatDecimal(value) {
|
|
|
6849
7398
|
function formatUsd(value) {
|
|
6850
7399
|
return `$${formatDecimal(value)}`;
|
|
6851
7400
|
}
|
|
6852
|
-
function
|
|
7401
|
+
function isRecord3(value) {
|
|
6853
7402
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
6854
7403
|
}
|
|
6855
7404
|
function stringField(source, ...keys) {
|
|
@@ -6876,7 +7425,7 @@ function arrayField(source, ...keys) {
|
|
|
6876
7425
|
function recordField(source, ...keys) {
|
|
6877
7426
|
for (const key of keys) {
|
|
6878
7427
|
const value = source[key];
|
|
6879
|
-
if (
|
|
7428
|
+
if (isRecord3(value)) return value;
|
|
6880
7429
|
}
|
|
6881
7430
|
return {};
|
|
6882
7431
|
}
|
|
@@ -6940,6 +7489,61 @@ function parseExecuteOptions(args) {
|
|
|
6940
7489
|
}
|
|
6941
7490
|
return { toolId, params, outputFormat, noPreview };
|
|
6942
7491
|
}
|
|
7492
|
+
function safeFileStem(value) {
|
|
7493
|
+
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
7494
|
+
}
|
|
7495
|
+
function shellQuote(value) {
|
|
7496
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
7497
|
+
}
|
|
7498
|
+
function powerShellQuote(value) {
|
|
7499
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
7500
|
+
}
|
|
7501
|
+
function seedToolListScript(input) {
|
|
7502
|
+
const stem = safeFileStem(input.toolId);
|
|
7503
|
+
const fileName = `${stem}-workflow-seed-${Date.now()}.play.ts`;
|
|
7504
|
+
const scriptDir = (0, import_node_fs8.mkdtempSync)((0, import_node_path10.join)((0, import_node_os7.tmpdir)(), "deepline-workflow-seed-"));
|
|
7505
|
+
(0, import_node_fs8.chmodSync)(scriptDir, 448);
|
|
7506
|
+
const scriptPath = (0, import_node_path10.join)(scriptDir, fileName);
|
|
7507
|
+
const projectDir = `deepline/projects/${stem}-workflow`;
|
|
7508
|
+
const playName = `${stem}-workflow`;
|
|
7509
|
+
const sampleRows = input.rows.length > 0 ? `${JSON.stringify(input.rows.slice(0, 2)).replace(/\]$/, "")}, ...]` : "[]";
|
|
7510
|
+
const columns = Object.keys(input.rows[0] ?? {}).join(", ");
|
|
7511
|
+
const rowKey = Object.prototype.hasOwnProperty.call(input.rows[0] ?? {}, "id") ? '"id"' : "(row) => JSON.stringify(row)";
|
|
7512
|
+
const script = `import { definePlay } from 'deepline';
|
|
7513
|
+
|
|
7514
|
+
export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
7515
|
+
const result = await ctx.tools.execute({
|
|
7516
|
+
id: ${JSON.stringify(input.toolId)},
|
|
7517
|
+
tool: ${JSON.stringify(input.toolId)},
|
|
7518
|
+
input: ${JSON.stringify(input.payload)},
|
|
7519
|
+
description: ${JSON.stringify(`Seed ${input.toolId} rows for workflow expansion.`)},
|
|
7520
|
+
});
|
|
7521
|
+
|
|
7522
|
+
const list = Object.values(result.lists)[0];
|
|
7523
|
+
const rows = (list?.get() ?? []).slice(0, 100);
|
|
7524
|
+
// ${sampleRows}
|
|
7525
|
+
// columns: ${columns}
|
|
7526
|
+
// .step('email_waterfall', (row, rowCtx) => rowCtx.runPlay('name_domain_email', 'name-and-domain-to-email', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), domain: String(row.domain ?? '') }, { description: 'Resolve email.' }))
|
|
7527
|
+
// .step('phone_waterfall', (row, rowCtx) => rowCtx.runPlay('contact_phone', 'contact-to-phone', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), email: String(row.email ?? '') }, { description: 'Resolve phone.' }))
|
|
7528
|
+
// ctx.map is idempotent by map key + row key; reruns reuse completed rows.
|
|
7529
|
+
const enrichedData = await ctx
|
|
7530
|
+
.map('enriched_data', rows, { key: ${rowKey} })
|
|
7531
|
+
.run({ description: 'Enrich seeded rows.' });
|
|
7532
|
+
|
|
7533
|
+
return {
|
|
7534
|
+
rows: enrichedData,
|
|
7535
|
+
count: await enrichedData.count(),
|
|
7536
|
+
};
|
|
7537
|
+
});
|
|
7538
|
+
`;
|
|
7539
|
+
(0, import_node_fs8.writeFileSync)(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
7540
|
+
return {
|
|
7541
|
+
path: scriptPath,
|
|
7542
|
+
projectDir,
|
|
7543
|
+
macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
|
|
7544
|
+
windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
|
|
7545
|
+
};
|
|
7546
|
+
}
|
|
6943
7547
|
async function executeTool(args) {
|
|
6944
7548
|
let parsed;
|
|
6945
7549
|
try {
|
|
@@ -6967,7 +7571,7 @@ async function executeTool(args) {
|
|
|
6967
7571
|
}
|
|
6968
7572
|
throw error;
|
|
6969
7573
|
}
|
|
6970
|
-
const rawResponse = await client.
|
|
7574
|
+
const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
|
|
6971
7575
|
const listConversion = tryConvertToList(rawResponse, {
|
|
6972
7576
|
listExtractorPaths: metadata.listExtractorPaths ?? []
|
|
6973
7577
|
});
|
|
@@ -6993,6 +7597,11 @@ async function executeTool(args) {
|
|
|
6993
7597
|
return 0;
|
|
6994
7598
|
}
|
|
6995
7599
|
const csv = writeCsvOutputFile(listConversion.rows, `${parsed.toolId}_output`);
|
|
7600
|
+
const seededScript = seedToolListScript({
|
|
7601
|
+
toolId: parsed.toolId,
|
|
7602
|
+
payload: parsed.params,
|
|
7603
|
+
rows: listConversion.rows
|
|
7604
|
+
});
|
|
6996
7605
|
if (parsed.outputFormat === "csv_file") {
|
|
6997
7606
|
process.stdout.write(`${JSON.stringify({
|
|
6998
7607
|
extracted_csv: csv.path,
|
|
@@ -7001,6 +7610,12 @@ async function executeTool(args) {
|
|
|
7001
7610
|
preview: csv.preview,
|
|
7002
7611
|
list_strategy: listConversion.strategy,
|
|
7003
7612
|
list_source_path: listConversion.sourcePath,
|
|
7613
|
+
starter_script: seededScript.path,
|
|
7614
|
+
project_dir: seededScript.projectDir,
|
|
7615
|
+
copy_to_project: {
|
|
7616
|
+
macos_linux: seededScript.macCopyCommand,
|
|
7617
|
+
windows_powershell: seededScript.windowsCopyCommand
|
|
7618
|
+
},
|
|
7004
7619
|
summary
|
|
7005
7620
|
})}
|
|
7006
7621
|
`);
|
|
@@ -7018,14 +7633,20 @@ async function executeTool(args) {
|
|
|
7018
7633
|
console.log(`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`);
|
|
7019
7634
|
}
|
|
7020
7635
|
console.log(`preview: ${JSON.stringify(csv.preview)}`);
|
|
7636
|
+
console.log(`starter script: ${seededScript.path}`);
|
|
7637
|
+
console.log(
|
|
7638
|
+
"next: Move the script into a project folder and expand it into a Deepline play. Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
|
|
7639
|
+
);
|
|
7640
|
+
console.log(`macOS/Linux: ${seededScript.macCopyCommand}`);
|
|
7641
|
+
console.log(`Windows PowerShell: ${seededScript.windowsCopyCommand}`);
|
|
7021
7642
|
return 0;
|
|
7022
7643
|
}
|
|
7023
7644
|
|
|
7024
7645
|
// src/cli/skills-sync.ts
|
|
7025
7646
|
var import_node_child_process2 = require("child_process");
|
|
7026
|
-
var
|
|
7027
|
-
var
|
|
7028
|
-
var
|
|
7647
|
+
var import_node_fs9 = require("fs");
|
|
7648
|
+
var import_node_os8 = require("os");
|
|
7649
|
+
var import_node_path11 = require("path");
|
|
7029
7650
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
7030
7651
|
var SDK_SKILL_NAME = "deepline-sdk";
|
|
7031
7652
|
var SKILL_AGENTS = ["codex", "claude-code", "cursor"];
|
|
@@ -7035,22 +7656,22 @@ function shouldSkipSkillsSync() {
|
|
|
7035
7656
|
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
7036
7657
|
}
|
|
7037
7658
|
function sdkSkillsVersionPath(baseUrl) {
|
|
7038
|
-
const home = process.env.HOME?.trim() || (0,
|
|
7039
|
-
return (0,
|
|
7659
|
+
const home = process.env.HOME?.trim() || (0, import_node_os8.homedir)();
|
|
7660
|
+
return (0, import_node_path11.join)(home, ".local", "deepline", baseUrlSlug(baseUrl), "sdk-skills", ".version");
|
|
7040
7661
|
}
|
|
7041
7662
|
function readLocalSkillsVersion(baseUrl) {
|
|
7042
7663
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
7043
|
-
if (!(0,
|
|
7664
|
+
if (!(0, import_node_fs9.existsSync)(path)) return "";
|
|
7044
7665
|
try {
|
|
7045
|
-
return (0,
|
|
7666
|
+
return (0, import_node_fs9.readFileSync)(path, "utf-8").trim();
|
|
7046
7667
|
} catch {
|
|
7047
7668
|
return "";
|
|
7048
7669
|
}
|
|
7049
7670
|
}
|
|
7050
7671
|
function writeLocalSkillsVersion(baseUrl, version) {
|
|
7051
7672
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
7052
|
-
(0,
|
|
7053
|
-
(0,
|
|
7673
|
+
(0, import_node_fs9.mkdirSync)((0, import_node_path11.dirname)(path), { recursive: true });
|
|
7674
|
+
(0, import_node_fs9.writeFileSync)(path, `${version}
|
|
7054
7675
|
`, "utf-8");
|
|
7055
7676
|
}
|
|
7056
7677
|
async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
@@ -7081,9 +7702,26 @@ async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
|
7081
7702
|
clearTimeout(timeout);
|
|
7082
7703
|
}
|
|
7083
7704
|
}
|
|
7084
|
-
function
|
|
7705
|
+
function buildSkillsInstallArgs(baseUrl) {
|
|
7706
|
+
const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
|
|
7707
|
+
return [
|
|
7708
|
+
"--yes",
|
|
7709
|
+
"skills",
|
|
7710
|
+
"add",
|
|
7711
|
+
packageUrl,
|
|
7712
|
+
"--agents",
|
|
7713
|
+
...SKILL_AGENTS,
|
|
7714
|
+
"--global",
|
|
7715
|
+
"--yes",
|
|
7716
|
+
"--skill",
|
|
7717
|
+
SDK_SKILL_NAME,
|
|
7718
|
+
"--full-depth"
|
|
7719
|
+
];
|
|
7720
|
+
}
|
|
7721
|
+
function buildBunxSkillsInstallArgs(baseUrl) {
|
|
7085
7722
|
const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
|
|
7086
|
-
|
|
7723
|
+
return [
|
|
7724
|
+
"--bun",
|
|
7087
7725
|
"skills",
|
|
7088
7726
|
"add",
|
|
7089
7727
|
packageUrl,
|
|
@@ -7095,8 +7733,40 @@ function runSkillsInstall(baseUrl) {
|
|
|
7095
7733
|
SDK_SKILL_NAME,
|
|
7096
7734
|
"--full-depth"
|
|
7097
7735
|
];
|
|
7736
|
+
}
|
|
7737
|
+
function hasCommand(command) {
|
|
7738
|
+
const result = (0, import_node_child_process2.spawnSync)(command, ["--version"], {
|
|
7739
|
+
stdio: "ignore",
|
|
7740
|
+
shell: process.platform === "win32"
|
|
7741
|
+
});
|
|
7742
|
+
return result.status === 0;
|
|
7743
|
+
}
|
|
7744
|
+
function shellQuote2(arg) {
|
|
7745
|
+
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
7746
|
+
}
|
|
7747
|
+
function resolveSkillsInstallCommands(baseUrl) {
|
|
7748
|
+
const npxArgs = buildSkillsInstallArgs(baseUrl);
|
|
7749
|
+
const npxInstall = {
|
|
7750
|
+
command: "npx",
|
|
7751
|
+
args: npxArgs,
|
|
7752
|
+
manualCommand: `npx ${npxArgs.map(shellQuote2).join(" ")}`
|
|
7753
|
+
};
|
|
7754
|
+
if (hasCommand("bunx")) {
|
|
7755
|
+
const bunxArgs = buildBunxSkillsInstallArgs(baseUrl);
|
|
7756
|
+
return [
|
|
7757
|
+
{
|
|
7758
|
+
command: "bunx",
|
|
7759
|
+
args: bunxArgs,
|
|
7760
|
+
manualCommand: `bunx ${bunxArgs.map(shellQuote2).join(" ")}`
|
|
7761
|
+
},
|
|
7762
|
+
npxInstall
|
|
7763
|
+
];
|
|
7764
|
+
}
|
|
7765
|
+
return [npxInstall];
|
|
7766
|
+
}
|
|
7767
|
+
function runOneSkillsInstall(install) {
|
|
7098
7768
|
return new Promise((resolve8) => {
|
|
7099
|
-
const child = (0, import_node_child_process2.spawn)(
|
|
7769
|
+
const child = (0, import_node_child_process2.spawn)(install.command, install.args, {
|
|
7100
7770
|
stdio: ["ignore", "ignore", "pipe"],
|
|
7101
7771
|
env: process.env
|
|
7102
7772
|
});
|
|
@@ -7105,37 +7775,111 @@ function runSkillsInstall(baseUrl) {
|
|
|
7105
7775
|
stderr += chunk.toString("utf-8");
|
|
7106
7776
|
});
|
|
7107
7777
|
child.on("error", (error) => {
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7778
|
+
resolve8({
|
|
7779
|
+
ok: false,
|
|
7780
|
+
detail: `failed to start ${install.command}: ${error.message}`,
|
|
7781
|
+
manualCommand: install.manualCommand
|
|
7782
|
+
});
|
|
7111
7783
|
});
|
|
7112
7784
|
child.on("close", (code) => {
|
|
7113
7785
|
if (code === 0) {
|
|
7114
|
-
resolve8(true);
|
|
7786
|
+
resolve8({ ok: true, detail: "", manualCommand: install.manualCommand });
|
|
7115
7787
|
return;
|
|
7116
7788
|
}
|
|
7117
7789
|
const detail = stderr.trim();
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
);
|
|
7123
|
-
resolve8(false);
|
|
7790
|
+
resolve8({
|
|
7791
|
+
ok: false,
|
|
7792
|
+
detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
|
|
7793
|
+
manualCommand: install.manualCommand
|
|
7794
|
+
});
|
|
7124
7795
|
});
|
|
7125
7796
|
});
|
|
7126
7797
|
}
|
|
7798
|
+
async function runSkillsInstall(baseUrl) {
|
|
7799
|
+
const failures = [];
|
|
7800
|
+
for (const install of resolveSkillsInstallCommands(baseUrl)) {
|
|
7801
|
+
const result = await runOneSkillsInstall(install);
|
|
7802
|
+
if (result.ok) return true;
|
|
7803
|
+
failures.push(result);
|
|
7804
|
+
}
|
|
7805
|
+
const details = failures.map((failure) => failure.detail).filter(Boolean).join("\n");
|
|
7806
|
+
const manualCommand = failures.at(-1)?.manualCommand;
|
|
7807
|
+
process.stderr.write(
|
|
7808
|
+
`SDK skills sync failed${details ? `:
|
|
7809
|
+
${details}` : ""}
|
|
7810
|
+
` + (manualCommand ? `Run manually: ${manualCommand}
|
|
7811
|
+
` : "")
|
|
7812
|
+
);
|
|
7813
|
+
return false;
|
|
7814
|
+
}
|
|
7815
|
+
function writeSdkSkillsStatusLine(line) {
|
|
7816
|
+
const progress = getActiveCliProgress();
|
|
7817
|
+
if (progress) {
|
|
7818
|
+
progress.writeLine(line);
|
|
7819
|
+
return;
|
|
7820
|
+
}
|
|
7821
|
+
process.stderr.write(`${line}
|
|
7822
|
+
`);
|
|
7823
|
+
}
|
|
7127
7824
|
async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
7128
7825
|
if (attemptedSync || shouldSkipSkillsSync()) return;
|
|
7129
7826
|
attemptedSync = true;
|
|
7130
7827
|
const localVersion = readLocalSkillsVersion(baseUrl);
|
|
7131
7828
|
const update = await fetchSkillsUpdate(baseUrl, localVersion);
|
|
7132
7829
|
if (!update?.needsUpdate || !update.remoteVersion) return;
|
|
7133
|
-
|
|
7134
|
-
progress?.writeLine("SDK skills changed; syncing deepline-sdk skill...") ?? process.stderr.write("SDK skills changed; syncing deepline-sdk skill...\n");
|
|
7830
|
+
writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-sdk skill...");
|
|
7135
7831
|
const installed = await runSkillsInstall(baseUrl);
|
|
7136
7832
|
if (!installed) return;
|
|
7137
7833
|
writeLocalSkillsVersion(baseUrl, update.remoteVersion);
|
|
7138
|
-
|
|
7834
|
+
writeSdkSkillsStatusLine("SDK skills are up to date.");
|
|
7835
|
+
}
|
|
7836
|
+
|
|
7837
|
+
// src/cli/trace.ts
|
|
7838
|
+
var cliTraceStartedAt = Date.now();
|
|
7839
|
+
function isTruthyEnv(value) {
|
|
7840
|
+
return value === "1" || value === "true" || value === "yes";
|
|
7841
|
+
}
|
|
7842
|
+
function isCliTraceEnabled() {
|
|
7843
|
+
return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
|
|
7844
|
+
}
|
|
7845
|
+
function recordCliTrace(event) {
|
|
7846
|
+
if (!isCliTraceEnabled()) {
|
|
7847
|
+
return;
|
|
7848
|
+
}
|
|
7849
|
+
const now = Date.now();
|
|
7850
|
+
const payload = {
|
|
7851
|
+
ts: now,
|
|
7852
|
+
source: "cli",
|
|
7853
|
+
sinceStartMs: now - cliTraceStartedAt,
|
|
7854
|
+
...event
|
|
7855
|
+
};
|
|
7856
|
+
process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
|
|
7857
|
+
`);
|
|
7858
|
+
}
|
|
7859
|
+
async function traceCliSpan(phase, fields, run) {
|
|
7860
|
+
if (!isCliTraceEnabled()) {
|
|
7861
|
+
return run();
|
|
7862
|
+
}
|
|
7863
|
+
const startedAt = Date.now();
|
|
7864
|
+
try {
|
|
7865
|
+
const result = await run();
|
|
7866
|
+
recordCliTrace({
|
|
7867
|
+
phase,
|
|
7868
|
+
ms: Date.now() - startedAt,
|
|
7869
|
+
ok: true,
|
|
7870
|
+
...fields
|
|
7871
|
+
});
|
|
7872
|
+
return result;
|
|
7873
|
+
} catch (error) {
|
|
7874
|
+
recordCliTrace({
|
|
7875
|
+
phase,
|
|
7876
|
+
ms: Date.now() - startedAt,
|
|
7877
|
+
ok: false,
|
|
7878
|
+
error: error instanceof Error ? error.message : String(error),
|
|
7879
|
+
...fields
|
|
7880
|
+
});
|
|
7881
|
+
throw error;
|
|
7882
|
+
}
|
|
7139
7883
|
}
|
|
7140
7884
|
|
|
7141
7885
|
// src/cli/index.ts
|
|
@@ -7159,7 +7903,7 @@ async function main() {
|
|
|
7159
7903
|
if (printStartupPhase) {
|
|
7160
7904
|
progress?.phase("loading deepline cli");
|
|
7161
7905
|
}
|
|
7162
|
-
const program = new
|
|
7906
|
+
const program = new import_commander.Command();
|
|
7163
7907
|
program.name("deepline").description("Deepline CLI (TypeScript SDK)").version(SDK_VERSION, "-v, --version", "Show version").showHelpAfterError().showSuggestionAfterError(true).addHelpText(
|
|
7164
7908
|
"after",
|
|
7165
7909
|
`
|
|
@@ -7249,4 +7993,3 @@ Output:
|
|
|
7249
7993
|
process.exit(process.exitCode ?? 0);
|
|
7250
7994
|
}
|
|
7251
7995
|
main();
|
|
7252
|
-
//# sourceMappingURL=index.js.map
|