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.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
|
-
import { Command
|
|
4
|
+
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/config.ts
|
|
7
7
|
import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
@@ -47,6 +47,16 @@ var ConfigError = class extends DeeplineError {
|
|
|
47
47
|
var PROD_URL = "https://code.deepline.com";
|
|
48
48
|
var DEFAULT_TIMEOUT = 6e4;
|
|
49
49
|
var DEFAULT_MAX_RETRIES = 3;
|
|
50
|
+
var ACTIVE_DEEPLINE_ENV_FILE = ".env.deepline";
|
|
51
|
+
function isProdBaseUrl(baseUrl) {
|
|
52
|
+
return baseUrl.trim().replace(/\/$/, "") === PROD_URL;
|
|
53
|
+
}
|
|
54
|
+
function profileNameForBaseUrl(baseUrl) {
|
|
55
|
+
return isProdBaseUrl(baseUrl) ? "prod" : "dev";
|
|
56
|
+
}
|
|
57
|
+
function projectEnvStartDir() {
|
|
58
|
+
return process.env.DEEPLINE_PROJECT_ENV_DIR?.trim() || process.cwd();
|
|
59
|
+
}
|
|
50
60
|
function baseUrlSlug(baseUrl) {
|
|
51
61
|
let url;
|
|
52
62
|
try {
|
|
@@ -82,16 +92,52 @@ function parseEnvFile(filePath) {
|
|
|
82
92
|
}
|
|
83
93
|
return env;
|
|
84
94
|
}
|
|
85
|
-
function
|
|
95
|
+
function findNearestEnvFile(names, startDir = process.cwd()) {
|
|
86
96
|
let current = resolve(startDir);
|
|
87
97
|
while (true) {
|
|
88
|
-
const
|
|
89
|
-
|
|
98
|
+
for (const name of names) {
|
|
99
|
+
const filePath = join(current, name);
|
|
100
|
+
if (existsSync(filePath)) return filePath;
|
|
101
|
+
}
|
|
90
102
|
const parent = dirname(current);
|
|
91
|
-
if (parent === current) return
|
|
103
|
+
if (parent === current) return null;
|
|
92
104
|
current = parent;
|
|
93
105
|
}
|
|
94
106
|
}
|
|
107
|
+
function findNearestEnv(names, startDir = process.cwd()) {
|
|
108
|
+
const filePath = findNearestEnvFile(names, startDir);
|
|
109
|
+
return filePath ? parseEnvFile(filePath) : {};
|
|
110
|
+
}
|
|
111
|
+
function findNearestWorktreeEnv(startDir = process.cwd()) {
|
|
112
|
+
return findNearestEnv([".env.worktree"], startDir);
|
|
113
|
+
}
|
|
114
|
+
function resolveProfileEnvFileNames() {
|
|
115
|
+
const explicitProfile = process.env.DEEPLINE_ENV_PROFILE?.trim() || process.env.DEEPLINE_PROFILE?.trim() || "";
|
|
116
|
+
const names = [];
|
|
117
|
+
if (explicitProfile) names.push(`.env.deepline.${explicitProfile}`);
|
|
118
|
+
const nodeEnv = process.env.NODE_ENV?.trim();
|
|
119
|
+
if (nodeEnv === "production") names.push(".env.deepline.prod");
|
|
120
|
+
else if (nodeEnv === "staging") names.push(".env.deepline.staging");
|
|
121
|
+
names.push(ACTIVE_DEEPLINE_ENV_FILE);
|
|
122
|
+
return names;
|
|
123
|
+
}
|
|
124
|
+
function resolveProjectAppEnvFileNames() {
|
|
125
|
+
const nodeEnv = process.env.NODE_ENV?.trim();
|
|
126
|
+
const names = [];
|
|
127
|
+
if (nodeEnv === "production") names.push(".env.prod");
|
|
128
|
+
if (nodeEnv === "staging") names.push(".env.staging");
|
|
129
|
+
names.push(".env.local", ".env");
|
|
130
|
+
return names;
|
|
131
|
+
}
|
|
132
|
+
function resolveBaseUrlFromEnvValues(env) {
|
|
133
|
+
return env.DEEPLINE_ORIGIN_URL?.trim() || env.DEEPLINE_API_BASE_URL?.trim() || "";
|
|
134
|
+
}
|
|
135
|
+
function loadProjectDeeplineEnv() {
|
|
136
|
+
return findNearestEnv(resolveProfileEnvFileNames(), projectEnvStartDir());
|
|
137
|
+
}
|
|
138
|
+
function loadProjectAppEnv() {
|
|
139
|
+
return findNearestEnv(resolveProjectAppEnvFileNames(), projectEnvStartDir());
|
|
140
|
+
}
|
|
95
141
|
function normalizeWorktreeBaseUrl(baseUrl, worktreeEnv = findNearestWorktreeEnv()) {
|
|
96
142
|
const trimmed = baseUrl.trim().replace(/\/$/, "");
|
|
97
143
|
if (!trimmed) return trimmed;
|
|
@@ -143,6 +189,10 @@ function autoDetectBaseUrl() {
|
|
|
143
189
|
if (envOrigin) return normalizeWorktreeBaseUrl(envOrigin);
|
|
144
190
|
const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
|
|
145
191
|
if (envBase) return normalizeWorktreeBaseUrl(envBase);
|
|
192
|
+
const projectDeeplineBaseUrl = resolveBaseUrlFromEnvValues(loadProjectDeeplineEnv());
|
|
193
|
+
if (projectDeeplineBaseUrl) return normalizeWorktreeBaseUrl(projectDeeplineBaseUrl);
|
|
194
|
+
const projectAppBaseUrl = resolveBaseUrlFromEnvValues(loadProjectAppEnv());
|
|
195
|
+
if (projectAppBaseUrl) return normalizeWorktreeBaseUrl(projectAppBaseUrl);
|
|
146
196
|
const worktreeBaseUrl = resolveWorktreeBaseUrl();
|
|
147
197
|
if (worktreeBaseUrl) return worktreeBaseUrl;
|
|
148
198
|
const globalEnv = loadGlobalCliEnv();
|
|
@@ -154,7 +204,9 @@ function resolveConfig(options) {
|
|
|
154
204
|
const requestedBaseUrl = options?.baseUrl?.trim() || autoDetectBaseUrl();
|
|
155
205
|
const baseUrl = normalizeWorktreeBaseUrl(requestedBaseUrl);
|
|
156
206
|
const cliEnv = loadCliEnv(baseUrl);
|
|
157
|
-
const
|
|
207
|
+
const projectDeeplineEnv = loadProjectDeeplineEnv();
|
|
208
|
+
const projectAppEnv = loadProjectAppEnv();
|
|
209
|
+
const apiKey = options?.apiKey?.trim() || process.env.DEEPLINE_API_KEY?.trim() || projectDeeplineEnv.DEEPLINE_API_KEY || projectAppEnv.DEEPLINE_API_KEY || cliEnv.DEEPLINE_API_KEY || "";
|
|
158
210
|
if (!apiKey) {
|
|
159
211
|
throw new ConfigError(
|
|
160
212
|
`No API key found. Set DEEPLINE_API_KEY env var, pass apiKey option, or run: deepline auth register`
|
|
@@ -167,10 +219,32 @@ function resolveConfig(options) {
|
|
|
167
219
|
maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
|
|
168
220
|
};
|
|
169
221
|
}
|
|
222
|
+
function mergeEnvFile(filePath, values) {
|
|
223
|
+
const existing = existsSync(filePath) ? parseEnvFile(filePath) : {};
|
|
224
|
+
const merged = { ...existing, ...values };
|
|
225
|
+
const dir = dirname(filePath);
|
|
226
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
227
|
+
const lines = Object.entries(merged).filter(([, value]) => value !== "").map(([key, value]) => `${key}=${value}`);
|
|
228
|
+
writeFileSync(filePath, `${lines.join("\n")}
|
|
229
|
+
`, "utf-8");
|
|
230
|
+
}
|
|
231
|
+
function saveProjectDeeplineEnvValues(baseUrl, values, startDir = projectEnvStartDir()) {
|
|
232
|
+
const root = resolve(startDir);
|
|
233
|
+
const profile = profileNameForBaseUrl(baseUrl);
|
|
234
|
+
const files = [
|
|
235
|
+
join(root, ACTIVE_DEEPLINE_ENV_FILE),
|
|
236
|
+
join(root, `.env.deepline.${profile}`)
|
|
237
|
+
];
|
|
238
|
+
if (profile === "dev") files.push(join(root, ".env"));
|
|
239
|
+
for (const filePath of files) {
|
|
240
|
+
mergeEnvFile(filePath, values);
|
|
241
|
+
}
|
|
242
|
+
return files;
|
|
243
|
+
}
|
|
170
244
|
|
|
171
245
|
// src/version.ts
|
|
172
|
-
var SDK_VERSION = "0.1.
|
|
173
|
-
var SDK_API_CONTRACT = "2026-
|
|
246
|
+
var SDK_VERSION = "0.1.19";
|
|
247
|
+
var SDK_API_CONTRACT = "2026-05-runs-v2";
|
|
174
248
|
|
|
175
249
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
176
250
|
var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
|
|
@@ -363,8 +437,12 @@ var HttpClient = class {
|
|
|
363
437
|
* @param path - API path
|
|
364
438
|
* @param body - Request body (will be JSON-serialized)
|
|
365
439
|
*/
|
|
366
|
-
async post(path, body) {
|
|
367
|
-
return this.request(path, {
|
|
440
|
+
async post(path, body, headers) {
|
|
441
|
+
return this.request(path, {
|
|
442
|
+
method: "POST",
|
|
443
|
+
body,
|
|
444
|
+
headers
|
|
445
|
+
});
|
|
368
446
|
}
|
|
369
447
|
/**
|
|
370
448
|
* Send a DELETE request.
|
|
@@ -444,6 +522,10 @@ function sleep(ms) {
|
|
|
444
522
|
|
|
445
523
|
// src/client.ts
|
|
446
524
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
525
|
+
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
526
|
+
function isRecord(value) {
|
|
527
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
528
|
+
}
|
|
447
529
|
function normalizePlayStatus(raw) {
|
|
448
530
|
const status = typeof raw.status === "string" ? raw.status : typeof raw.temporalStatus === "string" ? mapLegacyTemporalStatus(raw.temporalStatus) : "running";
|
|
449
531
|
const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
|
|
@@ -473,6 +555,7 @@ function mapLegacyTemporalStatus(status) {
|
|
|
473
555
|
var DeeplineClient = class {
|
|
474
556
|
http;
|
|
475
557
|
config;
|
|
558
|
+
runs;
|
|
476
559
|
/**
|
|
477
560
|
* @param options - Optional overrides for API key, base URL, timeout, and retries.
|
|
478
561
|
* @throws {@link ConfigError} if no API key can be resolved from any source.
|
|
@@ -480,6 +563,13 @@ var DeeplineClient = class {
|
|
|
480
563
|
constructor(options) {
|
|
481
564
|
this.config = resolveConfig(options);
|
|
482
565
|
this.http = new HttpClient(this.config);
|
|
566
|
+
this.runs = {
|
|
567
|
+
get: (runId) => this.getRunStatus(runId),
|
|
568
|
+
list: (options2) => this.listRuns(options2),
|
|
569
|
+
tail: (runId, options2) => this.tailRun(runId, options2),
|
|
570
|
+
logs: (runId, options2) => this.getRunLogs(runId, options2),
|
|
571
|
+
stop: (runId, options2) => this.stopRun(runId, options2)
|
|
572
|
+
};
|
|
483
573
|
}
|
|
484
574
|
/** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
|
|
485
575
|
get baseUrl() {
|
|
@@ -496,12 +586,27 @@ var DeeplineClient = class {
|
|
|
496
586
|
).filter((field) => Boolean(field?.name)) : [];
|
|
497
587
|
return fields.length > 0 ? { fields } : schema;
|
|
498
588
|
}
|
|
499
|
-
|
|
500
|
-
|
|
589
|
+
schemaMetadata(schema, key) {
|
|
590
|
+
if (!isRecord(schema)) return null;
|
|
591
|
+
const value = schema[key];
|
|
592
|
+
return isRecord(value) ? value : null;
|
|
593
|
+
}
|
|
594
|
+
playRunCommand(play, options) {
|
|
595
|
+
const target = play.reference || play.name;
|
|
596
|
+
if (options?.csvInput) {
|
|
597
|
+
const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
|
|
598
|
+
return `deepline plays run ${target} --${inputField} leads.csv --watch`;
|
|
599
|
+
}
|
|
600
|
+
return `deepline plays run ${target} --input '{...}' --watch`;
|
|
501
601
|
}
|
|
502
602
|
summarizePlayListItem(play, options) {
|
|
503
603
|
const aliases = play.aliases?.length ? play.aliases : [play.name];
|
|
504
|
-
const
|
|
604
|
+
const csvInput = this.schemaMetadata(play.inputSchema, "csvInput");
|
|
605
|
+
const rowOutputSchema = this.schemaMetadata(
|
|
606
|
+
play.outputSchema,
|
|
607
|
+
"rowOutputSchema"
|
|
608
|
+
);
|
|
609
|
+
const runCommand = this.playRunCommand(play, { csvInput });
|
|
505
610
|
return {
|
|
506
611
|
name: play.name,
|
|
507
612
|
...play.reference ? { reference: play.reference } : {},
|
|
@@ -513,6 +618,8 @@ var DeeplineClient = class {
|
|
|
513
618
|
aliases,
|
|
514
619
|
inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
|
|
515
620
|
outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
|
|
621
|
+
...csvInput ? { csvInput } : {},
|
|
622
|
+
...rowOutputSchema ? { rowOutputSchema } : {},
|
|
516
623
|
runCommand,
|
|
517
624
|
examples: [runCommand],
|
|
518
625
|
currentPublishedVersion: play.currentPublishedVersion ?? null,
|
|
@@ -551,6 +658,31 @@ var DeeplineClient = class {
|
|
|
551
658
|
);
|
|
552
659
|
return res.tools;
|
|
553
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* Search available tools using Deepline's ranked backend search.
|
|
663
|
+
*
|
|
664
|
+
* This is the same discovery surface used by the legacy CLI: it ranks across
|
|
665
|
+
* tool metadata, categories, agent guidance, and input schema fields.
|
|
666
|
+
*/
|
|
667
|
+
async searchTools(options = {}) {
|
|
668
|
+
const params = new URLSearchParams();
|
|
669
|
+
const query = options.query?.trim() ?? "";
|
|
670
|
+
params.set("q", query);
|
|
671
|
+
params.set(
|
|
672
|
+
"include_search_debug",
|
|
673
|
+
options.includeSearchDebug ? "true" : "false"
|
|
674
|
+
);
|
|
675
|
+
params.set("search_mode", options.searchMode ?? "v2");
|
|
676
|
+
if (options.categories?.trim()) {
|
|
677
|
+
params.set("categories", options.categories.trim());
|
|
678
|
+
}
|
|
679
|
+
if (options.searchTerms?.trim()) {
|
|
680
|
+
params.set("search_terms", options.searchTerms.trim());
|
|
681
|
+
}
|
|
682
|
+
return this.http.get(
|
|
683
|
+
`/api/v2/integrations/list?${params.toString()}`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
554
686
|
/**
|
|
555
687
|
* Get detailed metadata for a single tool.
|
|
556
688
|
*
|
|
@@ -579,55 +711,24 @@ var DeeplineClient = class {
|
|
|
579
711
|
);
|
|
580
712
|
}
|
|
581
713
|
/**
|
|
582
|
-
* Execute a tool and return the
|
|
583
|
-
*
|
|
584
|
-
* Sends the input payload to the tool and returns the `.result` field from the
|
|
585
|
-
* response. For the full response envelope (including job_id, credits, etc.),
|
|
586
|
-
* use {@link executeToolRaw}.
|
|
587
|
-
*
|
|
588
|
-
* @param toolId - Tool identifier (e.g. `"test_company_search"`)
|
|
589
|
-
* @param input - Tool-specific input parameters
|
|
590
|
-
* @returns The tool's output (shape varies by tool)
|
|
591
|
-
* @throws {@link DeeplineError} if the tool execution fails
|
|
592
|
-
*
|
|
593
|
-
* @example
|
|
594
|
-
* ```typescript
|
|
595
|
-
* const company = await client.executeTool('test_company_search', {
|
|
596
|
-
* domain: 'stripe.com',
|
|
597
|
-
* });
|
|
598
|
-
* console.log(company); // { name: "Stripe", industry: "Financial Services", ... }
|
|
599
|
-
* ```
|
|
600
|
-
*/
|
|
601
|
-
async executeTool(toolId, input) {
|
|
602
|
-
const res = await this.http.post(
|
|
603
|
-
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
604
|
-
{ payload: input }
|
|
605
|
-
);
|
|
606
|
-
return res.result ?? res;
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* Execute a tool and return the full response envelope.
|
|
714
|
+
* Execute a tool and return the standard execution envelope.
|
|
610
715
|
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
* @param input - Tool-specific input parameters
|
|
616
|
-
* @returns Full response with job metadata and result
|
|
617
|
-
*
|
|
618
|
-
* @example
|
|
619
|
-
* ```typescript
|
|
620
|
-
* const raw = await client.executeToolRaw('test_company_search', { domain: 'stripe.com' });
|
|
621
|
-
* console.log(`Job: ${raw.job_id}, Credits: ${raw.credits}`);
|
|
622
|
-
* console.log(`Result:`, raw.result);
|
|
623
|
-
* ```
|
|
716
|
+
* The `result.data` field contains the provider payload. `result.meta`
|
|
717
|
+
* contains provider/upstream metadata such as HTTP status or paging details.
|
|
718
|
+
* Top-level fields such as `status`, `job_id`, and `billing` describe the
|
|
719
|
+
* Deepline execution.
|
|
624
720
|
*/
|
|
625
|
-
async
|
|
721
|
+
async executeTool(toolId, input, options) {
|
|
722
|
+
const headers = options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : void 0;
|
|
626
723
|
return this.http.post(
|
|
627
724
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
628
|
-
{ payload: input }
|
|
725
|
+
{ payload: input },
|
|
726
|
+
headers
|
|
629
727
|
);
|
|
630
728
|
}
|
|
729
|
+
async executeToolRaw(toolId, input, options) {
|
|
730
|
+
return this.executeTool(toolId, input, options);
|
|
731
|
+
}
|
|
631
732
|
async queryCustomerDb(input) {
|
|
632
733
|
return this.http.post("/api/v2/db/query", {
|
|
633
734
|
sql: input.sql,
|
|
@@ -676,6 +777,7 @@ var DeeplineClient = class {
|
|
|
676
777
|
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
677
778
|
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
678
779
|
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
780
|
+
...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
|
|
679
781
|
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
680
782
|
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
681
783
|
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
@@ -700,6 +802,7 @@ var DeeplineClient = class {
|
|
|
700
802
|
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
701
803
|
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
702
804
|
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
805
|
+
...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
|
|
703
806
|
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
704
807
|
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
705
808
|
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
@@ -736,6 +839,7 @@ var DeeplineClient = class {
|
|
|
736
839
|
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
737
840
|
name: input.name,
|
|
738
841
|
sourceCode: input.sourceCode,
|
|
842
|
+
sourceFiles: input.sourceFiles,
|
|
739
843
|
artifact: input.artifact
|
|
740
844
|
});
|
|
741
845
|
return this.http.post("/api/v2/plays/artifacts", {
|
|
@@ -750,6 +854,7 @@ var DeeplineClient = class {
|
|
|
750
854
|
compilerManifest: artifact.compilerManifest ?? await this.compilePlayManifest({
|
|
751
855
|
name: artifact.name,
|
|
752
856
|
sourceCode: artifact.sourceCode,
|
|
857
|
+
sourceFiles: artifact.sourceFiles,
|
|
753
858
|
artifact: artifact.artifact
|
|
754
859
|
})
|
|
755
860
|
}))
|
|
@@ -776,11 +881,13 @@ var DeeplineClient = class {
|
|
|
776
881
|
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
777
882
|
name: input.name,
|
|
778
883
|
sourceCode: input.sourceCode,
|
|
884
|
+
sourceFiles: input.sourceFiles,
|
|
779
885
|
artifact: input.artifact
|
|
780
886
|
});
|
|
781
887
|
const registeredArtifact = await this.registerPlayArtifact({
|
|
782
888
|
name: input.name,
|
|
783
889
|
sourceCode: input.sourceCode,
|
|
890
|
+
sourceFiles: input.sourceFiles,
|
|
784
891
|
artifact: input.artifact,
|
|
785
892
|
compilerManifest,
|
|
786
893
|
publish: false
|
|
@@ -840,11 +947,13 @@ var DeeplineClient = class {
|
|
|
840
947
|
const compilerManifest = options?.compilerManifest ?? await this.compilePlayManifest({
|
|
841
948
|
name,
|
|
842
949
|
sourceCode,
|
|
950
|
+
sourceFiles: options?.sourceFiles,
|
|
843
951
|
artifact
|
|
844
952
|
});
|
|
845
953
|
const registeredArtifact = await this.registerPlayArtifact({
|
|
846
954
|
name,
|
|
847
955
|
sourceCode,
|
|
956
|
+
sourceFiles: options?.sourceFiles,
|
|
848
957
|
artifact,
|
|
849
958
|
compilerManifest,
|
|
850
959
|
publish: false
|
|
@@ -1028,6 +1137,112 @@ var DeeplineClient = class {
|
|
|
1028
1137
|
);
|
|
1029
1138
|
return response.runs ?? [];
|
|
1030
1139
|
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Get a run by id using the public runs resource model.
|
|
1142
|
+
*
|
|
1143
|
+
* This is the SDK equivalent of:
|
|
1144
|
+
*
|
|
1145
|
+
* ```bash
|
|
1146
|
+
* deepline runs get <run-id> --json
|
|
1147
|
+
* ```
|
|
1148
|
+
*/
|
|
1149
|
+
async getRunStatus(runId) {
|
|
1150
|
+
const response = await this.http.get(
|
|
1151
|
+
`/api/v2/runs/${encodeURIComponent(runId)}`
|
|
1152
|
+
);
|
|
1153
|
+
return normalizePlayStatus(response);
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* List play runs using the public runs resource model.
|
|
1157
|
+
*
|
|
1158
|
+
* This is the SDK equivalent of:
|
|
1159
|
+
*
|
|
1160
|
+
* ```bash
|
|
1161
|
+
* deepline runs list --play <play-name> --status failed --json
|
|
1162
|
+
* ```
|
|
1163
|
+
*/
|
|
1164
|
+
async listRuns(options) {
|
|
1165
|
+
const playName = options.play.trim();
|
|
1166
|
+
if (!playName) {
|
|
1167
|
+
throw new Error("runs.list requires options.play.");
|
|
1168
|
+
}
|
|
1169
|
+
const params = new URLSearchParams({ play: playName });
|
|
1170
|
+
const status = options.status?.trim();
|
|
1171
|
+
if (status) {
|
|
1172
|
+
params.set("status", status);
|
|
1173
|
+
}
|
|
1174
|
+
const response = await this.http.get(
|
|
1175
|
+
`/api/v2/runs?${params.toString()}`
|
|
1176
|
+
);
|
|
1177
|
+
return response.runs ?? [];
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Fetch the lightweight tail status for a run using the public runs resource model.
|
|
1181
|
+
*
|
|
1182
|
+
* This is the SDK equivalent of:
|
|
1183
|
+
*
|
|
1184
|
+
* ```bash
|
|
1185
|
+
* deepline runs tail <run-id> --json
|
|
1186
|
+
* ```
|
|
1187
|
+
*/
|
|
1188
|
+
async tailRun(runId, options) {
|
|
1189
|
+
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;
|
|
1190
|
+
const params = new URLSearchParams();
|
|
1191
|
+
if (Number.isFinite(afterLogIndex)) {
|
|
1192
|
+
params.set("afterLogIndex", String(Number(afterLogIndex)));
|
|
1193
|
+
}
|
|
1194
|
+
if (typeof options?.waitMs === "number") {
|
|
1195
|
+
params.set("waitMs", String(options.waitMs));
|
|
1196
|
+
}
|
|
1197
|
+
if (options?.terminalOnly) {
|
|
1198
|
+
params.set("terminalOnly", "true");
|
|
1199
|
+
}
|
|
1200
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
1201
|
+
const response = await this.http.get(
|
|
1202
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/tail${suffix}`
|
|
1203
|
+
);
|
|
1204
|
+
return normalizePlayStatus(response);
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Fetch persisted logs for a run using the public runs resource model.
|
|
1208
|
+
*
|
|
1209
|
+
* This is the SDK equivalent of:
|
|
1210
|
+
*
|
|
1211
|
+
* ```bash
|
|
1212
|
+
* deepline runs logs <run-id> --limit 200 --json
|
|
1213
|
+
* ```
|
|
1214
|
+
*/
|
|
1215
|
+
async getRunLogs(runId, options) {
|
|
1216
|
+
const status = await this.getRunStatus(runId);
|
|
1217
|
+
const logs = status.progress?.logs ?? [];
|
|
1218
|
+
const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
|
|
1219
|
+
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
1220
|
+
return {
|
|
1221
|
+
runId: status.runId,
|
|
1222
|
+
totalCount: logs.length,
|
|
1223
|
+
returnedCount: entries.length,
|
|
1224
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
1225
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
1226
|
+
truncated: logs.length > entries.length,
|
|
1227
|
+
hasMore: logs.length > entries.length,
|
|
1228
|
+
entries
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Stop a run by id using the public runs resource model.
|
|
1233
|
+
*
|
|
1234
|
+
* This is the SDK equivalent of:
|
|
1235
|
+
*
|
|
1236
|
+
* ```bash
|
|
1237
|
+
* deepline runs stop <run-id> --reason "stale lock" --json
|
|
1238
|
+
* ```
|
|
1239
|
+
*/
|
|
1240
|
+
async stopRun(runId, options) {
|
|
1241
|
+
return this.http.post(
|
|
1242
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/stop`,
|
|
1243
|
+
options?.reason ? { reason: options.reason } : {}
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1031
1246
|
async listPlays() {
|
|
1032
1247
|
const response = await this.http.get(
|
|
1033
1248
|
"/api/v2/plays"
|
|
@@ -1414,6 +1629,7 @@ function saveEnvValues(values, baseUrl) {
|
|
|
1414
1629
|
const merged = { ...existing, ...values };
|
|
1415
1630
|
const lines = Object.entries(merged).filter(([, v]) => v !== "").map(([k, v]) => `${k}=${v}`);
|
|
1416
1631
|
writeFileSync2(filePath, lines.join("\n") + "\n", "utf-8");
|
|
1632
|
+
saveProjectDeeplineEnvValues(baseUrl, values);
|
|
1417
1633
|
}
|
|
1418
1634
|
async function httpJson(method, url, apiKey, body) {
|
|
1419
1635
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -1953,24 +2169,70 @@ function registerBillingCommands(program) {
|
|
|
1953
2169
|
// src/cli/dataset-stats.ts
|
|
1954
2170
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
1955
2171
|
import { resolve as resolve3 } from "path";
|
|
1956
|
-
|
|
2172
|
+
var CSV_PROJECTED_FIELDS_KEY = "__deeplineCsvProjectedFields";
|
|
2173
|
+
function csvProjectedFields(row) {
|
|
2174
|
+
const serialized = row[CSV_PROJECTED_FIELDS_KEY];
|
|
2175
|
+
if (!Array.isArray(serialized)) {
|
|
2176
|
+
return /* @__PURE__ */ new Set();
|
|
2177
|
+
}
|
|
2178
|
+
return new Set(
|
|
2179
|
+
serialized.filter((field) => typeof field === "string")
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
function stripCsvProjectionFields(row) {
|
|
2183
|
+
const projectedFields = csvProjectedFields(row);
|
|
2184
|
+
if (projectedFields.size === 0 && !(CSV_PROJECTED_FIELDS_KEY in row)) {
|
|
2185
|
+
return row;
|
|
2186
|
+
}
|
|
2187
|
+
const stripped = { ...row };
|
|
2188
|
+
for (const field of projectedFields) {
|
|
2189
|
+
delete stripped[field];
|
|
2190
|
+
}
|
|
2191
|
+
delete stripped[CSV_PROJECTED_FIELDS_KEY];
|
|
2192
|
+
return stripped;
|
|
2193
|
+
}
|
|
2194
|
+
function stripCsvProjectionColumns(columns, rows) {
|
|
2195
|
+
const projectedFields = /* @__PURE__ */ new Set();
|
|
2196
|
+
let hasProjectionMetadata = false;
|
|
2197
|
+
for (const row of rows) {
|
|
2198
|
+
for (const field of csvProjectedFields(row)) {
|
|
2199
|
+
projectedFields.add(field);
|
|
2200
|
+
}
|
|
2201
|
+
hasProjectionMetadata ||= CSV_PROJECTED_FIELDS_KEY in row;
|
|
2202
|
+
}
|
|
2203
|
+
if (!hasProjectionMetadata && projectedFields.size === 0) {
|
|
2204
|
+
return columns;
|
|
2205
|
+
}
|
|
2206
|
+
return columns.filter(
|
|
2207
|
+
(column) => column !== CSV_PROJECTED_FIELDS_KEY && !projectedFields.has(column)
|
|
2208
|
+
);
|
|
2209
|
+
}
|
|
2210
|
+
function sanitizeCsvProjectionInfo(input) {
|
|
2211
|
+
const columns = stripCsvProjectionColumns(input.columns, input.rows);
|
|
2212
|
+
const rows = input.rows.map(stripCsvProjectionFields);
|
|
2213
|
+
return { rows, columns };
|
|
2214
|
+
}
|
|
2215
|
+
function isRecord2(value) {
|
|
1957
2216
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1958
2217
|
}
|
|
1959
2218
|
function isSerializedDataset(value) {
|
|
1960
|
-
return
|
|
2219
|
+
return isRecord2(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
|
|
1961
2220
|
}
|
|
1962
2221
|
function rowArray(value) {
|
|
1963
2222
|
if (!Array.isArray(value)) {
|
|
1964
2223
|
return null;
|
|
1965
2224
|
}
|
|
1966
2225
|
const rows = value.filter(
|
|
1967
|
-
(row) =>
|
|
2226
|
+
(row) => isRecord2(row)
|
|
1968
2227
|
);
|
|
1969
2228
|
return rows.length === value.length ? rows : null;
|
|
1970
2229
|
}
|
|
1971
2230
|
function readNumber(value) {
|
|
1972
2231
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
|
|
1973
2232
|
}
|
|
2233
|
+
function numericStat(value) {
|
|
2234
|
+
return readNumber(value) ?? 0;
|
|
2235
|
+
}
|
|
1974
2236
|
function inferColumns(rows) {
|
|
1975
2237
|
const columns = [];
|
|
1976
2238
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1988,12 +2250,12 @@ function inferColumns(rows) {
|
|
|
1988
2250
|
return columns;
|
|
1989
2251
|
}
|
|
1990
2252
|
function extractCanonicalRowsInfo(statusOrResult) {
|
|
1991
|
-
const root =
|
|
1992
|
-
const result =
|
|
2253
|
+
const root = isRecord2(statusOrResult) ? statusOrResult : null;
|
|
2254
|
+
const result = isRecord2(root?.result) ? root.result : root;
|
|
1993
2255
|
if (!result) {
|
|
1994
2256
|
return null;
|
|
1995
2257
|
}
|
|
1996
|
-
const metadata =
|
|
2258
|
+
const metadata = isRecord2(result._metadata) ? result._metadata : null;
|
|
1997
2259
|
const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
|
|
1998
2260
|
const candidates = [
|
|
1999
2261
|
{ source: "result.contacts", value: result.contacts, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
|
|
@@ -2001,8 +2263,8 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2001
2263
|
{ source: "result.rows", value: result.rows, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
|
|
2002
2264
|
{ source: "result.results", value: result.results, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count }
|
|
2003
2265
|
];
|
|
2004
|
-
if (
|
|
2005
|
-
const outputMetadata =
|
|
2266
|
+
if (isRecord2(result.output)) {
|
|
2267
|
+
const outputMetadata = isRecord2(result.output._metadata) ? result.output._metadata : null;
|
|
2006
2268
|
const outputTotalFromMetadata = outputMetadata?.totalRows ?? outputMetadata?.rowCount ?? outputMetadata?.count;
|
|
2007
2269
|
candidates.push(
|
|
2008
2270
|
{ source: "result.output.contacts", value: result.output.contacts, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count },
|
|
@@ -2013,12 +2275,17 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2013
2275
|
}
|
|
2014
2276
|
for (const candidate of candidates) {
|
|
2015
2277
|
if (isSerializedDataset(candidate.value)) {
|
|
2016
|
-
const
|
|
2017
|
-
const totalRows2 = readNumber(candidate.value.count) ??
|
|
2278
|
+
const rawRows = rowArray(candidate.value.preview) ?? [];
|
|
2279
|
+
const totalRows2 = readNumber(candidate.value.count) ?? rawRows.length;
|
|
2280
|
+
const rawColumns = Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string") ? candidate.value.columns : inferColumns(rawRows);
|
|
2281
|
+
const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
|
|
2282
|
+
rows: rawRows,
|
|
2283
|
+
columns: rawColumns
|
|
2284
|
+
});
|
|
2018
2285
|
return {
|
|
2019
2286
|
rows: rows2,
|
|
2020
2287
|
totalRows: totalRows2,
|
|
2021
|
-
columns
|
|
2288
|
+
columns,
|
|
2022
2289
|
complete: rows2.length === totalRows2,
|
|
2023
2290
|
source: candidate.source
|
|
2024
2291
|
};
|
|
@@ -2028,10 +2295,14 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2028
2295
|
continue;
|
|
2029
2296
|
}
|
|
2030
2297
|
const totalRows = readNumber(candidate.total) ?? rows.length;
|
|
2031
|
-
|
|
2298
|
+
const sanitized = sanitizeCsvProjectionInfo({
|
|
2032
2299
|
rows,
|
|
2300
|
+
columns: inferColumns(rows)
|
|
2301
|
+
});
|
|
2302
|
+
return {
|
|
2303
|
+
rows: sanitized.rows,
|
|
2033
2304
|
totalRows,
|
|
2034
|
-
columns:
|
|
2305
|
+
columns: sanitized.columns,
|
|
2035
2306
|
complete: rows.length === totalRows,
|
|
2036
2307
|
source: candidate.source
|
|
2037
2308
|
};
|
|
@@ -2041,6 +2312,31 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2041
2312
|
function percentText(numerator, denominator) {
|
|
2042
2313
|
return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
|
|
2043
2314
|
}
|
|
2315
|
+
function isDatasetExecutionStatsInput(value) {
|
|
2316
|
+
return isRecord2(value) && isRecord2(value.columnStats) && Object.values(value.columnStats).every(isRecord2);
|
|
2317
|
+
}
|
|
2318
|
+
function extractDatasetExecutionStats(statusOrResult) {
|
|
2319
|
+
if (!isRecord2(statusOrResult)) {
|
|
2320
|
+
return null;
|
|
2321
|
+
}
|
|
2322
|
+
const direct = statusOrResult.dataset_execution_stats;
|
|
2323
|
+
if (isDatasetExecutionStatsInput(direct)) {
|
|
2324
|
+
return direct;
|
|
2325
|
+
}
|
|
2326
|
+
const nested = isRecord2(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
|
|
2327
|
+
return isDatasetExecutionStatsInput(nested) ? nested : null;
|
|
2328
|
+
}
|
|
2329
|
+
function formatExecutionStats(raw, denominator) {
|
|
2330
|
+
return {
|
|
2331
|
+
queued: percentText(numericStat(raw.queued), denominator),
|
|
2332
|
+
running: percentText(numericStat(raw.running), denominator),
|
|
2333
|
+
"completed:executed": percentText(numericStat(raw.completed), denominator),
|
|
2334
|
+
"completed:reused": percentText(numericStat(raw.cached), denominator),
|
|
2335
|
+
"skipped:condition": percentText(numericStat(raw.skipped), denominator),
|
|
2336
|
+
"skipped:missed": percentText(numericStat(raw.missed), denominator),
|
|
2337
|
+
failed: percentText(numericStat(raw.failed), denominator)
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2044
2340
|
function countPercentText(count, denominator) {
|
|
2045
2341
|
return denominator > 0 ? `${count} (${Math.round(100 * count / denominator)}%)` : "0 (0%)";
|
|
2046
2342
|
}
|
|
@@ -2079,13 +2375,13 @@ function summarizeSampleValue(value, depth = 0) {
|
|
|
2079
2375
|
if (typeof parsed === "number" || typeof parsed === "boolean") return parsed;
|
|
2080
2376
|
if (depth >= 3) {
|
|
2081
2377
|
if (Array.isArray(parsed)) return [];
|
|
2082
|
-
if (
|
|
2378
|
+
if (isRecord2(parsed)) return {};
|
|
2083
2379
|
return compactScalar(parsed);
|
|
2084
2380
|
}
|
|
2085
2381
|
if (Array.isArray(parsed)) {
|
|
2086
2382
|
return parsed.slice(0, 3).map((item) => summarizeSampleValue(item, depth + 1));
|
|
2087
2383
|
}
|
|
2088
|
-
if (
|
|
2384
|
+
if (isRecord2(parsed)) {
|
|
2089
2385
|
const out = {};
|
|
2090
2386
|
for (const [key, nested] of Object.entries(parsed)) {
|
|
2091
2387
|
if (["__dl", "meta", "metadata"].includes(key)) {
|
|
@@ -2115,7 +2411,7 @@ function compactCell(value) {
|
|
|
2115
2411
|
}
|
|
2116
2412
|
return `[${parsed.length} items]`;
|
|
2117
2413
|
}
|
|
2118
|
-
if (
|
|
2414
|
+
if (isRecord2(parsed)) {
|
|
2119
2415
|
for (const key of ["matched_result", "output"]) {
|
|
2120
2416
|
if (parsed[key] !== null && parsed[key] !== void 0 && parsed[key] !== "") {
|
|
2121
2417
|
return compactCell(parsed[key]);
|
|
@@ -2133,15 +2429,16 @@ function compactCell(value) {
|
|
|
2133
2429
|
}
|
|
2134
2430
|
return compactScalar(parsed, 120);
|
|
2135
2431
|
}
|
|
2136
|
-
function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows)) {
|
|
2432
|
+
function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows), executionStats) {
|
|
2433
|
+
const sanitized = sanitizeCsvProjectionInfo({ rows, columns });
|
|
2137
2434
|
const columnStats = {};
|
|
2138
|
-
for (const column of columns) {
|
|
2435
|
+
for (const column of sanitized.columns) {
|
|
2139
2436
|
let nonEmpty = 0;
|
|
2140
2437
|
let empty = 0;
|
|
2141
2438
|
let sampleValue;
|
|
2142
2439
|
let sampleValueType = null;
|
|
2143
2440
|
const valueCounts = /* @__PURE__ */ new Map();
|
|
2144
|
-
for (const row of rows) {
|
|
2441
|
+
for (const row of sanitized.rows) {
|
|
2145
2442
|
const raw = row[column];
|
|
2146
2443
|
const value = compactCell(raw);
|
|
2147
2444
|
if (value) {
|
|
@@ -2160,6 +2457,10 @@ function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns
|
|
|
2160
2457
|
non_empty: percentText(nonEmpty, denominator),
|
|
2161
2458
|
unique: valueCounts.size
|
|
2162
2459
|
};
|
|
2460
|
+
const rawExecutionStats = executionStats?.columnStats[column];
|
|
2461
|
+
if (rawExecutionStats) {
|
|
2462
|
+
stat3.execution = formatExecutionStats(rawExecutionStats, totalRows);
|
|
2463
|
+
}
|
|
2163
2464
|
if (sampleValue !== void 0 && sampleValueType) {
|
|
2164
2465
|
stat3.sample_value = sampleValue;
|
|
2165
2466
|
stat3.sample_type = sampleValueType;
|
|
@@ -2193,10 +2494,14 @@ function writeCanonicalRowsCsv(rowsInfo, outPath) {
|
|
|
2193
2494
|
`Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; cannot export a complete CSV from this status payload yet.`
|
|
2194
2495
|
);
|
|
2195
2496
|
}
|
|
2497
|
+
const sanitized = sanitizeCsvProjectionInfo({
|
|
2498
|
+
rows: rowsInfo.rows,
|
|
2499
|
+
columns: rowsInfo.columns
|
|
2500
|
+
});
|
|
2196
2501
|
const resolved = resolve3(outPath);
|
|
2197
2502
|
writeFileSync3(
|
|
2198
2503
|
resolved,
|
|
2199
|
-
csvStringFromRows(
|
|
2504
|
+
csvStringFromRows(sanitized.rows, sanitized.columns),
|
|
2200
2505
|
"utf-8"
|
|
2201
2506
|
);
|
|
2202
2507
|
return resolved;
|
|
@@ -2488,14 +2793,12 @@ function registerOrgCommands(program) {
|
|
|
2488
2793
|
import { createHash as createHash3 } from "crypto";
|
|
2489
2794
|
import {
|
|
2490
2795
|
existsSync as existsSync4,
|
|
2491
|
-
mkdirSync as mkdirSync3,
|
|
2492
2796
|
readFileSync as readFileSync3,
|
|
2493
2797
|
readdirSync,
|
|
2494
2798
|
realpathSync,
|
|
2495
2799
|
writeFileSync as writeFileSync4
|
|
2496
2800
|
} from "fs";
|
|
2497
2801
|
import { basename as basename3, dirname as dirname6, join as join6, resolve as resolve7 } from "path";
|
|
2498
|
-
import { Option } from "commander";
|
|
2499
2802
|
|
|
2500
2803
|
// src/plays/bundle-play-file.ts
|
|
2501
2804
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -2510,7 +2813,6 @@ import { tmpdir } from "os";
|
|
|
2510
2813
|
import { basename, dirname as dirname3, extname, isAbsolute, join as join3, resolve as resolve4 } from "path";
|
|
2511
2814
|
import { builtinModules, createRequire } from "module";
|
|
2512
2815
|
import { build } from "esbuild";
|
|
2513
|
-
import ts from "typescript";
|
|
2514
2816
|
|
|
2515
2817
|
// ../shared_libs/play-runtime/backend.ts
|
|
2516
2818
|
var PLAY_RUNTIME_BACKENDS = {
|
|
@@ -2579,6 +2881,14 @@ var PLAY_SOURCE_FILE_PATTERN = /\.play\.(?:[cm]?[jt]sx?)$/i;
|
|
|
2579
2881
|
var NODE_BUILTIN_SET = new Set(
|
|
2580
2882
|
builtinModules.flatMap((name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`])
|
|
2581
2883
|
);
|
|
2884
|
+
function assertValidExportName(exportName) {
|
|
2885
|
+
if (exportName === "default") return;
|
|
2886
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(exportName)) {
|
|
2887
|
+
throw new Error(
|
|
2888
|
+
`Invalid play export name "${exportName}". Named prebuilt exports must be valid JavaScript identifiers.`
|
|
2889
|
+
);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2582
2892
|
function sha256(value) {
|
|
2583
2893
|
return createHash("sha256").update(value).digest("hex");
|
|
2584
2894
|
}
|
|
@@ -2586,56 +2896,6 @@ function formatEsbuildMessage(message) {
|
|
|
2586
2896
|
const location = message.location ? `${message.location.file}:${message.location.line}:${message.location.column}` : null;
|
|
2587
2897
|
return location ? `${location} ${message.text}` : message.text;
|
|
2588
2898
|
}
|
|
2589
|
-
function formatTypeScriptDiagnostic(diagnostic) {
|
|
2590
|
-
if (!diagnostic.file) {
|
|
2591
|
-
const message2 = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n").trim();
|
|
2592
|
-
return message2 || null;
|
|
2593
|
-
}
|
|
2594
|
-
const start = diagnostic.start ?? 0;
|
|
2595
|
-
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(start);
|
|
2596
|
-
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n").trim();
|
|
2597
|
-
if (!message) {
|
|
2598
|
-
return null;
|
|
2599
|
-
}
|
|
2600
|
-
return `${diagnostic.file.fileName}:${line + 1}:${character + 1} ${message}`;
|
|
2601
|
-
}
|
|
2602
|
-
function resolveBundledTypeRoots() {
|
|
2603
|
-
try {
|
|
2604
|
-
return [dirname3(dirname3(playArtifactRequire.resolve("@types/node/package.json")))];
|
|
2605
|
-
} catch {
|
|
2606
|
-
return [];
|
|
2607
|
-
}
|
|
2608
|
-
}
|
|
2609
|
-
function typecheckPlaySource(input, adapter) {
|
|
2610
|
-
const rootNames = Array.from(
|
|
2611
|
-
/* @__PURE__ */ new Set([
|
|
2612
|
-
...input.importPolicy.localFiles,
|
|
2613
|
-
...input.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
2614
|
-
])
|
|
2615
|
-
);
|
|
2616
|
-
const sdkTypesPath = adapter.sdkTypesEntryFile ?? adapter.sdkEntryFile;
|
|
2617
|
-
const program = ts.createProgram(rootNames, {
|
|
2618
|
-
target: ts.ScriptTarget.ES2023,
|
|
2619
|
-
// SDK source uses fetch/RequestInit/URL and node-aware config helpers.
|
|
2620
|
-
// The play runtime import policy below still bans Node modules from play
|
|
2621
|
-
// source for workers_edge bundles.
|
|
2622
|
-
lib: ["lib.es2023.d.ts", "lib.dom.d.ts"],
|
|
2623
|
-
module: ts.ModuleKind.ESNext,
|
|
2624
|
-
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
2625
|
-
paths: { deepline: [sdkTypesPath] },
|
|
2626
|
-
strict: true,
|
|
2627
|
-
skipLibCheck: true,
|
|
2628
|
-
noEmit: true,
|
|
2629
|
-
esModuleInterop: true,
|
|
2630
|
-
allowSyntheticDefaultImports: true,
|
|
2631
|
-
allowImportingTsExtensions: true,
|
|
2632
|
-
allowJs: true,
|
|
2633
|
-
resolveJsonModule: true,
|
|
2634
|
-
types: ["node"],
|
|
2635
|
-
typeRoots: resolveBundledTypeRoots()
|
|
2636
|
-
});
|
|
2637
|
-
return ts.getPreEmitDiagnostics(program).map(formatTypeScriptDiagnostic).filter((message) => Boolean(message));
|
|
2638
|
-
}
|
|
2639
2899
|
function isLocalSpecifier(specifier) {
|
|
2640
2900
|
return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/") || specifier.startsWith("file:");
|
|
2641
2901
|
}
|
|
@@ -2659,11 +2919,8 @@ function assertWithinPlayWorkspace(input) {
|
|
|
2659
2919
|
if (isPathInsideDirectory(input.resolvedPath, input.workspace.rootDir)) {
|
|
2660
2920
|
return;
|
|
2661
2921
|
}
|
|
2662
|
-
const position = input.sourceFile.getLineAndCharacterOfPosition(
|
|
2663
|
-
input.node.getStart(input.sourceFile)
|
|
2664
|
-
);
|
|
2665
2922
|
throw new Error(
|
|
2666
|
-
`${input.importer}:${
|
|
2923
|
+
`${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.`
|
|
2667
2924
|
);
|
|
2668
2925
|
}
|
|
2669
2926
|
function getPackageName(specifier) {
|
|
@@ -2673,72 +2930,135 @@ function getPackageName(specifier) {
|
|
|
2673
2930
|
}
|
|
2674
2931
|
return specifier.split("/")[0] ?? specifier;
|
|
2675
2932
|
}
|
|
2676
|
-
function scriptKindForFile(filePath) {
|
|
2677
|
-
const extension = extname(filePath).toLowerCase();
|
|
2678
|
-
switch (extension) {
|
|
2679
|
-
case ".tsx":
|
|
2680
|
-
return ts.ScriptKind.TSX;
|
|
2681
|
-
case ".jsx":
|
|
2682
|
-
return ts.ScriptKind.JSX;
|
|
2683
|
-
case ".js":
|
|
2684
|
-
case ".mjs":
|
|
2685
|
-
case ".cjs":
|
|
2686
|
-
return ts.ScriptKind.JS;
|
|
2687
|
-
case ".json":
|
|
2688
|
-
return ts.ScriptKind.JSON;
|
|
2689
|
-
default:
|
|
2690
|
-
return ts.ScriptKind.TS;
|
|
2691
|
-
}
|
|
2692
|
-
}
|
|
2693
2933
|
function isPlaySourceFile(filePath) {
|
|
2694
2934
|
return PLAY_SOURCE_FILE_PATTERN.test(filePath);
|
|
2695
2935
|
}
|
|
2696
|
-
function
|
|
2697
|
-
|
|
2698
|
-
|
|
2936
|
+
function stripCommentsToSpaces(source) {
|
|
2937
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
|
|
2938
|
+
/(^|[^:])\/\/.*$/gm,
|
|
2939
|
+
(match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
|
|
2940
|
+
);
|
|
2941
|
+
}
|
|
2942
|
+
function lineAndColumnAt(source, index) {
|
|
2943
|
+
const prefix = source.slice(0, index);
|
|
2944
|
+
const lines = prefix.split("\n");
|
|
2945
|
+
return { line: lines.length, column: lines[lines.length - 1].length + 1 };
|
|
2946
|
+
}
|
|
2947
|
+
function findSourceImportReferences(sourceCode) {
|
|
2948
|
+
const source = stripCommentsToSpaces(sourceCode);
|
|
2949
|
+
const references = [];
|
|
2950
|
+
const addReference = (specifier, specifierIndex, kind) => {
|
|
2951
|
+
if (!specifier) return;
|
|
2952
|
+
const position = lineAndColumnAt(sourceCode, specifierIndex);
|
|
2953
|
+
references.push({
|
|
2954
|
+
specifier,
|
|
2955
|
+
line: position.line,
|
|
2956
|
+
column: position.column,
|
|
2957
|
+
kind
|
|
2958
|
+
});
|
|
2959
|
+
};
|
|
2960
|
+
const staticImportPattern = /\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?(['"])([^'"\n]+)\1/g;
|
|
2961
|
+
for (const match of source.matchAll(staticImportPattern)) {
|
|
2962
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "static");
|
|
2963
|
+
}
|
|
2964
|
+
const dynamicImportPattern = /\bimport\s*\(\s*(['"])([^'"\n]+)\1/g;
|
|
2965
|
+
for (const match of source.matchAll(dynamicImportPattern)) {
|
|
2966
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "dynamic-import");
|
|
2967
|
+
}
|
|
2968
|
+
const requirePattern = /\brequire\s*\(\s*(['"])([^'"\n]+)\1/g;
|
|
2969
|
+
for (const match of source.matchAll(requirePattern)) {
|
|
2970
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "require");
|
|
2971
|
+
}
|
|
2972
|
+
const literalDynamicImportIndexes = new Set(
|
|
2973
|
+
[...source.matchAll(dynamicImportPattern)].map((match) => match.index)
|
|
2974
|
+
);
|
|
2975
|
+
for (const match of source.matchAll(/\bimport\s*\(/g)) {
|
|
2976
|
+
if (literalDynamicImportIndexes.has(match.index)) continue;
|
|
2977
|
+
const position = lineAndColumnAt(sourceCode, match.index);
|
|
2978
|
+
throw new Error(
|
|
2979
|
+
`:${position.line}:${position.column} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
2980
|
+
);
|
|
2981
|
+
}
|
|
2982
|
+
const literalRequireIndexes = new Set(
|
|
2983
|
+
[...source.matchAll(requirePattern)].map((match) => match.index)
|
|
2984
|
+
);
|
|
2985
|
+
for (const match of source.matchAll(/\brequire\s*\(/g)) {
|
|
2986
|
+
if (literalRequireIndexes.has(match.index)) continue;
|
|
2987
|
+
const position = lineAndColumnAt(sourceCode, match.index);
|
|
2988
|
+
throw new Error(
|
|
2989
|
+
`:${position.line}:${position.column} Dynamic require() is not allowed in plays. Use static imports or require("literal") only.`
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
return references.sort(
|
|
2993
|
+
(left, right) => left.line === right.line ? left.column - right.column : left.line - right.line
|
|
2994
|
+
);
|
|
2995
|
+
}
|
|
2996
|
+
function unquoteStringLiteral(literal) {
|
|
2997
|
+
const trimmed = literal.trim();
|
|
2998
|
+
const quote = trimmed[0];
|
|
2999
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
3000
|
+
return null;
|
|
3001
|
+
}
|
|
3002
|
+
try {
|
|
3003
|
+
return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
|
|
3004
|
+
} catch {
|
|
3005
|
+
return trimmed.slice(1, -1);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
function findMatchingBrace(source, openIndex) {
|
|
3009
|
+
let depth = 0;
|
|
3010
|
+
let quote = null;
|
|
3011
|
+
let escaped = false;
|
|
3012
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
3013
|
+
const char = source[index];
|
|
3014
|
+
if (quote) {
|
|
3015
|
+
if (escaped) {
|
|
3016
|
+
escaped = false;
|
|
3017
|
+
} else if (char === "\\") {
|
|
3018
|
+
escaped = true;
|
|
3019
|
+
} else if (char === quote) {
|
|
3020
|
+
quote = null;
|
|
3021
|
+
}
|
|
2699
3022
|
continue;
|
|
2700
3023
|
}
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
if (!matches) {
|
|
3024
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3025
|
+
quote = char;
|
|
2704
3026
|
continue;
|
|
2705
3027
|
}
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
)
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
const
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
3028
|
+
if (char === "{") depth += 1;
|
|
3029
|
+
if (char === "}") {
|
|
3030
|
+
depth -= 1;
|
|
3031
|
+
if (depth === 0) return index;
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
return -1;
|
|
3035
|
+
}
|
|
3036
|
+
function extractDefinedPlayName(sourceCode, _filePath) {
|
|
3037
|
+
const source = stripCommentsToSpaces(sourceCode);
|
|
3038
|
+
const callPattern = /(?:\b[A-Za-z_$][\w$]*\s*\.\s*)?\b(?:definePlay|defineWorkflow)\s*\(/g;
|
|
3039
|
+
for (const match of source.matchAll(callPattern)) {
|
|
3040
|
+
const openParen = match.index + match[0].length - 1;
|
|
3041
|
+
const firstArgStart = openParen + 1;
|
|
3042
|
+
const firstNonSpace = source.slice(firstArgStart).search(/\S/);
|
|
3043
|
+
if (firstNonSpace < 0) continue;
|
|
3044
|
+
const argIndex = firstArgStart + firstNonSpace;
|
|
3045
|
+
const quote = source[argIndex];
|
|
3046
|
+
if (quote === '"' || quote === "'") {
|
|
3047
|
+
const literalMatch = source.slice(argIndex).match(/^(['"])(?:\\.|(?!\1)[\s\S])*\1/);
|
|
3048
|
+
const value = literalMatch ? unquoteStringLiteral(literalMatch[0]) : null;
|
|
3049
|
+
if (value?.trim()) return value.trim();
|
|
3050
|
+
}
|
|
3051
|
+
if (quote === "{") {
|
|
3052
|
+
const closeBrace = findMatchingBrace(source, argIndex);
|
|
3053
|
+
if (closeBrace < 0) continue;
|
|
3054
|
+
const objectSource = source.slice(argIndex + 1, closeBrace);
|
|
3055
|
+
const idMatch = objectSource.match(/(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/);
|
|
3056
|
+
if (idMatch?.[2]?.trim()) {
|
|
3057
|
+
return idMatch[2].trim();
|
|
2736
3058
|
}
|
|
2737
3059
|
}
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
visit(sourceFile);
|
|
2741
|
-
return detectedPlayName;
|
|
3060
|
+
}
|
|
3061
|
+
return null;
|
|
2742
3062
|
}
|
|
2743
3063
|
function getPackageRequireCandidates(fromFile) {
|
|
2744
3064
|
const candidates = [
|
|
@@ -2773,6 +3093,29 @@ function workersPlayEntryAliasPlugin(playFilePath) {
|
|
|
2773
3093
|
}
|
|
2774
3094
|
};
|
|
2775
3095
|
}
|
|
3096
|
+
function workersNamedPlayEntryAliasPlugin(playFilePath, exportName) {
|
|
3097
|
+
return {
|
|
3098
|
+
name: "deepline-workers-named-play-entry-alias",
|
|
3099
|
+
setup(buildContext) {
|
|
3100
|
+
buildContext.onResolve(
|
|
3101
|
+
{ filter: new RegExp(`^${WORKERS_PLAY_ENTRY_VIRTUAL}$`) },
|
|
3102
|
+
() => ({
|
|
3103
|
+
path: `${playFilePath}.${exportName}.entry.ts`,
|
|
3104
|
+
namespace: "deepline-named-play-entry"
|
|
3105
|
+
})
|
|
3106
|
+
);
|
|
3107
|
+
buildContext.onLoad(
|
|
3108
|
+
{ filter: /.*/, namespace: "deepline-named-play-entry" },
|
|
3109
|
+
() => ({
|
|
3110
|
+
contents: `export { ${exportName} as default } from ${JSON.stringify(playFilePath)};
|
|
3111
|
+
`,
|
|
3112
|
+
loader: "ts",
|
|
3113
|
+
resolveDir: dirname3(playFilePath)
|
|
3114
|
+
})
|
|
3115
|
+
);
|
|
3116
|
+
}
|
|
3117
|
+
};
|
|
3118
|
+
}
|
|
2776
3119
|
function workersNodeBuiltinStubPlugin() {
|
|
2777
3120
|
const UNSUPPORTED = /* @__PURE__ */ new Set(["node:fs", "node:fs/promises", "node:os", "node:child_process"]);
|
|
2778
3121
|
return {
|
|
@@ -3025,18 +3368,10 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3025
3368
|
if (extname(absolutePath).toLowerCase() === ".json") {
|
|
3026
3369
|
return;
|
|
3027
3370
|
}
|
|
3028
|
-
const
|
|
3029
|
-
absolutePath,
|
|
3030
|
-
sourceCode2,
|
|
3031
|
-
ts.ScriptTarget.Latest,
|
|
3032
|
-
true,
|
|
3033
|
-
scriptKindForFile(absolutePath)
|
|
3034
|
-
);
|
|
3035
|
-
const handleSpecifier = async (specifier, node, kind) => {
|
|
3371
|
+
const handleSpecifier = async (specifier, line, column, kind) => {
|
|
3036
3372
|
if (kind === "dynamic-import") {
|
|
3037
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3038
3373
|
throw new Error(
|
|
3039
|
-
`${absolutePath}:${
|
|
3374
|
+
`${absolutePath}:${line}:${column} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
3040
3375
|
);
|
|
3041
3376
|
}
|
|
3042
3377
|
if (NODE_BUILTIN_SET.has(specifier)) {
|
|
@@ -3050,16 +3385,15 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3050
3385
|
specifier,
|
|
3051
3386
|
resolvedPath: resolved,
|
|
3052
3387
|
workspace,
|
|
3053
|
-
|
|
3054
|
-
|
|
3388
|
+
line,
|
|
3389
|
+
column
|
|
3055
3390
|
});
|
|
3056
3391
|
if (resolved !== absoluteEntryFile && isPlaySourceFile(resolved)) {
|
|
3057
3392
|
const importedSource = await readFile(resolved, "utf-8");
|
|
3058
3393
|
const importedPlayName = extractDefinedPlayName(importedSource, resolved);
|
|
3059
3394
|
if (!importedPlayName) {
|
|
3060
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3061
3395
|
throw new Error(
|
|
3062
|
-
`${absolutePath}:${
|
|
3396
|
+
`${absolutePath}:${line}:${column} Imported play file "${specifier}" must export definePlay(...) so it can be runtime-composed.`
|
|
3063
3397
|
);
|
|
3064
3398
|
}
|
|
3065
3399
|
importedPlayDependencies.set(resolved, {
|
|
@@ -3072,44 +3406,28 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3072
3406
|
return;
|
|
3073
3407
|
}
|
|
3074
3408
|
if (specifier.includes(":")) {
|
|
3075
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3076
3409
|
throw new Error(
|
|
3077
|
-
`${absolutePath}:${
|
|
3410
|
+
`${absolutePath}:${line}:${column} Unsupported import specifier "${specifier}". Allowed imports are relative files, Node builtins, and installed packages.`
|
|
3078
3411
|
);
|
|
3079
3412
|
}
|
|
3080
3413
|
const packageImport = resolvePackageImport(specifier, absolutePath, adapter);
|
|
3081
3414
|
packages.set(packageImport.name, packageImport.version);
|
|
3082
3415
|
};
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
await handleSpecifier(
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
throw new Error(
|
|
3092
|
-
`${absolutePath}:${position.line + 1}:${position.character + 1} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
3093
|
-
);
|
|
3094
|
-
}
|
|
3095
|
-
await handleSpecifier(node.arguments[0].text, node, "dynamic-import");
|
|
3096
|
-
}
|
|
3097
|
-
if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
3098
|
-
const firstArgument = node.arguments[0];
|
|
3099
|
-
if (node.arguments.length !== 1 || !firstArgument || !ts.isStringLiteralLike(firstArgument)) {
|
|
3100
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3101
|
-
throw new Error(
|
|
3102
|
-
`${absolutePath}:${position.line + 1}:${position.character + 1} Dynamic require() is not allowed in plays. Use static imports or require("literal") only.`
|
|
3103
|
-
);
|
|
3104
|
-
}
|
|
3105
|
-
await handleSpecifier(firstArgument.text, node, "require");
|
|
3106
|
-
}
|
|
3416
|
+
try {
|
|
3417
|
+
for (const reference of findSourceImportReferences(sourceCode2)) {
|
|
3418
|
+
await handleSpecifier(
|
|
3419
|
+
reference.specifier,
|
|
3420
|
+
reference.line,
|
|
3421
|
+
reference.column,
|
|
3422
|
+
reference.kind
|
|
3423
|
+
);
|
|
3107
3424
|
}
|
|
3108
|
-
|
|
3109
|
-
|
|
3425
|
+
} catch (error) {
|
|
3426
|
+
if (error instanceof Error && error.message.startsWith(":")) {
|
|
3427
|
+
throw new Error(`${absolutePath}${error.message}`);
|
|
3110
3428
|
}
|
|
3111
|
-
|
|
3112
|
-
|
|
3429
|
+
throw error;
|
|
3430
|
+
}
|
|
3113
3431
|
};
|
|
3114
3432
|
await visitFile(absoluteEntryFile);
|
|
3115
3433
|
const sourceCode = localFiles.get(absoluteEntryFile) ?? "";
|
|
@@ -3129,6 +3447,11 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3129
3447
|
const playName = extractDefinedPlayName(sourceCode, absoluteEntryFile);
|
|
3130
3448
|
return {
|
|
3131
3449
|
sourceCode,
|
|
3450
|
+
sourceFiles: Object.fromEntries(
|
|
3451
|
+
[...localFiles.entries()].sort(
|
|
3452
|
+
(left, right) => left[0].localeCompare(right[0])
|
|
3453
|
+
)
|
|
3454
|
+
),
|
|
3132
3455
|
sourceHash,
|
|
3133
3456
|
graphHash,
|
|
3134
3457
|
importPolicy: {
|
|
@@ -3192,8 +3515,7 @@ function normalizeSourceMapForRuntime(sourceMapText) {
|
|
|
3192
3515
|
parsed.sourceRoot = void 0;
|
|
3193
3516
|
return JSON.stringify(parsed);
|
|
3194
3517
|
}
|
|
3195
|
-
function
|
|
3196
|
-
const bundleBytes = Buffer.byteLength(bundledCode, "utf8");
|
|
3518
|
+
function getBundleSizeErrorForBytes(filePath, bundleBytes, artifactKind) {
|
|
3197
3519
|
if (bundleBytes > MAX_PLAY_BUNDLE_BYTES) {
|
|
3198
3520
|
return `${filePath} Play bundle exceeds the 30 MiB limit (${bundleBytes} bytes > ${MAX_PLAY_BUNDLE_BYTES} bytes).`;
|
|
3199
3521
|
}
|
|
@@ -3204,11 +3526,27 @@ function getBundleSizeError(filePath, bundledCode, artifactKind) {
|
|
|
3204
3526
|
}
|
|
3205
3527
|
return null;
|
|
3206
3528
|
}
|
|
3207
|
-
|
|
3529
|
+
function getBundleSizeError(filePath, bundledCode, artifactKind) {
|
|
3530
|
+
return getBundleSizeErrorForBytes(
|
|
3531
|
+
filePath,
|
|
3532
|
+
Buffer.byteLength(bundledCode, "utf8"),
|
|
3533
|
+
artifactKind
|
|
3534
|
+
);
|
|
3535
|
+
}
|
|
3536
|
+
async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter, exportName) {
|
|
3208
3537
|
const sdkAliasPlugin = localSdkAliasPlugin(adapter);
|
|
3209
3538
|
const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
|
|
3539
|
+
const namedExportShim = exportName === "default" ? null : `export { ${exportName} as default } from ${JSON.stringify(entryFile)};
|
|
3540
|
+
`;
|
|
3210
3541
|
const result = await build({
|
|
3211
|
-
|
|
3542
|
+
...namedExportShim ? {
|
|
3543
|
+
stdin: {
|
|
3544
|
+
contents: namedExportShim,
|
|
3545
|
+
resolveDir: dirname3(entryFile),
|
|
3546
|
+
sourcefile: `${basename(entryFile)}.${exportName}.entry.ts`,
|
|
3547
|
+
loader: "ts"
|
|
3548
|
+
}
|
|
3549
|
+
} : { entryPoints: [entryFile] },
|
|
3212
3550
|
absWorkingDir: adapter.projectRoot,
|
|
3213
3551
|
bundle: true,
|
|
3214
3552
|
format: "cjs",
|
|
@@ -3236,10 +3574,10 @@ async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter
|
|
|
3236
3574
|
outputExtension: "cjs"
|
|
3237
3575
|
};
|
|
3238
3576
|
}
|
|
3239
|
-
async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter) {
|
|
3577
|
+
async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter, exportName) {
|
|
3240
3578
|
const sdkAliasPlugin = localSdkAliasPlugin(adapter, { workersRuntime: true });
|
|
3241
3579
|
const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
|
|
3242
|
-
const playEntryAlias = workersPlayEntryAliasPlugin(playEntryFile);
|
|
3580
|
+
const playEntryAlias = exportName === "default" ? workersPlayEntryAliasPlugin(playEntryFile) : workersNamedPlayEntryAliasPlugin(playEntryFile, exportName);
|
|
3243
3581
|
const result = await build({
|
|
3244
3582
|
// Entry is the Workers harness; it imports the play via the virtual
|
|
3245
3583
|
// `deepline-play-entry` alias resolved by workersPlayEntryAliasPlugin.
|
|
@@ -3310,10 +3648,16 @@ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies,
|
|
|
3310
3648
|
async function bundlePlayFile(filePath, options) {
|
|
3311
3649
|
const adapter = options.adapter;
|
|
3312
3650
|
const target = options.target ?? PLAY_ARTIFACT_KINDS.cjsNode20;
|
|
3651
|
+
const exportName = options.exportName?.trim() || "default";
|
|
3652
|
+
assertValidExportName(exportName);
|
|
3313
3653
|
const absolutePath = await normalizeLocalPath(filePath);
|
|
3314
3654
|
adapter.warnAboutNonDevelopmentBundling?.(absolutePath);
|
|
3315
3655
|
try {
|
|
3316
3656
|
const analysis = await analyzeSourceGraph(absolutePath, adapter);
|
|
3657
|
+
analysis.graphHash = sha256(
|
|
3658
|
+
`${analysis.graphHash}
|
|
3659
|
+
entry-export:${exportName}`
|
|
3660
|
+
);
|
|
3317
3661
|
if (target === PLAY_ARTIFACT_KINDS.esmWorkers) {
|
|
3318
3662
|
const harnessFingerprint = await computeWorkersHarnessFingerprintWithAdapter(adapter);
|
|
3319
3663
|
analysis.graphHash = sha256(
|
|
@@ -3321,6 +3665,23 @@ async function bundlePlayFile(filePath, options) {
|
|
|
3321
3665
|
workers-harness:${harnessFingerprint}`
|
|
3322
3666
|
);
|
|
3323
3667
|
}
|
|
3668
|
+
const typecheckErrors = [
|
|
3669
|
+
...await adapter.typecheckPlaySource?.({
|
|
3670
|
+
sourceCode: analysis.sourceCode,
|
|
3671
|
+
sourcePath: absolutePath,
|
|
3672
|
+
importedFilePaths: [
|
|
3673
|
+
...analysis.importPolicy.localFiles,
|
|
3674
|
+
...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
3675
|
+
]
|
|
3676
|
+
}) ?? []
|
|
3677
|
+
];
|
|
3678
|
+
if (typecheckErrors.length > 0) {
|
|
3679
|
+
return {
|
|
3680
|
+
success: false,
|
|
3681
|
+
filePath: absolutePath,
|
|
3682
|
+
errors: typecheckErrors
|
|
3683
|
+
};
|
|
3684
|
+
}
|
|
3324
3685
|
const cachedArtifact = await readArtifactCache(analysis.graphHash, target, adapter);
|
|
3325
3686
|
const discoveredFiles = await adapter.discoverPackagedLocalFiles(absolutePath);
|
|
3326
3687
|
if (cachedArtifact) {
|
|
@@ -3340,6 +3701,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3340
3701
|
success: true,
|
|
3341
3702
|
artifact: { ...cachedArtifact, cacheHit: true },
|
|
3342
3703
|
sourceCode: analysis.sourceCode,
|
|
3704
|
+
sourceFiles: analysis.sourceFiles,
|
|
3343
3705
|
filePath: absolutePath,
|
|
3344
3706
|
playName: analysis.playName,
|
|
3345
3707
|
packagedFiles: discoveredFiles.files,
|
|
@@ -3347,25 +3709,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3347
3709
|
importedPlayDependencies: analysis.importedPlayDependencies
|
|
3348
3710
|
};
|
|
3349
3711
|
}
|
|
3350
|
-
const
|
|
3351
|
-
...adapter.typecheckSdkTypes === false ? [] : typecheckPlaySource(analysis, adapter),
|
|
3352
|
-
...await adapter.typecheckPlaySource?.({
|
|
3353
|
-
sourceCode: analysis.sourceCode,
|
|
3354
|
-
sourcePath: absolutePath,
|
|
3355
|
-
importedFilePaths: [
|
|
3356
|
-
...analysis.importPolicy.localFiles,
|
|
3357
|
-
...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
3358
|
-
]
|
|
3359
|
-
}) ?? []
|
|
3360
|
-
];
|
|
3361
|
-
if (typecheckErrors.length > 0) {
|
|
3362
|
-
return {
|
|
3363
|
-
success: false,
|
|
3364
|
-
filePath: absolutePath,
|
|
3365
|
-
errors: typecheckErrors
|
|
3366
|
-
};
|
|
3367
|
-
}
|
|
3368
|
-
const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter);
|
|
3712
|
+
const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
|
|
3369
3713
|
if (Array.isArray(buildOutcome)) {
|
|
3370
3714
|
return {
|
|
3371
3715
|
success: false,
|
|
@@ -3375,7 +3719,8 @@ workers-harness:${harnessFingerprint}`
|
|
|
3375
3719
|
}
|
|
3376
3720
|
const { bundledCode, sourceMapText, outputExtension } = buildOutcome;
|
|
3377
3721
|
const normalizedSourceMap = normalizeSourceMapForRuntime(sourceMapText);
|
|
3378
|
-
const
|
|
3722
|
+
const virtualBaseName = exportName === "default" ? basename(absolutePath).replace(/\.[^.]+$/, "") : `${basename(absolutePath).replace(/\.[^.]+$/, "")}.${exportName}`;
|
|
3723
|
+
const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${virtualBaseName}.${outputExtension}`;
|
|
3379
3724
|
const executableCode = `${bundledCode}
|
|
3380
3725
|
//# sourceMappingURL=${basename(virtualFilename)}.map
|
|
3381
3726
|
`;
|
|
@@ -3413,6 +3758,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3413
3758
|
success: true,
|
|
3414
3759
|
artifact,
|
|
3415
3760
|
sourceCode: analysis.sourceCode,
|
|
3761
|
+
sourceFiles: analysis.sourceFiles,
|
|
3416
3762
|
filePath: absolutePath,
|
|
3417
3763
|
playName: analysis.playName,
|
|
3418
3764
|
packagedFiles: discoveredFiles.files,
|
|
@@ -3494,7 +3840,6 @@ function resolveExecutionProfile(override) {
|
|
|
3494
3840
|
import { createHash as createHash2 } from "crypto";
|
|
3495
3841
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
3496
3842
|
import { basename as basename2, dirname as dirname4, extname as extname2, isAbsolute as isAbsolute2, join as join4, relative, resolve as resolve5 } from "path";
|
|
3497
|
-
import ts2 from "typescript";
|
|
3498
3843
|
var SOURCE_EXTENSIONS2 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
|
|
3499
3844
|
function sha2562(buffer) {
|
|
3500
3845
|
return createHash2("sha256").update(buffer).digest("hex");
|
|
@@ -3506,94 +3851,181 @@ function contentTypeForFile(filePath) {
|
|
|
3506
3851
|
if (extension === ".txt") return "text/plain";
|
|
3507
3852
|
return "application/octet-stream";
|
|
3508
3853
|
}
|
|
3509
|
-
function
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
return ts2.isIdentifier(target) && (target.text === "ctx" || target.text.endsWith("Ctx")) && node.expression.name.text === "csv";
|
|
3515
|
-
}
|
|
3516
|
-
function extractSourceFragment(source, node) {
|
|
3517
|
-
return source.slice(node.getStart(), node.getEnd()).trim();
|
|
3518
|
-
}
|
|
3519
|
-
function referencesInputIdentifier(node) {
|
|
3520
|
-
if (ts2.isIdentifier(node) && node.text === "input") {
|
|
3521
|
-
return true;
|
|
3522
|
-
}
|
|
3523
|
-
return node.getChildren().some((child) => referencesInputIdentifier(child));
|
|
3854
|
+
function stripCommentsToSpaces2(source) {
|
|
3855
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
|
|
3856
|
+
/(^|[^:])\/\/.*$/gm,
|
|
3857
|
+
(match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
|
|
3858
|
+
);
|
|
3524
3859
|
}
|
|
3525
|
-
function
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
return ts2.isIdentifier(node.expression) && node.expression.text === "input";
|
|
3531
|
-
}
|
|
3532
|
-
if (ts2.isIdentifier(node)) {
|
|
3533
|
-
return node.text === "input";
|
|
3534
|
-
}
|
|
3535
|
-
if (ts2.isParenthesizedExpression(node)) {
|
|
3536
|
-
return isRuntimeInputExpression(node.expression);
|
|
3860
|
+
function unquoteStringLiteral2(literal) {
|
|
3861
|
+
const trimmed = literal.trim();
|
|
3862
|
+
const quote = trimmed[0];
|
|
3863
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
3864
|
+
return null;
|
|
3537
3865
|
}
|
|
3538
|
-
|
|
3539
|
-
return
|
|
3866
|
+
try {
|
|
3867
|
+
return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
|
|
3868
|
+
} catch {
|
|
3869
|
+
return trimmed.slice(1, -1);
|
|
3540
3870
|
}
|
|
3541
|
-
|
|
3542
|
-
|
|
3871
|
+
}
|
|
3872
|
+
function splitTopLevelPlus(expression) {
|
|
3873
|
+
const parts = [];
|
|
3874
|
+
let start = 0;
|
|
3875
|
+
let depth = 0;
|
|
3876
|
+
let quote = null;
|
|
3877
|
+
let escaped = false;
|
|
3878
|
+
for (let index = 0; index < expression.length; index += 1) {
|
|
3879
|
+
const char = expression[index];
|
|
3880
|
+
if (quote) {
|
|
3881
|
+
if (escaped) {
|
|
3882
|
+
escaped = false;
|
|
3883
|
+
} else if (char === "\\") {
|
|
3884
|
+
escaped = true;
|
|
3885
|
+
} else if (char === quote) {
|
|
3886
|
+
quote = null;
|
|
3887
|
+
}
|
|
3888
|
+
continue;
|
|
3889
|
+
}
|
|
3890
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3891
|
+
quote = char;
|
|
3892
|
+
continue;
|
|
3893
|
+
}
|
|
3894
|
+
if (char === "(" || char === "[" || char === "{") depth += 1;
|
|
3895
|
+
if (char === ")" || char === "]" || char === "}") depth -= 1;
|
|
3896
|
+
if (char === "+" && depth === 0) {
|
|
3897
|
+
parts.push(expression.slice(start, index));
|
|
3898
|
+
start = index + 1;
|
|
3899
|
+
}
|
|
3543
3900
|
}
|
|
3544
|
-
|
|
3901
|
+
if (parts.length === 0) return null;
|
|
3902
|
+
parts.push(expression.slice(start));
|
|
3903
|
+
return parts;
|
|
3545
3904
|
}
|
|
3546
|
-
function
|
|
3547
|
-
|
|
3548
|
-
|
|
3905
|
+
function stripOuterParens(expression) {
|
|
3906
|
+
let value = expression.trim();
|
|
3907
|
+
while (value.startsWith("(") && value.endsWith(")")) {
|
|
3908
|
+
value = value.slice(1, -1).trim();
|
|
3549
3909
|
}
|
|
3550
|
-
|
|
3551
|
-
|
|
3910
|
+
return value;
|
|
3911
|
+
}
|
|
3912
|
+
function isRuntimeInputExpression(expression) {
|
|
3913
|
+
return /(^|[^\w$])input([^\w$]|$)/.test(expression);
|
|
3914
|
+
}
|
|
3915
|
+
function resolveStringExpression(expression, constants) {
|
|
3916
|
+
const value = stripOuterParens(expression);
|
|
3917
|
+
if (/^(['"])(?:\\.|(?!\1)[\s\S])*\1$/.test(value)) {
|
|
3918
|
+
return unquoteStringLiteral2(value);
|
|
3552
3919
|
}
|
|
3553
|
-
if (
|
|
3554
|
-
return
|
|
3920
|
+
if (/^`(?:\\.|[^`$]|\$(?!\{))*`$/.test(value)) {
|
|
3921
|
+
return value.slice(1, -1);
|
|
3555
3922
|
}
|
|
3556
|
-
if (
|
|
3557
|
-
|
|
3558
|
-
for (const span of node.templateSpans) {
|
|
3559
|
-
const resolved = resolveStringExpression(span.expression, constants);
|
|
3560
|
-
if (resolved == null) {
|
|
3561
|
-
return null;
|
|
3562
|
-
}
|
|
3563
|
-
value += resolved + span.literal.text;
|
|
3564
|
-
}
|
|
3565
|
-
return value;
|
|
3923
|
+
if (/^[A-Za-z_$][\w$]*$/.test(value)) {
|
|
3924
|
+
return constants.get(value) ?? null;
|
|
3566
3925
|
}
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
const
|
|
3570
|
-
|
|
3571
|
-
return null;
|
|
3572
|
-
}
|
|
3573
|
-
return left + right;
|
|
3926
|
+
const parts = splitTopLevelPlus(value);
|
|
3927
|
+
if (parts) {
|
|
3928
|
+
const resolved = parts.map((part) => resolveStringExpression(part, constants));
|
|
3929
|
+
return resolved.every((part) => part != null) ? resolved.join("") : null;
|
|
3574
3930
|
}
|
|
3575
3931
|
return null;
|
|
3576
3932
|
}
|
|
3577
|
-
function collectTopLevelStringConstants(
|
|
3933
|
+
function collectTopLevelStringConstants(sourceCode) {
|
|
3578
3934
|
const constants = /* @__PURE__ */ new Map();
|
|
3579
|
-
|
|
3580
|
-
|
|
3935
|
+
const source = stripCommentsToSpaces2(sourceCode);
|
|
3936
|
+
for (const match of source.matchAll(/(?:^|\n)\s*const\s+([A-Za-z_$][\w$]*)\s*=\s*([^;\n]+)/g)) {
|
|
3937
|
+
const resolved = resolveStringExpression(match[2], constants);
|
|
3938
|
+
if (resolved != null) {
|
|
3939
|
+
constants.set(match[1], resolved);
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
return constants;
|
|
3943
|
+
}
|
|
3944
|
+
function findMatchingGenericEnd(source, openIndex) {
|
|
3945
|
+
let depth = 0;
|
|
3946
|
+
let quote = null;
|
|
3947
|
+
let escaped = false;
|
|
3948
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
3949
|
+
const char = source[index];
|
|
3950
|
+
if (quote) {
|
|
3951
|
+
if (escaped) {
|
|
3952
|
+
escaped = false;
|
|
3953
|
+
} else if (char === "\\") {
|
|
3954
|
+
escaped = true;
|
|
3955
|
+
} else if (char === quote) {
|
|
3956
|
+
quote = null;
|
|
3957
|
+
}
|
|
3581
3958
|
continue;
|
|
3582
3959
|
}
|
|
3583
|
-
if (
|
|
3960
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3961
|
+
quote = char;
|
|
3584
3962
|
continue;
|
|
3585
3963
|
}
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3964
|
+
if (char === "<") depth += 1;
|
|
3965
|
+
if (char === ">") {
|
|
3966
|
+
depth -= 1;
|
|
3967
|
+
if (depth === 0) return index;
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
return -1;
|
|
3971
|
+
}
|
|
3972
|
+
function findCallOpenParen(source, afterCsvIndex) {
|
|
3973
|
+
let index = afterCsvIndex;
|
|
3974
|
+
while (/\s/.test(source[index] ?? "")) index += 1;
|
|
3975
|
+
if (source[index] === "<") {
|
|
3976
|
+
const genericEnd = findMatchingGenericEnd(source, index);
|
|
3977
|
+
if (genericEnd < 0) return -1;
|
|
3978
|
+
index = genericEnd + 1;
|
|
3979
|
+
while (/\s/.test(source[index] ?? "")) index += 1;
|
|
3980
|
+
}
|
|
3981
|
+
return source[index] === "(" ? index : -1;
|
|
3982
|
+
}
|
|
3983
|
+
function firstCallArgument(source, openParen) {
|
|
3984
|
+
let depth = 0;
|
|
3985
|
+
let quote = null;
|
|
3986
|
+
let escaped = false;
|
|
3987
|
+
const start = openParen + 1;
|
|
3988
|
+
for (let index = start; index < source.length; index += 1) {
|
|
3989
|
+
const char = source[index];
|
|
3990
|
+
if (quote) {
|
|
3991
|
+
if (escaped) {
|
|
3992
|
+
escaped = false;
|
|
3993
|
+
} else if (char === "\\") {
|
|
3994
|
+
escaped = true;
|
|
3995
|
+
} else if (char === quote) {
|
|
3996
|
+
quote = null;
|
|
3593
3997
|
}
|
|
3998
|
+
continue;
|
|
3594
3999
|
}
|
|
4000
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
4001
|
+
quote = char;
|
|
4002
|
+
continue;
|
|
4003
|
+
}
|
|
4004
|
+
if (char === "(" || char === "[" || char === "{") depth += 1;
|
|
4005
|
+
if (char === ")" && depth === 0) {
|
|
4006
|
+
const text = source.slice(start, index).trim();
|
|
4007
|
+
return text ? { text, start, end: index } : null;
|
|
4008
|
+
}
|
|
4009
|
+
if (char === "," && depth === 0) {
|
|
4010
|
+
const text = source.slice(start, index).trim();
|
|
4011
|
+
return text ? { text, start, end: index } : null;
|
|
4012
|
+
}
|
|
4013
|
+
if (char === ")" || char === "]" || char === "}") depth -= 1;
|
|
3595
4014
|
}
|
|
3596
|
-
return
|
|
4015
|
+
return null;
|
|
4016
|
+
}
|
|
4017
|
+
function localImportSpecifiers(sourceCode) {
|
|
4018
|
+
const source = stripCommentsToSpaces2(sourceCode);
|
|
4019
|
+
const specifiers = [];
|
|
4020
|
+
for (const match of source.matchAll(
|
|
4021
|
+
/\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?['"]([^'"]+)['"]/g
|
|
4022
|
+
)) {
|
|
4023
|
+
if (match[1]?.startsWith(".")) specifiers.push(match[1]);
|
|
4024
|
+
}
|
|
4025
|
+
for (const match of source.matchAll(/\brequire\s*\(\s*(['"])(\.[^'"]*)\1\s*\)/g)) {
|
|
4026
|
+
specifiers.push(match[2]);
|
|
4027
|
+
}
|
|
4028
|
+
return specifiers;
|
|
3597
4029
|
}
|
|
3598
4030
|
async function fileExists2(filePath) {
|
|
3599
4031
|
try {
|
|
@@ -3638,69 +4070,60 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
3638
4070
|
}
|
|
3639
4071
|
visitedFiles.add(absolutePath);
|
|
3640
4072
|
const sourceCode = await readFile2(absolutePath, "utf-8");
|
|
3641
|
-
const
|
|
3642
|
-
|
|
3643
|
-
sourceCode,
|
|
3644
|
-
ts2.ScriptTarget.Latest,
|
|
3645
|
-
true,
|
|
3646
|
-
ts2.ScriptKind.TS
|
|
3647
|
-
);
|
|
3648
|
-
const constants = collectTopLevelStringConstants(sourceFile);
|
|
4073
|
+
const scanSource = stripCommentsToSpaces2(sourceCode);
|
|
4074
|
+
const constants = collectTopLevelStringConstants(sourceCode);
|
|
3649
4075
|
const childVisits = [];
|
|
3650
|
-
const
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
4076
|
+
for (const match of scanSource.matchAll(/\b([A-Za-z_$][\w$]*)\s*\.\s*csv\b/g)) {
|
|
4077
|
+
const target = match[1];
|
|
4078
|
+
if (target !== "ctx" && !target.endsWith("Ctx")) {
|
|
4079
|
+
continue;
|
|
4080
|
+
}
|
|
4081
|
+
const openParen = findCallOpenParen(scanSource, match.index + match[0].length);
|
|
4082
|
+
if (openParen < 0) {
|
|
4083
|
+
continue;
|
|
4084
|
+
}
|
|
4085
|
+
const argument = firstCallArgument(scanSource, openParen);
|
|
4086
|
+
if (!argument) {
|
|
4087
|
+
unresolved.push({
|
|
4088
|
+
sourceFragment: "ctx.csv()",
|
|
4089
|
+
message: "ctx.csv() requires a file path string or input reference."
|
|
4090
|
+
});
|
|
4091
|
+
} else if (!isRuntimeInputExpression(argument.text)) {
|
|
4092
|
+
const resolvedPath = resolveStringExpression(argument.text, constants);
|
|
4093
|
+
if (resolvedPath == null) {
|
|
3654
4094
|
unresolved.push({
|
|
3655
|
-
sourceFragment:
|
|
3656
|
-
message: "ctx.csv()
|
|
4095
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4096
|
+
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."
|
|
3657
4097
|
});
|
|
3658
|
-
} else
|
|
3659
|
-
const
|
|
3660
|
-
if (resolvedPath
|
|
4098
|
+
} else {
|
|
4099
|
+
const absoluteCsvPath = resolve5(dirname4(absolutePath), resolvedPath);
|
|
4100
|
+
if (isAbsolute2(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
|
|
3661
4101
|
unresolved.push({
|
|
3662
|
-
sourceFragment:
|
|
3663
|
-
message: "
|
|
3664
|
-
});
|
|
3665
|
-
} else {
|
|
3666
|
-
const absoluteCsvPath = resolve5(dirname4(absolutePath), resolvedPath);
|
|
3667
|
-
if (isAbsolute2(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
|
|
3668
|
-
unresolved.push({
|
|
3669
|
-
sourceFragment: extractSourceFragment(sourceCode, argument),
|
|
3670
|
-
message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
|
|
3671
|
-
});
|
|
3672
|
-
return;
|
|
3673
|
-
}
|
|
3674
|
-
const buffer = await readFile2(absoluteCsvPath);
|
|
3675
|
-
const stats = await stat2(absoluteCsvPath);
|
|
3676
|
-
files.set(absoluteCsvPath, {
|
|
3677
|
-
sourceFragment: extractSourceFragment(sourceCode, argument),
|
|
3678
|
-
logicalPath: resolvedPath,
|
|
3679
|
-
absolutePath: absoluteCsvPath,
|
|
3680
|
-
bytes: stats.size,
|
|
3681
|
-
contentHash: sha2562(buffer),
|
|
3682
|
-
contentType: contentTypeForFile(absoluteCsvPath)
|
|
4102
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4103
|
+
message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
|
|
3683
4104
|
});
|
|
4105
|
+
continue;
|
|
3684
4106
|
}
|
|
4107
|
+
const buffer = await readFile2(absoluteCsvPath);
|
|
4108
|
+
const stats = await stat2(absoluteCsvPath);
|
|
4109
|
+
files.set(absoluteCsvPath, {
|
|
4110
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4111
|
+
logicalPath: resolvedPath,
|
|
4112
|
+
absolutePath: absoluteCsvPath,
|
|
4113
|
+
bytes: stats.size,
|
|
4114
|
+
contentHash: sha2562(buffer),
|
|
4115
|
+
contentType: contentTypeForFile(absoluteCsvPath)
|
|
4116
|
+
});
|
|
3685
4117
|
}
|
|
3686
4118
|
}
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
)
|
|
3692
|
-
)
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
childVisits.push(
|
|
3696
|
-
resolveLocalImport2(absolutePath, node.arguments[0].text).then(
|
|
3697
|
-
(resolvedImport) => visitSourceFile(resolvedImport)
|
|
3698
|
-
)
|
|
3699
|
-
);
|
|
3700
|
-
}
|
|
3701
|
-
await Promise.all(node.getChildren(sourceFile).map((child) => visitNode(child)));
|
|
3702
|
-
};
|
|
3703
|
-
await visitNode(sourceFile);
|
|
4119
|
+
}
|
|
4120
|
+
for (const specifier of localImportSpecifiers(sourceCode)) {
|
|
4121
|
+
childVisits.push(
|
|
4122
|
+
resolveLocalImport2(absolutePath, specifier).then(
|
|
4123
|
+
(resolvedImport) => visitSourceFile(resolvedImport)
|
|
4124
|
+
)
|
|
4125
|
+
);
|
|
4126
|
+
}
|
|
3704
4127
|
await Promise.all(childVisits);
|
|
3705
4128
|
};
|
|
3706
4129
|
await visitSourceFile(absoluteEntryFile);
|
|
@@ -3711,7 +4134,7 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
3711
4134
|
}
|
|
3712
4135
|
|
|
3713
4136
|
// src/plays/bundle-play-file.ts
|
|
3714
|
-
var PLAY_BUNDLE_CACHE_VERSION2 =
|
|
4137
|
+
var PLAY_BUNDLE_CACHE_VERSION2 = 26;
|
|
3715
4138
|
var MODULE_DIR = dirname5(fileURLToPath(import.meta.url));
|
|
3716
4139
|
var SDK_PACKAGE_ROOT = resolve6(MODULE_DIR, "..", "..");
|
|
3717
4140
|
var SOURCE_REPO_ROOT = resolve6(SDK_PACKAGE_ROOT, "..");
|
|
@@ -3726,7 +4149,7 @@ var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED
|
|
|
3726
4149
|
var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? resolve6(SOURCE_REPO_ROOT, "sdk", "src") : HAS_PACKAGED_BUNDLING_SOURCES ? resolve6(PACKAGED_REPO_ROOT, "sdk", "src") : resolve6(SDK_PACKAGE_ROOT, "src");
|
|
3727
4150
|
var SDK_PACKAGE_JSON = resolve6(SDK_PACKAGE_ROOT, "package.json");
|
|
3728
4151
|
var SDK_ENTRY_FILE = resolve6(SDK_SOURCE_ROOT, "index.ts");
|
|
3729
|
-
var SDK_TYPES_ENTRY_FILE = resolve6(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
4152
|
+
var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : resolve6(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
3730
4153
|
var SDK_WORKERS_ENTRY_FILE = resolve6(SDK_SOURCE_ROOT, "worker-play-entry.ts");
|
|
3731
4154
|
var WORKERS_HARNESS_ENTRY_FILE = resolve6(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
|
|
3732
4155
|
var WORKERS_HARNESS_FILES_DIR = resolve6(PROJECT_ROOT, "apps", "play-runner-workers", "src");
|
|
@@ -3758,7 +4181,7 @@ function createSdkPlayBundlingAdapter() {
|
|
|
3758
4181
|
sdkSourceRoot: SDK_SOURCE_ROOT,
|
|
3759
4182
|
sdkPackageJson: SDK_PACKAGE_JSON,
|
|
3760
4183
|
sdkEntryFile: SDK_ENTRY_FILE,
|
|
3761
|
-
sdkTypesEntryFile: existsSync3(SDK_TYPES_ENTRY_FILE) ?
|
|
4184
|
+
sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES || !existsSync3(SDK_TYPES_ENTRY_FILE) ? SDK_ENTRY_FILE : SDK_TYPES_ENTRY_FILE,
|
|
3762
4185
|
sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
|
|
3763
4186
|
workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
|
|
3764
4187
|
workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
|
|
@@ -3769,6 +4192,7 @@ function createSdkPlayBundlingAdapter() {
|
|
|
3769
4192
|
async function bundlePlayFile2(filePath, options = {}) {
|
|
3770
4193
|
return bundlePlayFile(filePath, {
|
|
3771
4194
|
target: options.target ?? defaultPlayBundleTarget(),
|
|
4195
|
+
exportName: options.exportName,
|
|
3772
4196
|
adapter: createSdkPlayBundlingAdapter()
|
|
3773
4197
|
});
|
|
3774
4198
|
}
|
|
@@ -3918,54 +4342,6 @@ function createCliProgress(enabled) {
|
|
|
3918
4342
|
return progress;
|
|
3919
4343
|
}
|
|
3920
4344
|
|
|
3921
|
-
// src/cli/trace.ts
|
|
3922
|
-
var cliTraceStartedAt = Date.now();
|
|
3923
|
-
function isTruthyEnv(value) {
|
|
3924
|
-
return value === "1" || value === "true" || value === "yes";
|
|
3925
|
-
}
|
|
3926
|
-
function isCliTraceEnabled() {
|
|
3927
|
-
return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
|
|
3928
|
-
}
|
|
3929
|
-
function recordCliTrace(event) {
|
|
3930
|
-
if (!isCliTraceEnabled()) {
|
|
3931
|
-
return;
|
|
3932
|
-
}
|
|
3933
|
-
const now = Date.now();
|
|
3934
|
-
const payload = {
|
|
3935
|
-
ts: now,
|
|
3936
|
-
source: "cli",
|
|
3937
|
-
sinceStartMs: now - cliTraceStartedAt,
|
|
3938
|
-
...event
|
|
3939
|
-
};
|
|
3940
|
-
process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
|
|
3941
|
-
`);
|
|
3942
|
-
}
|
|
3943
|
-
async function traceCliSpan(phase, fields, run) {
|
|
3944
|
-
if (!isCliTraceEnabled()) {
|
|
3945
|
-
return run();
|
|
3946
|
-
}
|
|
3947
|
-
const startedAt = Date.now();
|
|
3948
|
-
try {
|
|
3949
|
-
const result = await run();
|
|
3950
|
-
recordCliTrace({
|
|
3951
|
-
phase,
|
|
3952
|
-
ms: Date.now() - startedAt,
|
|
3953
|
-
ok: true,
|
|
3954
|
-
...fields
|
|
3955
|
-
});
|
|
3956
|
-
return result;
|
|
3957
|
-
} catch (error) {
|
|
3958
|
-
recordCliTrace({
|
|
3959
|
-
phase,
|
|
3960
|
-
ms: Date.now() - startedAt,
|
|
3961
|
-
ok: false,
|
|
3962
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3963
|
-
...fields
|
|
3964
|
-
});
|
|
3965
|
-
throw error;
|
|
3966
|
-
}
|
|
3967
|
-
}
|
|
3968
|
-
|
|
3969
4345
|
// src/cli/commands/play.ts
|
|
3970
4346
|
function parseReferencedPlayTarget(target) {
|
|
3971
4347
|
const trimmed = target.trim();
|
|
@@ -4013,67 +4389,6 @@ function defaultMaterializedPlayPath(reference) {
|
|
|
4013
4389
|
const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
4014
4390
|
return resolve7(`${safeName || "play"}.play.ts`);
|
|
4015
4391
|
}
|
|
4016
|
-
function sanitizeGeneratedPlayName(value) {
|
|
4017
|
-
return value.trim().toLowerCase().replace(/^prebuilt\//, "").replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "play";
|
|
4018
|
-
}
|
|
4019
|
-
function buildGeneratedCsvWrapperSource(input) {
|
|
4020
|
-
return `import { definePlay } from 'deepline';
|
|
4021
|
-
|
|
4022
|
-
export default definePlay(
|
|
4023
|
-
${JSON.stringify(input.wrapperName)},
|
|
4024
|
-
async (ctx, input: Record<string, unknown> & { file: string }) => {
|
|
4025
|
-
const rows = await ctx.csv<Record<string, unknown>>(input.file);
|
|
4026
|
-
const constants = Object.fromEntries(
|
|
4027
|
-
Object.entries(input).filter(([key]) => key !== 'file'),
|
|
4028
|
-
);
|
|
4029
|
-
|
|
4030
|
-
const mappedRows = await ctx
|
|
4031
|
-
.map('csv_rows', rows, {
|
|
4032
|
-
key: (row, index) =>
|
|
4033
|
-
String(
|
|
4034
|
-
row.id ??
|
|
4035
|
-
row.lead_id ??
|
|
4036
|
-
row.email ??
|
|
4037
|
-
row.linkedin_url ??
|
|
4038
|
-
row.domain ??
|
|
4039
|
-
index,
|
|
4040
|
-
),
|
|
4041
|
-
})
|
|
4042
|
-
.step('result', (row, rowCtx) =>
|
|
4043
|
-
rowCtx.runPlay(
|
|
4044
|
-
'row_play',
|
|
4045
|
-
${JSON.stringify(input.playRef)},
|
|
4046
|
-
{
|
|
4047
|
-
...constants,
|
|
4048
|
-
...row,
|
|
4049
|
-
},
|
|
4050
|
-
{
|
|
4051
|
-
description: 'Run the source play for this CSV row.',
|
|
4052
|
-
},
|
|
4053
|
-
),
|
|
4054
|
-
)
|
|
4055
|
-
.run({ description: 'Run the source play once per CSV row.' });
|
|
4056
|
-
|
|
4057
|
-
return { rows: mappedRows };
|
|
4058
|
-
},
|
|
4059
|
-
);
|
|
4060
|
-
`;
|
|
4061
|
-
}
|
|
4062
|
-
function writeGeneratedCsvWrapperPlay(playRef) {
|
|
4063
|
-
const baseName = sanitizeGeneratedPlayName(
|
|
4064
|
-
parseReferencedPlayTarget(playRef).unqualifiedPlayName
|
|
4065
|
-
);
|
|
4066
|
-
const wrapperName = `${baseName}-csv`;
|
|
4067
|
-
const outputDir = resolve7(".deepline", "generated");
|
|
4068
|
-
const outputPath = join6(outputDir, `${wrapperName}.play.ts`);
|
|
4069
|
-
mkdirSync3(outputDir, { recursive: true });
|
|
4070
|
-
writeFileSync4(
|
|
4071
|
-
outputPath,
|
|
4072
|
-
buildGeneratedCsvWrapperSource({ wrapperName, playRef }),
|
|
4073
|
-
"utf-8"
|
|
4074
|
-
);
|
|
4075
|
-
return outputPath;
|
|
4076
|
-
}
|
|
4077
4392
|
function materializeRemotePlaySource(input) {
|
|
4078
4393
|
if (isFileTarget(input.target)) {
|
|
4079
4394
|
return null;
|
|
@@ -4103,13 +4418,15 @@ function formatLoadedPlayMessage(materializedFile) {
|
|
|
4103
4418
|
return `Loaded play here: ${materializedFile.path}`;
|
|
4104
4419
|
}
|
|
4105
4420
|
function buildReadonlyPrebuiltPlayError(reference) {
|
|
4421
|
+
const localName = reference.split("/").slice(1).join("/") || "custom-play";
|
|
4106
4422
|
return new Error(
|
|
4107
4423
|
`Cannot edit or push ${reference} because Deepline prebuilt plays are read-only.
|
|
4108
4424
|
To make your own version:
|
|
4109
|
-
1.
|
|
4110
|
-
2. Change definePlay('${
|
|
4111
|
-
3. Run: deepline plays
|
|
4112
|
-
4.
|
|
4425
|
+
1. Run: deepline plays get ${reference} --source --out ./${localName}.play.ts
|
|
4426
|
+
2. Change definePlay('${localName}', ...) to a new play name you own.
|
|
4427
|
+
3. Run: deepline plays check ./${localName}.play.ts
|
|
4428
|
+
4. Run: deepline plays publish ./${localName}.play.ts
|
|
4429
|
+
5. Your play will then live under your workspace namespace.`
|
|
4113
4430
|
);
|
|
4114
4431
|
}
|
|
4115
4432
|
async function ensureEditableRemotePlay(client, target) {
|
|
@@ -4139,7 +4456,10 @@ function looksLikeFilePath(target) {
|
|
|
4139
4456
|
if (target.trim().toLowerCase().startsWith("prebuilt/")) {
|
|
4140
4457
|
return false;
|
|
4141
4458
|
}
|
|
4142
|
-
|
|
4459
|
+
if (target.startsWith("./") || target.startsWith("../") || target.startsWith("/") || target.startsWith("~/")) {
|
|
4460
|
+
return true;
|
|
4461
|
+
}
|
|
4462
|
+
return target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
|
|
4143
4463
|
}
|
|
4144
4464
|
function parsePositiveInteger2(value, flagName) {
|
|
4145
4465
|
const parsed = Number.parseInt(value, 10);
|
|
@@ -4156,6 +4476,142 @@ function parseJsonInput(raw) {
|
|
|
4156
4476
|
}
|
|
4157
4477
|
return parsed;
|
|
4158
4478
|
}
|
|
4479
|
+
function parseInputFieldFlag(rawFlag, nextArg) {
|
|
4480
|
+
const flag = rawFlag.slice(2);
|
|
4481
|
+
const equalsIndex = flag.indexOf("=");
|
|
4482
|
+
if (equalsIndex > 0) {
|
|
4483
|
+
const path = flag.slice(0, equalsIndex).trim();
|
|
4484
|
+
const value = flag.slice(equalsIndex + 1);
|
|
4485
|
+
if (!path) {
|
|
4486
|
+
throw new Error(`Invalid play input flag: ${rawFlag}`);
|
|
4487
|
+
}
|
|
4488
|
+
return { path, value };
|
|
4489
|
+
}
|
|
4490
|
+
if (!nextArg || nextArg.startsWith("--")) {
|
|
4491
|
+
throw new Error(`Play input flag ${rawFlag} requires a value.`);
|
|
4492
|
+
}
|
|
4493
|
+
return { path: flag, value: nextArg };
|
|
4494
|
+
}
|
|
4495
|
+
function parseInputFlagValue(raw) {
|
|
4496
|
+
const trimmed = raw.trim();
|
|
4497
|
+
if (!trimmed) return "";
|
|
4498
|
+
if (trimmed === "true" || trimmed === "false" || trimmed === "null" || trimmed.startsWith("{") || trimmed.startsWith("[") || /^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
4499
|
+
try {
|
|
4500
|
+
return JSON.parse(trimmed);
|
|
4501
|
+
} catch {
|
|
4502
|
+
return raw;
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
return raw;
|
|
4506
|
+
}
|
|
4507
|
+
function getDottedInputValue(input, path) {
|
|
4508
|
+
const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
4509
|
+
let cursor = input;
|
|
4510
|
+
for (const part of parts) {
|
|
4511
|
+
if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
|
|
4512
|
+
return void 0;
|
|
4513
|
+
}
|
|
4514
|
+
cursor = cursor[part];
|
|
4515
|
+
}
|
|
4516
|
+
return cursor;
|
|
4517
|
+
}
|
|
4518
|
+
function setDottedInputValue(input, path, value) {
|
|
4519
|
+
const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
4520
|
+
if (parts.length === 0) {
|
|
4521
|
+
throw new Error(`Invalid play input flag path: ${path}`);
|
|
4522
|
+
}
|
|
4523
|
+
let cursor = input;
|
|
4524
|
+
for (const part of parts.slice(0, -1)) {
|
|
4525
|
+
const existing = cursor[part];
|
|
4526
|
+
if (existing !== void 0 && (!existing || typeof existing !== "object" || Array.isArray(existing))) {
|
|
4527
|
+
throw new Error(
|
|
4528
|
+
`Cannot set --${path}; input.${part} is already a non-object value.`
|
|
4529
|
+
);
|
|
4530
|
+
}
|
|
4531
|
+
if (!existing) {
|
|
4532
|
+
cursor[part] = {};
|
|
4533
|
+
}
|
|
4534
|
+
cursor = cursor[part];
|
|
4535
|
+
}
|
|
4536
|
+
cursor[parts[parts.length - 1]] = value;
|
|
4537
|
+
}
|
|
4538
|
+
function schemaMetadata(schema, key) {
|
|
4539
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return null;
|
|
4540
|
+
const value = schema[key];
|
|
4541
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4542
|
+
}
|
|
4543
|
+
function stringMetadata(metadata, key) {
|
|
4544
|
+
const value = metadata?.[key];
|
|
4545
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4546
|
+
}
|
|
4547
|
+
function inputFieldFromCsvArg(csvArg) {
|
|
4548
|
+
if (typeof csvArg !== "string") return null;
|
|
4549
|
+
const match = /^input\.([A-Za-z_$][\w$]*)$/.exec(csvArg.trim());
|
|
4550
|
+
return match?.[1] ?? null;
|
|
4551
|
+
}
|
|
4552
|
+
function fileInputBindingsFromPlaySchema(inputSchema) {
|
|
4553
|
+
const csvInput = schemaMetadata(inputSchema, "csvInput");
|
|
4554
|
+
if (!csvInput) return [];
|
|
4555
|
+
return [
|
|
4556
|
+
{
|
|
4557
|
+
inputPath: stringMetadata(csvInput, "inputField") ?? "csv"
|
|
4558
|
+
}
|
|
4559
|
+
];
|
|
4560
|
+
}
|
|
4561
|
+
function fileInputBindingsFromStaticPipeline(staticPipeline) {
|
|
4562
|
+
if (!staticPipeline || typeof staticPipeline !== "object" || Array.isArray(staticPipeline)) {
|
|
4563
|
+
return [];
|
|
4564
|
+
}
|
|
4565
|
+
const inputField = inputFieldFromCsvArg(
|
|
4566
|
+
staticPipeline.csvArg
|
|
4567
|
+
);
|
|
4568
|
+
return inputField ? [{ inputPath: inputField }] : [];
|
|
4569
|
+
}
|
|
4570
|
+
function applyCsvShortcutInput(input) {
|
|
4571
|
+
const csvValue = getDottedInputValue(input.runtimeInput, "csv");
|
|
4572
|
+
if (csvValue == null || csvValue === "") return;
|
|
4573
|
+
const candidate = input.bindings.find((binding) => binding.inputPath !== "csv")?.inputPath ?? input.fallbackInputPath ?? null;
|
|
4574
|
+
if (!candidate || candidate === "csv") return;
|
|
4575
|
+
const existing = getDottedInputValue(input.runtimeInput, candidate);
|
|
4576
|
+
if (existing != null && existing !== "") return;
|
|
4577
|
+
setDottedInputValue(input.runtimeInput, candidate, csvValue);
|
|
4578
|
+
}
|
|
4579
|
+
function isLocalFilePathValue(value) {
|
|
4580
|
+
if (typeof value !== "string" || !value.trim()) return false;
|
|
4581
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
4582
|
+
return existsSync4(resolve7(value));
|
|
4583
|
+
}
|
|
4584
|
+
async function stageFileInputArgs(input) {
|
|
4585
|
+
const uniqueBindings = [
|
|
4586
|
+
...new Map(input.bindings.map((binding) => [binding.inputPath, binding])).values()
|
|
4587
|
+
];
|
|
4588
|
+
const localFiles = uniqueBindings.flatMap((binding) => {
|
|
4589
|
+
const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
|
|
4590
|
+
if (!isLocalFilePathValue(value)) return [];
|
|
4591
|
+
const absolutePath = resolve7(value);
|
|
4592
|
+
return [{ binding, absolutePath, logicalPath: basename3(absolutePath) }];
|
|
4593
|
+
});
|
|
4594
|
+
if (localFiles.length === 0) {
|
|
4595
|
+
return { inputFile: null, packagedFiles: [] };
|
|
4596
|
+
}
|
|
4597
|
+
input.progress.phase(
|
|
4598
|
+
localFiles.length === 1 ? "staging input file" : "staging input files"
|
|
4599
|
+
);
|
|
4600
|
+
const staged = await input.client.stagePlayFiles(
|
|
4601
|
+
localFiles.map((file) => stageFile(file.logicalPath, file.absolutePath))
|
|
4602
|
+
);
|
|
4603
|
+
for (const [index, file] of localFiles.entries()) {
|
|
4604
|
+
setDottedInputValue(input.runtimeInput, file.binding.inputPath, file.logicalPath);
|
|
4605
|
+
const stagedFile = staged[index];
|
|
4606
|
+
if (stagedFile && stagedFile.logicalPath !== file.logicalPath) {
|
|
4607
|
+
setDottedInputValue(input.runtimeInput, file.binding.inputPath, stagedFile.logicalPath);
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
return {
|
|
4611
|
+
inputFile: staged[0] ?? null,
|
|
4612
|
+
packagedFiles: staged.slice(1)
|
|
4613
|
+
};
|
|
4614
|
+
}
|
|
4159
4615
|
function stageFile(logicalPath, absolutePath) {
|
|
4160
4616
|
const buffer = readFileSync3(absolutePath);
|
|
4161
4617
|
return {
|
|
@@ -4243,6 +4699,7 @@ async function compileBundledPlayGraphManifests(client, graph) {
|
|
|
4243
4699
|
node.compilerManifest = await client.compilePlayManifest({
|
|
4244
4700
|
name,
|
|
4245
4701
|
sourceCode: node.sourceCode,
|
|
4702
|
+
sourceFiles: node.sourceFiles,
|
|
4246
4703
|
artifact: node.artifact,
|
|
4247
4704
|
importedPlayDependencies: node.importedPlayDependencies.map(
|
|
4248
4705
|
(dependency) => {
|
|
@@ -4292,6 +4749,7 @@ async function publishImportedPlayDependencies(client, graph) {
|
|
|
4292
4749
|
await client.registerPlayArtifact({
|
|
4293
4750
|
name: node.playName,
|
|
4294
4751
|
sourceCode: node.sourceCode,
|
|
4752
|
+
sourceFiles: node.sourceFiles,
|
|
4295
4753
|
artifact: node.artifact,
|
|
4296
4754
|
compilerManifest: requireCompilerManifest(node),
|
|
4297
4755
|
publish: true
|
|
@@ -4309,67 +4767,6 @@ function formatTimestamp(value) {
|
|
|
4309
4767
|
function formatRunLine(run) {
|
|
4310
4768
|
return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)}`;
|
|
4311
4769
|
}
|
|
4312
|
-
function parsePlayRunTarget(input) {
|
|
4313
|
-
const { args, usage } = input;
|
|
4314
|
-
let runId = null;
|
|
4315
|
-
let playName = null;
|
|
4316
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
4317
|
-
const arg = args[index];
|
|
4318
|
-
if (arg === "--json") {
|
|
4319
|
-
continue;
|
|
4320
|
-
}
|
|
4321
|
-
if (arg === "--reason" || arg === "--interval-ms" || arg === "--poll-interval-ms") {
|
|
4322
|
-
index += 1;
|
|
4323
|
-
continue;
|
|
4324
|
-
}
|
|
4325
|
-
if (arg === "--run-id" && args[index + 1]) {
|
|
4326
|
-
runId = args[++index].trim();
|
|
4327
|
-
continue;
|
|
4328
|
-
}
|
|
4329
|
-
if (arg === "--name" && args[index + 1] && input.allowName) {
|
|
4330
|
-
playName = parseReferencedPlayTarget(args[++index]).playName;
|
|
4331
|
-
continue;
|
|
4332
|
-
}
|
|
4333
|
-
if (arg.startsWith("--")) {
|
|
4334
|
-
continue;
|
|
4335
|
-
}
|
|
4336
|
-
throw new DeeplineError(
|
|
4337
|
-
`Unexpected positional target "${arg}". Use --run-id for run ids.
|
|
4338
|
-
${usage}`
|
|
4339
|
-
);
|
|
4340
|
-
}
|
|
4341
|
-
const explicitTargets = [runId, playName].filter(Boolean).length;
|
|
4342
|
-
if (explicitTargets > 1) {
|
|
4343
|
-
throw new DeeplineError(`Choose exactly one play run target.
|
|
4344
|
-
${usage}`);
|
|
4345
|
-
}
|
|
4346
|
-
if (runId) {
|
|
4347
|
-
return { kind: "run", runId };
|
|
4348
|
-
}
|
|
4349
|
-
if (playName) {
|
|
4350
|
-
return { kind: "name", name: playName };
|
|
4351
|
-
}
|
|
4352
|
-
throw new DeeplineError(usage);
|
|
4353
|
-
}
|
|
4354
|
-
async function resolvePlayRunId(client, target) {
|
|
4355
|
-
if (target.kind === "run") {
|
|
4356
|
-
try {
|
|
4357
|
-
const status = await client.getPlayStatus(target.runId);
|
|
4358
|
-
return status.runId;
|
|
4359
|
-
} catch (error) {
|
|
4360
|
-
if (!(error instanceof DeeplineError) || error.statusCode !== 404) {
|
|
4361
|
-
throw error;
|
|
4362
|
-
}
|
|
4363
|
-
throw new DeeplineError(`No play run found for run id: ${target.runId}`);
|
|
4364
|
-
}
|
|
4365
|
-
}
|
|
4366
|
-
const runs = await client.listPlayRuns(target.name);
|
|
4367
|
-
const workflowId = runs[0]?.workflowId ?? "";
|
|
4368
|
-
if (!workflowId) {
|
|
4369
|
-
throw new DeeplineError(`No runs found for play: ${target.name}`);
|
|
4370
|
-
}
|
|
4371
|
-
return workflowId;
|
|
4372
|
-
}
|
|
4373
4770
|
function isTransientPlayStatusPollError(error) {
|
|
4374
4771
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
4375
4772
|
return error.statusCode >= 500 && error.statusCode < 600;
|
|
@@ -4473,7 +4870,7 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
4473
4870
|
if (input.waitTimeoutMs !== null && Date.now() - input.startedAt >= input.waitTimeoutMs) {
|
|
4474
4871
|
const hasRealRunId = input.workflowId.length > 0 && input.workflowId !== "pending";
|
|
4475
4872
|
const phaseSuffix = input.lastPhase && input.lastPhase.trim() ? ` (last observed phase: ${input.lastPhase.trim()})` : "";
|
|
4476
|
-
const tailHint = hasRealRunId ? ` Run 'deepline
|
|
4873
|
+
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.`;
|
|
4477
4874
|
throw new DeeplineError(
|
|
4478
4875
|
`Timed out waiting for play ${hasRealRunId ? input.workflowId : "<no run id>"} after ${Math.ceil(input.waitTimeoutMs / 1e3)}s${phaseSuffix}.${tailHint}`,
|
|
4479
4876
|
void 0,
|
|
@@ -4483,68 +4880,8 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
4483
4880
|
...input.lastPhase ? { phase: input.lastPhase } : {},
|
|
4484
4881
|
timeoutMs: input.waitTimeoutMs
|
|
4485
4882
|
}
|
|
4486
|
-
);
|
|
4487
|
-
}
|
|
4488
|
-
}
|
|
4489
|
-
async function waitForPlayCompletionByStream(input) {
|
|
4490
|
-
const controller = new AbortController();
|
|
4491
|
-
let timedOut = false;
|
|
4492
|
-
let lastPhase = null;
|
|
4493
|
-
const timeout = input.waitTimeoutMs === null ? null : setTimeout(
|
|
4494
|
-
() => {
|
|
4495
|
-
timedOut = true;
|
|
4496
|
-
controller.abort();
|
|
4497
|
-
},
|
|
4498
|
-
Math.max(1, input.waitTimeoutMs - (Date.now() - input.startedAt))
|
|
4499
|
-
);
|
|
4500
|
-
try {
|
|
4501
|
-
for await (const event of input.client.streamPlayRunEvents(
|
|
4502
|
-
input.workflowId,
|
|
4503
|
-
{ signal: controller.signal }
|
|
4504
|
-
)) {
|
|
4505
|
-
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
4506
|
-
const phase = describeLiveEventPhase(event);
|
|
4507
|
-
if (phase) {
|
|
4508
|
-
lastPhase = phase;
|
|
4509
|
-
input.progress.phase(phase);
|
|
4510
|
-
}
|
|
4511
|
-
printPlayLogLines({
|
|
4512
|
-
lines: getLogLinesFromLiveEvent(event),
|
|
4513
|
-
status: null,
|
|
4514
|
-
jsonOutput: input.jsonOutput,
|
|
4515
|
-
emitLogs: input.emitLogs,
|
|
4516
|
-
state: input.state,
|
|
4517
|
-
progress: input.progress
|
|
4518
|
-
});
|
|
4519
|
-
const status = getStatusFromLiveEvent(event);
|
|
4520
|
-
if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
|
|
4521
|
-
const finalStatus = await input.client.getPlayStatus(input.workflowId);
|
|
4522
|
-
if (TERMINAL_PLAY_STATUSES2.has(finalStatus.status)) {
|
|
4523
|
-
return finalStatus;
|
|
4524
|
-
}
|
|
4525
|
-
}
|
|
4526
|
-
}
|
|
4527
|
-
} catch (error) {
|
|
4528
|
-
if (timedOut) {
|
|
4529
|
-
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
4530
|
-
}
|
|
4531
|
-
throw error;
|
|
4532
|
-
} finally {
|
|
4533
|
-
if (timeout) {
|
|
4534
|
-
clearTimeout(timeout);
|
|
4535
|
-
}
|
|
4536
|
-
}
|
|
4537
|
-
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
4538
|
-
throw new DeeplineError(
|
|
4539
|
-
`Play live stream ended before the run reached a terminal state runId=${input.workflowId}${phaseSuffix}.`,
|
|
4540
|
-
void 0,
|
|
4541
|
-
"PLAY_LIVE_STREAM_ENDED",
|
|
4542
|
-
{
|
|
4543
|
-
runId: input.workflowId,
|
|
4544
|
-
workflowId: input.workflowId,
|
|
4545
|
-
...lastPhase ? { phase: lastPhase } : {}
|
|
4546
|
-
}
|
|
4547
|
-
);
|
|
4883
|
+
);
|
|
4884
|
+
}
|
|
4548
4885
|
}
|
|
4549
4886
|
async function startAndWaitForPlayCompletionByStream(input) {
|
|
4550
4887
|
const startedAt = Date.now();
|
|
@@ -4564,24 +4901,10 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4564
4901
|
},
|
|
4565
4902
|
Math.max(1, input.waitTimeoutMs)
|
|
4566
4903
|
);
|
|
4567
|
-
recordCliTrace({
|
|
4568
|
-
phase: "cli.start_stream_request",
|
|
4569
|
-
playName: input.playName
|
|
4570
|
-
});
|
|
4571
4904
|
try {
|
|
4572
|
-
let eventCount = 0;
|
|
4573
4905
|
for await (const event of input.client.startPlayRunStream(input.request, {
|
|
4574
4906
|
signal: controller.signal
|
|
4575
4907
|
})) {
|
|
4576
|
-
eventCount += 1;
|
|
4577
|
-
if (eventCount === 1) {
|
|
4578
|
-
recordCliTrace({
|
|
4579
|
-
phase: "cli.start_stream_first_event",
|
|
4580
|
-
ms: Date.now() - startedAt,
|
|
4581
|
-
playName: input.playName,
|
|
4582
|
-
eventType: event.type
|
|
4583
|
-
});
|
|
4584
|
-
}
|
|
4585
4908
|
const eventRunId = getEventPayload(event).runId;
|
|
4586
4909
|
if (typeof eventRunId === "string" && eventRunId && eventRunId !== "pending") {
|
|
4587
4910
|
lastKnownWorkflowId = eventRunId;
|
|
@@ -4622,14 +4945,6 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4622
4945
|
});
|
|
4623
4946
|
const finalStatus = getFinalStatusFromLiveEvent(event);
|
|
4624
4947
|
if (finalStatus) {
|
|
4625
|
-
recordCliTrace({
|
|
4626
|
-
phase: "cli.start_stream_final_event",
|
|
4627
|
-
ms: Date.now() - startedAt,
|
|
4628
|
-
playName: input.playName,
|
|
4629
|
-
runId: finalStatus.runId,
|
|
4630
|
-
status: finalStatus.status,
|
|
4631
|
-
eventCount
|
|
4632
|
-
});
|
|
4633
4948
|
return finalStatus;
|
|
4634
4949
|
}
|
|
4635
4950
|
}
|
|
@@ -4647,10 +4962,12 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4647
4962
|
clearTimeout(timeout);
|
|
4648
4963
|
}
|
|
4649
4964
|
const reason = error instanceof Error ? error.message : String(error);
|
|
4650
|
-
|
|
4651
|
-
|
|
4965
|
+
if (!input.jsonOutput) {
|
|
4966
|
+
process.stderr.write(
|
|
4967
|
+
`[play watch] start stream failed after run ${lastKnownWorkflowId}; falling back to polling (${reason})
|
|
4652
4968
|
`
|
|
4653
|
-
|
|
4969
|
+
);
|
|
4970
|
+
}
|
|
4654
4971
|
return waitForPlayCompletionByPolling({
|
|
4655
4972
|
client: input.client,
|
|
4656
4973
|
workflowId: lastKnownWorkflowId,
|
|
@@ -4669,6 +4986,24 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4669
4986
|
clearTimeout(timeout);
|
|
4670
4987
|
}
|
|
4671
4988
|
}
|
|
4989
|
+
if (lastKnownWorkflowId) {
|
|
4990
|
+
if (!input.jsonOutput) {
|
|
4991
|
+
input.progress.writeLine(
|
|
4992
|
+
`[play watch] start stream ended after run ${lastKnownWorkflowId}; falling back to polling`
|
|
4993
|
+
);
|
|
4994
|
+
}
|
|
4995
|
+
return waitForPlayCompletionByPolling({
|
|
4996
|
+
client: input.client,
|
|
4997
|
+
workflowId: lastKnownWorkflowId,
|
|
4998
|
+
pollIntervalMs: 500,
|
|
4999
|
+
jsonOutput: input.jsonOutput,
|
|
5000
|
+
emitLogs: input.emitLogs,
|
|
5001
|
+
waitTimeoutMs: input.waitTimeoutMs,
|
|
5002
|
+
startedAt,
|
|
5003
|
+
state,
|
|
5004
|
+
progress: input.progress
|
|
5005
|
+
});
|
|
5006
|
+
}
|
|
4672
5007
|
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
4673
5008
|
const idSuffix = lastKnownWorkflowId ? ` runId=${lastKnownWorkflowId}` : "";
|
|
4674
5009
|
throw new DeeplineError(
|
|
@@ -4746,38 +5081,6 @@ async function waitForPlayCompletionByPolling(input) {
|
|
|
4746
5081
|
}
|
|
4747
5082
|
}
|
|
4748
5083
|
}
|
|
4749
|
-
async function waitForPlayCompletion(input) {
|
|
4750
|
-
const startedAt = Date.now();
|
|
4751
|
-
const state = {
|
|
4752
|
-
lastLogIndex: 0,
|
|
4753
|
-
emittedRunnerStarted: false
|
|
4754
|
-
};
|
|
4755
|
-
try {
|
|
4756
|
-
return await waitForPlayCompletionByStream({
|
|
4757
|
-
...input,
|
|
4758
|
-
startedAt,
|
|
4759
|
-
state,
|
|
4760
|
-
progress: input.progress
|
|
4761
|
-
});
|
|
4762
|
-
} catch (error) {
|
|
4763
|
-
assertPlayWaitNotTimedOut({
|
|
4764
|
-
workflowId: input.workflowId,
|
|
4765
|
-
startedAt,
|
|
4766
|
-
waitTimeoutMs: input.waitTimeoutMs
|
|
4767
|
-
});
|
|
4768
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
4769
|
-
process.stderr.write(
|
|
4770
|
-
`[play watch] SSE stream failed; falling back to polling (${reason})
|
|
4771
|
-
`
|
|
4772
|
-
);
|
|
4773
|
-
return waitForPlayCompletionByPolling({
|
|
4774
|
-
...input,
|
|
4775
|
-
startedAt,
|
|
4776
|
-
state,
|
|
4777
|
-
progress: input.progress
|
|
4778
|
-
});
|
|
4779
|
-
}
|
|
4780
|
-
}
|
|
4781
5084
|
function formatInteger(value) {
|
|
4782
5085
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
|
|
4783
5086
|
}
|
|
@@ -4906,18 +5209,153 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
4906
5209
|
}
|
|
4907
5210
|
function buildRunNextCommands(runId) {
|
|
4908
5211
|
return {
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
logs: `deepline runs logs ${runId}
|
|
5212
|
+
get: `deepline runs get ${runId} --json`,
|
|
5213
|
+
tail: `deepline runs tail ${runId} --json`,
|
|
5214
|
+
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
5215
|
+
logs: `deepline runs logs ${runId} --out run.log --json`,
|
|
5216
|
+
exportCsv: `deepline runs export ${runId} --out output.csv`
|
|
5217
|
+
};
|
|
5218
|
+
}
|
|
5219
|
+
var RUN_LOG_PREVIEW_LIMIT = 20;
|
|
5220
|
+
function getRecordField(value, key) {
|
|
5221
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value[key] : void 0;
|
|
5222
|
+
}
|
|
5223
|
+
function getNumericField(value, key) {
|
|
5224
|
+
const field = getRecordField(value, key);
|
|
5225
|
+
return typeof field === "number" && Number.isFinite(field) ? field : null;
|
|
5226
|
+
}
|
|
5227
|
+
function getStringField(value, key) {
|
|
5228
|
+
const field = getRecordField(value, key);
|
|
5229
|
+
return typeof field === "string" && field.trim() ? field : null;
|
|
5230
|
+
}
|
|
5231
|
+
function normalizeRunStatusForEnvelope(status) {
|
|
5232
|
+
const run = status.run ?? null;
|
|
5233
|
+
return {
|
|
5234
|
+
id: status.runId,
|
|
5235
|
+
playName: status.playName ?? status.name ?? getStringField(run, "playName") ?? null,
|
|
5236
|
+
status: status.status,
|
|
5237
|
+
runtime: getStringField(status, "runtime") ?? getStringField(status, "runtimeBackend") ?? getStringField(run, "runtime") ?? null,
|
|
5238
|
+
startedAt: getStringField(run, "startTime") ?? getStringField(run, "startedAt") ?? null,
|
|
5239
|
+
updatedAt: getStringField(status, "updatedAt") ?? getStringField(run, "updatedAt") ?? null,
|
|
5240
|
+
finishedAt: getStringField(run, "closeTime") ?? getStringField(run, "finishedAt") ?? null,
|
|
5241
|
+
source: getRecordField(status, "source") ?? getRecordField(status, "artifact") ?? null
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
5244
|
+
function normalizeProgressForEnvelope(status, rowsInfo) {
|
|
5245
|
+
const progress = status.progress;
|
|
5246
|
+
const total = getNumericField(progress, "totalRows") ?? getNumericField(progress, "total") ?? rowsInfo?.totalRows ?? null;
|
|
5247
|
+
const failed = getNumericField(progress, "failed") ?? getNumericField(progress, "failedRows") ?? null;
|
|
5248
|
+
const completed = getNumericField(progress, "completed") ?? getNumericField(progress, "completedRows") ?? (status.status === "completed" ? total : null);
|
|
5249
|
+
const pending = getNumericField(progress, "pending") ?? (typeof total === "number" && typeof completed === "number" && typeof failed === "number" ? Math.max(0, total - completed - failed) : null);
|
|
5250
|
+
return {
|
|
5251
|
+
total,
|
|
5252
|
+
completed,
|
|
5253
|
+
pending,
|
|
5254
|
+
failed,
|
|
5255
|
+
executed: getNumericField(progress, "executed"),
|
|
5256
|
+
reused: getNumericField(progress, "reused"),
|
|
5257
|
+
skipped: getNumericField(progress, "skipped"),
|
|
5258
|
+
retried: getNumericField(progress, "retried"),
|
|
5259
|
+
degraded: typeof getRecordField(progress, "degraded") === "boolean" ? getRecordField(progress, "degraded") : null,
|
|
5260
|
+
duplicates: getRecordField(progress, "duplicates") ?? null,
|
|
5261
|
+
active: getStringField(progress, "status") ?? getStringField(status, "activeStep") ?? getStringField(status, "activeNodeId") ?? null,
|
|
5262
|
+
wait: status.wait ?? null
|
|
5263
|
+
};
|
|
5264
|
+
}
|
|
5265
|
+
function normalizeOutputsForEnvelope(rowsInfo, exportedPath) {
|
|
5266
|
+
if (!rowsInfo) {
|
|
5267
|
+
return exportedPath ? [{ name: "output", kind: "file", path: exportedPath }] : [];
|
|
5268
|
+
}
|
|
5269
|
+
return [
|
|
5270
|
+
{
|
|
5271
|
+
name: "rows",
|
|
5272
|
+
kind: "dataset",
|
|
5273
|
+
rowCount: rowsInfo.totalRows,
|
|
5274
|
+
columns: rowsInfo.columns,
|
|
5275
|
+
preview: rowsInfo.rows.slice(0, 5),
|
|
5276
|
+
previewRowCount: Math.min(rowsInfo.rows.length, 5),
|
|
5277
|
+
previewLimit: 5,
|
|
5278
|
+
complete: rowsInfo.complete,
|
|
5279
|
+
source: rowsInfo.source,
|
|
5280
|
+
...exportedPath ? { csv_path: exportedPath } : {}
|
|
5281
|
+
}
|
|
5282
|
+
];
|
|
5283
|
+
}
|
|
5284
|
+
function normalizeStepsForEnvelope(status) {
|
|
5285
|
+
const directSteps = getRecordField(status, "steps");
|
|
5286
|
+
if (Array.isArray(directSteps)) {
|
|
5287
|
+
return directSteps;
|
|
5288
|
+
}
|
|
5289
|
+
const timeline = getRecordField(status, "timeline");
|
|
5290
|
+
if (Array.isArray(timeline)) {
|
|
5291
|
+
return timeline;
|
|
5292
|
+
}
|
|
5293
|
+
return [];
|
|
5294
|
+
}
|
|
5295
|
+
function normalizeErrorsForEnvelope(status, error) {
|
|
5296
|
+
const directErrors = getRecordField(status, "errors");
|
|
5297
|
+
if (Array.isArray(directErrors)) {
|
|
5298
|
+
return directErrors.filter(
|
|
5299
|
+
(entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)
|
|
5300
|
+
);
|
|
5301
|
+
}
|
|
5302
|
+
if (!error) {
|
|
5303
|
+
return [];
|
|
5304
|
+
}
|
|
5305
|
+
return [
|
|
5306
|
+
{
|
|
5307
|
+
code: getStringField(status, "errorCode") ?? "RUN_FAILED",
|
|
5308
|
+
phase: getStringField(status, "errorPhase") ?? "runtime",
|
|
5309
|
+
message: error,
|
|
5310
|
+
retryable: typeof getRecordField(status, "retryable") === "boolean" ? getRecordField(status, "retryable") : null,
|
|
5311
|
+
nextAction: `deepline runs get ${status.runId} --json`
|
|
5312
|
+
}
|
|
5313
|
+
];
|
|
5314
|
+
}
|
|
5315
|
+
function normalizeLogsForEnvelope(status) {
|
|
5316
|
+
const logs = Array.isArray(status.progress?.logs) ? status.progress.logs : [];
|
|
5317
|
+
const offset = typeof status.progress?.logOffset === "number" && Number.isFinite(status.progress.logOffset) ? Math.max(0, Math.trunc(status.progress.logOffset)) : 0;
|
|
5318
|
+
const totalCount = offset + logs.length;
|
|
5319
|
+
const entries = logs.slice(Math.max(0, logs.length - RUN_LOG_PREVIEW_LIMIT));
|
|
5320
|
+
const firstSequence = entries.length === 0 ? null : offset + logs.length - entries.length + 1;
|
|
5321
|
+
const lastSequence = totalCount === 0 ? null : totalCount;
|
|
5322
|
+
return {
|
|
5323
|
+
totalCount,
|
|
5324
|
+
returnedCount: entries.length,
|
|
5325
|
+
firstSequence,
|
|
5326
|
+
lastSequence,
|
|
5327
|
+
truncated: totalCount > entries.length,
|
|
5328
|
+
hasMore: totalCount > entries.length,
|
|
5329
|
+
entries,
|
|
5330
|
+
nextCursor: lastSequence
|
|
4913
5331
|
};
|
|
4914
5332
|
}
|
|
5333
|
+
function stripProviderSpendFromBilling(value) {
|
|
5334
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5335
|
+
return value;
|
|
5336
|
+
}
|
|
5337
|
+
const next = {};
|
|
5338
|
+
for (const [key, item] of Object.entries(value)) {
|
|
5339
|
+
if (key === "providerCostUsd" || key === "totalProviderCostUsd") {
|
|
5340
|
+
continue;
|
|
5341
|
+
}
|
|
5342
|
+
next[key] = item;
|
|
5343
|
+
}
|
|
5344
|
+
return next;
|
|
5345
|
+
}
|
|
4915
5346
|
function compactPlayStatus(status, options) {
|
|
4916
5347
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
4917
5348
|
const result = status && typeof status === "object" ? status.result : null;
|
|
4918
5349
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
4919
|
-
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
4920
|
-
|
|
5350
|
+
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5351
|
+
rowsInfo.rows,
|
|
5352
|
+
rowsInfo.totalRows,
|
|
5353
|
+
rowsInfo.columns,
|
|
5354
|
+
extractDatasetExecutionStats(status)
|
|
5355
|
+
) : null;
|
|
5356
|
+
const billing = status && typeof status === "object" ? stripProviderSpendFromBilling(
|
|
5357
|
+
status.billing
|
|
5358
|
+
) : null;
|
|
4921
5359
|
const progressError = status.progress?.error;
|
|
4922
5360
|
const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
|
|
4923
5361
|
return {
|
|
@@ -4926,18 +5364,38 @@ function compactPlayStatus(status, options) {
|
|
|
4926
5364
|
...typeof status.name === "string" ? { name: status.name } : {},
|
|
4927
5365
|
...typeof status.playName === "string" ? { playName: status.playName } : {},
|
|
4928
5366
|
status: status.status,
|
|
5367
|
+
run: normalizeRunStatusForEnvelope(status),
|
|
5368
|
+
progress: normalizeProgressForEnvelope(status, rowsInfo),
|
|
5369
|
+
outputs: normalizeOutputsForEnvelope(rowsInfo, options?.exportedPath),
|
|
5370
|
+
steps: normalizeStepsForEnvelope(status),
|
|
5371
|
+
errors: normalizeErrorsForEnvelope(status, error),
|
|
5372
|
+
logs: normalizeLogsForEnvelope(status),
|
|
4929
5373
|
...error ? { error } : {},
|
|
4930
5374
|
...warnings.length > 0 ? { warnings } : {},
|
|
4931
5375
|
output: buildOutputSummary(rowsInfo, options?.exportedPath) ?? result ?? null,
|
|
4932
5376
|
...result !== void 0 ? { result } : {},
|
|
4933
5377
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
4934
5378
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
4935
|
-
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0,
|
|
5379
|
+
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
|
|
4936
5380
|
...billing ? { billing } : {},
|
|
4937
|
-
...status.run ? { run: status.run } : {},
|
|
4938
5381
|
next: buildRunNextCommands(status.runId)
|
|
4939
5382
|
};
|
|
4940
5383
|
}
|
|
5384
|
+
function enrichPlayStatusWithDatasetStats(status) {
|
|
5385
|
+
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
5386
|
+
if (!rowsInfo?.complete) {
|
|
5387
|
+
return status;
|
|
5388
|
+
}
|
|
5389
|
+
return {
|
|
5390
|
+
...status,
|
|
5391
|
+
dataset_stats: buildDatasetStats(
|
|
5392
|
+
rowsInfo.rows,
|
|
5393
|
+
rowsInfo.totalRows,
|
|
5394
|
+
rowsInfo.columns,
|
|
5395
|
+
extractDatasetExecutionStats(status)
|
|
5396
|
+
)
|
|
5397
|
+
};
|
|
5398
|
+
}
|
|
4941
5399
|
function formatDatasetStatsLines(datasetStats) {
|
|
4942
5400
|
if (!datasetStats) {
|
|
4943
5401
|
return [];
|
|
@@ -4947,10 +5405,11 @@ function formatDatasetStatsLines(datasetStats) {
|
|
|
4947
5405
|
0,
|
|
4948
5406
|
12
|
|
4949
5407
|
)) {
|
|
4950
|
-
const topValues = stat3.top_values ? `,
|
|
4951
|
-
const sample = stat3.sample_value !== void 0 ? `,
|
|
5408
|
+
const topValues = stat3.top_values ? `, top_values=${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
|
|
5409
|
+
const sample = stat3.sample_value !== void 0 ? `, sample_value=${JSON.stringify(stat3.sample_value)}` : "";
|
|
5410
|
+
const execution = stat3.execution ? `, execution=${Object.entries(stat3.execution).map(([bucket, count]) => `${bucket}=${count}`).join(", ")}` : "";
|
|
4952
5411
|
lines.push(
|
|
4953
|
-
` ${column}:
|
|
5412
|
+
` ${column}: non_empty=${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}${execution}`
|
|
4954
5413
|
);
|
|
4955
5414
|
}
|
|
4956
5415
|
return lines;
|
|
@@ -4959,7 +5418,7 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
4959
5418
|
if (jsonOutput) {
|
|
4960
5419
|
process.stdout.write(
|
|
4961
5420
|
`${JSON.stringify(
|
|
4962
|
-
options?.fullJson ? status : compactPlayStatus(status, options)
|
|
5421
|
+
options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status, options)
|
|
4963
5422
|
)}
|
|
4964
5423
|
`
|
|
4965
5424
|
);
|
|
@@ -4973,7 +5432,12 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
4973
5432
|
lines.push(`${success ? "\u2713" : "\u2717"} ${publicStatus} ${runId}`);
|
|
4974
5433
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
4975
5434
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
4976
|
-
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5435
|
+
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5436
|
+
rowsInfo.rows,
|
|
5437
|
+
rowsInfo.totalRows,
|
|
5438
|
+
rowsInfo.columns,
|
|
5439
|
+
extractDatasetExecutionStats(status)
|
|
5440
|
+
) : null;
|
|
4977
5441
|
const outputSummary = buildOutputSummary(rowsInfo, options?.exportedPath);
|
|
4978
5442
|
if (outputSummary) {
|
|
4979
5443
|
const columns = Array.isArray(outputSummary.columns) ? outputSummary.columns.length : 0;
|
|
@@ -5092,10 +5556,10 @@ function writeStartedPlayRun(input) {
|
|
|
5092
5556
|
const lines = [
|
|
5093
5557
|
`Started ${input.playName}`,
|
|
5094
5558
|
` run id: ${input.runId}`,
|
|
5095
|
-
`
|
|
5096
|
-
` tail logs: deepline
|
|
5097
|
-
` stop run: deepline
|
|
5098
|
-
` result JSON: deepline
|
|
5559
|
+
` get status: deepline runs get ${input.runId} --json`,
|
|
5560
|
+
` tail logs: deepline runs tail ${input.runId} --json`,
|
|
5561
|
+
` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`,
|
|
5562
|
+
` result JSON: deepline runs get ${input.runId} --json`
|
|
5099
5563
|
];
|
|
5100
5564
|
if (input.dashboardUrl) {
|
|
5101
5565
|
lines.push(` play page: ${input.dashboardUrl}`);
|
|
@@ -5108,10 +5572,9 @@ function writeStartedPlayRun(input) {
|
|
|
5108
5572
|
console.log(output);
|
|
5109
5573
|
}
|
|
5110
5574
|
function parsePlayRunOptions(args) {
|
|
5111
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--
|
|
5575
|
+
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]";
|
|
5112
5576
|
let filePath = null;
|
|
5113
5577
|
let playName = null;
|
|
5114
|
-
let csvPath = null;
|
|
5115
5578
|
let input = null;
|
|
5116
5579
|
let revisionId = null;
|
|
5117
5580
|
let revisionSelector = null;
|
|
@@ -5132,10 +5595,6 @@ function parsePlayRunOptions(args) {
|
|
|
5132
5595
|
playName = parseReferencedPlayTarget(args[++index]).playName;
|
|
5133
5596
|
continue;
|
|
5134
5597
|
}
|
|
5135
|
-
if (arg === "--csv" && args[index + 1]) {
|
|
5136
|
-
csvPath = resolve7(args[++index]);
|
|
5137
|
-
continue;
|
|
5138
|
-
}
|
|
5139
5598
|
if ((arg === "--input" || arg === "-i") && args[index + 1]) {
|
|
5140
5599
|
input = parseJsonInput(args[++index]);
|
|
5141
5600
|
continue;
|
|
@@ -5185,8 +5644,13 @@ function parsePlayRunOptions(args) {
|
|
|
5185
5644
|
continue;
|
|
5186
5645
|
}
|
|
5187
5646
|
if (arg.startsWith("--")) {
|
|
5188
|
-
|
|
5189
|
-
|
|
5647
|
+
const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
|
|
5648
|
+
input ??= {};
|
|
5649
|
+
setDottedInputValue(input, path, parseInputFlagValue(value));
|
|
5650
|
+
if (!arg.includes("=")) {
|
|
5651
|
+
index += 1;
|
|
5652
|
+
}
|
|
5653
|
+
continue;
|
|
5190
5654
|
}
|
|
5191
5655
|
if (!arg.startsWith("--") && !filePath && !playName) {
|
|
5192
5656
|
if (isFileTarget(arg) || looksLikeFilePath(arg)) {
|
|
@@ -5222,7 +5686,6 @@ ${usage}`);
|
|
|
5222
5686
|
}
|
|
5223
5687
|
return {
|
|
5224
5688
|
target: filePath ? { kind: "file", path: filePath } : { kind: "name", name: playName },
|
|
5225
|
-
csvPath,
|
|
5226
5689
|
input,
|
|
5227
5690
|
revisionId,
|
|
5228
5691
|
revisionSelector,
|
|
@@ -5243,6 +5706,10 @@ function parsePlayCheckOptions(args) {
|
|
|
5243
5706
|
const jsonOutput = argsWantJson(args);
|
|
5244
5707
|
return { target, jsonOutput };
|
|
5245
5708
|
}
|
|
5709
|
+
function shouldUseLocalOnlyPlayCheck() {
|
|
5710
|
+
const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
|
|
5711
|
+
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
5712
|
+
}
|
|
5246
5713
|
async function handlePlayCheck(args) {
|
|
5247
5714
|
const options = parsePlayCheckOptions(args);
|
|
5248
5715
|
if (!isFileTarget(options.target)) {
|
|
@@ -5268,10 +5735,28 @@ async function handlePlayCheck(args) {
|
|
|
5268
5735
|
return 1;
|
|
5269
5736
|
}
|
|
5270
5737
|
const playName = graph.root.playName ?? extractPlayName(sourceCode, absolutePlayPath);
|
|
5738
|
+
if (shouldUseLocalOnlyPlayCheck()) {
|
|
5739
|
+
const result2 = {
|
|
5740
|
+
valid: true,
|
|
5741
|
+
errors: [],
|
|
5742
|
+
staticPipeline: graph.root.compilerManifest?.staticPipeline ?? null,
|
|
5743
|
+
artifactHash: graph.root.artifact.artifactHash,
|
|
5744
|
+
graphHash: graph.root.artifact.graphHash
|
|
5745
|
+
};
|
|
5746
|
+
if (options.jsonOutput) {
|
|
5747
|
+
process.stdout.write(`${JSON.stringify({ name: playName, ...result2 })}
|
|
5748
|
+
`);
|
|
5749
|
+
} else {
|
|
5750
|
+
console.log(`\u2713 ${playName} passed local play check`);
|
|
5751
|
+
console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
|
|
5752
|
+
}
|
|
5753
|
+
return 0;
|
|
5754
|
+
}
|
|
5271
5755
|
const client = new DeeplineClient();
|
|
5272
5756
|
const result = await client.checkPlayArtifact({
|
|
5273
5757
|
name: playName,
|
|
5274
5758
|
sourceCode: graph.root.sourceCode,
|
|
5759
|
+
sourceFiles: graph.root.sourceFiles,
|
|
5275
5760
|
artifact: graph.root.artifact
|
|
5276
5761
|
});
|
|
5277
5762
|
if (options.jsonOutput) {
|
|
@@ -5297,34 +5782,12 @@ async function handleFileBackedRun(options) {
|
|
|
5297
5782
|
const client = new DeeplineClient();
|
|
5298
5783
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
5299
5784
|
const absolutePlayPath = resolve7(options.target.path);
|
|
5300
|
-
recordCliTrace({
|
|
5301
|
-
phase: "cli.play_run_file_start",
|
|
5302
|
-
playPath: absolutePlayPath,
|
|
5303
|
-
watch: options.watch,
|
|
5304
|
-
hasCsv: Boolean(options.csvPath),
|
|
5305
|
-
force: options.force
|
|
5306
|
-
});
|
|
5307
5785
|
progress.phase("compiling play");
|
|
5308
|
-
const readSourceStartedAt = Date.now();
|
|
5309
5786
|
const sourceCode = readFileSync3(absolutePlayPath, "utf-8");
|
|
5310
|
-
recordCliTrace({
|
|
5311
|
-
phase: "cli.read_play_source",
|
|
5312
|
-
ms: Date.now() - readSourceStartedAt,
|
|
5313
|
-
bytes: sourceCode.length,
|
|
5314
|
-
playPath: absolutePlayPath
|
|
5315
|
-
});
|
|
5316
5787
|
let graph;
|
|
5317
5788
|
try {
|
|
5318
|
-
graph = await
|
|
5319
|
-
|
|
5320
|
-
{ playPath: absolutePlayPath },
|
|
5321
|
-
() => collectBundledPlayGraph(absolutePlayPath)
|
|
5322
|
-
);
|
|
5323
|
-
await traceCliSpan(
|
|
5324
|
-
"cli.compile_play_manifest",
|
|
5325
|
-
{ playPath: absolutePlayPath, nodeCount: graph.nodes.size },
|
|
5326
|
-
() => compileBundledPlayGraphManifests(client, graph)
|
|
5327
|
-
);
|
|
5789
|
+
graph = await collectBundledPlayGraph(absolutePlayPath);
|
|
5790
|
+
await compileBundledPlayGraphManifests(client, graph);
|
|
5328
5791
|
progress.phase("compiled play");
|
|
5329
5792
|
} catch (error) {
|
|
5330
5793
|
progress.fail();
|
|
@@ -5335,87 +5798,65 @@ async function handleFileBackedRun(options) {
|
|
|
5335
5798
|
const playName = bundleResult.playName ?? extractPlayName(sourceCode, absolutePlayPath);
|
|
5336
5799
|
try {
|
|
5337
5800
|
progress.phase("publishing imported plays");
|
|
5338
|
-
await
|
|
5339
|
-
"cli.publish_imported_plays",
|
|
5340
|
-
{ playName, nodeCount: graph.nodes.size },
|
|
5341
|
-
() => publishImportedPlayDependencies(client, graph)
|
|
5342
|
-
);
|
|
5801
|
+
await publishImportedPlayDependencies(client, graph);
|
|
5343
5802
|
} catch (error) {
|
|
5344
5803
|
progress.fail();
|
|
5345
5804
|
console.error(error instanceof Error ? error.message : String(error));
|
|
5346
5805
|
return 1;
|
|
5347
5806
|
}
|
|
5348
5807
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5349
|
-
const prepareFilesStartedAt = Date.now();
|
|
5350
5808
|
const packagedFileUploads = bundleResult.packagedFiles.map(
|
|
5351
5809
|
(file) => stageFile(file.logicalPath, file.absolutePath)
|
|
5352
5810
|
);
|
|
5353
|
-
const
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5811
|
+
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
5812
|
+
requireCompilerManifest(bundleResult).staticPipeline
|
|
5813
|
+
);
|
|
5814
|
+
applyCsvShortcutInput({
|
|
5815
|
+
runtimeInput,
|
|
5816
|
+
bindings: fileInputBindings,
|
|
5817
|
+
fallbackInputPath: "file"
|
|
5818
|
+
});
|
|
5819
|
+
const stagedFileInputs = await stageFileInputArgs({
|
|
5820
|
+
client,
|
|
5821
|
+
runtimeInput,
|
|
5822
|
+
bindings: fileInputBindings,
|
|
5823
|
+
progress
|
|
5363
5824
|
});
|
|
5364
5825
|
const startRequest = {
|
|
5365
5826
|
name: playName,
|
|
5366
5827
|
sourceCode: bundleResult.sourceCode,
|
|
5828
|
+
sourceFiles: bundleResult.sourceFiles,
|
|
5367
5829
|
runtimeArtifact: bundleResult.artifact,
|
|
5368
5830
|
compilerManifest: requireCompilerManifest(bundleResult),
|
|
5369
|
-
inputFileUpload,
|
|
5370
5831
|
packagedFileUploads,
|
|
5371
5832
|
...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
|
|
5833
|
+
...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
|
|
5834
|
+
...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
|
|
5372
5835
|
...options.force ? { force: true } : {}
|
|
5373
5836
|
};
|
|
5374
5837
|
if (options.watch) {
|
|
5375
5838
|
progress.phase("starting run");
|
|
5376
|
-
const finalStatus = await
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
() => startAndWaitForPlayCompletionByStream({
|
|
5380
|
-
client,
|
|
5381
|
-
request: startRequest,
|
|
5382
|
-
playName,
|
|
5383
|
-
jsonOutput: options.jsonOutput,
|
|
5384
|
-
emitLogs: options.emitLogs,
|
|
5385
|
-
waitTimeoutMs: options.waitTimeoutMs,
|
|
5386
|
-
progress
|
|
5387
|
-
})
|
|
5388
|
-
);
|
|
5389
|
-
const exportStartedAt = Date.now();
|
|
5390
|
-
const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
|
|
5391
|
-
recordCliTrace({
|
|
5392
|
-
phase: "cli.export_rows",
|
|
5393
|
-
ms: Date.now() - exportStartedAt,
|
|
5839
|
+
const finalStatus = await startAndWaitForPlayCompletionByStream({
|
|
5840
|
+
client,
|
|
5841
|
+
request: startRequest,
|
|
5394
5842
|
playName,
|
|
5395
|
-
|
|
5843
|
+
jsonOutput: options.jsonOutput,
|
|
5844
|
+
emitLogs: options.emitLogs,
|
|
5845
|
+
waitTimeoutMs: options.waitTimeoutMs,
|
|
5846
|
+
progress
|
|
5396
5847
|
});
|
|
5848
|
+
const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
|
|
5397
5849
|
if (finalStatus.status === "completed") {
|
|
5398
5850
|
progress.complete();
|
|
5399
5851
|
} else {
|
|
5400
5852
|
progress.fail();
|
|
5401
5853
|
}
|
|
5402
|
-
recordCliTrace({
|
|
5403
|
-
phase: "cli.write_play_result",
|
|
5404
|
-
playName,
|
|
5405
|
-
status: finalStatus.status,
|
|
5406
|
-
runId: finalStatus.runId
|
|
5407
|
-
});
|
|
5408
5854
|
writePlayResult(finalStatus, options.jsonOutput, { exportedPath });
|
|
5409
5855
|
return finalStatus.status === "completed" ? 0 : 1;
|
|
5410
5856
|
}
|
|
5411
5857
|
progress.phase("starting run");
|
|
5412
|
-
const started = await
|
|
5413
|
-
|
|
5414
|
-
{ playName },
|
|
5415
|
-
() => client.startPlayRun(startRequest)
|
|
5416
|
-
);
|
|
5417
|
-
const fallbackDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
5418
|
-
const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
|
|
5858
|
+
const started = await client.startPlayRun(startRequest);
|
|
5859
|
+
const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
5419
5860
|
progress.phase(`loading play on ${dashboardUrl}`);
|
|
5420
5861
|
progress.complete();
|
|
5421
5862
|
writeStartedPlayRun({
|
|
@@ -5423,7 +5864,7 @@ async function handleFileBackedRun(options) {
|
|
|
5423
5864
|
playName,
|
|
5424
5865
|
status: started.status,
|
|
5425
5866
|
statusUrl: started.statusUrl,
|
|
5426
|
-
dashboardUrl,
|
|
5867
|
+
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
5427
5868
|
jsonOutput: options.jsonOutput,
|
|
5428
5869
|
progress
|
|
5429
5870
|
});
|
|
@@ -5449,9 +5890,8 @@ async function handleNamedRun(options) {
|
|
|
5449
5890
|
}
|
|
5450
5891
|
const client = new DeeplineClient();
|
|
5451
5892
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
5452
|
-
let stagedInputFile = null;
|
|
5453
5893
|
progress.phase("loading play definition");
|
|
5454
|
-
await assertCanonicalNamedPlayReference(client, options.target.name);
|
|
5894
|
+
const playDetail = await assertCanonicalNamedPlayReference(client, options.target.name);
|
|
5455
5895
|
progress.phase("selecting revision");
|
|
5456
5896
|
const selectedRevisionId = await resolveNamedRunRevisionId({
|
|
5457
5897
|
client,
|
|
@@ -5459,19 +5899,27 @@ async function handleNamedRun(options) {
|
|
|
5459
5899
|
revisionId: options.revisionId,
|
|
5460
5900
|
selector: options.revisionSelector
|
|
5461
5901
|
});
|
|
5462
|
-
if (options.csvPath) {
|
|
5463
|
-
progress.phase("staging input file");
|
|
5464
|
-
const [staged] = await client.stagePlayFiles([
|
|
5465
|
-
stageFile(basename3(options.csvPath), options.csvPath)
|
|
5466
|
-
]);
|
|
5467
|
-
stagedInputFile = staged ?? null;
|
|
5468
|
-
}
|
|
5469
5902
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5903
|
+
const fileInputBindings = [
|
|
5904
|
+
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
5905
|
+
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
5906
|
+
];
|
|
5907
|
+
applyCsvShortcutInput({
|
|
5908
|
+
runtimeInput,
|
|
5909
|
+
bindings: fileInputBindings
|
|
5910
|
+
});
|
|
5911
|
+
const stagedFileInputs = await stageFileInputArgs({
|
|
5912
|
+
client,
|
|
5913
|
+
runtimeInput,
|
|
5914
|
+
bindings: fileInputBindings,
|
|
5915
|
+
progress
|
|
5916
|
+
});
|
|
5470
5917
|
const startRequest = {
|
|
5471
5918
|
name: options.target.name,
|
|
5472
5919
|
...selectedRevisionId ? { revisionId: selectedRevisionId } : {},
|
|
5473
5920
|
...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
|
|
5474
|
-
...
|
|
5921
|
+
...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
|
|
5922
|
+
...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
|
|
5475
5923
|
...options.force ? { force: true } : {}
|
|
5476
5924
|
};
|
|
5477
5925
|
if (options.watch) {
|
|
@@ -5485,22 +5933,6 @@ async function handleNamedRun(options) {
|
|
|
5485
5933
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
5486
5934
|
progress
|
|
5487
5935
|
});
|
|
5488
|
-
if (finalStatus.status !== "completed" && options.csvPath) {
|
|
5489
|
-
progress.phase("generating csv wrapper play");
|
|
5490
|
-
const generatedPlayPath = writeGeneratedCsvWrapperPlay(
|
|
5491
|
-
options.target.name
|
|
5492
|
-
);
|
|
5493
|
-
progress.writeLogLine(
|
|
5494
|
-
`Generated CSV wrapper play: ${generatedPlayPath}`
|
|
5495
|
-
);
|
|
5496
|
-
progress.phase("running generated csv wrapper play");
|
|
5497
|
-
return handleFileBackedRun({
|
|
5498
|
-
...options,
|
|
5499
|
-
target: { kind: "file", path: generatedPlayPath },
|
|
5500
|
-
revisionId: null,
|
|
5501
|
-
revisionSelector: null
|
|
5502
|
-
});
|
|
5503
|
-
}
|
|
5504
5936
|
const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
|
|
5505
5937
|
if (finalStatus.status === "completed") {
|
|
5506
5938
|
progress.complete();
|
|
@@ -5512,11 +5944,10 @@ async function handleNamedRun(options) {
|
|
|
5512
5944
|
}
|
|
5513
5945
|
progress.phase("starting run");
|
|
5514
5946
|
const started = await client.startPlayRun(startRequest);
|
|
5515
|
-
const
|
|
5947
|
+
const dashboardUrl = buildPlayDashboardUrl(
|
|
5516
5948
|
client.baseUrl,
|
|
5517
5949
|
options.target.name
|
|
5518
5950
|
);
|
|
5519
|
-
const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
|
|
5520
5951
|
progress.phase(`loading play on ${dashboardUrl}`);
|
|
5521
5952
|
progress.complete();
|
|
5522
5953
|
writeStartedPlayRun({
|
|
@@ -5524,7 +5955,7 @@ async function handleNamedRun(options) {
|
|
|
5524
5955
|
playName: started.name ?? options.target.name,
|
|
5525
5956
|
status: started.status,
|
|
5526
5957
|
statusUrl: started.statusUrl,
|
|
5527
|
-
dashboardUrl,
|
|
5958
|
+
dashboardUrl: started.dashboardUrl ?? dashboardUrl,
|
|
5528
5959
|
jsonOutput: options.jsonOutput,
|
|
5529
5960
|
progress
|
|
5530
5961
|
});
|
|
@@ -5558,80 +5989,101 @@ async function handlePlayRun(args) {
|
|
|
5558
5989
|
}
|
|
5559
5990
|
return handleNamedRun(options);
|
|
5560
5991
|
}
|
|
5561
|
-
|
|
5562
|
-
const usage = "Usage: deepline play tail --run-id <run-id> [--interval-ms 1000] [--json]\n deepline play tail --name <name> [--interval-ms 1000] [--json]";
|
|
5563
|
-
let target;
|
|
5564
|
-
try {
|
|
5565
|
-
target = parsePlayRunTarget({ args, usage, allowName: true });
|
|
5566
|
-
} catch (error) {
|
|
5567
|
-
console.error(error instanceof Error ? error.message : usage);
|
|
5568
|
-
return 1;
|
|
5569
|
-
}
|
|
5570
|
-
const client = new DeeplineClient();
|
|
5571
|
-
const jsonOutput = argsWantJson(args);
|
|
5572
|
-
const emitLogs = !jsonOutput || args.includes("--logs");
|
|
5573
|
-
let intervalMs = 500;
|
|
5992
|
+
function parseRunIdPositional(args, usage) {
|
|
5574
5993
|
for (let index = 0; index < args.length; index += 1) {
|
|
5575
5994
|
const arg = args[index];
|
|
5576
|
-
if (
|
|
5577
|
-
|
|
5995
|
+
if (arg === "--json" || arg === "--full" || arg === "--logs" || arg === "--compact" || arg === "--limit") {
|
|
5996
|
+
if (arg === "--limit" && args[index + 1]) {
|
|
5997
|
+
index += 1;
|
|
5998
|
+
}
|
|
5999
|
+
continue;
|
|
6000
|
+
}
|
|
6001
|
+
if ((arg === "--out" || arg === "--cursor" || arg === "--reason" || arg === "--interval-ms" || arg === "--poll-interval-ms") && args[index + 1]) {
|
|
6002
|
+
index += 1;
|
|
6003
|
+
continue;
|
|
6004
|
+
}
|
|
6005
|
+
if (!arg.startsWith("--")) {
|
|
6006
|
+
return arg;
|
|
5578
6007
|
}
|
|
5579
6008
|
}
|
|
5580
|
-
|
|
5581
|
-
const progress = getActiveCliProgress() ?? createCliProgress(!jsonOutput);
|
|
5582
|
-
progress.phase(`tailing ${workflowId}`);
|
|
5583
|
-
const finalStatus = await waitForPlayCompletion({
|
|
5584
|
-
client,
|
|
5585
|
-
workflowId,
|
|
5586
|
-
pollIntervalMs: intervalMs,
|
|
5587
|
-
jsonOutput,
|
|
5588
|
-
emitLogs,
|
|
5589
|
-
waitTimeoutMs: null,
|
|
5590
|
-
progress
|
|
5591
|
-
});
|
|
5592
|
-
if (finalStatus.status === "completed") {
|
|
5593
|
-
progress.complete();
|
|
5594
|
-
} else {
|
|
5595
|
-
progress.fail();
|
|
5596
|
-
}
|
|
5597
|
-
writePlayResult(finalStatus, jsonOutput);
|
|
5598
|
-
return finalStatus.status === "completed" ? 0 : 1;
|
|
6009
|
+
throw new DeeplineError(usage);
|
|
5599
6010
|
}
|
|
5600
|
-
async function
|
|
5601
|
-
const usage = "Usage: deepline
|
|
5602
|
-
let
|
|
6011
|
+
async function handleRunGet(args) {
|
|
6012
|
+
const usage = "Usage: deepline runs get <run-id> [--json] [--full]";
|
|
6013
|
+
let runId;
|
|
5603
6014
|
try {
|
|
5604
|
-
|
|
6015
|
+
runId = parseRunIdPositional(args, usage);
|
|
5605
6016
|
} catch (error) {
|
|
5606
6017
|
console.error(error instanceof Error ? error.message : usage);
|
|
5607
6018
|
return 1;
|
|
5608
6019
|
}
|
|
5609
6020
|
const client = new DeeplineClient();
|
|
5610
|
-
const
|
|
5611
|
-
const status = await client.getPlayStatus(workflowId);
|
|
6021
|
+
const status = await client.runs.get(runId);
|
|
5612
6022
|
writePlayResult(status, argsWantJson(args), {
|
|
5613
6023
|
fullJson: args.includes("--full")
|
|
5614
6024
|
});
|
|
5615
6025
|
return 0;
|
|
5616
6026
|
}
|
|
5617
|
-
function
|
|
6027
|
+
async function handleRunsList(args) {
|
|
6028
|
+
const usage = "Usage: deepline runs list --play <play-name> [--status <status>] [--json]";
|
|
6029
|
+
let playName = null;
|
|
6030
|
+
let statusFilter = null;
|
|
5618
6031
|
for (let index = 0; index < args.length; index += 1) {
|
|
5619
6032
|
const arg = args[index];
|
|
5620
|
-
if (arg === "--
|
|
6033
|
+
if ((arg === "--play" || arg === "--name") && args[index + 1]) {
|
|
6034
|
+
playName = parseReferencedPlayTarget(args[++index]).playName;
|
|
5621
6035
|
continue;
|
|
5622
6036
|
}
|
|
5623
|
-
if (arg === "--
|
|
5624
|
-
|
|
6037
|
+
if (arg === "--status" && args[index + 1]) {
|
|
6038
|
+
statusFilter = args[++index].trim().toLowerCase();
|
|
5625
6039
|
continue;
|
|
5626
6040
|
}
|
|
5627
|
-
if (
|
|
5628
|
-
|
|
6041
|
+
if (arg === "--json" || arg === "--compact") {
|
|
6042
|
+
continue;
|
|
5629
6043
|
}
|
|
5630
6044
|
}
|
|
5631
|
-
|
|
6045
|
+
if (!playName) {
|
|
6046
|
+
console.error(usage);
|
|
6047
|
+
return 1;
|
|
6048
|
+
}
|
|
6049
|
+
const client = new DeeplineClient();
|
|
6050
|
+
const runs = (await client.runs.list({
|
|
6051
|
+
play: playName,
|
|
6052
|
+
...statusFilter ? { status: statusFilter } : {}
|
|
6053
|
+
})).map((run) => ({
|
|
6054
|
+
runId: run.workflowId,
|
|
6055
|
+
workflowId: run.workflowId,
|
|
6056
|
+
temporalRunId: run.runId,
|
|
6057
|
+
status: String(run.status ?? "").toLowerCase(),
|
|
6058
|
+
startedAt: run.startTime,
|
|
6059
|
+
finishedAt: run.closeTime,
|
|
6060
|
+
executionTime: run.executionTime,
|
|
6061
|
+
playName: run.memo?.playName ?? playName
|
|
6062
|
+
}));
|
|
6063
|
+
if (argsWantJson(args)) {
|
|
6064
|
+
process.stdout.write(
|
|
6065
|
+
`${JSON.stringify({
|
|
6066
|
+
runs,
|
|
6067
|
+
count: runs.length,
|
|
6068
|
+
next: {
|
|
6069
|
+
get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
|
|
6070
|
+
}
|
|
6071
|
+
})}
|
|
6072
|
+
`
|
|
6073
|
+
);
|
|
6074
|
+
} else {
|
|
6075
|
+
if (runs.length === 0) {
|
|
6076
|
+
console.log(`No runs found for ${playName}.`);
|
|
6077
|
+
} else {
|
|
6078
|
+
for (const run of runs) {
|
|
6079
|
+
console.log(`${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
return 0;
|
|
5632
6084
|
}
|
|
5633
|
-
async function
|
|
5634
|
-
const usage = "Usage: deepline runs
|
|
6085
|
+
async function handleRunTail(args) {
|
|
6086
|
+
const usage = "Usage: deepline runs tail <run-id> [--json] [--compact] [--cursor <cursor>]";
|
|
5635
6087
|
let runId;
|
|
5636
6088
|
try {
|
|
5637
6089
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -5640,14 +6092,24 @@ async function handleRunStatus(args) {
|
|
|
5640
6092
|
return 1;
|
|
5641
6093
|
}
|
|
5642
6094
|
const client = new DeeplineClient();
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
6095
|
+
let afterLogIndex;
|
|
6096
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6097
|
+
const arg = args[index];
|
|
6098
|
+
if (arg === "--cursor" && args[index + 1]) {
|
|
6099
|
+
const parsed = Number(args[++index]);
|
|
6100
|
+
if (Number.isInteger(parsed) && parsed >= 0) {
|
|
6101
|
+
afterLogIndex = parsed;
|
|
6102
|
+
}
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
const status = await client.runs.tail(runId, {
|
|
6106
|
+
...afterLogIndex !== void 0 ? { afterLogIndex } : {}
|
|
5646
6107
|
});
|
|
5647
|
-
|
|
6108
|
+
writePlayResult(status, argsWantJson(args));
|
|
6109
|
+
return status.status === "failed" ? 1 : 0;
|
|
5648
6110
|
}
|
|
5649
6111
|
async function handleRunLogs(args) {
|
|
5650
|
-
const usage = "Usage: deepline runs logs <run-id> [--json]";
|
|
6112
|
+
const usage = "Usage: deepline runs logs <run-id> [--limit 200] [--out run.log] [--json]";
|
|
5651
6113
|
let runId;
|
|
5652
6114
|
try {
|
|
5653
6115
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -5655,14 +6117,86 @@ async function handleRunLogs(args) {
|
|
|
5655
6117
|
console.error(error instanceof Error ? error.message : usage);
|
|
5656
6118
|
return 1;
|
|
5657
6119
|
}
|
|
6120
|
+
let limit = 200;
|
|
6121
|
+
let outPath = null;
|
|
6122
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6123
|
+
const arg = args[index];
|
|
6124
|
+
if (arg === "--limit" && args[index + 1]) {
|
|
6125
|
+
limit = parsePositiveInteger2(args[++index], "--limit");
|
|
6126
|
+
continue;
|
|
6127
|
+
}
|
|
6128
|
+
if (arg === "--out" && args[index + 1]) {
|
|
6129
|
+
outPath = resolve7(args[++index]);
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
5658
6132
|
const client = new DeeplineClient();
|
|
5659
|
-
const status = await client.
|
|
6133
|
+
const status = await client.runs.get(runId);
|
|
5660
6134
|
const logs = status.progress?.logs ?? [];
|
|
6135
|
+
if (outPath) {
|
|
6136
|
+
writeFileSync4(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
6137
|
+
if (argsWantJson(args)) {
|
|
6138
|
+
process.stdout.write(
|
|
6139
|
+
`${JSON.stringify({
|
|
6140
|
+
runId: status.runId,
|
|
6141
|
+
log_path: outPath,
|
|
6142
|
+
lineCount: logs.length
|
|
6143
|
+
})}
|
|
6144
|
+
`
|
|
6145
|
+
);
|
|
6146
|
+
} else {
|
|
6147
|
+
console.log(`Wrote ${logs.length} log lines to ${outPath}`);
|
|
6148
|
+
}
|
|
6149
|
+
return 0;
|
|
6150
|
+
}
|
|
6151
|
+
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
6152
|
+
if (argsWantJson(args)) {
|
|
6153
|
+
process.stdout.write(
|
|
6154
|
+
`${JSON.stringify({
|
|
6155
|
+
runId: status.runId,
|
|
6156
|
+
totalCount: logs.length,
|
|
6157
|
+
returnedCount: entries.length,
|
|
6158
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
6159
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
6160
|
+
truncated: logs.length > entries.length,
|
|
6161
|
+
hasMore: logs.length > entries.length,
|
|
6162
|
+
entries,
|
|
6163
|
+
next: {
|
|
6164
|
+
export: `deepline runs logs ${status.runId} --out run.log --json`
|
|
6165
|
+
}
|
|
6166
|
+
})}
|
|
6167
|
+
`
|
|
6168
|
+
);
|
|
6169
|
+
} else {
|
|
6170
|
+
process.stdout.write(`${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`);
|
|
6171
|
+
}
|
|
6172
|
+
return 0;
|
|
6173
|
+
}
|
|
6174
|
+
async function handleRunStop(args) {
|
|
6175
|
+
const usage = 'Usage: deepline runs stop <run-id> [--reason "text"] [--json]';
|
|
6176
|
+
let runId;
|
|
6177
|
+
try {
|
|
6178
|
+
runId = parseRunIdPositional(args, usage);
|
|
6179
|
+
} catch (error) {
|
|
6180
|
+
console.error(error instanceof Error ? error.message : usage);
|
|
6181
|
+
return 1;
|
|
6182
|
+
}
|
|
6183
|
+
let reason;
|
|
6184
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6185
|
+
const arg = args[index];
|
|
6186
|
+
if (arg === "--reason" && args[index + 1]) {
|
|
6187
|
+
reason = args[++index];
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
const client = new DeeplineClient();
|
|
6191
|
+
const result = await client.runs.stop(runId, { reason });
|
|
5661
6192
|
if (argsWantJson(args)) {
|
|
5662
|
-
process.stdout.write(`${JSON.stringify(
|
|
6193
|
+
process.stdout.write(`${JSON.stringify(result)}
|
|
5663
6194
|
`);
|
|
5664
6195
|
} else {
|
|
5665
|
-
|
|
6196
|
+
console.log(`Stopped ${result.runId}`);
|
|
6197
|
+
if (result.hitlCancelledCount > 0) {
|
|
6198
|
+
console.log(` cancelled HITL waits: ${result.hitlCancelledCount}`);
|
|
6199
|
+
}
|
|
5666
6200
|
}
|
|
5667
6201
|
return 0;
|
|
5668
6202
|
}
|
|
@@ -5705,37 +6239,6 @@ async function handleRunExport(args) {
|
|
|
5705
6239
|
}
|
|
5706
6240
|
return 0;
|
|
5707
6241
|
}
|
|
5708
|
-
async function handlePlayStop(args) {
|
|
5709
|
-
const usage = 'Usage: deepline play stop --run-id <run-id> [--reason "text"] [--json]';
|
|
5710
|
-
let target;
|
|
5711
|
-
try {
|
|
5712
|
-
target = parsePlayRunTarget({ args, usage, allowName: false });
|
|
5713
|
-
} catch (error) {
|
|
5714
|
-
console.error(error instanceof Error ? error.message : usage);
|
|
5715
|
-
return 1;
|
|
5716
|
-
}
|
|
5717
|
-
const client = new DeeplineClient();
|
|
5718
|
-
const jsonOutput = argsWantJson(args);
|
|
5719
|
-
let reason;
|
|
5720
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
5721
|
-
const arg = args[index];
|
|
5722
|
-
if (arg === "--reason" && args[index + 1]) {
|
|
5723
|
-
reason = args[++index];
|
|
5724
|
-
}
|
|
5725
|
-
}
|
|
5726
|
-
const workflowId = await resolvePlayRunId(client, target);
|
|
5727
|
-
const result = await client.stopPlay(workflowId, { reason });
|
|
5728
|
-
if (jsonOutput) {
|
|
5729
|
-
process.stdout.write(`${JSON.stringify(result)}
|
|
5730
|
-
`);
|
|
5731
|
-
} else {
|
|
5732
|
-
console.log(`Stopped ${result.runId}`);
|
|
5733
|
-
if (result.hitlCancelledCount > 0) {
|
|
5734
|
-
console.log(` cancelled HITL waits: ${result.hitlCancelledCount}`);
|
|
5735
|
-
}
|
|
5736
|
-
}
|
|
5737
|
-
return 0;
|
|
5738
|
-
}
|
|
5739
6242
|
async function handlePlayGet(args) {
|
|
5740
6243
|
const target = args[0];
|
|
5741
6244
|
if (!target) {
|
|
@@ -5743,8 +6246,9 @@ async function handlePlayGet(args) {
|
|
|
5743
6246
|
return 1;
|
|
5744
6247
|
}
|
|
5745
6248
|
const client = new DeeplineClient();
|
|
5746
|
-
const
|
|
6249
|
+
const explicitJson = args.includes("--json");
|
|
5747
6250
|
const sourceOutput = args.includes("--source");
|
|
6251
|
+
const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
|
|
5748
6252
|
let outPath = null;
|
|
5749
6253
|
for (let index = 1; index < args.length; index += 1) {
|
|
5750
6254
|
const arg = args[index];
|
|
@@ -5755,7 +6259,7 @@ async function handlePlayGet(args) {
|
|
|
5755
6259
|
const playName = isFileTarget(target) ? extractPlayName(readFileSync3(resolve7(target), "utf-8"), resolve7(target)) : parseReferencedPlayTarget(target).playName;
|
|
5756
6260
|
const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
|
|
5757
6261
|
const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
|
|
5758
|
-
const materializedFile =
|
|
6262
|
+
const materializedFile = outPath ? materializeRemotePlaySource({
|
|
5759
6263
|
target,
|
|
5760
6264
|
playName,
|
|
5761
6265
|
sourceCode: resolvedSource,
|
|
@@ -5795,6 +6299,10 @@ async function handlePlayGet(args) {
|
|
|
5795
6299
|
}
|
|
5796
6300
|
return 0;
|
|
5797
6301
|
}
|
|
6302
|
+
if (outPath && loadedMessage) {
|
|
6303
|
+
console.log(loadedMessage);
|
|
6304
|
+
return 0;
|
|
6305
|
+
}
|
|
5798
6306
|
console.log(`Play: ${formatPlayReference(detail.play)}`);
|
|
5799
6307
|
console.log(
|
|
5800
6308
|
`Working version: ${detail.play.workingRevision?.version ?? "\u2014"}`
|
|
@@ -5818,33 +6326,6 @@ async function handlePlayGet(args) {
|
|
|
5818
6326
|
}
|
|
5819
6327
|
return 0;
|
|
5820
6328
|
}
|
|
5821
|
-
async function handlePlayRuns(args) {
|
|
5822
|
-
const nameIndex = args.indexOf("--name");
|
|
5823
|
-
const name = nameIndex >= 0 ? args[nameIndex + 1] : void 0;
|
|
5824
|
-
if (!name) {
|
|
5825
|
-
console.error("Usage: deepline play runs --name <name> [--json]");
|
|
5826
|
-
return 1;
|
|
5827
|
-
}
|
|
5828
|
-
const client = new DeeplineClient();
|
|
5829
|
-
const jsonOutput = argsWantJson(args);
|
|
5830
|
-
await assertCanonicalNamedPlayReference(client, name);
|
|
5831
|
-
const runs = await client.listPlayRuns(
|
|
5832
|
-
parseReferencedPlayTarget(name).playName
|
|
5833
|
-
);
|
|
5834
|
-
if (jsonOutput) {
|
|
5835
|
-
process.stdout.write(`${JSON.stringify({ runs })}
|
|
5836
|
-
`);
|
|
5837
|
-
return 0;
|
|
5838
|
-
}
|
|
5839
|
-
if (runs.length === 0) {
|
|
5840
|
-
console.log(`No runs found for ${name}.`);
|
|
5841
|
-
return 0;
|
|
5842
|
-
}
|
|
5843
|
-
for (const run of runs) {
|
|
5844
|
-
console.log(formatRunLine(run));
|
|
5845
|
-
}
|
|
5846
|
-
return 0;
|
|
5847
|
-
}
|
|
5848
6329
|
function formatVersionLine(version) {
|
|
5849
6330
|
const revisionLabel = version.artifactHash?.slice(0, 12) ?? "unknown-revision";
|
|
5850
6331
|
return `v${version.version} ${revisionLabel} ${formatTimestamp(version.createdAt)}`;
|
|
@@ -5961,7 +6442,26 @@ function printPlayDescription(play) {
|
|
|
5961
6442
|
console.log(` ${line}`);
|
|
5962
6443
|
}
|
|
5963
6444
|
}
|
|
6445
|
+
if (play.csvInput) {
|
|
6446
|
+
console.log(" CSV input:");
|
|
6447
|
+
const rendered = JSON.stringify(play.csvInput, null, 2);
|
|
6448
|
+
for (const line of rendered.split("\n")) {
|
|
6449
|
+
console.log(` ${line}`);
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
if (play.rowOutputSchema) {
|
|
6453
|
+
console.log(" Row output schema:");
|
|
6454
|
+
const rendered = JSON.stringify(play.rowOutputSchema, null, 2);
|
|
6455
|
+
for (const line of rendered.split("\n")) {
|
|
6456
|
+
console.log(` ${line}`);
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
5964
6459
|
console.log(` Run: ${play.runCommand}`);
|
|
6460
|
+
if (play.origin === "prebuilt" && !play.canEdit) {
|
|
6461
|
+
console.log(
|
|
6462
|
+
` Customize: deepline plays get ${reference} --source --out ./my-play.play.ts`
|
|
6463
|
+
);
|
|
6464
|
+
}
|
|
5965
6465
|
}
|
|
5966
6466
|
async function handlePlaySearch(args) {
|
|
5967
6467
|
let options;
|
|
@@ -6059,6 +6559,7 @@ async function handlePlayPublish(args) {
|
|
|
6059
6559
|
const published = await client.registerPlayArtifact({
|
|
6060
6560
|
name: rootPlayName,
|
|
6061
6561
|
sourceCode: graph.root.sourceCode,
|
|
6562
|
+
sourceFiles: graph.root.sourceFiles,
|
|
6062
6563
|
artifact: graph.root.artifact,
|
|
6063
6564
|
compilerManifest: requireCompilerManifest(graph.root),
|
|
6064
6565
|
publish: true
|
|
@@ -6100,6 +6601,45 @@ async function handlePlayPublish(args) {
|
|
|
6100
6601
|
`);
|
|
6101
6602
|
return result.success ? 0 : 1;
|
|
6102
6603
|
}
|
|
6604
|
+
async function handlePlayDelete(args) {
|
|
6605
|
+
const playName = args[0];
|
|
6606
|
+
if (!playName) {
|
|
6607
|
+
console.error("Usage: deepline plays delete <play-name> --yes [--json]");
|
|
6608
|
+
return 1;
|
|
6609
|
+
}
|
|
6610
|
+
const confirmed = args.includes("--yes") || args.includes("-y") || args.includes("--force");
|
|
6611
|
+
if (!confirmed) {
|
|
6612
|
+
console.error(
|
|
6613
|
+
"Refusing to delete without --yes. This deletes the org-owned play, its revisions, trigger bindings, and local run records."
|
|
6614
|
+
);
|
|
6615
|
+
return 1;
|
|
6616
|
+
}
|
|
6617
|
+
const client = new DeeplineClient();
|
|
6618
|
+
let detail;
|
|
6619
|
+
try {
|
|
6620
|
+
detail = await client.getPlay(parseReferencedPlayTarget(playName).playName);
|
|
6621
|
+
} catch (error) {
|
|
6622
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
6623
|
+
return 1;
|
|
6624
|
+
}
|
|
6625
|
+
if (detail.play.ownerType === "deepline" || detail.play.origin === "prebuilt") {
|
|
6626
|
+
console.error(`Cannot delete prebuilt play: ${formatPlayReference(detail.play)}`);
|
|
6627
|
+
return 1;
|
|
6628
|
+
}
|
|
6629
|
+
const result = await client.deletePlay(
|
|
6630
|
+
parseReferencedPlayTarget(formatPlayReference(detail.play)).playName
|
|
6631
|
+
);
|
|
6632
|
+
if (argsWantJson(args)) {
|
|
6633
|
+
process.stdout.write(`${JSON.stringify(result)}
|
|
6634
|
+
`);
|
|
6635
|
+
return result.deleted ? 0 : 1;
|
|
6636
|
+
}
|
|
6637
|
+
process.stdout.write(
|
|
6638
|
+
`Deleted ${result.name}: revisions=${result.deletedRevisionCount}, bindings=${result.deletedBindingCount}, runs=${result.deletedRunCount}
|
|
6639
|
+
`
|
|
6640
|
+
);
|
|
6641
|
+
return result.deleted ? 0 : 1;
|
|
6642
|
+
}
|
|
6103
6643
|
function registerPlayCommands(program) {
|
|
6104
6644
|
const play = program.command("plays").alias("play").description("Search, validate, run, and manage cloud plays.").addHelpText(
|
|
6105
6645
|
"after",
|
|
@@ -6133,21 +6673,23 @@ Examples:
|
|
|
6133
6673
|
...options.json ? ["--json"] : []
|
|
6134
6674
|
]);
|
|
6135
6675
|
});
|
|
6136
|
-
play.command("run [target]").description("Run a play file or named play.").addHelpText(
|
|
6676
|
+
play.command("run [target]").description("Run a play file or named play.").allowUnknownOption(true).allowExcessArguments(true).addHelpText(
|
|
6137
6677
|
"after",
|
|
6138
6678
|
`
|
|
6139
6679
|
Notes:
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6680
|
+
Local play files are bundled locally, then validated and executed in Deepline cloud.
|
|
6681
|
+
Named plays run the stored live cloud revision.
|
|
6682
|
+
Unknown --foo and --foo.bar flags are treated as play input args.
|
|
6683
|
+
File-like input args accept local paths; the CLI stages those files before submit.
|
|
6684
|
+
Run performs server preflight automatically. Use \`deepline plays check <file>\`
|
|
6685
|
+
to validate without starting a run.
|
|
6144
6686
|
|
|
6145
6687
|
Examples:
|
|
6146
6688
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
|
|
6147
6689
|
deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
|
|
6148
6690
|
deepline plays run enrich.play.ts --csv leads.csv --watch --out leads-enriched.csv
|
|
6149
6691
|
`
|
|
6150
|
-
).option("--file <path>", "Local play file to run").option("--name <name>", "Saved play name to run").option("
|
|
6692
|
+
).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(
|
|
6151
6693
|
"--revision-id <id>",
|
|
6152
6694
|
"Run a specific saved revision instead of the live revision"
|
|
6153
6695
|
).option(
|
|
@@ -6156,12 +6698,21 @@ Examples:
|
|
|
6156
6698
|
).option("--watch", "Stream logs until completion").option(
|
|
6157
6699
|
"--logs",
|
|
6158
6700
|
"When output is non-interactive, stream play logs to stderr while waiting"
|
|
6159
|
-
).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) => {
|
|
6701
|
+
).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) => {
|
|
6702
|
+
const passthroughArgs = [...command.args];
|
|
6703
|
+
const explicitTarget = options.file || options.name;
|
|
6704
|
+
const targetIsInputFlag = typeof target === "string" && target.startsWith("--");
|
|
6705
|
+
const effectiveTarget = explicitTarget || targetIsInputFlag ? null : target;
|
|
6706
|
+
if (explicitTarget && typeof target === "string" && !targetIsInputFlag && !passthroughArgs.includes(target)) {
|
|
6707
|
+
passthroughArgs.push(target);
|
|
6708
|
+
}
|
|
6709
|
+
if (effectiveTarget && passthroughArgs[0] === effectiveTarget) {
|
|
6710
|
+
passthroughArgs.shift();
|
|
6711
|
+
}
|
|
6160
6712
|
process.exitCode = await handlePlayRun([
|
|
6161
|
-
...
|
|
6713
|
+
...effectiveTarget ? [effectiveTarget] : [],
|
|
6162
6714
|
...options.file ? ["--file", options.file] : [],
|
|
6163
6715
|
...options.name ? ["--name", options.name] : [],
|
|
6164
|
-
...options.csv ? ["--csv", options.csv] : [],
|
|
6165
6716
|
...options.input ? ["--input", options.input] : [],
|
|
6166
6717
|
...options.live ? ["--live"] : [],
|
|
6167
6718
|
...options.latest ? ["--latest"] : [],
|
|
@@ -6172,7 +6723,8 @@ Examples:
|
|
|
6172
6723
|
...options.pollIntervalMs ? ["--poll-interval-ms", options.pollIntervalMs] : [],
|
|
6173
6724
|
...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
|
|
6174
6725
|
...options.force ? ["--force"] : [],
|
|
6175
|
-
...options.json ? ["--json"] : []
|
|
6726
|
+
...options.json ? ["--json"] : [],
|
|
6727
|
+
...passthroughArgs
|
|
6176
6728
|
]);
|
|
6177
6729
|
});
|
|
6178
6730
|
play.command("get <target>").description("Fetch full play details.").addHelpText(
|
|
@@ -6186,12 +6738,10 @@ Notes:
|
|
|
6186
6738
|
Examples:
|
|
6187
6739
|
deepline plays get person-linkedin-to-email
|
|
6188
6740
|
deepline plays get person-linkedin-to-email --json | jq '.play.liveRevision'
|
|
6741
|
+
deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source > email-waterfall.play.ts
|
|
6742
|
+
deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source --out ./email-waterfall.play.ts
|
|
6189
6743
|
`
|
|
6190
|
-
).option("--json", "Emit JSON output. Also automatic when stdout is piped").
|
|
6191
|
-
new Option("--source", "Materialize or print the source code").hideHelp()
|
|
6192
|
-
).addOption(
|
|
6193
|
-
new Option("--out <path>", "Write source to a specific path").hideHelp()
|
|
6194
|
-
).action(async (target, options) => {
|
|
6744
|
+
).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) => {
|
|
6195
6745
|
process.exitCode = await handlePlayGet([
|
|
6196
6746
|
target,
|
|
6197
6747
|
...options.json ? ["--json"] : [],
|
|
@@ -6230,50 +6780,12 @@ Examples:
|
|
|
6230
6780
|
...options.json ? ["--json"] : []
|
|
6231
6781
|
]);
|
|
6232
6782
|
});
|
|
6233
|
-
play.command("runs").description("List runs for a named play.").option("--name <name>", "Saved play name").option("--json", "Emit JSON output").action(async (options) => {
|
|
6234
|
-
process.exitCode = await handlePlayRuns([
|
|
6235
|
-
...options.name ? ["--name", options.name] : [],
|
|
6236
|
-
...options.json ? ["--json"] : []
|
|
6237
|
-
]);
|
|
6238
|
-
});
|
|
6239
6783
|
play.command("versions").description("List revisions for a named play.").option("--name <name>", "Saved play name").option("--json", "Emit JSON output").action(async (options) => {
|
|
6240
6784
|
process.exitCode = await handlePlayVersions([
|
|
6241
6785
|
...options.name ? ["--name", options.name] : [],
|
|
6242
6786
|
...options.json ? ["--json"] : []
|
|
6243
6787
|
]);
|
|
6244
6788
|
});
|
|
6245
|
-
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) => {
|
|
6246
|
-
process.exitCode = await handlePlayTail([
|
|
6247
|
-
...options.runId ? ["--run-id", options.runId] : [],
|
|
6248
|
-
...options.name ? ["--name", options.name] : [],
|
|
6249
|
-
...options.intervalMs ? ["--interval-ms", options.intervalMs] : [],
|
|
6250
|
-
...options.logs ? ["--logs"] : [],
|
|
6251
|
-
...options.json ? ["--json"] : []
|
|
6252
|
-
]);
|
|
6253
|
-
});
|
|
6254
|
-
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) => {
|
|
6255
|
-
process.exitCode = await handlePlayStatus([
|
|
6256
|
-
...options.runId ? ["--run-id", options.runId] : [],
|
|
6257
|
-
...options.name ? ["--name", options.name] : [],
|
|
6258
|
-
...options.json ? ["--json"] : [],
|
|
6259
|
-
...options.full ? ["--full"] : []
|
|
6260
|
-
]);
|
|
6261
|
-
});
|
|
6262
|
-
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) => {
|
|
6263
|
-
process.exitCode = await handleRunExport([
|
|
6264
|
-
options.runId,
|
|
6265
|
-
"--out",
|
|
6266
|
-
options.out,
|
|
6267
|
-
...options.json ? ["--json"] : []
|
|
6268
|
-
]);
|
|
6269
|
-
});
|
|
6270
|
-
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) => {
|
|
6271
|
-
process.exitCode = await handlePlayStop([
|
|
6272
|
-
...options.runId ? ["--run-id", options.runId] : [],
|
|
6273
|
-
...options.reason ? ["--reason", options.reason] : [],
|
|
6274
|
-
...options.json ? ["--json"] : []
|
|
6275
|
-
]);
|
|
6276
|
-
});
|
|
6277
6789
|
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) => {
|
|
6278
6790
|
process.exitCode = await handlePlayPublish([
|
|
6279
6791
|
target,
|
|
@@ -6282,40 +6794,81 @@ Examples:
|
|
|
6282
6794
|
...options.json ? ["--json"] : []
|
|
6283
6795
|
]);
|
|
6284
6796
|
});
|
|
6285
|
-
|
|
6797
|
+
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) => {
|
|
6798
|
+
process.exitCode = await handlePlayDelete([
|
|
6799
|
+
target,
|
|
6800
|
+
...options.yes ? ["--yes"] : [],
|
|
6801
|
+
...options.json ? ["--json"] : []
|
|
6802
|
+
]);
|
|
6803
|
+
});
|
|
6804
|
+
const runs = program.command("runs").description("Inspect, tail, stop, and export play runs.").addHelpText(
|
|
6286
6805
|
"after",
|
|
6287
6806
|
`
|
|
6288
6807
|
Examples:
|
|
6289
|
-
deepline runs
|
|
6808
|
+
deepline runs get play/my-play/run/20260501t000000-000 --json
|
|
6809
|
+
deepline runs tail play/my-play/run/20260501t000000-000
|
|
6810
|
+
deepline runs logs play/my-play/run/20260501t000000-000 --out run.log --json
|
|
6811
|
+
deepline runs list --play my-play --status failed --json
|
|
6812
|
+
deepline runs stop play/my-play/run/20260501t000000-000 --reason "stale lock" --json
|
|
6290
6813
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
6291
|
-
deepline runs logs play/my-play/run/20260501t000000-000
|
|
6292
6814
|
`
|
|
6293
6815
|
);
|
|
6294
|
-
runs.command("
|
|
6295
|
-
process.exitCode = await
|
|
6816
|
+
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) => {
|
|
6817
|
+
process.exitCode = await handleRunGet([
|
|
6296
6818
|
runId,
|
|
6297
6819
|
...options.json ? ["--json"] : [],
|
|
6298
6820
|
...options.full ? ["--full"] : []
|
|
6299
6821
|
]);
|
|
6300
6822
|
});
|
|
6301
|
-
runs.command("
|
|
6302
|
-
process.exitCode = await
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
options.
|
|
6823
|
+
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) => {
|
|
6824
|
+
process.exitCode = await handleRunsList([
|
|
6825
|
+
"--play",
|
|
6826
|
+
options.play,
|
|
6827
|
+
...options.status ? ["--status", options.status] : [],
|
|
6828
|
+
...options.compact ? ["--compact"] : [],
|
|
6306
6829
|
...options.json ? ["--json"] : []
|
|
6307
6830
|
]);
|
|
6308
6831
|
});
|
|
6309
|
-
runs.command("
|
|
6832
|
+
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) => {
|
|
6833
|
+
process.exitCode = await handleRunTail([
|
|
6834
|
+
runId,
|
|
6835
|
+
...options.json ? ["--json"] : [],
|
|
6836
|
+
...options.compact ? ["--compact"] : [],
|
|
6837
|
+
...options.cursor ? ["--cursor", options.cursor] : []
|
|
6838
|
+
]);
|
|
6839
|
+
});
|
|
6840
|
+
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) => {
|
|
6310
6841
|
process.exitCode = await handleRunLogs([
|
|
6311
6842
|
runId,
|
|
6843
|
+
...options.limit ? ["--limit", options.limit] : [],
|
|
6844
|
+
...options.out ? ["--out", options.out] : [],
|
|
6845
|
+
...options.json ? ["--json"] : []
|
|
6846
|
+
]);
|
|
6847
|
+
});
|
|
6848
|
+
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) => {
|
|
6849
|
+
process.exitCode = await handleRunStop([
|
|
6850
|
+
runId,
|
|
6851
|
+
...options.reason ? ["--reason", options.reason] : [],
|
|
6852
|
+
...options.json ? ["--json"] : []
|
|
6853
|
+
]);
|
|
6854
|
+
});
|
|
6855
|
+
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) => {
|
|
6856
|
+
process.exitCode = await handleRunExport([
|
|
6857
|
+
runId,
|
|
6858
|
+
"--out",
|
|
6859
|
+
options.out,
|
|
6312
6860
|
...options.json ? ["--json"] : []
|
|
6313
6861
|
]);
|
|
6314
6862
|
});
|
|
6315
6863
|
}
|
|
6316
6864
|
|
|
6865
|
+
// src/cli/commands/tools.ts
|
|
6866
|
+
import { chmodSync, mkdtempSync, writeFileSync as writeFileSync6 } from "fs";
|
|
6867
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
6868
|
+
import { join as join8 } from "path";
|
|
6869
|
+
|
|
6317
6870
|
// src/tool-output.ts
|
|
6318
|
-
import { mkdirSync as
|
|
6871
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
6319
6872
|
import { homedir as homedir3 } from "os";
|
|
6320
6873
|
import { join as join7 } from "path";
|
|
6321
6874
|
function isPlainObject(value) {
|
|
@@ -6393,7 +6946,7 @@ function tryConvertToList(payload, options) {
|
|
|
6393
6946
|
}
|
|
6394
6947
|
function ensureOutputDir() {
|
|
6395
6948
|
const outputDir = join7(homedir3(), ".local", "share", "deepline", "data");
|
|
6396
|
-
|
|
6949
|
+
mkdirSync3(outputDir, { recursive: true });
|
|
6397
6950
|
return outputDir;
|
|
6398
6951
|
}
|
|
6399
6952
|
function writeJsonOutputFile(payload, stem) {
|
|
@@ -6482,31 +7035,23 @@ async function listTools(args) {
|
|
|
6482
7035
|
}
|
|
6483
7036
|
return 0;
|
|
6484
7037
|
}
|
|
6485
|
-
function
|
|
6486
|
-
const
|
|
6487
|
-
tool.toolId,
|
|
6488
|
-
tool.provider,
|
|
6489
|
-
tool.displayName,
|
|
6490
|
-
tool.description,
|
|
6491
|
-
tool.operation,
|
|
6492
|
-
tool.operationId,
|
|
6493
|
-
...tool.operationAliases ?? [],
|
|
6494
|
-
...tool.categories ?? [],
|
|
6495
|
-
tool.inputSchema ? JSON.stringify(tool.inputSchema) : ""
|
|
6496
|
-
].filter(Boolean).join(" ").toLowerCase();
|
|
6497
|
-
return terms.every((term) => haystack.includes(term));
|
|
6498
|
-
}
|
|
6499
|
-
async function searchTools(args) {
|
|
6500
|
-
const query = args[0]?.trim();
|
|
7038
|
+
async function searchTools(queryInput, options = {}) {
|
|
7039
|
+
const query = queryInput.trim();
|
|
6501
7040
|
if (!query) {
|
|
6502
7041
|
console.error("Usage: deepline tools search <query> [--json]");
|
|
6503
7042
|
return 1;
|
|
6504
7043
|
}
|
|
6505
|
-
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
6506
7044
|
const client = new DeeplineClient();
|
|
6507
|
-
const
|
|
6508
|
-
|
|
6509
|
-
|
|
7045
|
+
const result = await client.searchTools({
|
|
7046
|
+
query,
|
|
7047
|
+
categories: options.categories,
|
|
7048
|
+
searchTerms: options.searchTerms,
|
|
7049
|
+
searchMode: options.searchMode,
|
|
7050
|
+
includeSearchDebug: options.includeSearchDebug
|
|
7051
|
+
});
|
|
7052
|
+
const items = result.tools.map(toListedTool);
|
|
7053
|
+
if (options.json || shouldEmitJson()) {
|
|
7054
|
+
process.stdout.write(`${JSON.stringify({ ...result, tools: items })}
|
|
6510
7055
|
`);
|
|
6511
7056
|
return 0;
|
|
6512
7057
|
}
|
|
@@ -6560,11 +7105,14 @@ Common commands:
|
|
|
6560
7105
|
...options.json ? ["--json"] : []
|
|
6561
7106
|
]);
|
|
6562
7107
|
});
|
|
6563
|
-
tools.command("search <query>").description("Search available tools.").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
6564
|
-
process.exitCode = await searchTools(
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
7108
|
+
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) => {
|
|
7109
|
+
process.exitCode = await searchTools(query, {
|
|
7110
|
+
json: options.json,
|
|
7111
|
+
categories: options.categories,
|
|
7112
|
+
searchTerms: options.searchTerms ?? options.search_terms,
|
|
7113
|
+
searchMode: options.searchMode === "v1" || options.searchMode === "v2" ? options.searchMode : void 0,
|
|
7114
|
+
includeSearchDebug: Boolean(options.includeSearchDebug)
|
|
7115
|
+
});
|
|
6568
7116
|
});
|
|
6569
7117
|
tools.command("get <toolId>").alias("describe").description("Show metadata for a tool.").addHelpText(
|
|
6570
7118
|
"after",
|
|
@@ -6646,7 +7194,7 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
6646
7194
|
const operation = typeof tool.operation === "string" ? tool.operation : "";
|
|
6647
7195
|
const displayBase = operation && operation.startsWith(`${tool.provider}_`) ? operation.slice(String(tool.provider).length + 1) : operation ? `${tool.provider} ${operation}`.trim() : toolId;
|
|
6648
7196
|
const displayName = titleCase(displayBase || String(tool.displayName || toolId));
|
|
6649
|
-
const cost =
|
|
7197
|
+
const cost = isRecord3(tool.cost) ? tool.cost : null;
|
|
6650
7198
|
const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
|
|
6651
7199
|
const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
|
|
6652
7200
|
const deeplineUsdPerCredit = numberField(tool, "deeplineUsdPerCredit", "deepline_usd_per_credit");
|
|
@@ -6691,7 +7239,7 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
6691
7239
|
if (stepContributions.length) {
|
|
6692
7240
|
console.log(" step contributions:");
|
|
6693
7241
|
for (const item of stepContributions) {
|
|
6694
|
-
if (!
|
|
7242
|
+
if (!isRecord3(item)) continue;
|
|
6695
7243
|
const stepTool = typeof item.tool === "string" ? item.tool.trim() : "";
|
|
6696
7244
|
const low = typeof item.lowCredits === "number" ? item.lowCredits : null;
|
|
6697
7245
|
const high = typeof item.highCredits === "number" ? item.highCredits : null;
|
|
@@ -6778,12 +7326,12 @@ function printToolCost(input) {
|
|
|
6778
7326
|
return false;
|
|
6779
7327
|
}
|
|
6780
7328
|
function toolInputFieldsForDisplay(inputSchema) {
|
|
6781
|
-
if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(
|
|
6782
|
-
const jsonSchema =
|
|
6783
|
-
const properties =
|
|
7329
|
+
if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord3);
|
|
7330
|
+
const jsonSchema = isRecord3(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
|
|
7331
|
+
const properties = isRecord3(jsonSchema.properties) ? jsonSchema.properties : {};
|
|
6784
7332
|
const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
|
|
6785
7333
|
return Object.entries(properties).map(([name, value]) => {
|
|
6786
|
-
const property =
|
|
7334
|
+
const property = isRecord3(value) ? value : {};
|
|
6787
7335
|
return {
|
|
6788
7336
|
name,
|
|
6789
7337
|
type: typeof property.type === "string" ? property.type : "unknown",
|
|
@@ -6810,7 +7358,7 @@ function printJsonPreview(label, payload) {
|
|
|
6810
7358
|
}
|
|
6811
7359
|
function samplePayload(samples, key) {
|
|
6812
7360
|
const entry = samples[key];
|
|
6813
|
-
if (!
|
|
7361
|
+
if (!isRecord3(entry)) return void 0;
|
|
6814
7362
|
return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
|
|
6815
7363
|
}
|
|
6816
7364
|
function isPlayTool(tool) {
|
|
@@ -6831,7 +7379,7 @@ function formatDecimal(value) {
|
|
|
6831
7379
|
function formatUsd(value) {
|
|
6832
7380
|
return `$${formatDecimal(value)}`;
|
|
6833
7381
|
}
|
|
6834
|
-
function
|
|
7382
|
+
function isRecord3(value) {
|
|
6835
7383
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
6836
7384
|
}
|
|
6837
7385
|
function stringField(source, ...keys) {
|
|
@@ -6858,7 +7406,7 @@ function arrayField(source, ...keys) {
|
|
|
6858
7406
|
function recordField(source, ...keys) {
|
|
6859
7407
|
for (const key of keys) {
|
|
6860
7408
|
const value = source[key];
|
|
6861
|
-
if (
|
|
7409
|
+
if (isRecord3(value)) return value;
|
|
6862
7410
|
}
|
|
6863
7411
|
return {};
|
|
6864
7412
|
}
|
|
@@ -6922,6 +7470,61 @@ function parseExecuteOptions(args) {
|
|
|
6922
7470
|
}
|
|
6923
7471
|
return { toolId, params, outputFormat, noPreview };
|
|
6924
7472
|
}
|
|
7473
|
+
function safeFileStem(value) {
|
|
7474
|
+
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
7475
|
+
}
|
|
7476
|
+
function shellQuote(value) {
|
|
7477
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
7478
|
+
}
|
|
7479
|
+
function powerShellQuote(value) {
|
|
7480
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
7481
|
+
}
|
|
7482
|
+
function seedToolListScript(input) {
|
|
7483
|
+
const stem = safeFileStem(input.toolId);
|
|
7484
|
+
const fileName = `${stem}-workflow-seed-${Date.now()}.play.ts`;
|
|
7485
|
+
const scriptDir = mkdtempSync(join8(tmpdir3(), "deepline-workflow-seed-"));
|
|
7486
|
+
chmodSync(scriptDir, 448);
|
|
7487
|
+
const scriptPath = join8(scriptDir, fileName);
|
|
7488
|
+
const projectDir = `deepline/projects/${stem}-workflow`;
|
|
7489
|
+
const playName = `${stem}-workflow`;
|
|
7490
|
+
const sampleRows = input.rows.length > 0 ? `${JSON.stringify(input.rows.slice(0, 2)).replace(/\]$/, "")}, ...]` : "[]";
|
|
7491
|
+
const columns = Object.keys(input.rows[0] ?? {}).join(", ");
|
|
7492
|
+
const rowKey = Object.prototype.hasOwnProperty.call(input.rows[0] ?? {}, "id") ? '"id"' : "(row) => JSON.stringify(row)";
|
|
7493
|
+
const script = `import { definePlay } from 'deepline';
|
|
7494
|
+
|
|
7495
|
+
export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
7496
|
+
const result = await ctx.tools.execute({
|
|
7497
|
+
id: ${JSON.stringify(input.toolId)},
|
|
7498
|
+
tool: ${JSON.stringify(input.toolId)},
|
|
7499
|
+
input: ${JSON.stringify(input.payload)},
|
|
7500
|
+
description: ${JSON.stringify(`Seed ${input.toolId} rows for workflow expansion.`)},
|
|
7501
|
+
});
|
|
7502
|
+
|
|
7503
|
+
const list = Object.values(result.lists)[0];
|
|
7504
|
+
const rows = (list?.get() ?? []).slice(0, 100);
|
|
7505
|
+
// ${sampleRows}
|
|
7506
|
+
// columns: ${columns}
|
|
7507
|
+
// .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.' }))
|
|
7508
|
+
// .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.' }))
|
|
7509
|
+
// ctx.map is idempotent by map key + row key; reruns reuse completed rows.
|
|
7510
|
+
const enrichedData = await ctx
|
|
7511
|
+
.map('enriched_data', rows, { key: ${rowKey} })
|
|
7512
|
+
.run({ description: 'Enrich seeded rows.' });
|
|
7513
|
+
|
|
7514
|
+
return {
|
|
7515
|
+
rows: enrichedData,
|
|
7516
|
+
count: await enrichedData.count(),
|
|
7517
|
+
};
|
|
7518
|
+
});
|
|
7519
|
+
`;
|
|
7520
|
+
writeFileSync6(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
7521
|
+
return {
|
|
7522
|
+
path: scriptPath,
|
|
7523
|
+
projectDir,
|
|
7524
|
+
macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
|
|
7525
|
+
windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
|
|
7526
|
+
};
|
|
7527
|
+
}
|
|
6925
7528
|
async function executeTool(args) {
|
|
6926
7529
|
let parsed;
|
|
6927
7530
|
try {
|
|
@@ -6949,7 +7552,7 @@ async function executeTool(args) {
|
|
|
6949
7552
|
}
|
|
6950
7553
|
throw error;
|
|
6951
7554
|
}
|
|
6952
|
-
const rawResponse = await client.
|
|
7555
|
+
const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
|
|
6953
7556
|
const listConversion = tryConvertToList(rawResponse, {
|
|
6954
7557
|
listExtractorPaths: metadata.listExtractorPaths ?? []
|
|
6955
7558
|
});
|
|
@@ -6975,6 +7578,11 @@ async function executeTool(args) {
|
|
|
6975
7578
|
return 0;
|
|
6976
7579
|
}
|
|
6977
7580
|
const csv = writeCsvOutputFile(listConversion.rows, `${parsed.toolId}_output`);
|
|
7581
|
+
const seededScript = seedToolListScript({
|
|
7582
|
+
toolId: parsed.toolId,
|
|
7583
|
+
payload: parsed.params,
|
|
7584
|
+
rows: listConversion.rows
|
|
7585
|
+
});
|
|
6978
7586
|
if (parsed.outputFormat === "csv_file") {
|
|
6979
7587
|
process.stdout.write(`${JSON.stringify({
|
|
6980
7588
|
extracted_csv: csv.path,
|
|
@@ -6983,6 +7591,12 @@ async function executeTool(args) {
|
|
|
6983
7591
|
preview: csv.preview,
|
|
6984
7592
|
list_strategy: listConversion.strategy,
|
|
6985
7593
|
list_source_path: listConversion.sourcePath,
|
|
7594
|
+
starter_script: seededScript.path,
|
|
7595
|
+
project_dir: seededScript.projectDir,
|
|
7596
|
+
copy_to_project: {
|
|
7597
|
+
macos_linux: seededScript.macCopyCommand,
|
|
7598
|
+
windows_powershell: seededScript.windowsCopyCommand
|
|
7599
|
+
},
|
|
6986
7600
|
summary
|
|
6987
7601
|
})}
|
|
6988
7602
|
`);
|
|
@@ -7000,14 +7614,20 @@ async function executeTool(args) {
|
|
|
7000
7614
|
console.log(`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`);
|
|
7001
7615
|
}
|
|
7002
7616
|
console.log(`preview: ${JSON.stringify(csv.preview)}`);
|
|
7617
|
+
console.log(`starter script: ${seededScript.path}`);
|
|
7618
|
+
console.log(
|
|
7619
|
+
"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."
|
|
7620
|
+
);
|
|
7621
|
+
console.log(`macOS/Linux: ${seededScript.macCopyCommand}`);
|
|
7622
|
+
console.log(`Windows PowerShell: ${seededScript.windowsCopyCommand}`);
|
|
7003
7623
|
return 0;
|
|
7004
7624
|
}
|
|
7005
7625
|
|
|
7006
7626
|
// src/cli/skills-sync.ts
|
|
7007
|
-
import { spawn } from "child_process";
|
|
7008
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
7627
|
+
import { spawn, spawnSync } from "child_process";
|
|
7628
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
7009
7629
|
import { homedir as homedir4 } from "os";
|
|
7010
|
-
import { dirname as dirname7, join as
|
|
7630
|
+
import { dirname as dirname7, join as join9 } from "path";
|
|
7011
7631
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
7012
7632
|
var SDK_SKILL_NAME = "deepline-sdk";
|
|
7013
7633
|
var SKILL_AGENTS = ["codex", "claude-code", "cursor"];
|
|
@@ -7018,7 +7638,7 @@ function shouldSkipSkillsSync() {
|
|
|
7018
7638
|
}
|
|
7019
7639
|
function sdkSkillsVersionPath(baseUrl) {
|
|
7020
7640
|
const home = process.env.HOME?.trim() || homedir4();
|
|
7021
|
-
return
|
|
7641
|
+
return join9(home, ".local", "deepline", baseUrlSlug(baseUrl), "sdk-skills", ".version");
|
|
7022
7642
|
}
|
|
7023
7643
|
function readLocalSkillsVersion(baseUrl) {
|
|
7024
7644
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
@@ -7031,8 +7651,8 @@ function readLocalSkillsVersion(baseUrl) {
|
|
|
7031
7651
|
}
|
|
7032
7652
|
function writeLocalSkillsVersion(baseUrl, version) {
|
|
7033
7653
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
7034
|
-
|
|
7035
|
-
|
|
7654
|
+
mkdirSync4(dirname7(path), { recursive: true });
|
|
7655
|
+
writeFileSync7(path, `${version}
|
|
7036
7656
|
`, "utf-8");
|
|
7037
7657
|
}
|
|
7038
7658
|
async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
@@ -7063,9 +7683,26 @@ async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
|
7063
7683
|
clearTimeout(timeout);
|
|
7064
7684
|
}
|
|
7065
7685
|
}
|
|
7066
|
-
function
|
|
7686
|
+
function buildSkillsInstallArgs(baseUrl) {
|
|
7687
|
+
const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
|
|
7688
|
+
return [
|
|
7689
|
+
"--yes",
|
|
7690
|
+
"skills",
|
|
7691
|
+
"add",
|
|
7692
|
+
packageUrl,
|
|
7693
|
+
"--agents",
|
|
7694
|
+
...SKILL_AGENTS,
|
|
7695
|
+
"--global",
|
|
7696
|
+
"--yes",
|
|
7697
|
+
"--skill",
|
|
7698
|
+
SDK_SKILL_NAME,
|
|
7699
|
+
"--full-depth"
|
|
7700
|
+
];
|
|
7701
|
+
}
|
|
7702
|
+
function buildBunxSkillsInstallArgs(baseUrl) {
|
|
7067
7703
|
const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
|
|
7068
|
-
|
|
7704
|
+
return [
|
|
7705
|
+
"--bun",
|
|
7069
7706
|
"skills",
|
|
7070
7707
|
"add",
|
|
7071
7708
|
packageUrl,
|
|
@@ -7077,8 +7714,40 @@ function runSkillsInstall(baseUrl) {
|
|
|
7077
7714
|
SDK_SKILL_NAME,
|
|
7078
7715
|
"--full-depth"
|
|
7079
7716
|
];
|
|
7717
|
+
}
|
|
7718
|
+
function hasCommand(command) {
|
|
7719
|
+
const result = spawnSync(command, ["--version"], {
|
|
7720
|
+
stdio: "ignore",
|
|
7721
|
+
shell: process.platform === "win32"
|
|
7722
|
+
});
|
|
7723
|
+
return result.status === 0;
|
|
7724
|
+
}
|
|
7725
|
+
function shellQuote2(arg) {
|
|
7726
|
+
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
7727
|
+
}
|
|
7728
|
+
function resolveSkillsInstallCommands(baseUrl) {
|
|
7729
|
+
const npxArgs = buildSkillsInstallArgs(baseUrl);
|
|
7730
|
+
const npxInstall = {
|
|
7731
|
+
command: "npx",
|
|
7732
|
+
args: npxArgs,
|
|
7733
|
+
manualCommand: `npx ${npxArgs.map(shellQuote2).join(" ")}`
|
|
7734
|
+
};
|
|
7735
|
+
if (hasCommand("bunx")) {
|
|
7736
|
+
const bunxArgs = buildBunxSkillsInstallArgs(baseUrl);
|
|
7737
|
+
return [
|
|
7738
|
+
{
|
|
7739
|
+
command: "bunx",
|
|
7740
|
+
args: bunxArgs,
|
|
7741
|
+
manualCommand: `bunx ${bunxArgs.map(shellQuote2).join(" ")}`
|
|
7742
|
+
},
|
|
7743
|
+
npxInstall
|
|
7744
|
+
];
|
|
7745
|
+
}
|
|
7746
|
+
return [npxInstall];
|
|
7747
|
+
}
|
|
7748
|
+
function runOneSkillsInstall(install) {
|
|
7080
7749
|
return new Promise((resolve8) => {
|
|
7081
|
-
const child = spawn(
|
|
7750
|
+
const child = spawn(install.command, install.args, {
|
|
7082
7751
|
stdio: ["ignore", "ignore", "pipe"],
|
|
7083
7752
|
env: process.env
|
|
7084
7753
|
});
|
|
@@ -7087,37 +7756,111 @@ function runSkillsInstall(baseUrl) {
|
|
|
7087
7756
|
stderr += chunk.toString("utf-8");
|
|
7088
7757
|
});
|
|
7089
7758
|
child.on("error", (error) => {
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7759
|
+
resolve8({
|
|
7760
|
+
ok: false,
|
|
7761
|
+
detail: `failed to start ${install.command}: ${error.message}`,
|
|
7762
|
+
manualCommand: install.manualCommand
|
|
7763
|
+
});
|
|
7093
7764
|
});
|
|
7094
7765
|
child.on("close", (code) => {
|
|
7095
7766
|
if (code === 0) {
|
|
7096
|
-
resolve8(true);
|
|
7767
|
+
resolve8({ ok: true, detail: "", manualCommand: install.manualCommand });
|
|
7097
7768
|
return;
|
|
7098
7769
|
}
|
|
7099
7770
|
const detail = stderr.trim();
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
);
|
|
7105
|
-
resolve8(false);
|
|
7771
|
+
resolve8({
|
|
7772
|
+
ok: false,
|
|
7773
|
+
detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
|
|
7774
|
+
manualCommand: install.manualCommand
|
|
7775
|
+
});
|
|
7106
7776
|
});
|
|
7107
7777
|
});
|
|
7108
7778
|
}
|
|
7779
|
+
async function runSkillsInstall(baseUrl) {
|
|
7780
|
+
const failures = [];
|
|
7781
|
+
for (const install of resolveSkillsInstallCommands(baseUrl)) {
|
|
7782
|
+
const result = await runOneSkillsInstall(install);
|
|
7783
|
+
if (result.ok) return true;
|
|
7784
|
+
failures.push(result);
|
|
7785
|
+
}
|
|
7786
|
+
const details = failures.map((failure) => failure.detail).filter(Boolean).join("\n");
|
|
7787
|
+
const manualCommand = failures.at(-1)?.manualCommand;
|
|
7788
|
+
process.stderr.write(
|
|
7789
|
+
`SDK skills sync failed${details ? `:
|
|
7790
|
+
${details}` : ""}
|
|
7791
|
+
` + (manualCommand ? `Run manually: ${manualCommand}
|
|
7792
|
+
` : "")
|
|
7793
|
+
);
|
|
7794
|
+
return false;
|
|
7795
|
+
}
|
|
7796
|
+
function writeSdkSkillsStatusLine(line) {
|
|
7797
|
+
const progress = getActiveCliProgress();
|
|
7798
|
+
if (progress) {
|
|
7799
|
+
progress.writeLine(line);
|
|
7800
|
+
return;
|
|
7801
|
+
}
|
|
7802
|
+
process.stderr.write(`${line}
|
|
7803
|
+
`);
|
|
7804
|
+
}
|
|
7109
7805
|
async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
7110
7806
|
if (attemptedSync || shouldSkipSkillsSync()) return;
|
|
7111
7807
|
attemptedSync = true;
|
|
7112
7808
|
const localVersion = readLocalSkillsVersion(baseUrl);
|
|
7113
7809
|
const update = await fetchSkillsUpdate(baseUrl, localVersion);
|
|
7114
7810
|
if (!update?.needsUpdate || !update.remoteVersion) return;
|
|
7115
|
-
|
|
7116
|
-
progress?.writeLine("SDK skills changed; syncing deepline-sdk skill...") ?? process.stderr.write("SDK skills changed; syncing deepline-sdk skill...\n");
|
|
7811
|
+
writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-sdk skill...");
|
|
7117
7812
|
const installed = await runSkillsInstall(baseUrl);
|
|
7118
7813
|
if (!installed) return;
|
|
7119
7814
|
writeLocalSkillsVersion(baseUrl, update.remoteVersion);
|
|
7120
|
-
|
|
7815
|
+
writeSdkSkillsStatusLine("SDK skills are up to date.");
|
|
7816
|
+
}
|
|
7817
|
+
|
|
7818
|
+
// src/cli/trace.ts
|
|
7819
|
+
var cliTraceStartedAt = Date.now();
|
|
7820
|
+
function isTruthyEnv(value) {
|
|
7821
|
+
return value === "1" || value === "true" || value === "yes";
|
|
7822
|
+
}
|
|
7823
|
+
function isCliTraceEnabled() {
|
|
7824
|
+
return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
|
|
7825
|
+
}
|
|
7826
|
+
function recordCliTrace(event) {
|
|
7827
|
+
if (!isCliTraceEnabled()) {
|
|
7828
|
+
return;
|
|
7829
|
+
}
|
|
7830
|
+
const now = Date.now();
|
|
7831
|
+
const payload = {
|
|
7832
|
+
ts: now,
|
|
7833
|
+
source: "cli",
|
|
7834
|
+
sinceStartMs: now - cliTraceStartedAt,
|
|
7835
|
+
...event
|
|
7836
|
+
};
|
|
7837
|
+
process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
|
|
7838
|
+
`);
|
|
7839
|
+
}
|
|
7840
|
+
async function traceCliSpan(phase, fields, run) {
|
|
7841
|
+
if (!isCliTraceEnabled()) {
|
|
7842
|
+
return run();
|
|
7843
|
+
}
|
|
7844
|
+
const startedAt = Date.now();
|
|
7845
|
+
try {
|
|
7846
|
+
const result = await run();
|
|
7847
|
+
recordCliTrace({
|
|
7848
|
+
phase,
|
|
7849
|
+
ms: Date.now() - startedAt,
|
|
7850
|
+
ok: true,
|
|
7851
|
+
...fields
|
|
7852
|
+
});
|
|
7853
|
+
return result;
|
|
7854
|
+
} catch (error) {
|
|
7855
|
+
recordCliTrace({
|
|
7856
|
+
phase,
|
|
7857
|
+
ms: Date.now() - startedAt,
|
|
7858
|
+
ok: false,
|
|
7859
|
+
error: error instanceof Error ? error.message : String(error),
|
|
7860
|
+
...fields
|
|
7861
|
+
});
|
|
7862
|
+
throw error;
|
|
7863
|
+
}
|
|
7121
7864
|
}
|
|
7122
7865
|
|
|
7123
7866
|
// src/cli/index.ts
|
|
@@ -7141,7 +7884,7 @@ async function main() {
|
|
|
7141
7884
|
if (printStartupPhase) {
|
|
7142
7885
|
progress?.phase("loading deepline cli");
|
|
7143
7886
|
}
|
|
7144
|
-
const program = new
|
|
7887
|
+
const program = new Command();
|
|
7145
7888
|
program.name("deepline").description("Deepline CLI (TypeScript SDK)").version(SDK_VERSION, "-v, --version", "Show version").showHelpAfterError().showSuggestionAfterError(true).addHelpText(
|
|
7146
7889
|
"after",
|
|
7147
7890
|
`
|
|
@@ -7231,4 +7974,3 @@ Output:
|
|
|
7231
7974
|
process.exit(process.exitCode ?? 0);
|
|
7232
7975
|
}
|
|
7233
7976
|
main();
|
|
7234
|
-
//# sourceMappingURL=index.mjs.map
|