deepline 0.1.12 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -6
- package/dist/cli/index.js +1346 -717
- package/dist/cli/index.mjs +1342 -713
- package/dist/index.d.mts +199 -23
- package/dist/index.d.ts +199 -23
- package/dist/index.js +221 -14
- package/dist/index.mjs +221 -14
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +214 -77
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +85 -60
- package/dist/repo/apps/play-runner-workers/src/entry.ts +385 -66
- package/dist/repo/sdk/src/client.ts +237 -0
- package/dist/repo/sdk/src/config.ts +125 -8
- package/dist/repo/sdk/src/http.ts +29 -5
- package/dist/repo/sdk/src/play.ts +19 -36
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +22 -8
- package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
- package/dist/repo/sdk/src/types.ts +25 -0
- package/dist/repo/sdk/src/version.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +237 -145
- package/dist/repo/shared_libs/plays/bundling/index.ts +206 -229
- package/dist/repo/shared_libs/plays/dataset.ts +28 -0
- package/dist/repo/shared_libs/plays/row-identity.ts +59 -4
- 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/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 -3441
- package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
- package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -415
- 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 -4242
- package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -725
- 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 -395
- 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
|
@@ -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.20";
|
|
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";
|
|
@@ -276,7 +350,8 @@ var HttpClient = class {
|
|
|
276
350
|
parsed = body;
|
|
277
351
|
}
|
|
278
352
|
if (!response.ok) {
|
|
279
|
-
const
|
|
353
|
+
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
354
|
+
const msg = typeof errorValue === "string" ? errorValue : errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string" ? errorValue.message : typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string" ? parsed.message : `HTTP ${response.status}`;
|
|
280
355
|
throw new DeeplineError(msg, response.status, "API_ERROR", {
|
|
281
356
|
response: parsed
|
|
282
357
|
});
|
|
@@ -363,8 +438,12 @@ var HttpClient = class {
|
|
|
363
438
|
* @param path - API path
|
|
364
439
|
* @param body - Request body (will be JSON-serialized)
|
|
365
440
|
*/
|
|
366
|
-
async post(path, body) {
|
|
367
|
-
return this.request(path, {
|
|
441
|
+
async post(path, body, headers) {
|
|
442
|
+
return this.request(path, {
|
|
443
|
+
method: "POST",
|
|
444
|
+
body,
|
|
445
|
+
headers
|
|
446
|
+
});
|
|
368
447
|
}
|
|
369
448
|
/**
|
|
370
449
|
* Send a DELETE request.
|
|
@@ -444,6 +523,7 @@ function sleep(ms) {
|
|
|
444
523
|
|
|
445
524
|
// src/client.ts
|
|
446
525
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
526
|
+
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
447
527
|
function isRecord(value) {
|
|
448
528
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
449
529
|
}
|
|
@@ -476,6 +556,7 @@ function mapLegacyTemporalStatus(status) {
|
|
|
476
556
|
var DeeplineClient = class {
|
|
477
557
|
http;
|
|
478
558
|
config;
|
|
559
|
+
runs;
|
|
479
560
|
/**
|
|
480
561
|
* @param options - Optional overrides for API key, base URL, timeout, and retries.
|
|
481
562
|
* @throws {@link ConfigError} if no API key can be resolved from any source.
|
|
@@ -483,6 +564,13 @@ var DeeplineClient = class {
|
|
|
483
564
|
constructor(options) {
|
|
484
565
|
this.config = resolveConfig(options);
|
|
485
566
|
this.http = new HttpClient(this.config);
|
|
567
|
+
this.runs = {
|
|
568
|
+
get: (runId) => this.getRunStatus(runId),
|
|
569
|
+
list: (options2) => this.listRuns(options2),
|
|
570
|
+
tail: (runId, options2) => this.tailRun(runId, options2),
|
|
571
|
+
logs: (runId, options2) => this.getRunLogs(runId, options2),
|
|
572
|
+
stop: (runId, options2) => this.stopRun(runId, options2)
|
|
573
|
+
};
|
|
486
574
|
}
|
|
487
575
|
/** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
|
|
488
576
|
get baseUrl() {
|
|
@@ -571,6 +659,31 @@ var DeeplineClient = class {
|
|
|
571
659
|
);
|
|
572
660
|
return res.tools;
|
|
573
661
|
}
|
|
662
|
+
/**
|
|
663
|
+
* Search available tools using Deepline's ranked backend search.
|
|
664
|
+
*
|
|
665
|
+
* This is the same discovery surface used by the legacy CLI: it ranks across
|
|
666
|
+
* tool metadata, categories, agent guidance, and input schema fields.
|
|
667
|
+
*/
|
|
668
|
+
async searchTools(options = {}) {
|
|
669
|
+
const params = new URLSearchParams();
|
|
670
|
+
const query = options.query?.trim() ?? "";
|
|
671
|
+
params.set("q", query);
|
|
672
|
+
params.set(
|
|
673
|
+
"include_search_debug",
|
|
674
|
+
options.includeSearchDebug ? "true" : "false"
|
|
675
|
+
);
|
|
676
|
+
params.set("search_mode", options.searchMode ?? "v2");
|
|
677
|
+
if (options.categories?.trim()) {
|
|
678
|
+
params.set("categories", options.categories.trim());
|
|
679
|
+
}
|
|
680
|
+
if (options.searchTerms?.trim()) {
|
|
681
|
+
params.set("search_terms", options.searchTerms.trim());
|
|
682
|
+
}
|
|
683
|
+
return this.http.get(
|
|
684
|
+
`/api/v2/integrations/list?${params.toString()}`
|
|
685
|
+
);
|
|
686
|
+
}
|
|
574
687
|
/**
|
|
575
688
|
* Get detailed metadata for a single tool.
|
|
576
689
|
*
|
|
@@ -606,12 +719,17 @@ var DeeplineClient = class {
|
|
|
606
719
|
* Top-level fields such as `status`, `job_id`, and `billing` describe the
|
|
607
720
|
* Deepline execution.
|
|
608
721
|
*/
|
|
609
|
-
async executeTool(toolId, input) {
|
|
722
|
+
async executeTool(toolId, input, options) {
|
|
723
|
+
const headers = options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : void 0;
|
|
610
724
|
return this.http.post(
|
|
611
725
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
612
|
-
{ payload: input }
|
|
726
|
+
{ payload: input },
|
|
727
|
+
headers
|
|
613
728
|
);
|
|
614
729
|
}
|
|
730
|
+
async executeToolRaw(toolId, input, options) {
|
|
731
|
+
return this.executeTool(toolId, input, options);
|
|
732
|
+
}
|
|
615
733
|
async queryCustomerDb(input) {
|
|
616
734
|
return this.http.post("/api/v2/db/query", {
|
|
617
735
|
sql: input.sql,
|
|
@@ -660,6 +778,7 @@ var DeeplineClient = class {
|
|
|
660
778
|
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
661
779
|
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
662
780
|
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
781
|
+
...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
|
|
663
782
|
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
664
783
|
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
665
784
|
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
@@ -684,6 +803,7 @@ var DeeplineClient = class {
|
|
|
684
803
|
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
685
804
|
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
686
805
|
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
806
|
+
...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
|
|
687
807
|
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
688
808
|
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
689
809
|
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
@@ -720,6 +840,7 @@ var DeeplineClient = class {
|
|
|
720
840
|
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
721
841
|
name: input.name,
|
|
722
842
|
sourceCode: input.sourceCode,
|
|
843
|
+
sourceFiles: input.sourceFiles,
|
|
723
844
|
artifact: input.artifact
|
|
724
845
|
});
|
|
725
846
|
return this.http.post("/api/v2/plays/artifacts", {
|
|
@@ -734,6 +855,7 @@ var DeeplineClient = class {
|
|
|
734
855
|
compilerManifest: artifact.compilerManifest ?? await this.compilePlayManifest({
|
|
735
856
|
name: artifact.name,
|
|
736
857
|
sourceCode: artifact.sourceCode,
|
|
858
|
+
sourceFiles: artifact.sourceFiles,
|
|
737
859
|
artifact: artifact.artifact
|
|
738
860
|
})
|
|
739
861
|
}))
|
|
@@ -760,11 +882,13 @@ var DeeplineClient = class {
|
|
|
760
882
|
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
761
883
|
name: input.name,
|
|
762
884
|
sourceCode: input.sourceCode,
|
|
885
|
+
sourceFiles: input.sourceFiles,
|
|
763
886
|
artifact: input.artifact
|
|
764
887
|
});
|
|
765
888
|
const registeredArtifact = await this.registerPlayArtifact({
|
|
766
889
|
name: input.name,
|
|
767
890
|
sourceCode: input.sourceCode,
|
|
891
|
+
sourceFiles: input.sourceFiles,
|
|
768
892
|
artifact: input.artifact,
|
|
769
893
|
compilerManifest,
|
|
770
894
|
publish: false
|
|
@@ -824,11 +948,13 @@ var DeeplineClient = class {
|
|
|
824
948
|
const compilerManifest = options?.compilerManifest ?? await this.compilePlayManifest({
|
|
825
949
|
name,
|
|
826
950
|
sourceCode,
|
|
951
|
+
sourceFiles: options?.sourceFiles,
|
|
827
952
|
artifact
|
|
828
953
|
});
|
|
829
954
|
const registeredArtifact = await this.registerPlayArtifact({
|
|
830
955
|
name,
|
|
831
956
|
sourceCode,
|
|
957
|
+
sourceFiles: options?.sourceFiles,
|
|
832
958
|
artifact,
|
|
833
959
|
compilerManifest,
|
|
834
960
|
publish: false
|
|
@@ -1012,6 +1138,112 @@ var DeeplineClient = class {
|
|
|
1012
1138
|
);
|
|
1013
1139
|
return response.runs ?? [];
|
|
1014
1140
|
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Get a run by id using the public runs resource model.
|
|
1143
|
+
*
|
|
1144
|
+
* This is the SDK equivalent of:
|
|
1145
|
+
*
|
|
1146
|
+
* ```bash
|
|
1147
|
+
* deepline runs get <run-id> --json
|
|
1148
|
+
* ```
|
|
1149
|
+
*/
|
|
1150
|
+
async getRunStatus(runId) {
|
|
1151
|
+
const response = await this.http.get(
|
|
1152
|
+
`/api/v2/runs/${encodeURIComponent(runId)}`
|
|
1153
|
+
);
|
|
1154
|
+
return normalizePlayStatus(response);
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* List play runs using the public runs resource model.
|
|
1158
|
+
*
|
|
1159
|
+
* This is the SDK equivalent of:
|
|
1160
|
+
*
|
|
1161
|
+
* ```bash
|
|
1162
|
+
* deepline runs list --play <play-name> --status failed --json
|
|
1163
|
+
* ```
|
|
1164
|
+
*/
|
|
1165
|
+
async listRuns(options) {
|
|
1166
|
+
const playName = options.play.trim();
|
|
1167
|
+
if (!playName) {
|
|
1168
|
+
throw new Error("runs.list requires options.play.");
|
|
1169
|
+
}
|
|
1170
|
+
const params = new URLSearchParams({ play: playName });
|
|
1171
|
+
const status = options.status?.trim();
|
|
1172
|
+
if (status) {
|
|
1173
|
+
params.set("status", status);
|
|
1174
|
+
}
|
|
1175
|
+
const response = await this.http.get(
|
|
1176
|
+
`/api/v2/runs?${params.toString()}`
|
|
1177
|
+
);
|
|
1178
|
+
return response.runs ?? [];
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Fetch the lightweight tail status for a run using the public runs resource model.
|
|
1182
|
+
*
|
|
1183
|
+
* This is the SDK equivalent of:
|
|
1184
|
+
*
|
|
1185
|
+
* ```bash
|
|
1186
|
+
* deepline runs tail <run-id> --json
|
|
1187
|
+
* ```
|
|
1188
|
+
*/
|
|
1189
|
+
async tailRun(runId, options) {
|
|
1190
|
+
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;
|
|
1191
|
+
const params = new URLSearchParams();
|
|
1192
|
+
if (Number.isFinite(afterLogIndex)) {
|
|
1193
|
+
params.set("afterLogIndex", String(Number(afterLogIndex)));
|
|
1194
|
+
}
|
|
1195
|
+
if (typeof options?.waitMs === "number") {
|
|
1196
|
+
params.set("waitMs", String(options.waitMs));
|
|
1197
|
+
}
|
|
1198
|
+
if (options?.terminalOnly) {
|
|
1199
|
+
params.set("terminalOnly", "true");
|
|
1200
|
+
}
|
|
1201
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
1202
|
+
const response = await this.http.get(
|
|
1203
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/tail${suffix}`
|
|
1204
|
+
);
|
|
1205
|
+
return normalizePlayStatus(response);
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Fetch persisted logs for a run using the public runs resource model.
|
|
1209
|
+
*
|
|
1210
|
+
* This is the SDK equivalent of:
|
|
1211
|
+
*
|
|
1212
|
+
* ```bash
|
|
1213
|
+
* deepline runs logs <run-id> --limit 200 --json
|
|
1214
|
+
* ```
|
|
1215
|
+
*/
|
|
1216
|
+
async getRunLogs(runId, options) {
|
|
1217
|
+
const status = await this.getRunStatus(runId);
|
|
1218
|
+
const logs = status.progress?.logs ?? [];
|
|
1219
|
+
const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
|
|
1220
|
+
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
1221
|
+
return {
|
|
1222
|
+
runId: status.runId,
|
|
1223
|
+
totalCount: logs.length,
|
|
1224
|
+
returnedCount: entries.length,
|
|
1225
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
1226
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
1227
|
+
truncated: logs.length > entries.length,
|
|
1228
|
+
hasMore: logs.length > entries.length,
|
|
1229
|
+
entries
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Stop a run by id using the public runs resource model.
|
|
1234
|
+
*
|
|
1235
|
+
* This is the SDK equivalent of:
|
|
1236
|
+
*
|
|
1237
|
+
* ```bash
|
|
1238
|
+
* deepline runs stop <run-id> --reason "stale lock" --json
|
|
1239
|
+
* ```
|
|
1240
|
+
*/
|
|
1241
|
+
async stopRun(runId, options) {
|
|
1242
|
+
return this.http.post(
|
|
1243
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/stop`,
|
|
1244
|
+
options?.reason ? { reason: options.reason } : {}
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1015
1247
|
async listPlays() {
|
|
1016
1248
|
const response = await this.http.get(
|
|
1017
1249
|
"/api/v2/plays"
|
|
@@ -1398,6 +1630,7 @@ function saveEnvValues(values, baseUrl) {
|
|
|
1398
1630
|
const merged = { ...existing, ...values };
|
|
1399
1631
|
const lines = Object.entries(merged).filter(([, v]) => v !== "").map(([k, v]) => `${k}=${v}`);
|
|
1400
1632
|
writeFileSync2(filePath, lines.join("\n") + "\n", "utf-8");
|
|
1633
|
+
saveProjectDeeplineEnvValues(baseUrl, values);
|
|
1401
1634
|
}
|
|
1402
1635
|
async function httpJson(method, url, apiKey, body) {
|
|
1403
1636
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -1998,6 +2231,9 @@ function rowArray(value) {
|
|
|
1998
2231
|
function readNumber(value) {
|
|
1999
2232
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
|
|
2000
2233
|
}
|
|
2234
|
+
function numericStat(value) {
|
|
2235
|
+
return readNumber(value) ?? 0;
|
|
2236
|
+
}
|
|
2001
2237
|
function inferColumns(rows) {
|
|
2002
2238
|
const columns = [];
|
|
2003
2239
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2077,6 +2313,31 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2077
2313
|
function percentText(numerator, denominator) {
|
|
2078
2314
|
return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
|
|
2079
2315
|
}
|
|
2316
|
+
function isDatasetExecutionStatsInput(value) {
|
|
2317
|
+
return isRecord2(value) && isRecord2(value.columnStats) && Object.values(value.columnStats).every(isRecord2);
|
|
2318
|
+
}
|
|
2319
|
+
function extractDatasetExecutionStats(statusOrResult) {
|
|
2320
|
+
if (!isRecord2(statusOrResult)) {
|
|
2321
|
+
return null;
|
|
2322
|
+
}
|
|
2323
|
+
const direct = statusOrResult.dataset_execution_stats;
|
|
2324
|
+
if (isDatasetExecutionStatsInput(direct)) {
|
|
2325
|
+
return direct;
|
|
2326
|
+
}
|
|
2327
|
+
const nested = isRecord2(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
|
|
2328
|
+
return isDatasetExecutionStatsInput(nested) ? nested : null;
|
|
2329
|
+
}
|
|
2330
|
+
function formatExecutionStats(raw, denominator) {
|
|
2331
|
+
return {
|
|
2332
|
+
queued: percentText(numericStat(raw.queued), denominator),
|
|
2333
|
+
running: percentText(numericStat(raw.running), denominator),
|
|
2334
|
+
"completed:executed": percentText(numericStat(raw.completed), denominator),
|
|
2335
|
+
"completed:reused": percentText(numericStat(raw.cached), denominator),
|
|
2336
|
+
"skipped:condition": percentText(numericStat(raw.skipped), denominator),
|
|
2337
|
+
"skipped:missed": percentText(numericStat(raw.missed), denominator),
|
|
2338
|
+
failed: percentText(numericStat(raw.failed), denominator)
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2080
2341
|
function countPercentText(count, denominator) {
|
|
2081
2342
|
return denominator > 0 ? `${count} (${Math.round(100 * count / denominator)}%)` : "0 (0%)";
|
|
2082
2343
|
}
|
|
@@ -2169,7 +2430,7 @@ function compactCell(value) {
|
|
|
2169
2430
|
}
|
|
2170
2431
|
return compactScalar(parsed, 120);
|
|
2171
2432
|
}
|
|
2172
|
-
function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows)) {
|
|
2433
|
+
function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows), executionStats) {
|
|
2173
2434
|
const sanitized = sanitizeCsvProjectionInfo({ rows, columns });
|
|
2174
2435
|
const columnStats = {};
|
|
2175
2436
|
for (const column of sanitized.columns) {
|
|
@@ -2197,6 +2458,10 @@ function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns
|
|
|
2197
2458
|
non_empty: percentText(nonEmpty, denominator),
|
|
2198
2459
|
unique: valueCounts.size
|
|
2199
2460
|
};
|
|
2461
|
+
const rawExecutionStats = executionStats?.columnStats[column];
|
|
2462
|
+
if (rawExecutionStats) {
|
|
2463
|
+
stat3.execution = formatExecutionStats(rawExecutionStats, totalRows);
|
|
2464
|
+
}
|
|
2200
2465
|
if (sampleValue !== void 0 && sampleValueType) {
|
|
2201
2466
|
stat3.sample_value = sampleValue;
|
|
2202
2467
|
stat3.sample_type = sampleValueType;
|
|
@@ -2549,7 +2814,6 @@ import { tmpdir } from "os";
|
|
|
2549
2814
|
import { basename, dirname as dirname3, extname, isAbsolute, join as join3, resolve as resolve4 } from "path";
|
|
2550
2815
|
import { builtinModules, createRequire } from "module";
|
|
2551
2816
|
import { build } from "esbuild";
|
|
2552
|
-
import ts from "typescript";
|
|
2553
2817
|
|
|
2554
2818
|
// ../shared_libs/play-runtime/backend.ts
|
|
2555
2819
|
var PLAY_RUNTIME_BACKENDS = {
|
|
@@ -2633,56 +2897,6 @@ function formatEsbuildMessage(message) {
|
|
|
2633
2897
|
const location = message.location ? `${message.location.file}:${message.location.line}:${message.location.column}` : null;
|
|
2634
2898
|
return location ? `${location} ${message.text}` : message.text;
|
|
2635
2899
|
}
|
|
2636
|
-
function formatTypeScriptDiagnostic(diagnostic) {
|
|
2637
|
-
if (!diagnostic.file) {
|
|
2638
|
-
const message2 = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n").trim();
|
|
2639
|
-
return message2 || null;
|
|
2640
|
-
}
|
|
2641
|
-
const start = diagnostic.start ?? 0;
|
|
2642
|
-
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(start);
|
|
2643
|
-
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n").trim();
|
|
2644
|
-
if (!message) {
|
|
2645
|
-
return null;
|
|
2646
|
-
}
|
|
2647
|
-
return `${diagnostic.file.fileName}:${line + 1}:${character + 1} ${message}`;
|
|
2648
|
-
}
|
|
2649
|
-
function resolveBundledTypeRoots() {
|
|
2650
|
-
try {
|
|
2651
|
-
return [dirname3(dirname3(playArtifactRequire.resolve("@types/node/package.json")))];
|
|
2652
|
-
} catch {
|
|
2653
|
-
return [];
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
function typecheckPlaySource(input, adapter) {
|
|
2657
|
-
const rootNames = Array.from(
|
|
2658
|
-
/* @__PURE__ */ new Set([
|
|
2659
|
-
...input.importPolicy.localFiles,
|
|
2660
|
-
...input.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
2661
|
-
])
|
|
2662
|
-
);
|
|
2663
|
-
const sdkTypesPath = adapter.sdkTypesEntryFile ?? adapter.sdkEntryFile;
|
|
2664
|
-
const program = ts.createProgram(rootNames, {
|
|
2665
|
-
target: ts.ScriptTarget.ES2023,
|
|
2666
|
-
// SDK source uses fetch/RequestInit/URL and node-aware config helpers.
|
|
2667
|
-
// The play runtime import policy below still bans Node modules from play
|
|
2668
|
-
// source for workers_edge bundles.
|
|
2669
|
-
lib: ["lib.es2023.d.ts", "lib.dom.d.ts"],
|
|
2670
|
-
module: ts.ModuleKind.ESNext,
|
|
2671
|
-
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
2672
|
-
paths: { deepline: [sdkTypesPath] },
|
|
2673
|
-
strict: true,
|
|
2674
|
-
skipLibCheck: true,
|
|
2675
|
-
noEmit: true,
|
|
2676
|
-
esModuleInterop: true,
|
|
2677
|
-
allowSyntheticDefaultImports: true,
|
|
2678
|
-
allowImportingTsExtensions: true,
|
|
2679
|
-
allowJs: true,
|
|
2680
|
-
resolveJsonModule: true,
|
|
2681
|
-
types: ["node"],
|
|
2682
|
-
typeRoots: resolveBundledTypeRoots()
|
|
2683
|
-
});
|
|
2684
|
-
return ts.getPreEmitDiagnostics(program).map(formatTypeScriptDiagnostic).filter((message) => Boolean(message));
|
|
2685
|
-
}
|
|
2686
2900
|
function isLocalSpecifier(specifier) {
|
|
2687
2901
|
return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/") || specifier.startsWith("file:");
|
|
2688
2902
|
}
|
|
@@ -2706,11 +2920,8 @@ function assertWithinPlayWorkspace(input) {
|
|
|
2706
2920
|
if (isPathInsideDirectory(input.resolvedPath, input.workspace.rootDir)) {
|
|
2707
2921
|
return;
|
|
2708
2922
|
}
|
|
2709
|
-
const position = input.sourceFile.getLineAndCharacterOfPosition(
|
|
2710
|
-
input.node.getStart(input.sourceFile)
|
|
2711
|
-
);
|
|
2712
2923
|
throw new Error(
|
|
2713
|
-
`${input.importer}:${
|
|
2924
|
+
`${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.`
|
|
2714
2925
|
);
|
|
2715
2926
|
}
|
|
2716
2927
|
function getPackageName(specifier) {
|
|
@@ -2720,72 +2931,135 @@ function getPackageName(specifier) {
|
|
|
2720
2931
|
}
|
|
2721
2932
|
return specifier.split("/")[0] ?? specifier;
|
|
2722
2933
|
}
|
|
2723
|
-
function scriptKindForFile(filePath) {
|
|
2724
|
-
const extension = extname(filePath).toLowerCase();
|
|
2725
|
-
switch (extension) {
|
|
2726
|
-
case ".tsx":
|
|
2727
|
-
return ts.ScriptKind.TSX;
|
|
2728
|
-
case ".jsx":
|
|
2729
|
-
return ts.ScriptKind.JSX;
|
|
2730
|
-
case ".js":
|
|
2731
|
-
case ".mjs":
|
|
2732
|
-
case ".cjs":
|
|
2733
|
-
return ts.ScriptKind.JS;
|
|
2734
|
-
case ".json":
|
|
2735
|
-
return ts.ScriptKind.JSON;
|
|
2736
|
-
default:
|
|
2737
|
-
return ts.ScriptKind.TS;
|
|
2738
|
-
}
|
|
2739
|
-
}
|
|
2740
2934
|
function isPlaySourceFile(filePath) {
|
|
2741
2935
|
return PLAY_SOURCE_FILE_PATTERN.test(filePath);
|
|
2742
2936
|
}
|
|
2743
|
-
function
|
|
2744
|
-
|
|
2745
|
-
|
|
2937
|
+
function stripCommentsToSpaces(source) {
|
|
2938
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
|
|
2939
|
+
/(^|[^:])\/\/.*$/gm,
|
|
2940
|
+
(match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2943
|
+
function lineAndColumnAt(source, index) {
|
|
2944
|
+
const prefix = source.slice(0, index);
|
|
2945
|
+
const lines = prefix.split("\n");
|
|
2946
|
+
return { line: lines.length, column: lines[lines.length - 1].length + 1 };
|
|
2947
|
+
}
|
|
2948
|
+
function findSourceImportReferences(sourceCode) {
|
|
2949
|
+
const source = stripCommentsToSpaces(sourceCode);
|
|
2950
|
+
const references = [];
|
|
2951
|
+
const addReference = (specifier, specifierIndex, kind) => {
|
|
2952
|
+
if (!specifier) return;
|
|
2953
|
+
const position = lineAndColumnAt(sourceCode, specifierIndex);
|
|
2954
|
+
references.push({
|
|
2955
|
+
specifier,
|
|
2956
|
+
line: position.line,
|
|
2957
|
+
column: position.column,
|
|
2958
|
+
kind
|
|
2959
|
+
});
|
|
2960
|
+
};
|
|
2961
|
+
const staticImportPattern = /\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?(['"])([^'"\n]+)\1/g;
|
|
2962
|
+
for (const match of source.matchAll(staticImportPattern)) {
|
|
2963
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "static");
|
|
2964
|
+
}
|
|
2965
|
+
const dynamicImportPattern = /\bimport\s*\(\s*(['"])([^'"\n]+)\1/g;
|
|
2966
|
+
for (const match of source.matchAll(dynamicImportPattern)) {
|
|
2967
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "dynamic-import");
|
|
2968
|
+
}
|
|
2969
|
+
const requirePattern = /\brequire\s*\(\s*(['"])([^'"\n]+)\1/g;
|
|
2970
|
+
for (const match of source.matchAll(requirePattern)) {
|
|
2971
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "require");
|
|
2972
|
+
}
|
|
2973
|
+
const literalDynamicImportIndexes = new Set(
|
|
2974
|
+
[...source.matchAll(dynamicImportPattern)].map((match) => match.index)
|
|
2975
|
+
);
|
|
2976
|
+
for (const match of source.matchAll(/\bimport\s*\(/g)) {
|
|
2977
|
+
if (literalDynamicImportIndexes.has(match.index)) continue;
|
|
2978
|
+
const position = lineAndColumnAt(sourceCode, match.index);
|
|
2979
|
+
throw new Error(
|
|
2980
|
+
`:${position.line}:${position.column} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
2981
|
+
);
|
|
2982
|
+
}
|
|
2983
|
+
const literalRequireIndexes = new Set(
|
|
2984
|
+
[...source.matchAll(requirePattern)].map((match) => match.index)
|
|
2985
|
+
);
|
|
2986
|
+
for (const match of source.matchAll(/\brequire\s*\(/g)) {
|
|
2987
|
+
if (literalRequireIndexes.has(match.index)) continue;
|
|
2988
|
+
const position = lineAndColumnAt(sourceCode, match.index);
|
|
2989
|
+
throw new Error(
|
|
2990
|
+
`:${position.line}:${position.column} Dynamic require() is not allowed in plays. Use static imports or require("literal") only.`
|
|
2991
|
+
);
|
|
2992
|
+
}
|
|
2993
|
+
return references.sort(
|
|
2994
|
+
(left, right) => left.line === right.line ? left.column - right.column : left.line - right.line
|
|
2995
|
+
);
|
|
2996
|
+
}
|
|
2997
|
+
function unquoteStringLiteral(literal) {
|
|
2998
|
+
const trimmed = literal.trim();
|
|
2999
|
+
const quote = trimmed[0];
|
|
3000
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
3001
|
+
return null;
|
|
3002
|
+
}
|
|
3003
|
+
try {
|
|
3004
|
+
return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
|
|
3005
|
+
} catch {
|
|
3006
|
+
return trimmed.slice(1, -1);
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
function findMatchingBrace(source, openIndex) {
|
|
3010
|
+
let depth = 0;
|
|
3011
|
+
let quote = null;
|
|
3012
|
+
let escaped = false;
|
|
3013
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
3014
|
+
const char = source[index];
|
|
3015
|
+
if (quote) {
|
|
3016
|
+
if (escaped) {
|
|
3017
|
+
escaped = false;
|
|
3018
|
+
} else if (char === "\\") {
|
|
3019
|
+
escaped = true;
|
|
3020
|
+
} else if (char === quote) {
|
|
3021
|
+
quote = null;
|
|
3022
|
+
}
|
|
2746
3023
|
continue;
|
|
2747
3024
|
}
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
if (!matches) {
|
|
3025
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3026
|
+
quote = char;
|
|
2751
3027
|
continue;
|
|
2752
3028
|
}
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
)
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
const
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
3029
|
+
if (char === "{") depth += 1;
|
|
3030
|
+
if (char === "}") {
|
|
3031
|
+
depth -= 1;
|
|
3032
|
+
if (depth === 0) return index;
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
return -1;
|
|
3036
|
+
}
|
|
3037
|
+
function extractDefinedPlayName(sourceCode, _filePath) {
|
|
3038
|
+
const source = stripCommentsToSpaces(sourceCode);
|
|
3039
|
+
const callPattern = /(?:\b[A-Za-z_$][\w$]*\s*\.\s*)?\b(?:definePlay|defineWorkflow)\s*\(/g;
|
|
3040
|
+
for (const match of source.matchAll(callPattern)) {
|
|
3041
|
+
const openParen = match.index + match[0].length - 1;
|
|
3042
|
+
const firstArgStart = openParen + 1;
|
|
3043
|
+
const firstNonSpace = source.slice(firstArgStart).search(/\S/);
|
|
3044
|
+
if (firstNonSpace < 0) continue;
|
|
3045
|
+
const argIndex = firstArgStart + firstNonSpace;
|
|
3046
|
+
const quote = source[argIndex];
|
|
3047
|
+
if (quote === '"' || quote === "'") {
|
|
3048
|
+
const literalMatch = source.slice(argIndex).match(/^(['"])(?:\\.|(?!\1)[\s\S])*\1/);
|
|
3049
|
+
const value = literalMatch ? unquoteStringLiteral(literalMatch[0]) : null;
|
|
3050
|
+
if (value?.trim()) return value.trim();
|
|
3051
|
+
}
|
|
3052
|
+
if (quote === "{") {
|
|
3053
|
+
const closeBrace = findMatchingBrace(source, argIndex);
|
|
3054
|
+
if (closeBrace < 0) continue;
|
|
3055
|
+
const objectSource = source.slice(argIndex + 1, closeBrace);
|
|
3056
|
+
const idMatch = objectSource.match(/(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/);
|
|
3057
|
+
if (idMatch?.[2]?.trim()) {
|
|
3058
|
+
return idMatch[2].trim();
|
|
2783
3059
|
}
|
|
2784
3060
|
}
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
visit(sourceFile);
|
|
2788
|
-
return detectedPlayName;
|
|
3061
|
+
}
|
|
3062
|
+
return null;
|
|
2789
3063
|
}
|
|
2790
3064
|
function getPackageRequireCandidates(fromFile) {
|
|
2791
3065
|
const candidates = [
|
|
@@ -3095,18 +3369,10 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3095
3369
|
if (extname(absolutePath).toLowerCase() === ".json") {
|
|
3096
3370
|
return;
|
|
3097
3371
|
}
|
|
3098
|
-
const
|
|
3099
|
-
absolutePath,
|
|
3100
|
-
sourceCode2,
|
|
3101
|
-
ts.ScriptTarget.Latest,
|
|
3102
|
-
true,
|
|
3103
|
-
scriptKindForFile(absolutePath)
|
|
3104
|
-
);
|
|
3105
|
-
const handleSpecifier = async (specifier, node, kind) => {
|
|
3372
|
+
const handleSpecifier = async (specifier, line, column, kind) => {
|
|
3106
3373
|
if (kind === "dynamic-import") {
|
|
3107
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3108
3374
|
throw new Error(
|
|
3109
|
-
`${absolutePath}:${
|
|
3375
|
+
`${absolutePath}:${line}:${column} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
3110
3376
|
);
|
|
3111
3377
|
}
|
|
3112
3378
|
if (NODE_BUILTIN_SET.has(specifier)) {
|
|
@@ -3120,16 +3386,15 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3120
3386
|
specifier,
|
|
3121
3387
|
resolvedPath: resolved,
|
|
3122
3388
|
workspace,
|
|
3123
|
-
|
|
3124
|
-
|
|
3389
|
+
line,
|
|
3390
|
+
column
|
|
3125
3391
|
});
|
|
3126
3392
|
if (resolved !== absoluteEntryFile && isPlaySourceFile(resolved)) {
|
|
3127
3393
|
const importedSource = await readFile(resolved, "utf-8");
|
|
3128
3394
|
const importedPlayName = extractDefinedPlayName(importedSource, resolved);
|
|
3129
3395
|
if (!importedPlayName) {
|
|
3130
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3131
3396
|
throw new Error(
|
|
3132
|
-
`${absolutePath}:${
|
|
3397
|
+
`${absolutePath}:${line}:${column} Imported play file "${specifier}" must export definePlay(...) so it can be runtime-composed.`
|
|
3133
3398
|
);
|
|
3134
3399
|
}
|
|
3135
3400
|
importedPlayDependencies.set(resolved, {
|
|
@@ -3142,44 +3407,28 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3142
3407
|
return;
|
|
3143
3408
|
}
|
|
3144
3409
|
if (specifier.includes(":")) {
|
|
3145
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3146
3410
|
throw new Error(
|
|
3147
|
-
`${absolutePath}:${
|
|
3411
|
+
`${absolutePath}:${line}:${column} Unsupported import specifier "${specifier}". Allowed imports are relative files, Node builtins, and installed packages.`
|
|
3148
3412
|
);
|
|
3149
3413
|
}
|
|
3150
3414
|
const packageImport = resolvePackageImport(specifier, absolutePath, adapter);
|
|
3151
3415
|
packages.set(packageImport.name, packageImport.version);
|
|
3152
3416
|
};
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
await handleSpecifier(
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
throw new Error(
|
|
3162
|
-
`${absolutePath}:${position.line + 1}:${position.character + 1} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
3163
|
-
);
|
|
3164
|
-
}
|
|
3165
|
-
await handleSpecifier(node.arguments[0].text, node, "dynamic-import");
|
|
3166
|
-
}
|
|
3167
|
-
if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
3168
|
-
const firstArgument = node.arguments[0];
|
|
3169
|
-
if (node.arguments.length !== 1 || !firstArgument || !ts.isStringLiteralLike(firstArgument)) {
|
|
3170
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3171
|
-
throw new Error(
|
|
3172
|
-
`${absolutePath}:${position.line + 1}:${position.character + 1} Dynamic require() is not allowed in plays. Use static imports or require("literal") only.`
|
|
3173
|
-
);
|
|
3174
|
-
}
|
|
3175
|
-
await handleSpecifier(firstArgument.text, node, "require");
|
|
3176
|
-
}
|
|
3417
|
+
try {
|
|
3418
|
+
for (const reference of findSourceImportReferences(sourceCode2)) {
|
|
3419
|
+
await handleSpecifier(
|
|
3420
|
+
reference.specifier,
|
|
3421
|
+
reference.line,
|
|
3422
|
+
reference.column,
|
|
3423
|
+
reference.kind
|
|
3424
|
+
);
|
|
3177
3425
|
}
|
|
3178
|
-
|
|
3179
|
-
|
|
3426
|
+
} catch (error) {
|
|
3427
|
+
if (error instanceof Error && error.message.startsWith(":")) {
|
|
3428
|
+
throw new Error(`${absolutePath}${error.message}`);
|
|
3180
3429
|
}
|
|
3181
|
-
|
|
3182
|
-
|
|
3430
|
+
throw error;
|
|
3431
|
+
}
|
|
3183
3432
|
};
|
|
3184
3433
|
await visitFile(absoluteEntryFile);
|
|
3185
3434
|
const sourceCode = localFiles.get(absoluteEntryFile) ?? "";
|
|
@@ -3199,6 +3448,11 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3199
3448
|
const playName = extractDefinedPlayName(sourceCode, absoluteEntryFile);
|
|
3200
3449
|
return {
|
|
3201
3450
|
sourceCode,
|
|
3451
|
+
sourceFiles: Object.fromEntries(
|
|
3452
|
+
[...localFiles.entries()].sort(
|
|
3453
|
+
(left, right) => left[0].localeCompare(right[0])
|
|
3454
|
+
)
|
|
3455
|
+
),
|
|
3202
3456
|
sourceHash,
|
|
3203
3457
|
graphHash,
|
|
3204
3458
|
importPolicy: {
|
|
@@ -3262,8 +3516,7 @@ function normalizeSourceMapForRuntime(sourceMapText) {
|
|
|
3262
3516
|
parsed.sourceRoot = void 0;
|
|
3263
3517
|
return JSON.stringify(parsed);
|
|
3264
3518
|
}
|
|
3265
|
-
function
|
|
3266
|
-
const bundleBytes = Buffer.byteLength(bundledCode, "utf8");
|
|
3519
|
+
function getBundleSizeErrorForBytes(filePath, bundleBytes, artifactKind) {
|
|
3267
3520
|
if (bundleBytes > MAX_PLAY_BUNDLE_BYTES) {
|
|
3268
3521
|
return `${filePath} Play bundle exceeds the 30 MiB limit (${bundleBytes} bytes > ${MAX_PLAY_BUNDLE_BYTES} bytes).`;
|
|
3269
3522
|
}
|
|
@@ -3274,6 +3527,13 @@ function getBundleSizeError(filePath, bundledCode, artifactKind) {
|
|
|
3274
3527
|
}
|
|
3275
3528
|
return null;
|
|
3276
3529
|
}
|
|
3530
|
+
function getBundleSizeError(filePath, bundledCode, artifactKind) {
|
|
3531
|
+
return getBundleSizeErrorForBytes(
|
|
3532
|
+
filePath,
|
|
3533
|
+
Buffer.byteLength(bundledCode, "utf8"),
|
|
3534
|
+
artifactKind
|
|
3535
|
+
);
|
|
3536
|
+
}
|
|
3277
3537
|
async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter, exportName) {
|
|
3278
3538
|
const sdkAliasPlugin = localSdkAliasPlugin(adapter);
|
|
3279
3539
|
const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
|
|
@@ -3406,6 +3666,23 @@ entry-export:${exportName}`
|
|
|
3406
3666
|
workers-harness:${harnessFingerprint}`
|
|
3407
3667
|
);
|
|
3408
3668
|
}
|
|
3669
|
+
const typecheckErrors = [
|
|
3670
|
+
...await adapter.typecheckPlaySource?.({
|
|
3671
|
+
sourceCode: analysis.sourceCode,
|
|
3672
|
+
sourcePath: absolutePath,
|
|
3673
|
+
importedFilePaths: [
|
|
3674
|
+
...analysis.importPolicy.localFiles,
|
|
3675
|
+
...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
3676
|
+
]
|
|
3677
|
+
}) ?? []
|
|
3678
|
+
];
|
|
3679
|
+
if (typecheckErrors.length > 0) {
|
|
3680
|
+
return {
|
|
3681
|
+
success: false,
|
|
3682
|
+
filePath: absolutePath,
|
|
3683
|
+
errors: typecheckErrors
|
|
3684
|
+
};
|
|
3685
|
+
}
|
|
3409
3686
|
const cachedArtifact = await readArtifactCache(analysis.graphHash, target, adapter);
|
|
3410
3687
|
const discoveredFiles = await adapter.discoverPackagedLocalFiles(absolutePath);
|
|
3411
3688
|
if (cachedArtifact) {
|
|
@@ -3425,6 +3702,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3425
3702
|
success: true,
|
|
3426
3703
|
artifact: { ...cachedArtifact, cacheHit: true },
|
|
3427
3704
|
sourceCode: analysis.sourceCode,
|
|
3705
|
+
sourceFiles: analysis.sourceFiles,
|
|
3428
3706
|
filePath: absolutePath,
|
|
3429
3707
|
playName: analysis.playName,
|
|
3430
3708
|
packagedFiles: discoveredFiles.files,
|
|
@@ -3432,24 +3710,6 @@ workers-harness:${harnessFingerprint}`
|
|
|
3432
3710
|
importedPlayDependencies: analysis.importedPlayDependencies
|
|
3433
3711
|
};
|
|
3434
3712
|
}
|
|
3435
|
-
const typecheckErrors = [
|
|
3436
|
-
...adapter.typecheckSdkTypes === false ? [] : typecheckPlaySource(analysis, adapter),
|
|
3437
|
-
...await adapter.typecheckPlaySource?.({
|
|
3438
|
-
sourceCode: analysis.sourceCode,
|
|
3439
|
-
sourcePath: absolutePath,
|
|
3440
|
-
importedFilePaths: [
|
|
3441
|
-
...analysis.importPolicy.localFiles,
|
|
3442
|
-
...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
3443
|
-
]
|
|
3444
|
-
}) ?? []
|
|
3445
|
-
];
|
|
3446
|
-
if (typecheckErrors.length > 0) {
|
|
3447
|
-
return {
|
|
3448
|
-
success: false,
|
|
3449
|
-
filePath: absolutePath,
|
|
3450
|
-
errors: typecheckErrors
|
|
3451
|
-
};
|
|
3452
|
-
}
|
|
3453
3713
|
const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
|
|
3454
3714
|
if (Array.isArray(buildOutcome)) {
|
|
3455
3715
|
return {
|
|
@@ -3499,6 +3759,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3499
3759
|
success: true,
|
|
3500
3760
|
artifact,
|
|
3501
3761
|
sourceCode: analysis.sourceCode,
|
|
3762
|
+
sourceFiles: analysis.sourceFiles,
|
|
3502
3763
|
filePath: absolutePath,
|
|
3503
3764
|
playName: analysis.playName,
|
|
3504
3765
|
packagedFiles: discoveredFiles.files,
|
|
@@ -3580,7 +3841,6 @@ function resolveExecutionProfile(override) {
|
|
|
3580
3841
|
import { createHash as createHash2 } from "crypto";
|
|
3581
3842
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
3582
3843
|
import { basename as basename2, dirname as dirname4, extname as extname2, isAbsolute as isAbsolute2, join as join4, relative, resolve as resolve5 } from "path";
|
|
3583
|
-
import ts2 from "typescript";
|
|
3584
3844
|
var SOURCE_EXTENSIONS2 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
|
|
3585
3845
|
function sha2562(buffer) {
|
|
3586
3846
|
return createHash2("sha256").update(buffer).digest("hex");
|
|
@@ -3592,94 +3852,181 @@ function contentTypeForFile(filePath) {
|
|
|
3592
3852
|
if (extension === ".txt") return "text/plain";
|
|
3593
3853
|
return "application/octet-stream";
|
|
3594
3854
|
}
|
|
3595
|
-
function
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
return ts2.isIdentifier(target) && (target.text === "ctx" || target.text.endsWith("Ctx")) && node.expression.name.text === "csv";
|
|
3601
|
-
}
|
|
3602
|
-
function extractSourceFragment(source, node) {
|
|
3603
|
-
return source.slice(node.getStart(), node.getEnd()).trim();
|
|
3604
|
-
}
|
|
3605
|
-
function referencesInputIdentifier(node) {
|
|
3606
|
-
if (ts2.isIdentifier(node) && node.text === "input") {
|
|
3607
|
-
return true;
|
|
3608
|
-
}
|
|
3609
|
-
return node.getChildren().some((child) => referencesInputIdentifier(child));
|
|
3855
|
+
function stripCommentsToSpaces2(source) {
|
|
3856
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
|
|
3857
|
+
/(^|[^:])\/\/.*$/gm,
|
|
3858
|
+
(match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
|
|
3859
|
+
);
|
|
3610
3860
|
}
|
|
3611
|
-
function
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
return ts2.isIdentifier(node.expression) && node.expression.text === "input";
|
|
3617
|
-
}
|
|
3618
|
-
if (ts2.isIdentifier(node)) {
|
|
3619
|
-
return node.text === "input";
|
|
3620
|
-
}
|
|
3621
|
-
if (ts2.isParenthesizedExpression(node)) {
|
|
3622
|
-
return isRuntimeInputExpression(node.expression);
|
|
3861
|
+
function unquoteStringLiteral2(literal) {
|
|
3862
|
+
const trimmed = literal.trim();
|
|
3863
|
+
const quote = trimmed[0];
|
|
3864
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
3865
|
+
return null;
|
|
3623
3866
|
}
|
|
3624
|
-
|
|
3625
|
-
return
|
|
3867
|
+
try {
|
|
3868
|
+
return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
|
|
3869
|
+
} catch {
|
|
3870
|
+
return trimmed.slice(1, -1);
|
|
3626
3871
|
}
|
|
3627
|
-
|
|
3628
|
-
|
|
3872
|
+
}
|
|
3873
|
+
function splitTopLevelPlus(expression) {
|
|
3874
|
+
const parts = [];
|
|
3875
|
+
let start = 0;
|
|
3876
|
+
let depth = 0;
|
|
3877
|
+
let quote = null;
|
|
3878
|
+
let escaped = false;
|
|
3879
|
+
for (let index = 0; index < expression.length; index += 1) {
|
|
3880
|
+
const char = expression[index];
|
|
3881
|
+
if (quote) {
|
|
3882
|
+
if (escaped) {
|
|
3883
|
+
escaped = false;
|
|
3884
|
+
} else if (char === "\\") {
|
|
3885
|
+
escaped = true;
|
|
3886
|
+
} else if (char === quote) {
|
|
3887
|
+
quote = null;
|
|
3888
|
+
}
|
|
3889
|
+
continue;
|
|
3890
|
+
}
|
|
3891
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3892
|
+
quote = char;
|
|
3893
|
+
continue;
|
|
3894
|
+
}
|
|
3895
|
+
if (char === "(" || char === "[" || char === "{") depth += 1;
|
|
3896
|
+
if (char === ")" || char === "]" || char === "}") depth -= 1;
|
|
3897
|
+
if (char === "+" && depth === 0) {
|
|
3898
|
+
parts.push(expression.slice(start, index));
|
|
3899
|
+
start = index + 1;
|
|
3900
|
+
}
|
|
3629
3901
|
}
|
|
3630
|
-
|
|
3902
|
+
if (parts.length === 0) return null;
|
|
3903
|
+
parts.push(expression.slice(start));
|
|
3904
|
+
return parts;
|
|
3631
3905
|
}
|
|
3632
|
-
function
|
|
3633
|
-
|
|
3634
|
-
|
|
3906
|
+
function stripOuterParens(expression) {
|
|
3907
|
+
let value = expression.trim();
|
|
3908
|
+
while (value.startsWith("(") && value.endsWith(")")) {
|
|
3909
|
+
value = value.slice(1, -1).trim();
|
|
3635
3910
|
}
|
|
3636
|
-
|
|
3637
|
-
|
|
3911
|
+
return value;
|
|
3912
|
+
}
|
|
3913
|
+
function isRuntimeInputExpression(expression) {
|
|
3914
|
+
return /(^|[^\w$])input([^\w$]|$)/.test(expression);
|
|
3915
|
+
}
|
|
3916
|
+
function resolveStringExpression(expression, constants) {
|
|
3917
|
+
const value = stripOuterParens(expression);
|
|
3918
|
+
if (/^(['"])(?:\\.|(?!\1)[\s\S])*\1$/.test(value)) {
|
|
3919
|
+
return unquoteStringLiteral2(value);
|
|
3638
3920
|
}
|
|
3639
|
-
if (
|
|
3640
|
-
return
|
|
3921
|
+
if (/^`(?:\\.|[^`$]|\$(?!\{))*`$/.test(value)) {
|
|
3922
|
+
return value.slice(1, -1);
|
|
3641
3923
|
}
|
|
3642
|
-
if (
|
|
3643
|
-
|
|
3644
|
-
for (const span of node.templateSpans) {
|
|
3645
|
-
const resolved = resolveStringExpression(span.expression, constants);
|
|
3646
|
-
if (resolved == null) {
|
|
3647
|
-
return null;
|
|
3648
|
-
}
|
|
3649
|
-
value += resolved + span.literal.text;
|
|
3650
|
-
}
|
|
3651
|
-
return value;
|
|
3924
|
+
if (/^[A-Za-z_$][\w$]*$/.test(value)) {
|
|
3925
|
+
return constants.get(value) ?? null;
|
|
3652
3926
|
}
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
const
|
|
3656
|
-
|
|
3657
|
-
return null;
|
|
3658
|
-
}
|
|
3659
|
-
return left + right;
|
|
3927
|
+
const parts = splitTopLevelPlus(value);
|
|
3928
|
+
if (parts) {
|
|
3929
|
+
const resolved = parts.map((part) => resolveStringExpression(part, constants));
|
|
3930
|
+
return resolved.every((part) => part != null) ? resolved.join("") : null;
|
|
3660
3931
|
}
|
|
3661
3932
|
return null;
|
|
3662
3933
|
}
|
|
3663
|
-
function collectTopLevelStringConstants(
|
|
3934
|
+
function collectTopLevelStringConstants(sourceCode) {
|
|
3664
3935
|
const constants = /* @__PURE__ */ new Map();
|
|
3665
|
-
|
|
3666
|
-
|
|
3936
|
+
const source = stripCommentsToSpaces2(sourceCode);
|
|
3937
|
+
for (const match of source.matchAll(/(?:^|\n)\s*const\s+([A-Za-z_$][\w$]*)\s*=\s*([^;\n]+)/g)) {
|
|
3938
|
+
const resolved = resolveStringExpression(match[2], constants);
|
|
3939
|
+
if (resolved != null) {
|
|
3940
|
+
constants.set(match[1], resolved);
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
return constants;
|
|
3944
|
+
}
|
|
3945
|
+
function findMatchingGenericEnd(source, openIndex) {
|
|
3946
|
+
let depth = 0;
|
|
3947
|
+
let quote = null;
|
|
3948
|
+
let escaped = false;
|
|
3949
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
3950
|
+
const char = source[index];
|
|
3951
|
+
if (quote) {
|
|
3952
|
+
if (escaped) {
|
|
3953
|
+
escaped = false;
|
|
3954
|
+
} else if (char === "\\") {
|
|
3955
|
+
escaped = true;
|
|
3956
|
+
} else if (char === quote) {
|
|
3957
|
+
quote = null;
|
|
3958
|
+
}
|
|
3667
3959
|
continue;
|
|
3668
3960
|
}
|
|
3669
|
-
if (
|
|
3961
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3962
|
+
quote = char;
|
|
3670
3963
|
continue;
|
|
3671
3964
|
}
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3965
|
+
if (char === "<") depth += 1;
|
|
3966
|
+
if (char === ">") {
|
|
3967
|
+
depth -= 1;
|
|
3968
|
+
if (depth === 0) return index;
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
return -1;
|
|
3972
|
+
}
|
|
3973
|
+
function findCallOpenParen(source, afterCsvIndex) {
|
|
3974
|
+
let index = afterCsvIndex;
|
|
3975
|
+
while (/\s/.test(source[index] ?? "")) index += 1;
|
|
3976
|
+
if (source[index] === "<") {
|
|
3977
|
+
const genericEnd = findMatchingGenericEnd(source, index);
|
|
3978
|
+
if (genericEnd < 0) return -1;
|
|
3979
|
+
index = genericEnd + 1;
|
|
3980
|
+
while (/\s/.test(source[index] ?? "")) index += 1;
|
|
3981
|
+
}
|
|
3982
|
+
return source[index] === "(" ? index : -1;
|
|
3983
|
+
}
|
|
3984
|
+
function firstCallArgument(source, openParen) {
|
|
3985
|
+
let depth = 0;
|
|
3986
|
+
let quote = null;
|
|
3987
|
+
let escaped = false;
|
|
3988
|
+
const start = openParen + 1;
|
|
3989
|
+
for (let index = start; index < source.length; index += 1) {
|
|
3990
|
+
const char = source[index];
|
|
3991
|
+
if (quote) {
|
|
3992
|
+
if (escaped) {
|
|
3993
|
+
escaped = false;
|
|
3994
|
+
} else if (char === "\\") {
|
|
3995
|
+
escaped = true;
|
|
3996
|
+
} else if (char === quote) {
|
|
3997
|
+
quote = null;
|
|
3679
3998
|
}
|
|
3999
|
+
continue;
|
|
4000
|
+
}
|
|
4001
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
4002
|
+
quote = char;
|
|
4003
|
+
continue;
|
|
4004
|
+
}
|
|
4005
|
+
if (char === "(" || char === "[" || char === "{") depth += 1;
|
|
4006
|
+
if (char === ")" && depth === 0) {
|
|
4007
|
+
const text = source.slice(start, index).trim();
|
|
4008
|
+
return text ? { text, start, end: index } : null;
|
|
3680
4009
|
}
|
|
4010
|
+
if (char === "," && depth === 0) {
|
|
4011
|
+
const text = source.slice(start, index).trim();
|
|
4012
|
+
return text ? { text, start, end: index } : null;
|
|
4013
|
+
}
|
|
4014
|
+
if (char === ")" || char === "]" || char === "}") depth -= 1;
|
|
3681
4015
|
}
|
|
3682
|
-
return
|
|
4016
|
+
return null;
|
|
4017
|
+
}
|
|
4018
|
+
function localImportSpecifiers(sourceCode) {
|
|
4019
|
+
const source = stripCommentsToSpaces2(sourceCode);
|
|
4020
|
+
const specifiers = [];
|
|
4021
|
+
for (const match of source.matchAll(
|
|
4022
|
+
/\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?['"]([^'"]+)['"]/g
|
|
4023
|
+
)) {
|
|
4024
|
+
if (match[1]?.startsWith(".")) specifiers.push(match[1]);
|
|
4025
|
+
}
|
|
4026
|
+
for (const match of source.matchAll(/\brequire\s*\(\s*(['"])(\.[^'"]*)\1\s*\)/g)) {
|
|
4027
|
+
specifiers.push(match[2]);
|
|
4028
|
+
}
|
|
4029
|
+
return specifiers;
|
|
3683
4030
|
}
|
|
3684
4031
|
async function fileExists2(filePath) {
|
|
3685
4032
|
try {
|
|
@@ -3724,69 +4071,60 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
3724
4071
|
}
|
|
3725
4072
|
visitedFiles.add(absolutePath);
|
|
3726
4073
|
const sourceCode = await readFile2(absolutePath, "utf-8");
|
|
3727
|
-
const
|
|
3728
|
-
|
|
3729
|
-
sourceCode,
|
|
3730
|
-
ts2.ScriptTarget.Latest,
|
|
3731
|
-
true,
|
|
3732
|
-
ts2.ScriptKind.TS
|
|
3733
|
-
);
|
|
3734
|
-
const constants = collectTopLevelStringConstants(sourceFile);
|
|
4074
|
+
const scanSource = stripCommentsToSpaces2(sourceCode);
|
|
4075
|
+
const constants = collectTopLevelStringConstants(sourceCode);
|
|
3735
4076
|
const childVisits = [];
|
|
3736
|
-
const
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
4077
|
+
for (const match of scanSource.matchAll(/\b([A-Za-z_$][\w$]*)\s*\.\s*csv\b/g)) {
|
|
4078
|
+
const target = match[1];
|
|
4079
|
+
if (target !== "ctx" && !target.endsWith("Ctx")) {
|
|
4080
|
+
continue;
|
|
4081
|
+
}
|
|
4082
|
+
const openParen = findCallOpenParen(scanSource, match.index + match[0].length);
|
|
4083
|
+
if (openParen < 0) {
|
|
4084
|
+
continue;
|
|
4085
|
+
}
|
|
4086
|
+
const argument = firstCallArgument(scanSource, openParen);
|
|
4087
|
+
if (!argument) {
|
|
4088
|
+
unresolved.push({
|
|
4089
|
+
sourceFragment: "ctx.csv()",
|
|
4090
|
+
message: "ctx.csv() requires a file path string or input reference."
|
|
4091
|
+
});
|
|
4092
|
+
} else if (!isRuntimeInputExpression(argument.text)) {
|
|
4093
|
+
const resolvedPath = resolveStringExpression(argument.text, constants);
|
|
4094
|
+
if (resolvedPath == null) {
|
|
3740
4095
|
unresolved.push({
|
|
3741
|
-
sourceFragment:
|
|
3742
|
-
message: "ctx.csv()
|
|
4096
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4097
|
+
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."
|
|
3743
4098
|
});
|
|
3744
|
-
} else
|
|
3745
|
-
const
|
|
3746
|
-
if (resolvedPath
|
|
4099
|
+
} else {
|
|
4100
|
+
const absoluteCsvPath = resolve5(dirname4(absolutePath), resolvedPath);
|
|
4101
|
+
if (isAbsolute2(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
|
|
3747
4102
|
unresolved.push({
|
|
3748
|
-
sourceFragment:
|
|
3749
|
-
message: "
|
|
3750
|
-
});
|
|
3751
|
-
} else {
|
|
3752
|
-
const absoluteCsvPath = resolve5(dirname4(absolutePath), resolvedPath);
|
|
3753
|
-
if (isAbsolute2(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
|
|
3754
|
-
unresolved.push({
|
|
3755
|
-
sourceFragment: extractSourceFragment(sourceCode, argument),
|
|
3756
|
-
message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
|
|
3757
|
-
});
|
|
3758
|
-
return;
|
|
3759
|
-
}
|
|
3760
|
-
const buffer = await readFile2(absoluteCsvPath);
|
|
3761
|
-
const stats = await stat2(absoluteCsvPath);
|
|
3762
|
-
files.set(absoluteCsvPath, {
|
|
3763
|
-
sourceFragment: extractSourceFragment(sourceCode, argument),
|
|
3764
|
-
logicalPath: resolvedPath,
|
|
3765
|
-
absolutePath: absoluteCsvPath,
|
|
3766
|
-
bytes: stats.size,
|
|
3767
|
-
contentHash: sha2562(buffer),
|
|
3768
|
-
contentType: contentTypeForFile(absoluteCsvPath)
|
|
4103
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4104
|
+
message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
|
|
3769
4105
|
});
|
|
4106
|
+
continue;
|
|
3770
4107
|
}
|
|
4108
|
+
const buffer = await readFile2(absoluteCsvPath);
|
|
4109
|
+
const stats = await stat2(absoluteCsvPath);
|
|
4110
|
+
files.set(absoluteCsvPath, {
|
|
4111
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4112
|
+
logicalPath: resolvedPath,
|
|
4113
|
+
absolutePath: absoluteCsvPath,
|
|
4114
|
+
bytes: stats.size,
|
|
4115
|
+
contentHash: sha2562(buffer),
|
|
4116
|
+
contentType: contentTypeForFile(absoluteCsvPath)
|
|
4117
|
+
});
|
|
3771
4118
|
}
|
|
3772
4119
|
}
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
)
|
|
3778
|
-
)
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
childVisits.push(
|
|
3782
|
-
resolveLocalImport2(absolutePath, node.arguments[0].text).then(
|
|
3783
|
-
(resolvedImport) => visitSourceFile(resolvedImport)
|
|
3784
|
-
)
|
|
3785
|
-
);
|
|
3786
|
-
}
|
|
3787
|
-
await Promise.all(node.getChildren(sourceFile).map((child) => visitNode(child)));
|
|
3788
|
-
};
|
|
3789
|
-
await visitNode(sourceFile);
|
|
4120
|
+
}
|
|
4121
|
+
for (const specifier of localImportSpecifiers(sourceCode)) {
|
|
4122
|
+
childVisits.push(
|
|
4123
|
+
resolveLocalImport2(absolutePath, specifier).then(
|
|
4124
|
+
(resolvedImport) => visitSourceFile(resolvedImport)
|
|
4125
|
+
)
|
|
4126
|
+
);
|
|
4127
|
+
}
|
|
3790
4128
|
await Promise.all(childVisits);
|
|
3791
4129
|
};
|
|
3792
4130
|
await visitSourceFile(absoluteEntryFile);
|
|
@@ -3797,7 +4135,7 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
3797
4135
|
}
|
|
3798
4136
|
|
|
3799
4137
|
// src/plays/bundle-play-file.ts
|
|
3800
|
-
var PLAY_BUNDLE_CACHE_VERSION2 =
|
|
4138
|
+
var PLAY_BUNDLE_CACHE_VERSION2 = 26;
|
|
3801
4139
|
var MODULE_DIR = dirname5(fileURLToPath(import.meta.url));
|
|
3802
4140
|
var SDK_PACKAGE_ROOT = resolve6(MODULE_DIR, "..", "..");
|
|
3803
4141
|
var SOURCE_REPO_ROOT = resolve6(SDK_PACKAGE_ROOT, "..");
|
|
@@ -3812,7 +4150,7 @@ var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED
|
|
|
3812
4150
|
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");
|
|
3813
4151
|
var SDK_PACKAGE_JSON = resolve6(SDK_PACKAGE_ROOT, "package.json");
|
|
3814
4152
|
var SDK_ENTRY_FILE = resolve6(SDK_SOURCE_ROOT, "index.ts");
|
|
3815
|
-
var SDK_TYPES_ENTRY_FILE = resolve6(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
4153
|
+
var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : resolve6(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
3816
4154
|
var SDK_WORKERS_ENTRY_FILE = resolve6(SDK_SOURCE_ROOT, "worker-play-entry.ts");
|
|
3817
4155
|
var WORKERS_HARNESS_ENTRY_FILE = resolve6(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
|
|
3818
4156
|
var WORKERS_HARNESS_FILES_DIR = resolve6(PROJECT_ROOT, "apps", "play-runner-workers", "src");
|
|
@@ -3844,7 +4182,7 @@ function createSdkPlayBundlingAdapter() {
|
|
|
3844
4182
|
sdkSourceRoot: SDK_SOURCE_ROOT,
|
|
3845
4183
|
sdkPackageJson: SDK_PACKAGE_JSON,
|
|
3846
4184
|
sdkEntryFile: SDK_ENTRY_FILE,
|
|
3847
|
-
sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES
|
|
4185
|
+
sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES || !existsSync3(SDK_TYPES_ENTRY_FILE) ? SDK_ENTRY_FILE : SDK_TYPES_ENTRY_FILE,
|
|
3848
4186
|
sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
|
|
3849
4187
|
workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
|
|
3850
4188
|
workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
|
|
@@ -4081,13 +4419,15 @@ function formatLoadedPlayMessage(materializedFile) {
|
|
|
4081
4419
|
return `Loaded play here: ${materializedFile.path}`;
|
|
4082
4420
|
}
|
|
4083
4421
|
function buildReadonlyPrebuiltPlayError(reference) {
|
|
4422
|
+
const localName = reference.split("/").slice(1).join("/") || "custom-play";
|
|
4084
4423
|
return new Error(
|
|
4085
4424
|
`Cannot edit or push ${reference} because Deepline prebuilt plays are read-only.
|
|
4086
4425
|
To make your own version:
|
|
4087
|
-
1.
|
|
4088
|
-
2. Change definePlay('${
|
|
4089
|
-
3. Run: deepline plays
|
|
4090
|
-
4.
|
|
4426
|
+
1. Run: deepline plays get ${reference} --source --out ./${localName}.play.ts
|
|
4427
|
+
2. Change definePlay('${localName}', ...) to a new play name you own.
|
|
4428
|
+
3. Run: deepline plays check ./${localName}.play.ts
|
|
4429
|
+
4. Run: deepline plays publish ./${localName}.play.ts
|
|
4430
|
+
5. Your play will then live under your workspace namespace.`
|
|
4091
4431
|
);
|
|
4092
4432
|
}
|
|
4093
4433
|
async function ensureEditableRemotePlay(client, target) {
|
|
@@ -4228,6 +4568,15 @@ function fileInputBindingsFromStaticPipeline(staticPipeline) {
|
|
|
4228
4568
|
);
|
|
4229
4569
|
return inputField ? [{ inputPath: inputField }] : [];
|
|
4230
4570
|
}
|
|
4571
|
+
function applyCsvShortcutInput(input) {
|
|
4572
|
+
const csvValue = getDottedInputValue(input.runtimeInput, "csv");
|
|
4573
|
+
if (csvValue == null || csvValue === "") return;
|
|
4574
|
+
const candidate = input.bindings.find((binding) => binding.inputPath !== "csv")?.inputPath ?? input.fallbackInputPath ?? null;
|
|
4575
|
+
if (!candidate || candidate === "csv") return;
|
|
4576
|
+
const existing = getDottedInputValue(input.runtimeInput, candidate);
|
|
4577
|
+
if (existing != null && existing !== "") return;
|
|
4578
|
+
setDottedInputValue(input.runtimeInput, candidate, csvValue);
|
|
4579
|
+
}
|
|
4231
4580
|
function isLocalFilePathValue(value) {
|
|
4232
4581
|
if (typeof value !== "string" || !value.trim()) return false;
|
|
4233
4582
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
@@ -4351,6 +4700,7 @@ async function compileBundledPlayGraphManifests(client, graph) {
|
|
|
4351
4700
|
node.compilerManifest = await client.compilePlayManifest({
|
|
4352
4701
|
name,
|
|
4353
4702
|
sourceCode: node.sourceCode,
|
|
4703
|
+
sourceFiles: node.sourceFiles,
|
|
4354
4704
|
artifact: node.artifact,
|
|
4355
4705
|
importedPlayDependencies: node.importedPlayDependencies.map(
|
|
4356
4706
|
(dependency) => {
|
|
@@ -4400,6 +4750,7 @@ async function publishImportedPlayDependencies(client, graph) {
|
|
|
4400
4750
|
await client.registerPlayArtifact({
|
|
4401
4751
|
name: node.playName,
|
|
4402
4752
|
sourceCode: node.sourceCode,
|
|
4753
|
+
sourceFiles: node.sourceFiles,
|
|
4403
4754
|
artifact: node.artifact,
|
|
4404
4755
|
compilerManifest: requireCompilerManifest(node),
|
|
4405
4756
|
publish: true
|
|
@@ -4417,67 +4768,6 @@ function formatTimestamp(value) {
|
|
|
4417
4768
|
function formatRunLine(run) {
|
|
4418
4769
|
return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)}`;
|
|
4419
4770
|
}
|
|
4420
|
-
function parsePlayRunTarget(input) {
|
|
4421
|
-
const { args, usage } = input;
|
|
4422
|
-
let runId = null;
|
|
4423
|
-
let playName = null;
|
|
4424
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
4425
|
-
const arg = args[index];
|
|
4426
|
-
if (arg === "--json") {
|
|
4427
|
-
continue;
|
|
4428
|
-
}
|
|
4429
|
-
if (arg === "--reason" || arg === "--interval-ms" || arg === "--poll-interval-ms") {
|
|
4430
|
-
index += 1;
|
|
4431
|
-
continue;
|
|
4432
|
-
}
|
|
4433
|
-
if (arg === "--run-id" && args[index + 1]) {
|
|
4434
|
-
runId = args[++index].trim();
|
|
4435
|
-
continue;
|
|
4436
|
-
}
|
|
4437
|
-
if (arg === "--name" && args[index + 1] && input.allowName) {
|
|
4438
|
-
playName = parseReferencedPlayTarget(args[++index]).playName;
|
|
4439
|
-
continue;
|
|
4440
|
-
}
|
|
4441
|
-
if (arg.startsWith("--")) {
|
|
4442
|
-
continue;
|
|
4443
|
-
}
|
|
4444
|
-
throw new DeeplineError(
|
|
4445
|
-
`Unexpected positional target "${arg}". Use --run-id for run ids.
|
|
4446
|
-
${usage}`
|
|
4447
|
-
);
|
|
4448
|
-
}
|
|
4449
|
-
const explicitTargets = [runId, playName].filter(Boolean).length;
|
|
4450
|
-
if (explicitTargets > 1) {
|
|
4451
|
-
throw new DeeplineError(`Choose exactly one play run target.
|
|
4452
|
-
${usage}`);
|
|
4453
|
-
}
|
|
4454
|
-
if (runId) {
|
|
4455
|
-
return { kind: "run", runId };
|
|
4456
|
-
}
|
|
4457
|
-
if (playName) {
|
|
4458
|
-
return { kind: "name", name: playName };
|
|
4459
|
-
}
|
|
4460
|
-
throw new DeeplineError(usage);
|
|
4461
|
-
}
|
|
4462
|
-
async function resolvePlayRunId(client, target) {
|
|
4463
|
-
if (target.kind === "run") {
|
|
4464
|
-
try {
|
|
4465
|
-
const status = await client.getPlayStatus(target.runId);
|
|
4466
|
-
return status.runId;
|
|
4467
|
-
} catch (error) {
|
|
4468
|
-
if (!(error instanceof DeeplineError) || error.statusCode !== 404) {
|
|
4469
|
-
throw error;
|
|
4470
|
-
}
|
|
4471
|
-
throw new DeeplineError(`No play run found for run id: ${target.runId}`);
|
|
4472
|
-
}
|
|
4473
|
-
}
|
|
4474
|
-
const runs = await client.listPlayRuns(target.name);
|
|
4475
|
-
const workflowId = runs[0]?.workflowId ?? "";
|
|
4476
|
-
if (!workflowId) {
|
|
4477
|
-
throw new DeeplineError(`No runs found for play: ${target.name}`);
|
|
4478
|
-
}
|
|
4479
|
-
return workflowId;
|
|
4480
|
-
}
|
|
4481
4771
|
function isTransientPlayStatusPollError(error) {
|
|
4482
4772
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
4483
4773
|
return error.statusCode >= 500 && error.statusCode < 600;
|
|
@@ -4581,7 +4871,7 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
4581
4871
|
if (input.waitTimeoutMs !== null && Date.now() - input.startedAt >= input.waitTimeoutMs) {
|
|
4582
4872
|
const hasRealRunId = input.workflowId.length > 0 && input.workflowId !== "pending";
|
|
4583
4873
|
const phaseSuffix = input.lastPhase && input.lastPhase.trim() ? ` (last observed phase: ${input.lastPhase.trim()})` : "";
|
|
4584
|
-
const tailHint = hasRealRunId ? ` Run 'deepline
|
|
4874
|
+
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.`;
|
|
4585
4875
|
throw new DeeplineError(
|
|
4586
4876
|
`Timed out waiting for play ${hasRealRunId ? input.workflowId : "<no run id>"} after ${Math.ceil(input.waitTimeoutMs / 1e3)}s${phaseSuffix}.${tailHint}`,
|
|
4587
4877
|
void 0,
|
|
@@ -4594,66 +4884,6 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
4594
4884
|
);
|
|
4595
4885
|
}
|
|
4596
4886
|
}
|
|
4597
|
-
async function waitForPlayCompletionByStream(input) {
|
|
4598
|
-
const controller = new AbortController();
|
|
4599
|
-
let timedOut = false;
|
|
4600
|
-
let lastPhase = null;
|
|
4601
|
-
const timeout = input.waitTimeoutMs === null ? null : setTimeout(
|
|
4602
|
-
() => {
|
|
4603
|
-
timedOut = true;
|
|
4604
|
-
controller.abort();
|
|
4605
|
-
},
|
|
4606
|
-
Math.max(1, input.waitTimeoutMs - (Date.now() - input.startedAt))
|
|
4607
|
-
);
|
|
4608
|
-
try {
|
|
4609
|
-
for await (const event of input.client.streamPlayRunEvents(
|
|
4610
|
-
input.workflowId,
|
|
4611
|
-
{ signal: controller.signal }
|
|
4612
|
-
)) {
|
|
4613
|
-
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
4614
|
-
const phase = describeLiveEventPhase(event);
|
|
4615
|
-
if (phase) {
|
|
4616
|
-
lastPhase = phase;
|
|
4617
|
-
input.progress.phase(phase);
|
|
4618
|
-
}
|
|
4619
|
-
printPlayLogLines({
|
|
4620
|
-
lines: getLogLinesFromLiveEvent(event),
|
|
4621
|
-
status: null,
|
|
4622
|
-
jsonOutput: input.jsonOutput,
|
|
4623
|
-
emitLogs: input.emitLogs,
|
|
4624
|
-
state: input.state,
|
|
4625
|
-
progress: input.progress
|
|
4626
|
-
});
|
|
4627
|
-
const status = getStatusFromLiveEvent(event);
|
|
4628
|
-
if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
|
|
4629
|
-
const finalStatus = await input.client.getPlayStatus(input.workflowId);
|
|
4630
|
-
if (TERMINAL_PLAY_STATUSES2.has(finalStatus.status)) {
|
|
4631
|
-
return finalStatus;
|
|
4632
|
-
}
|
|
4633
|
-
}
|
|
4634
|
-
}
|
|
4635
|
-
} catch (error) {
|
|
4636
|
-
if (timedOut) {
|
|
4637
|
-
assertPlayWaitNotTimedOut({ ...input, lastPhase });
|
|
4638
|
-
}
|
|
4639
|
-
throw error;
|
|
4640
|
-
} finally {
|
|
4641
|
-
if (timeout) {
|
|
4642
|
-
clearTimeout(timeout);
|
|
4643
|
-
}
|
|
4644
|
-
}
|
|
4645
|
-
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
4646
|
-
throw new DeeplineError(
|
|
4647
|
-
`Play live stream ended before the run reached a terminal state runId=${input.workflowId}${phaseSuffix}.`,
|
|
4648
|
-
void 0,
|
|
4649
|
-
"PLAY_LIVE_STREAM_ENDED",
|
|
4650
|
-
{
|
|
4651
|
-
runId: input.workflowId,
|
|
4652
|
-
workflowId: input.workflowId,
|
|
4653
|
-
...lastPhase ? { phase: lastPhase } : {}
|
|
4654
|
-
}
|
|
4655
|
-
);
|
|
4656
|
-
}
|
|
4657
4887
|
async function startAndWaitForPlayCompletionByStream(input) {
|
|
4658
4888
|
const startedAt = Date.now();
|
|
4659
4889
|
const state = {
|
|
@@ -4733,10 +4963,12 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4733
4963
|
clearTimeout(timeout);
|
|
4734
4964
|
}
|
|
4735
4965
|
const reason = error instanceof Error ? error.message : String(error);
|
|
4736
|
-
|
|
4737
|
-
|
|
4966
|
+
if (!input.jsonOutput) {
|
|
4967
|
+
process.stderr.write(
|
|
4968
|
+
`[play watch] start stream failed after run ${lastKnownWorkflowId}; falling back to polling (${reason})
|
|
4738
4969
|
`
|
|
4739
|
-
|
|
4970
|
+
);
|
|
4971
|
+
}
|
|
4740
4972
|
return waitForPlayCompletionByPolling({
|
|
4741
4973
|
client: input.client,
|
|
4742
4974
|
workflowId: lastKnownWorkflowId,
|
|
@@ -4755,6 +4987,24 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4755
4987
|
clearTimeout(timeout);
|
|
4756
4988
|
}
|
|
4757
4989
|
}
|
|
4990
|
+
if (lastKnownWorkflowId) {
|
|
4991
|
+
if (!input.jsonOutput) {
|
|
4992
|
+
input.progress.writeLine(
|
|
4993
|
+
`[play watch] start stream ended after run ${lastKnownWorkflowId}; falling back to polling`
|
|
4994
|
+
);
|
|
4995
|
+
}
|
|
4996
|
+
return waitForPlayCompletionByPolling({
|
|
4997
|
+
client: input.client,
|
|
4998
|
+
workflowId: lastKnownWorkflowId,
|
|
4999
|
+
pollIntervalMs: 500,
|
|
5000
|
+
jsonOutput: input.jsonOutput,
|
|
5001
|
+
emitLogs: input.emitLogs,
|
|
5002
|
+
waitTimeoutMs: input.waitTimeoutMs,
|
|
5003
|
+
startedAt,
|
|
5004
|
+
state,
|
|
5005
|
+
progress: input.progress
|
|
5006
|
+
});
|
|
5007
|
+
}
|
|
4758
5008
|
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
4759
5009
|
const idSuffix = lastKnownWorkflowId ? ` runId=${lastKnownWorkflowId}` : "";
|
|
4760
5010
|
throw new DeeplineError(
|
|
@@ -4832,38 +5082,6 @@ async function waitForPlayCompletionByPolling(input) {
|
|
|
4832
5082
|
}
|
|
4833
5083
|
}
|
|
4834
5084
|
}
|
|
4835
|
-
async function waitForPlayCompletion(input) {
|
|
4836
|
-
const startedAt = Date.now();
|
|
4837
|
-
const state = {
|
|
4838
|
-
lastLogIndex: 0,
|
|
4839
|
-
emittedRunnerStarted: false
|
|
4840
|
-
};
|
|
4841
|
-
try {
|
|
4842
|
-
return await waitForPlayCompletionByStream({
|
|
4843
|
-
...input,
|
|
4844
|
-
startedAt,
|
|
4845
|
-
state,
|
|
4846
|
-
progress: input.progress
|
|
4847
|
-
});
|
|
4848
|
-
} catch (error) {
|
|
4849
|
-
assertPlayWaitNotTimedOut({
|
|
4850
|
-
workflowId: input.workflowId,
|
|
4851
|
-
startedAt,
|
|
4852
|
-
waitTimeoutMs: input.waitTimeoutMs
|
|
4853
|
-
});
|
|
4854
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
4855
|
-
process.stderr.write(
|
|
4856
|
-
`[play watch] SSE stream failed; falling back to polling (${reason})
|
|
4857
|
-
`
|
|
4858
|
-
);
|
|
4859
|
-
return waitForPlayCompletionByPolling({
|
|
4860
|
-
...input,
|
|
4861
|
-
startedAt,
|
|
4862
|
-
state,
|
|
4863
|
-
progress: input.progress
|
|
4864
|
-
});
|
|
4865
|
-
}
|
|
4866
|
-
}
|
|
4867
5085
|
function formatInteger(value) {
|
|
4868
5086
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
|
|
4869
5087
|
}
|
|
@@ -4970,14 +5188,20 @@ function formatReturnValue(result) {
|
|
|
4970
5188
|
}
|
|
4971
5189
|
return lines;
|
|
4972
5190
|
}
|
|
4973
|
-
function buildOutputSummary(rowsInfo, exportedPath) {
|
|
5191
|
+
function buildOutputSummary(rowsInfo, runId, exportedPath) {
|
|
4974
5192
|
if (!rowsInfo) {
|
|
4975
5193
|
return exportedPath ? { csv_path: exportedPath } : null;
|
|
4976
5194
|
}
|
|
5195
|
+
const isPartial = !rowsInfo.complete;
|
|
4977
5196
|
return {
|
|
4978
5197
|
kind: "rows",
|
|
4979
5198
|
rowCount: rowsInfo.totalRows,
|
|
4980
5199
|
previewRowCount: rowsInfo.rows.length,
|
|
5200
|
+
...isPartial ? {
|
|
5201
|
+
isPartial: true,
|
|
5202
|
+
previewCount: rowsInfo.rows.length,
|
|
5203
|
+
totalCount: rowsInfo.totalRows
|
|
5204
|
+
} : { isPartial: false },
|
|
4981
5205
|
complete: rowsInfo.complete,
|
|
4982
5206
|
columns: rowsInfo.columns,
|
|
4983
5207
|
source: rowsInfo.source,
|
|
@@ -4988,21 +5212,174 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
4988
5212
|
if (status.status === "completed" && rowsInfo?.totalRows === 0) {
|
|
4989
5213
|
return ["Run completed with 0 output rows."];
|
|
4990
5214
|
}
|
|
5215
|
+
if (rowsInfo && !rowsInfo.complete) {
|
|
5216
|
+
return [
|
|
5217
|
+
`Run output is partial: showing ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}.`
|
|
5218
|
+
];
|
|
5219
|
+
}
|
|
5220
|
+
return [];
|
|
5221
|
+
}
|
|
5222
|
+
function buildRunNextCommands(runId, rowsInfo) {
|
|
5223
|
+
const commands = {
|
|
5224
|
+
get: `deepline runs get ${runId} --json`,
|
|
5225
|
+
tail: `deepline runs tail ${runId} --json`,
|
|
5226
|
+
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
5227
|
+
logs: `deepline runs logs ${runId} --out run.log --json`
|
|
5228
|
+
};
|
|
5229
|
+
if (!rowsInfo || rowsInfo.complete) {
|
|
5230
|
+
commands.exportCsv = buildRunExportCommand(runId);
|
|
5231
|
+
}
|
|
5232
|
+
return commands;
|
|
5233
|
+
}
|
|
5234
|
+
function buildRunExportCommand(runId) {
|
|
5235
|
+
return `deepline runs export ${runId} --out output.csv`;
|
|
5236
|
+
}
|
|
5237
|
+
var RUN_LOG_PREVIEW_LIMIT = 20;
|
|
5238
|
+
function getRecordField(value, key) {
|
|
5239
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value[key] : void 0;
|
|
5240
|
+
}
|
|
5241
|
+
function getNumericField(value, key) {
|
|
5242
|
+
const field = getRecordField(value, key);
|
|
5243
|
+
return typeof field === "number" && Number.isFinite(field) ? field : null;
|
|
5244
|
+
}
|
|
5245
|
+
function getStringField(value, key) {
|
|
5246
|
+
const field = getRecordField(value, key);
|
|
5247
|
+
return typeof field === "string" && field.trim() ? field : null;
|
|
5248
|
+
}
|
|
5249
|
+
function normalizeRunStatusForEnvelope(status) {
|
|
5250
|
+
const run = status.run ?? null;
|
|
5251
|
+
return {
|
|
5252
|
+
id: status.runId,
|
|
5253
|
+
playName: status.playName ?? status.name ?? getStringField(run, "playName") ?? null,
|
|
5254
|
+
status: status.status,
|
|
5255
|
+
runtime: getStringField(status, "runtime") ?? getStringField(status, "runtimeBackend") ?? getStringField(run, "runtime") ?? null,
|
|
5256
|
+
startedAt: getStringField(run, "startTime") ?? getStringField(run, "startedAt") ?? null,
|
|
5257
|
+
updatedAt: getStringField(status, "updatedAt") ?? getStringField(run, "updatedAt") ?? null,
|
|
5258
|
+
finishedAt: getStringField(run, "closeTime") ?? getStringField(run, "finishedAt") ?? null,
|
|
5259
|
+
source: getRecordField(status, "source") ?? getRecordField(status, "artifact") ?? null
|
|
5260
|
+
};
|
|
5261
|
+
}
|
|
5262
|
+
function normalizeProgressForEnvelope(status, rowsInfo) {
|
|
5263
|
+
const progress = status.progress;
|
|
5264
|
+
const total = getNumericField(progress, "totalRows") ?? getNumericField(progress, "total") ?? rowsInfo?.totalRows ?? null;
|
|
5265
|
+
const failed = getNumericField(progress, "failed") ?? getNumericField(progress, "failedRows") ?? null;
|
|
5266
|
+
const completed = getNumericField(progress, "completed") ?? getNumericField(progress, "completedRows") ?? (status.status === "completed" ? total : null);
|
|
5267
|
+
const pending = getNumericField(progress, "pending") ?? (typeof total === "number" && typeof completed === "number" && typeof failed === "number" ? Math.max(0, total - completed - failed) : null);
|
|
5268
|
+
return {
|
|
5269
|
+
total,
|
|
5270
|
+
completed,
|
|
5271
|
+
pending,
|
|
5272
|
+
failed,
|
|
5273
|
+
executed: getNumericField(progress, "executed"),
|
|
5274
|
+
reused: getNumericField(progress, "reused"),
|
|
5275
|
+
skipped: getNumericField(progress, "skipped"),
|
|
5276
|
+
retried: getNumericField(progress, "retried"),
|
|
5277
|
+
degraded: typeof getRecordField(progress, "degraded") === "boolean" ? getRecordField(progress, "degraded") : null,
|
|
5278
|
+
duplicates: getRecordField(progress, "duplicates") ?? null,
|
|
5279
|
+
active: getStringField(progress, "status") ?? getStringField(status, "activeStep") ?? getStringField(status, "activeNodeId") ?? null,
|
|
5280
|
+
wait: status.wait ?? null
|
|
5281
|
+
};
|
|
5282
|
+
}
|
|
5283
|
+
function normalizeOutputsForEnvelope(rowsInfo, runId, exportedPath) {
|
|
5284
|
+
if (!rowsInfo) {
|
|
5285
|
+
return exportedPath ? [{ name: "output", kind: "file", path: exportedPath }] : [];
|
|
5286
|
+
}
|
|
5287
|
+
const isPartial = !rowsInfo.complete;
|
|
5288
|
+
return [
|
|
5289
|
+
{
|
|
5290
|
+
name: "rows",
|
|
5291
|
+
kind: "dataset",
|
|
5292
|
+
rowCount: rowsInfo.totalRows,
|
|
5293
|
+
columns: rowsInfo.columns,
|
|
5294
|
+
preview: rowsInfo.rows.slice(0, 5),
|
|
5295
|
+
previewRowCount: Math.min(rowsInfo.rows.length, 5),
|
|
5296
|
+
previewLimit: 5,
|
|
5297
|
+
...isPartial ? {
|
|
5298
|
+
isPartial: true,
|
|
5299
|
+
previewCount: rowsInfo.rows.length,
|
|
5300
|
+
totalCount: rowsInfo.totalRows
|
|
5301
|
+
} : { isPartial: false },
|
|
5302
|
+
complete: rowsInfo.complete,
|
|
5303
|
+
source: rowsInfo.source,
|
|
5304
|
+
...exportedPath ? { csv_path: exportedPath } : {}
|
|
5305
|
+
}
|
|
5306
|
+
];
|
|
5307
|
+
}
|
|
5308
|
+
function normalizeStepsForEnvelope(status) {
|
|
5309
|
+
const directSteps = getRecordField(status, "steps");
|
|
5310
|
+
if (Array.isArray(directSteps)) {
|
|
5311
|
+
return directSteps;
|
|
5312
|
+
}
|
|
5313
|
+
const timeline = getRecordField(status, "timeline");
|
|
5314
|
+
if (Array.isArray(timeline)) {
|
|
5315
|
+
return timeline;
|
|
5316
|
+
}
|
|
4991
5317
|
return [];
|
|
4992
5318
|
}
|
|
4993
|
-
function
|
|
5319
|
+
function normalizeErrorsForEnvelope(status, error) {
|
|
5320
|
+
const directErrors = getRecordField(status, "errors");
|
|
5321
|
+
if (Array.isArray(directErrors)) {
|
|
5322
|
+
return directErrors.filter(
|
|
5323
|
+
(entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)
|
|
5324
|
+
);
|
|
5325
|
+
}
|
|
5326
|
+
if (!error) {
|
|
5327
|
+
return [];
|
|
5328
|
+
}
|
|
5329
|
+
return [
|
|
5330
|
+
{
|
|
5331
|
+
code: getStringField(status, "errorCode") ?? "RUN_FAILED",
|
|
5332
|
+
phase: getStringField(status, "errorPhase") ?? "runtime",
|
|
5333
|
+
message: error,
|
|
5334
|
+
retryable: typeof getRecordField(status, "retryable") === "boolean" ? getRecordField(status, "retryable") : null,
|
|
5335
|
+
nextAction: `deepline runs get ${status.runId} --json`
|
|
5336
|
+
}
|
|
5337
|
+
];
|
|
5338
|
+
}
|
|
5339
|
+
function normalizeLogsForEnvelope(status) {
|
|
5340
|
+
const logs = Array.isArray(status.progress?.logs) ? status.progress.logs : [];
|
|
5341
|
+
const offset = typeof status.progress?.logOffset === "number" && Number.isFinite(status.progress.logOffset) ? Math.max(0, Math.trunc(status.progress.logOffset)) : 0;
|
|
5342
|
+
const totalCount = offset + logs.length;
|
|
5343
|
+
const entries = logs.slice(Math.max(0, logs.length - RUN_LOG_PREVIEW_LIMIT));
|
|
5344
|
+
const firstSequence = entries.length === 0 ? null : offset + logs.length - entries.length + 1;
|
|
5345
|
+
const lastSequence = totalCount === 0 ? null : totalCount;
|
|
4994
5346
|
return {
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
5347
|
+
totalCount,
|
|
5348
|
+
returnedCount: entries.length,
|
|
5349
|
+
firstSequence,
|
|
5350
|
+
lastSequence,
|
|
5351
|
+
truncated: totalCount > entries.length,
|
|
5352
|
+
hasMore: totalCount > entries.length,
|
|
5353
|
+
entries,
|
|
5354
|
+
nextCursor: lastSequence
|
|
4998
5355
|
};
|
|
4999
5356
|
}
|
|
5357
|
+
function stripProviderSpendFromBilling(value) {
|
|
5358
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5359
|
+
return value;
|
|
5360
|
+
}
|
|
5361
|
+
const next = {};
|
|
5362
|
+
for (const [key, item] of Object.entries(value)) {
|
|
5363
|
+
if (key === "providerCostUsd" || key === "totalProviderCostUsd") {
|
|
5364
|
+
continue;
|
|
5365
|
+
}
|
|
5366
|
+
next[key] = item;
|
|
5367
|
+
}
|
|
5368
|
+
return next;
|
|
5369
|
+
}
|
|
5000
5370
|
function compactPlayStatus(status, options) {
|
|
5001
5371
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
5002
5372
|
const result = status && typeof status === "object" ? status.result : null;
|
|
5003
5373
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
5004
|
-
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5005
|
-
|
|
5374
|
+
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5375
|
+
rowsInfo.rows,
|
|
5376
|
+
rowsInfo.totalRows,
|
|
5377
|
+
rowsInfo.columns,
|
|
5378
|
+
extractDatasetExecutionStats(status)
|
|
5379
|
+
) : null;
|
|
5380
|
+
const billing = status && typeof status === "object" ? stripProviderSpendFromBilling(
|
|
5381
|
+
status.billing
|
|
5382
|
+
) : null;
|
|
5006
5383
|
const progressError = status.progress?.error;
|
|
5007
5384
|
const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
|
|
5008
5385
|
return {
|
|
@@ -5011,16 +5388,25 @@ function compactPlayStatus(status, options) {
|
|
|
5011
5388
|
...typeof status.name === "string" ? { name: status.name } : {},
|
|
5012
5389
|
...typeof status.playName === "string" ? { playName: status.playName } : {},
|
|
5013
5390
|
status: status.status,
|
|
5391
|
+
run: normalizeRunStatusForEnvelope(status),
|
|
5392
|
+
progress: normalizeProgressForEnvelope(status, rowsInfo),
|
|
5393
|
+
outputs: normalizeOutputsForEnvelope(
|
|
5394
|
+
rowsInfo,
|
|
5395
|
+
status.runId,
|
|
5396
|
+
options?.exportedPath
|
|
5397
|
+
),
|
|
5398
|
+
steps: normalizeStepsForEnvelope(status),
|
|
5399
|
+
errors: normalizeErrorsForEnvelope(status, error),
|
|
5400
|
+
logs: normalizeLogsForEnvelope(status),
|
|
5014
5401
|
...error ? { error } : {},
|
|
5015
5402
|
...warnings.length > 0 ? { warnings } : {},
|
|
5016
|
-
output: buildOutputSummary(rowsInfo, options?.exportedPath) ?? result ?? null,
|
|
5403
|
+
output: buildOutputSummary(rowsInfo, status.runId, options?.exportedPath) ?? result ?? null,
|
|
5017
5404
|
...result !== void 0 ? { result } : {},
|
|
5018
5405
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
5019
5406
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
5020
5407
|
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
|
|
5021
5408
|
...billing ? { billing } : {},
|
|
5022
|
-
|
|
5023
|
-
next: buildRunNextCommands(status.runId)
|
|
5409
|
+
next: buildRunNextCommands(status.runId, rowsInfo)
|
|
5024
5410
|
};
|
|
5025
5411
|
}
|
|
5026
5412
|
function enrichPlayStatusWithDatasetStats(status) {
|
|
@@ -5033,7 +5419,8 @@ function enrichPlayStatusWithDatasetStats(status) {
|
|
|
5033
5419
|
dataset_stats: buildDatasetStats(
|
|
5034
5420
|
rowsInfo.rows,
|
|
5035
5421
|
rowsInfo.totalRows,
|
|
5036
|
-
rowsInfo.columns
|
|
5422
|
+
rowsInfo.columns,
|
|
5423
|
+
extractDatasetExecutionStats(status)
|
|
5037
5424
|
)
|
|
5038
5425
|
};
|
|
5039
5426
|
}
|
|
@@ -5048,8 +5435,9 @@ function formatDatasetStatsLines(datasetStats) {
|
|
|
5048
5435
|
)) {
|
|
5049
5436
|
const topValues = stat3.top_values ? `, top_values=${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
|
|
5050
5437
|
const sample = stat3.sample_value !== void 0 ? `, sample_value=${JSON.stringify(stat3.sample_value)}` : "";
|
|
5438
|
+
const execution = stat3.execution ? `, execution=${Object.entries(stat3.execution).map(([bucket, count]) => `${bucket}=${count}`).join(", ")}` : "";
|
|
5051
5439
|
lines.push(
|
|
5052
|
-
` ${column}: non_empty=${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}`
|
|
5440
|
+
` ${column}: non_empty=${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}${execution}`
|
|
5053
5441
|
);
|
|
5054
5442
|
}
|
|
5055
5443
|
return lines;
|
|
@@ -5072,14 +5460,28 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
5072
5460
|
lines.push(`${success ? "\u2713" : "\u2717"} ${publicStatus} ${runId}`);
|
|
5073
5461
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
5074
5462
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
5075
|
-
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5076
|
-
|
|
5463
|
+
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5464
|
+
rowsInfo.rows,
|
|
5465
|
+
rowsInfo.totalRows,
|
|
5466
|
+
rowsInfo.columns,
|
|
5467
|
+
extractDatasetExecutionStats(status)
|
|
5468
|
+
) : null;
|
|
5469
|
+
const outputSummary = buildOutputSummary(
|
|
5470
|
+
rowsInfo,
|
|
5471
|
+
runId,
|
|
5472
|
+
options?.exportedPath
|
|
5473
|
+
);
|
|
5077
5474
|
if (outputSummary) {
|
|
5078
5475
|
const columns = Array.isArray(outputSummary.columns) ? outputSummary.columns.length : 0;
|
|
5079
5476
|
const path = typeof outputSummary.csv_path === "string" ? ` file=${outputSummary.csv_path}` : "";
|
|
5080
5477
|
lines.push(
|
|
5081
5478
|
` output: rows=${formatInteger(outputSummary.rowCount)} columns=${formatInteger(columns)}${path}`
|
|
5082
5479
|
);
|
|
5480
|
+
if (outputSummary.isPartial === true) {
|
|
5481
|
+
lines.push(
|
|
5482
|
+
` partial output: showing ${formatInteger(outputSummary.previewCount)} preview row(s) of ${formatInteger(outputSummary.totalCount)}`
|
|
5483
|
+
);
|
|
5484
|
+
}
|
|
5083
5485
|
}
|
|
5084
5486
|
for (const warning of warnings) {
|
|
5085
5487
|
lines.push(` warning: ${warning}`);
|
|
@@ -5109,6 +5511,11 @@ function exportPlayStatusRows(status, outPath) {
|
|
|
5109
5511
|
`Run ${status.runId} did not expose a row-shaped final output to export.`
|
|
5110
5512
|
);
|
|
5111
5513
|
}
|
|
5514
|
+
if (!rowsInfo.complete) {
|
|
5515
|
+
throw new DeeplineError(
|
|
5516
|
+
`Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; full dataset export is not available from this status response yet.`
|
|
5517
|
+
);
|
|
5518
|
+
}
|
|
5112
5519
|
return writeCanonicalRowsCsv(rowsInfo, outPath);
|
|
5113
5520
|
}
|
|
5114
5521
|
function renderServerResultView(value) {
|
|
@@ -5191,10 +5598,10 @@ function writeStartedPlayRun(input) {
|
|
|
5191
5598
|
const lines = [
|
|
5192
5599
|
`Started ${input.playName}`,
|
|
5193
5600
|
` run id: ${input.runId}`,
|
|
5194
|
-
`
|
|
5195
|
-
` tail logs: deepline
|
|
5196
|
-
` stop run: deepline
|
|
5197
|
-
` result JSON: deepline
|
|
5601
|
+
` get status: deepline runs get ${input.runId} --json`,
|
|
5602
|
+
` tail logs: deepline runs tail ${input.runId} --json`,
|
|
5603
|
+
` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`,
|
|
5604
|
+
` result JSON: deepline runs get ${input.runId} --json`
|
|
5198
5605
|
];
|
|
5199
5606
|
if (input.dashboardUrl) {
|
|
5200
5607
|
lines.push(` play page: ${input.dashboardUrl}`);
|
|
@@ -5341,6 +5748,10 @@ function parsePlayCheckOptions(args) {
|
|
|
5341
5748
|
const jsonOutput = argsWantJson(args);
|
|
5342
5749
|
return { target, jsonOutput };
|
|
5343
5750
|
}
|
|
5751
|
+
function shouldUseLocalOnlyPlayCheck() {
|
|
5752
|
+
const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
|
|
5753
|
+
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
5754
|
+
}
|
|
5344
5755
|
async function handlePlayCheck(args) {
|
|
5345
5756
|
const options = parsePlayCheckOptions(args);
|
|
5346
5757
|
if (!isFileTarget(options.target)) {
|
|
@@ -5366,10 +5777,28 @@ async function handlePlayCheck(args) {
|
|
|
5366
5777
|
return 1;
|
|
5367
5778
|
}
|
|
5368
5779
|
const playName = graph.root.playName ?? extractPlayName(sourceCode, absolutePlayPath);
|
|
5780
|
+
if (shouldUseLocalOnlyPlayCheck()) {
|
|
5781
|
+
const result2 = {
|
|
5782
|
+
valid: true,
|
|
5783
|
+
errors: [],
|
|
5784
|
+
staticPipeline: graph.root.compilerManifest?.staticPipeline ?? null,
|
|
5785
|
+
artifactHash: graph.root.artifact.artifactHash,
|
|
5786
|
+
graphHash: graph.root.artifact.graphHash
|
|
5787
|
+
};
|
|
5788
|
+
if (options.jsonOutput) {
|
|
5789
|
+
process.stdout.write(`${JSON.stringify({ name: playName, ...result2 })}
|
|
5790
|
+
`);
|
|
5791
|
+
} else {
|
|
5792
|
+
console.log(`\u2713 ${playName} passed local play check`);
|
|
5793
|
+
console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
|
|
5794
|
+
}
|
|
5795
|
+
return 0;
|
|
5796
|
+
}
|
|
5369
5797
|
const client = new DeeplineClient();
|
|
5370
5798
|
const result = await client.checkPlayArtifact({
|
|
5371
5799
|
name: playName,
|
|
5372
5800
|
sourceCode: graph.root.sourceCode,
|
|
5801
|
+
sourceFiles: graph.root.sourceFiles,
|
|
5373
5802
|
artifact: graph.root.artifact
|
|
5374
5803
|
});
|
|
5375
5804
|
if (options.jsonOutput) {
|
|
@@ -5421,17 +5850,24 @@ async function handleFileBackedRun(options) {
|
|
|
5421
5850
|
const packagedFileUploads = bundleResult.packagedFiles.map(
|
|
5422
5851
|
(file) => stageFile(file.logicalPath, file.absolutePath)
|
|
5423
5852
|
);
|
|
5853
|
+
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
5854
|
+
requireCompilerManifest(bundleResult).staticPipeline
|
|
5855
|
+
);
|
|
5856
|
+
applyCsvShortcutInput({
|
|
5857
|
+
runtimeInput,
|
|
5858
|
+
bindings: fileInputBindings,
|
|
5859
|
+
fallbackInputPath: "file"
|
|
5860
|
+
});
|
|
5424
5861
|
const stagedFileInputs = await stageFileInputArgs({
|
|
5425
5862
|
client,
|
|
5426
5863
|
runtimeInput,
|
|
5427
|
-
bindings:
|
|
5428
|
-
requireCompilerManifest(bundleResult).staticPipeline
|
|
5429
|
-
),
|
|
5864
|
+
bindings: fileInputBindings,
|
|
5430
5865
|
progress
|
|
5431
5866
|
});
|
|
5432
5867
|
const startRequest = {
|
|
5433
5868
|
name: playName,
|
|
5434
5869
|
sourceCode: bundleResult.sourceCode,
|
|
5870
|
+
sourceFiles: bundleResult.sourceFiles,
|
|
5435
5871
|
runtimeArtifact: bundleResult.artifact,
|
|
5436
5872
|
compilerManifest: requireCompilerManifest(bundleResult),
|
|
5437
5873
|
packagedFileUploads,
|
|
@@ -5506,13 +5942,18 @@ async function handleNamedRun(options) {
|
|
|
5506
5942
|
selector: options.revisionSelector
|
|
5507
5943
|
});
|
|
5508
5944
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5945
|
+
const fileInputBindings = [
|
|
5946
|
+
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
5947
|
+
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
5948
|
+
];
|
|
5949
|
+
applyCsvShortcutInput({
|
|
5950
|
+
runtimeInput,
|
|
5951
|
+
bindings: fileInputBindings
|
|
5952
|
+
});
|
|
5509
5953
|
const stagedFileInputs = await stageFileInputArgs({
|
|
5510
5954
|
client,
|
|
5511
5955
|
runtimeInput,
|
|
5512
|
-
bindings:
|
|
5513
|
-
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
5514
|
-
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
5515
|
-
],
|
|
5956
|
+
bindings: fileInputBindings,
|
|
5516
5957
|
progress
|
|
5517
5958
|
});
|
|
5518
5959
|
const startRequest = {
|
|
@@ -5590,80 +6031,101 @@ async function handlePlayRun(args) {
|
|
|
5590
6031
|
}
|
|
5591
6032
|
return handleNamedRun(options);
|
|
5592
6033
|
}
|
|
5593
|
-
|
|
5594
|
-
const usage = "Usage: deepline play tail --run-id <run-id> [--interval-ms 1000] [--json]\n deepline play tail --name <name> [--interval-ms 1000] [--json]";
|
|
5595
|
-
let target;
|
|
5596
|
-
try {
|
|
5597
|
-
target = parsePlayRunTarget({ args, usage, allowName: true });
|
|
5598
|
-
} catch (error) {
|
|
5599
|
-
console.error(error instanceof Error ? error.message : usage);
|
|
5600
|
-
return 1;
|
|
5601
|
-
}
|
|
5602
|
-
const client = new DeeplineClient();
|
|
5603
|
-
const jsonOutput = argsWantJson(args);
|
|
5604
|
-
const emitLogs = !jsonOutput || args.includes("--logs");
|
|
5605
|
-
let intervalMs = 500;
|
|
6034
|
+
function parseRunIdPositional(args, usage) {
|
|
5606
6035
|
for (let index = 0; index < args.length; index += 1) {
|
|
5607
6036
|
const arg = args[index];
|
|
5608
|
-
if (
|
|
5609
|
-
|
|
6037
|
+
if (arg === "--json" || arg === "--full" || arg === "--logs" || arg === "--compact" || arg === "--limit") {
|
|
6038
|
+
if (arg === "--limit" && args[index + 1]) {
|
|
6039
|
+
index += 1;
|
|
6040
|
+
}
|
|
6041
|
+
continue;
|
|
6042
|
+
}
|
|
6043
|
+
if ((arg === "--out" || arg === "--cursor" || arg === "--reason" || arg === "--interval-ms" || arg === "--poll-interval-ms") && args[index + 1]) {
|
|
6044
|
+
index += 1;
|
|
6045
|
+
continue;
|
|
6046
|
+
}
|
|
6047
|
+
if (!arg.startsWith("--")) {
|
|
6048
|
+
return arg;
|
|
5610
6049
|
}
|
|
5611
6050
|
}
|
|
5612
|
-
|
|
5613
|
-
const progress = getActiveCliProgress() ?? createCliProgress(!jsonOutput);
|
|
5614
|
-
progress.phase(`tailing ${workflowId}`);
|
|
5615
|
-
const finalStatus = await waitForPlayCompletion({
|
|
5616
|
-
client,
|
|
5617
|
-
workflowId,
|
|
5618
|
-
pollIntervalMs: intervalMs,
|
|
5619
|
-
jsonOutput,
|
|
5620
|
-
emitLogs,
|
|
5621
|
-
waitTimeoutMs: null,
|
|
5622
|
-
progress
|
|
5623
|
-
});
|
|
5624
|
-
if (finalStatus.status === "completed") {
|
|
5625
|
-
progress.complete();
|
|
5626
|
-
} else {
|
|
5627
|
-
progress.fail();
|
|
5628
|
-
}
|
|
5629
|
-
writePlayResult(finalStatus, jsonOutput);
|
|
5630
|
-
return finalStatus.status === "completed" ? 0 : 1;
|
|
6051
|
+
throw new DeeplineError(usage);
|
|
5631
6052
|
}
|
|
5632
|
-
async function
|
|
5633
|
-
const usage = "Usage: deepline
|
|
5634
|
-
let
|
|
6053
|
+
async function handleRunGet(args) {
|
|
6054
|
+
const usage = "Usage: deepline runs get <run-id> [--json] [--full]";
|
|
6055
|
+
let runId;
|
|
5635
6056
|
try {
|
|
5636
|
-
|
|
6057
|
+
runId = parseRunIdPositional(args, usage);
|
|
5637
6058
|
} catch (error) {
|
|
5638
6059
|
console.error(error instanceof Error ? error.message : usage);
|
|
5639
6060
|
return 1;
|
|
5640
6061
|
}
|
|
5641
6062
|
const client = new DeeplineClient();
|
|
5642
|
-
const
|
|
5643
|
-
const status = await client.getPlayStatus(workflowId);
|
|
6063
|
+
const status = await client.runs.get(runId);
|
|
5644
6064
|
writePlayResult(status, argsWantJson(args), {
|
|
5645
6065
|
fullJson: args.includes("--full")
|
|
5646
6066
|
});
|
|
5647
6067
|
return 0;
|
|
5648
6068
|
}
|
|
5649
|
-
function
|
|
6069
|
+
async function handleRunsList(args) {
|
|
6070
|
+
const usage = "Usage: deepline runs list --play <play-name> [--status <status>] [--json]";
|
|
6071
|
+
let playName = null;
|
|
6072
|
+
let statusFilter = null;
|
|
5650
6073
|
for (let index = 0; index < args.length; index += 1) {
|
|
5651
6074
|
const arg = args[index];
|
|
5652
|
-
if (arg === "--
|
|
6075
|
+
if ((arg === "--play" || arg === "--name") && args[index + 1]) {
|
|
6076
|
+
playName = parseReferencedPlayTarget(args[++index]).playName;
|
|
5653
6077
|
continue;
|
|
5654
6078
|
}
|
|
5655
|
-
if (arg === "--
|
|
5656
|
-
|
|
6079
|
+
if (arg === "--status" && args[index + 1]) {
|
|
6080
|
+
statusFilter = args[++index].trim().toLowerCase();
|
|
5657
6081
|
continue;
|
|
5658
6082
|
}
|
|
5659
|
-
if (
|
|
5660
|
-
|
|
6083
|
+
if (arg === "--json" || arg === "--compact") {
|
|
6084
|
+
continue;
|
|
5661
6085
|
}
|
|
5662
6086
|
}
|
|
5663
|
-
|
|
6087
|
+
if (!playName) {
|
|
6088
|
+
console.error(usage);
|
|
6089
|
+
return 1;
|
|
6090
|
+
}
|
|
6091
|
+
const client = new DeeplineClient();
|
|
6092
|
+
const runs = (await client.runs.list({
|
|
6093
|
+
play: playName,
|
|
6094
|
+
...statusFilter ? { status: statusFilter } : {}
|
|
6095
|
+
})).map((run) => ({
|
|
6096
|
+
runId: run.workflowId,
|
|
6097
|
+
workflowId: run.workflowId,
|
|
6098
|
+
temporalRunId: run.runId,
|
|
6099
|
+
status: String(run.status ?? "").toLowerCase(),
|
|
6100
|
+
startedAt: run.startTime,
|
|
6101
|
+
finishedAt: run.closeTime,
|
|
6102
|
+
executionTime: run.executionTime,
|
|
6103
|
+
playName: run.memo?.playName ?? playName
|
|
6104
|
+
}));
|
|
6105
|
+
if (argsWantJson(args)) {
|
|
6106
|
+
process.stdout.write(
|
|
6107
|
+
`${JSON.stringify({
|
|
6108
|
+
runs,
|
|
6109
|
+
count: runs.length,
|
|
6110
|
+
next: {
|
|
6111
|
+
get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
|
|
6112
|
+
}
|
|
6113
|
+
})}
|
|
6114
|
+
`
|
|
6115
|
+
);
|
|
6116
|
+
} else {
|
|
6117
|
+
if (runs.length === 0) {
|
|
6118
|
+
console.log(`No runs found for ${playName}.`);
|
|
6119
|
+
} else {
|
|
6120
|
+
for (const run of runs) {
|
|
6121
|
+
console.log(`${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
6124
|
+
}
|
|
6125
|
+
return 0;
|
|
5664
6126
|
}
|
|
5665
|
-
async function
|
|
5666
|
-
const usage = "Usage: deepline runs
|
|
6127
|
+
async function handleRunTail(args) {
|
|
6128
|
+
const usage = "Usage: deepline runs tail <run-id> [--json] [--compact] [--cursor <cursor>]";
|
|
5667
6129
|
let runId;
|
|
5668
6130
|
try {
|
|
5669
6131
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -5672,14 +6134,24 @@ async function handleRunStatus(args) {
|
|
|
5672
6134
|
return 1;
|
|
5673
6135
|
}
|
|
5674
6136
|
const client = new DeeplineClient();
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
6137
|
+
let afterLogIndex;
|
|
6138
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6139
|
+
const arg = args[index];
|
|
6140
|
+
if (arg === "--cursor" && args[index + 1]) {
|
|
6141
|
+
const parsed = Number(args[++index]);
|
|
6142
|
+
if (Number.isInteger(parsed) && parsed >= 0) {
|
|
6143
|
+
afterLogIndex = parsed;
|
|
6144
|
+
}
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
const status = await client.runs.tail(runId, {
|
|
6148
|
+
...afterLogIndex !== void 0 ? { afterLogIndex } : {}
|
|
5678
6149
|
});
|
|
5679
|
-
|
|
6150
|
+
writePlayResult(status, argsWantJson(args));
|
|
6151
|
+
return status.status === "failed" ? 1 : 0;
|
|
5680
6152
|
}
|
|
5681
6153
|
async function handleRunLogs(args) {
|
|
5682
|
-
const usage = "Usage: deepline runs logs <run-id> [--json]";
|
|
6154
|
+
const usage = "Usage: deepline runs logs <run-id> [--limit 200] [--out run.log] [--json]";
|
|
5683
6155
|
let runId;
|
|
5684
6156
|
try {
|
|
5685
6157
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -5687,14 +6159,86 @@ async function handleRunLogs(args) {
|
|
|
5687
6159
|
console.error(error instanceof Error ? error.message : usage);
|
|
5688
6160
|
return 1;
|
|
5689
6161
|
}
|
|
6162
|
+
let limit = 200;
|
|
6163
|
+
let outPath = null;
|
|
6164
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6165
|
+
const arg = args[index];
|
|
6166
|
+
if (arg === "--limit" && args[index + 1]) {
|
|
6167
|
+
limit = parsePositiveInteger2(args[++index], "--limit");
|
|
6168
|
+
continue;
|
|
6169
|
+
}
|
|
6170
|
+
if (arg === "--out" && args[index + 1]) {
|
|
6171
|
+
outPath = resolve7(args[++index]);
|
|
6172
|
+
}
|
|
6173
|
+
}
|
|
5690
6174
|
const client = new DeeplineClient();
|
|
5691
|
-
const status = await client.
|
|
6175
|
+
const status = await client.runs.get(runId);
|
|
5692
6176
|
const logs = status.progress?.logs ?? [];
|
|
6177
|
+
if (outPath) {
|
|
6178
|
+
writeFileSync4(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
6179
|
+
if (argsWantJson(args)) {
|
|
6180
|
+
process.stdout.write(
|
|
6181
|
+
`${JSON.stringify({
|
|
6182
|
+
runId: status.runId,
|
|
6183
|
+
log_path: outPath,
|
|
6184
|
+
lineCount: logs.length
|
|
6185
|
+
})}
|
|
6186
|
+
`
|
|
6187
|
+
);
|
|
6188
|
+
} else {
|
|
6189
|
+
console.log(`Wrote ${logs.length} log lines to ${outPath}`);
|
|
6190
|
+
}
|
|
6191
|
+
return 0;
|
|
6192
|
+
}
|
|
6193
|
+
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
5693
6194
|
if (argsWantJson(args)) {
|
|
5694
|
-
process.stdout.write(
|
|
6195
|
+
process.stdout.write(
|
|
6196
|
+
`${JSON.stringify({
|
|
6197
|
+
runId: status.runId,
|
|
6198
|
+
totalCount: logs.length,
|
|
6199
|
+
returnedCount: entries.length,
|
|
6200
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
6201
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
6202
|
+
truncated: logs.length > entries.length,
|
|
6203
|
+
hasMore: logs.length > entries.length,
|
|
6204
|
+
entries,
|
|
6205
|
+
next: {
|
|
6206
|
+
export: `deepline runs logs ${status.runId} --out run.log --json`
|
|
6207
|
+
}
|
|
6208
|
+
})}
|
|
6209
|
+
`
|
|
6210
|
+
);
|
|
6211
|
+
} else {
|
|
6212
|
+
process.stdout.write(`${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`);
|
|
6213
|
+
}
|
|
6214
|
+
return 0;
|
|
6215
|
+
}
|
|
6216
|
+
async function handleRunStop(args) {
|
|
6217
|
+
const usage = 'Usage: deepline runs stop <run-id> [--reason "text"] [--json]';
|
|
6218
|
+
let runId;
|
|
6219
|
+
try {
|
|
6220
|
+
runId = parseRunIdPositional(args, usage);
|
|
6221
|
+
} catch (error) {
|
|
6222
|
+
console.error(error instanceof Error ? error.message : usage);
|
|
6223
|
+
return 1;
|
|
6224
|
+
}
|
|
6225
|
+
let reason;
|
|
6226
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6227
|
+
const arg = args[index];
|
|
6228
|
+
if (arg === "--reason" && args[index + 1]) {
|
|
6229
|
+
reason = args[++index];
|
|
6230
|
+
}
|
|
6231
|
+
}
|
|
6232
|
+
const client = new DeeplineClient();
|
|
6233
|
+
const result = await client.runs.stop(runId, { reason });
|
|
6234
|
+
if (argsWantJson(args)) {
|
|
6235
|
+
process.stdout.write(`${JSON.stringify(result)}
|
|
5695
6236
|
`);
|
|
5696
6237
|
} else {
|
|
5697
|
-
|
|
6238
|
+
console.log(`Stopped ${result.runId}`);
|
|
6239
|
+
if (result.hitlCancelledCount > 0) {
|
|
6240
|
+
console.log(` cancelled HITL waits: ${result.hitlCancelledCount}`);
|
|
6241
|
+
}
|
|
5698
6242
|
}
|
|
5699
6243
|
return 0;
|
|
5700
6244
|
}
|
|
@@ -5737,37 +6281,6 @@ async function handleRunExport(args) {
|
|
|
5737
6281
|
}
|
|
5738
6282
|
return 0;
|
|
5739
6283
|
}
|
|
5740
|
-
async function handlePlayStop(args) {
|
|
5741
|
-
const usage = 'Usage: deepline play stop --run-id <run-id> [--reason "text"] [--json]';
|
|
5742
|
-
let target;
|
|
5743
|
-
try {
|
|
5744
|
-
target = parsePlayRunTarget({ args, usage, allowName: false });
|
|
5745
|
-
} catch (error) {
|
|
5746
|
-
console.error(error instanceof Error ? error.message : usage);
|
|
5747
|
-
return 1;
|
|
5748
|
-
}
|
|
5749
|
-
const client = new DeeplineClient();
|
|
5750
|
-
const jsonOutput = argsWantJson(args);
|
|
5751
|
-
let reason;
|
|
5752
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
5753
|
-
const arg = args[index];
|
|
5754
|
-
if (arg === "--reason" && args[index + 1]) {
|
|
5755
|
-
reason = args[++index];
|
|
5756
|
-
}
|
|
5757
|
-
}
|
|
5758
|
-
const workflowId = await resolvePlayRunId(client, target);
|
|
5759
|
-
const result = await client.stopPlay(workflowId, { reason });
|
|
5760
|
-
if (jsonOutput) {
|
|
5761
|
-
process.stdout.write(`${JSON.stringify(result)}
|
|
5762
|
-
`);
|
|
5763
|
-
} else {
|
|
5764
|
-
console.log(`Stopped ${result.runId}`);
|
|
5765
|
-
if (result.hitlCancelledCount > 0) {
|
|
5766
|
-
console.log(` cancelled HITL waits: ${result.hitlCancelledCount}`);
|
|
5767
|
-
}
|
|
5768
|
-
}
|
|
5769
|
-
return 0;
|
|
5770
|
-
}
|
|
5771
6284
|
async function handlePlayGet(args) {
|
|
5772
6285
|
const target = args[0];
|
|
5773
6286
|
if (!target) {
|
|
@@ -5855,33 +6368,6 @@ async function handlePlayGet(args) {
|
|
|
5855
6368
|
}
|
|
5856
6369
|
return 0;
|
|
5857
6370
|
}
|
|
5858
|
-
async function handlePlayRuns(args) {
|
|
5859
|
-
const nameIndex = args.indexOf("--name");
|
|
5860
|
-
const name = nameIndex >= 0 ? args[nameIndex + 1] : void 0;
|
|
5861
|
-
if (!name) {
|
|
5862
|
-
console.error("Usage: deepline play runs --name <name> [--json]");
|
|
5863
|
-
return 1;
|
|
5864
|
-
}
|
|
5865
|
-
const client = new DeeplineClient();
|
|
5866
|
-
const jsonOutput = argsWantJson(args);
|
|
5867
|
-
await assertCanonicalNamedPlayReference(client, name);
|
|
5868
|
-
const runs = await client.listPlayRuns(
|
|
5869
|
-
parseReferencedPlayTarget(name).playName
|
|
5870
|
-
);
|
|
5871
|
-
if (jsonOutput) {
|
|
5872
|
-
process.stdout.write(`${JSON.stringify({ runs })}
|
|
5873
|
-
`);
|
|
5874
|
-
return 0;
|
|
5875
|
-
}
|
|
5876
|
-
if (runs.length === 0) {
|
|
5877
|
-
console.log(`No runs found for ${name}.`);
|
|
5878
|
-
return 0;
|
|
5879
|
-
}
|
|
5880
|
-
for (const run of runs) {
|
|
5881
|
-
console.log(formatRunLine(run));
|
|
5882
|
-
}
|
|
5883
|
-
return 0;
|
|
5884
|
-
}
|
|
5885
6371
|
function formatVersionLine(version) {
|
|
5886
6372
|
const revisionLabel = version.artifactHash?.slice(0, 12) ?? "unknown-revision";
|
|
5887
6373
|
return `v${version.version} ${revisionLabel} ${formatTimestamp(version.createdAt)}`;
|
|
@@ -6013,6 +6499,11 @@ function printPlayDescription(play) {
|
|
|
6013
6499
|
}
|
|
6014
6500
|
}
|
|
6015
6501
|
console.log(` Run: ${play.runCommand}`);
|
|
6502
|
+
if (play.origin === "prebuilt" && !play.canEdit) {
|
|
6503
|
+
console.log(
|
|
6504
|
+
` Customize: deepline plays get ${reference} --source --out ./my-play.play.ts`
|
|
6505
|
+
);
|
|
6506
|
+
}
|
|
6016
6507
|
}
|
|
6017
6508
|
async function handlePlaySearch(args) {
|
|
6018
6509
|
let options;
|
|
@@ -6110,6 +6601,7 @@ async function handlePlayPublish(args) {
|
|
|
6110
6601
|
const published = await client.registerPlayArtifact({
|
|
6111
6602
|
name: rootPlayName,
|
|
6112
6603
|
sourceCode: graph.root.sourceCode,
|
|
6604
|
+
sourceFiles: graph.root.sourceFiles,
|
|
6113
6605
|
artifact: graph.root.artifact,
|
|
6114
6606
|
compilerManifest: requireCompilerManifest(graph.root),
|
|
6115
6607
|
publish: true
|
|
@@ -6330,50 +6822,12 @@ Examples:
|
|
|
6330
6822
|
...options.json ? ["--json"] : []
|
|
6331
6823
|
]);
|
|
6332
6824
|
});
|
|
6333
|
-
play.command("runs").description("List runs for a named play.").option("--name <name>", "Saved play name").option("--json", "Emit JSON output").action(async (options) => {
|
|
6334
|
-
process.exitCode = await handlePlayRuns([
|
|
6335
|
-
...options.name ? ["--name", options.name] : [],
|
|
6336
|
-
...options.json ? ["--json"] : []
|
|
6337
|
-
]);
|
|
6338
|
-
});
|
|
6339
6825
|
play.command("versions").description("List revisions for a named play.").option("--name <name>", "Saved play name").option("--json", "Emit JSON output").action(async (options) => {
|
|
6340
6826
|
process.exitCode = await handlePlayVersions([
|
|
6341
6827
|
...options.name ? ["--name", options.name] : [],
|
|
6342
6828
|
...options.json ? ["--json"] : []
|
|
6343
6829
|
]);
|
|
6344
6830
|
});
|
|
6345
|
-
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) => {
|
|
6346
|
-
process.exitCode = await handlePlayTail([
|
|
6347
|
-
...options.runId ? ["--run-id", options.runId] : [],
|
|
6348
|
-
...options.name ? ["--name", options.name] : [],
|
|
6349
|
-
...options.intervalMs ? ["--interval-ms", options.intervalMs] : [],
|
|
6350
|
-
...options.logs ? ["--logs"] : [],
|
|
6351
|
-
...options.json ? ["--json"] : []
|
|
6352
|
-
]);
|
|
6353
|
-
});
|
|
6354
|
-
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", "Debug only: with --json, emit the raw status payload").action(async (options) => {
|
|
6355
|
-
process.exitCode = await handlePlayStatus([
|
|
6356
|
-
...options.runId ? ["--run-id", options.runId] : [],
|
|
6357
|
-
...options.name ? ["--name", options.name] : [],
|
|
6358
|
-
...options.json ? ["--json"] : [],
|
|
6359
|
-
...options.full ? ["--full"] : []
|
|
6360
|
-
]);
|
|
6361
|
-
});
|
|
6362
|
-
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) => {
|
|
6363
|
-
process.exitCode = await handleRunExport([
|
|
6364
|
-
options.runId,
|
|
6365
|
-
"--out",
|
|
6366
|
-
options.out,
|
|
6367
|
-
...options.json ? ["--json"] : []
|
|
6368
|
-
]);
|
|
6369
|
-
});
|
|
6370
|
-
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) => {
|
|
6371
|
-
process.exitCode = await handlePlayStop([
|
|
6372
|
-
...options.runId ? ["--run-id", options.runId] : [],
|
|
6373
|
-
...options.reason ? ["--reason", options.reason] : [],
|
|
6374
|
-
...options.json ? ["--json"] : []
|
|
6375
|
-
]);
|
|
6376
|
-
});
|
|
6377
6831
|
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) => {
|
|
6378
6832
|
process.exitCode = await handlePlayPublish([
|
|
6379
6833
|
target,
|
|
@@ -6389,38 +6843,72 @@ Examples:
|
|
|
6389
6843
|
...options.json ? ["--json"] : []
|
|
6390
6844
|
]);
|
|
6391
6845
|
});
|
|
6392
|
-
const runs = program.command("runs").description("Inspect and export play runs.").addHelpText(
|
|
6846
|
+
const runs = program.command("runs").description("Inspect, tail, stop, and export play runs.").addHelpText(
|
|
6393
6847
|
"after",
|
|
6394
6848
|
`
|
|
6395
6849
|
Examples:
|
|
6396
|
-
deepline runs
|
|
6850
|
+
deepline runs get play/my-play/run/20260501t000000-000 --json
|
|
6851
|
+
deepline runs tail play/my-play/run/20260501t000000-000
|
|
6852
|
+
deepline runs logs play/my-play/run/20260501t000000-000 --out run.log --json
|
|
6853
|
+
deepline runs list --play my-play --status failed --json
|
|
6854
|
+
deepline runs stop play/my-play/run/20260501t000000-000 --reason "stale lock" --json
|
|
6397
6855
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
6398
|
-
deepline runs logs play/my-play/run/20260501t000000-000
|
|
6399
6856
|
`
|
|
6400
6857
|
);
|
|
6401
|
-
runs.command("
|
|
6402
|
-
process.exitCode = await
|
|
6858
|
+
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) => {
|
|
6859
|
+
process.exitCode = await handleRunGet([
|
|
6403
6860
|
runId,
|
|
6404
6861
|
...options.json ? ["--json"] : [],
|
|
6405
6862
|
...options.full ? ["--full"] : []
|
|
6406
6863
|
]);
|
|
6407
6864
|
});
|
|
6408
|
-
runs.command("
|
|
6409
|
-
process.exitCode = await
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
options.
|
|
6865
|
+
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) => {
|
|
6866
|
+
process.exitCode = await handleRunsList([
|
|
6867
|
+
"--play",
|
|
6868
|
+
options.play,
|
|
6869
|
+
...options.status ? ["--status", options.status] : [],
|
|
6870
|
+
...options.compact ? ["--compact"] : [],
|
|
6413
6871
|
...options.json ? ["--json"] : []
|
|
6414
6872
|
]);
|
|
6415
6873
|
});
|
|
6416
|
-
runs.command("
|
|
6874
|
+
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) => {
|
|
6875
|
+
process.exitCode = await handleRunTail([
|
|
6876
|
+
runId,
|
|
6877
|
+
...options.json ? ["--json"] : [],
|
|
6878
|
+
...options.compact ? ["--compact"] : [],
|
|
6879
|
+
...options.cursor ? ["--cursor", options.cursor] : []
|
|
6880
|
+
]);
|
|
6881
|
+
});
|
|
6882
|
+
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) => {
|
|
6417
6883
|
process.exitCode = await handleRunLogs([
|
|
6418
6884
|
runId,
|
|
6885
|
+
...options.limit ? ["--limit", options.limit] : [],
|
|
6886
|
+
...options.out ? ["--out", options.out] : [],
|
|
6887
|
+
...options.json ? ["--json"] : []
|
|
6888
|
+
]);
|
|
6889
|
+
});
|
|
6890
|
+
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) => {
|
|
6891
|
+
process.exitCode = await handleRunStop([
|
|
6892
|
+
runId,
|
|
6893
|
+
...options.reason ? ["--reason", options.reason] : [],
|
|
6894
|
+
...options.json ? ["--json"] : []
|
|
6895
|
+
]);
|
|
6896
|
+
});
|
|
6897
|
+
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) => {
|
|
6898
|
+
process.exitCode = await handleRunExport([
|
|
6899
|
+
runId,
|
|
6900
|
+
"--out",
|
|
6901
|
+
options.out,
|
|
6419
6902
|
...options.json ? ["--json"] : []
|
|
6420
6903
|
]);
|
|
6421
6904
|
});
|
|
6422
6905
|
}
|
|
6423
6906
|
|
|
6907
|
+
// src/cli/commands/tools.ts
|
|
6908
|
+
import { chmodSync, mkdtempSync, writeFileSync as writeFileSync6 } from "fs";
|
|
6909
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
6910
|
+
import { join as join8 } from "path";
|
|
6911
|
+
|
|
6424
6912
|
// src/tool-output.ts
|
|
6425
6913
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
6426
6914
|
import { homedir as homedir3 } from "os";
|
|
@@ -6589,31 +7077,23 @@ async function listTools(args) {
|
|
|
6589
7077
|
}
|
|
6590
7078
|
return 0;
|
|
6591
7079
|
}
|
|
6592
|
-
function
|
|
6593
|
-
const
|
|
6594
|
-
tool.toolId,
|
|
6595
|
-
tool.provider,
|
|
6596
|
-
tool.displayName,
|
|
6597
|
-
tool.description,
|
|
6598
|
-
tool.operation,
|
|
6599
|
-
tool.operationId,
|
|
6600
|
-
...tool.operationAliases ?? [],
|
|
6601
|
-
...tool.categories ?? [],
|
|
6602
|
-
tool.inputSchema ? JSON.stringify(tool.inputSchema) : ""
|
|
6603
|
-
].filter(Boolean).join(" ").toLowerCase();
|
|
6604
|
-
return terms.every((term) => haystack.includes(term));
|
|
6605
|
-
}
|
|
6606
|
-
async function searchTools(args) {
|
|
6607
|
-
const query = args[0]?.trim();
|
|
7080
|
+
async function searchTools(queryInput, options = {}) {
|
|
7081
|
+
const query = queryInput.trim();
|
|
6608
7082
|
if (!query) {
|
|
6609
7083
|
console.error("Usage: deepline tools search <query> [--json]");
|
|
6610
7084
|
return 1;
|
|
6611
7085
|
}
|
|
6612
|
-
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
6613
7086
|
const client = new DeeplineClient();
|
|
6614
|
-
const
|
|
6615
|
-
|
|
6616
|
-
|
|
7087
|
+
const result = await client.searchTools({
|
|
7088
|
+
query,
|
|
7089
|
+
categories: options.categories,
|
|
7090
|
+
searchTerms: options.searchTerms,
|
|
7091
|
+
searchMode: options.searchMode,
|
|
7092
|
+
includeSearchDebug: options.includeSearchDebug
|
|
7093
|
+
});
|
|
7094
|
+
const items = result.tools.map(toListedTool);
|
|
7095
|
+
if (options.json || shouldEmitJson()) {
|
|
7096
|
+
process.stdout.write(`${JSON.stringify({ ...result, tools: items })}
|
|
6617
7097
|
`);
|
|
6618
7098
|
return 0;
|
|
6619
7099
|
}
|
|
@@ -6667,11 +7147,14 @@ Common commands:
|
|
|
6667
7147
|
...options.json ? ["--json"] : []
|
|
6668
7148
|
]);
|
|
6669
7149
|
});
|
|
6670
|
-
tools.command("search <query>").description("Search available tools.").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
6671
|
-
process.exitCode = await searchTools(
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
7150
|
+
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) => {
|
|
7151
|
+
process.exitCode = await searchTools(query, {
|
|
7152
|
+
json: options.json,
|
|
7153
|
+
categories: options.categories,
|
|
7154
|
+
searchTerms: options.searchTerms ?? options.search_terms,
|
|
7155
|
+
searchMode: options.searchMode === "v1" || options.searchMode === "v2" ? options.searchMode : void 0,
|
|
7156
|
+
includeSearchDebug: Boolean(options.includeSearchDebug)
|
|
7157
|
+
});
|
|
6675
7158
|
});
|
|
6676
7159
|
tools.command("get <toolId>").alias("describe").description("Show metadata for a tool.").addHelpText(
|
|
6677
7160
|
"after",
|
|
@@ -7029,6 +7512,61 @@ function parseExecuteOptions(args) {
|
|
|
7029
7512
|
}
|
|
7030
7513
|
return { toolId, params, outputFormat, noPreview };
|
|
7031
7514
|
}
|
|
7515
|
+
function safeFileStem(value) {
|
|
7516
|
+
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
7517
|
+
}
|
|
7518
|
+
function shellQuote(value) {
|
|
7519
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
7520
|
+
}
|
|
7521
|
+
function powerShellQuote(value) {
|
|
7522
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
7523
|
+
}
|
|
7524
|
+
function seedToolListScript(input) {
|
|
7525
|
+
const stem = safeFileStem(input.toolId);
|
|
7526
|
+
const fileName = `${stem}-workflow-seed-${Date.now()}.play.ts`;
|
|
7527
|
+
const scriptDir = mkdtempSync(join8(tmpdir3(), "deepline-workflow-seed-"));
|
|
7528
|
+
chmodSync(scriptDir, 448);
|
|
7529
|
+
const scriptPath = join8(scriptDir, fileName);
|
|
7530
|
+
const projectDir = `deepline/projects/${stem}-workflow`;
|
|
7531
|
+
const playName = `${stem}-workflow`;
|
|
7532
|
+
const sampleRows = input.rows.length > 0 ? `${JSON.stringify(input.rows.slice(0, 2)).replace(/\]$/, "")}, ...]` : "[]";
|
|
7533
|
+
const columns = Object.keys(input.rows[0] ?? {}).join(", ");
|
|
7534
|
+
const rowKey = Object.prototype.hasOwnProperty.call(input.rows[0] ?? {}, "id") ? '"id"' : "(row) => JSON.stringify(row)";
|
|
7535
|
+
const script = `import { definePlay } from 'deepline';
|
|
7536
|
+
|
|
7537
|
+
export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
7538
|
+
const result = await ctx.tools.execute({
|
|
7539
|
+
id: ${JSON.stringify(input.toolId)},
|
|
7540
|
+
tool: ${JSON.stringify(input.toolId)},
|
|
7541
|
+
input: ${JSON.stringify(input.payload)},
|
|
7542
|
+
description: ${JSON.stringify(`Seed ${input.toolId} rows for workflow expansion.`)},
|
|
7543
|
+
});
|
|
7544
|
+
|
|
7545
|
+
const list = Object.values(result.lists)[0];
|
|
7546
|
+
const rows = (list?.get() ?? []).slice(0, 100);
|
|
7547
|
+
// ${sampleRows}
|
|
7548
|
+
// columns: ${columns}
|
|
7549
|
+
// .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.' }))
|
|
7550
|
+
// .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.' }))
|
|
7551
|
+
// ctx.map is idempotent by map key + row key; reruns reuse completed rows.
|
|
7552
|
+
const enrichedData = await ctx
|
|
7553
|
+
.map('enriched_data', rows, { key: ${rowKey} })
|
|
7554
|
+
.run({ description: 'Enrich seeded rows.' });
|
|
7555
|
+
|
|
7556
|
+
return {
|
|
7557
|
+
rows: enrichedData,
|
|
7558
|
+
count: await enrichedData.count(),
|
|
7559
|
+
};
|
|
7560
|
+
});
|
|
7561
|
+
`;
|
|
7562
|
+
writeFileSync6(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
7563
|
+
return {
|
|
7564
|
+
path: scriptPath,
|
|
7565
|
+
projectDir,
|
|
7566
|
+
macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
|
|
7567
|
+
windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
|
|
7568
|
+
};
|
|
7569
|
+
}
|
|
7032
7570
|
async function executeTool(args) {
|
|
7033
7571
|
let parsed;
|
|
7034
7572
|
try {
|
|
@@ -7082,6 +7620,11 @@ async function executeTool(args) {
|
|
|
7082
7620
|
return 0;
|
|
7083
7621
|
}
|
|
7084
7622
|
const csv = writeCsvOutputFile(listConversion.rows, `${parsed.toolId}_output`);
|
|
7623
|
+
const seededScript = seedToolListScript({
|
|
7624
|
+
toolId: parsed.toolId,
|
|
7625
|
+
payload: parsed.params,
|
|
7626
|
+
rows: listConversion.rows
|
|
7627
|
+
});
|
|
7085
7628
|
if (parsed.outputFormat === "csv_file") {
|
|
7086
7629
|
process.stdout.write(`${JSON.stringify({
|
|
7087
7630
|
extracted_csv: csv.path,
|
|
@@ -7090,6 +7633,12 @@ async function executeTool(args) {
|
|
|
7090
7633
|
preview: csv.preview,
|
|
7091
7634
|
list_strategy: listConversion.strategy,
|
|
7092
7635
|
list_source_path: listConversion.sourcePath,
|
|
7636
|
+
starter_script: seededScript.path,
|
|
7637
|
+
project_dir: seededScript.projectDir,
|
|
7638
|
+
copy_to_project: {
|
|
7639
|
+
macos_linux: seededScript.macCopyCommand,
|
|
7640
|
+
windows_powershell: seededScript.windowsCopyCommand
|
|
7641
|
+
},
|
|
7093
7642
|
summary
|
|
7094
7643
|
})}
|
|
7095
7644
|
`);
|
|
@@ -7107,14 +7656,20 @@ async function executeTool(args) {
|
|
|
7107
7656
|
console.log(`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`);
|
|
7108
7657
|
}
|
|
7109
7658
|
console.log(`preview: ${JSON.stringify(csv.preview)}`);
|
|
7659
|
+
console.log(`starter script: ${seededScript.path}`);
|
|
7660
|
+
console.log(
|
|
7661
|
+
"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."
|
|
7662
|
+
);
|
|
7663
|
+
console.log(`macOS/Linux: ${seededScript.macCopyCommand}`);
|
|
7664
|
+
console.log(`Windows PowerShell: ${seededScript.windowsCopyCommand}`);
|
|
7110
7665
|
return 0;
|
|
7111
7666
|
}
|
|
7112
7667
|
|
|
7113
7668
|
// src/cli/skills-sync.ts
|
|
7114
|
-
import { spawn } from "child_process";
|
|
7115
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as
|
|
7669
|
+
import { spawn, spawnSync } from "child_process";
|
|
7670
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
7116
7671
|
import { homedir as homedir4 } from "os";
|
|
7117
|
-
import { dirname as dirname7, join as
|
|
7672
|
+
import { dirname as dirname7, join as join9 } from "path";
|
|
7118
7673
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
7119
7674
|
var SDK_SKILL_NAME = "deepline-sdk";
|
|
7120
7675
|
var SKILL_AGENTS = ["codex", "claude-code", "cursor"];
|
|
@@ -7125,7 +7680,7 @@ function shouldSkipSkillsSync() {
|
|
|
7125
7680
|
}
|
|
7126
7681
|
function sdkSkillsVersionPath(baseUrl) {
|
|
7127
7682
|
const home = process.env.HOME?.trim() || homedir4();
|
|
7128
|
-
return
|
|
7683
|
+
return join9(home, ".local", "deepline", baseUrlSlug(baseUrl), "sdk-skills", ".version");
|
|
7129
7684
|
}
|
|
7130
7685
|
function readLocalSkillsVersion(baseUrl) {
|
|
7131
7686
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
@@ -7139,7 +7694,7 @@ function readLocalSkillsVersion(baseUrl) {
|
|
|
7139
7694
|
function writeLocalSkillsVersion(baseUrl, version) {
|
|
7140
7695
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
7141
7696
|
mkdirSync4(dirname7(path), { recursive: true });
|
|
7142
|
-
|
|
7697
|
+
writeFileSync7(path, `${version}
|
|
7143
7698
|
`, "utf-8");
|
|
7144
7699
|
}
|
|
7145
7700
|
async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
@@ -7170,9 +7725,10 @@ async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
|
7170
7725
|
clearTimeout(timeout);
|
|
7171
7726
|
}
|
|
7172
7727
|
}
|
|
7173
|
-
function
|
|
7728
|
+
function buildSkillsInstallArgs(baseUrl) {
|
|
7174
7729
|
const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
|
|
7175
|
-
|
|
7730
|
+
return [
|
|
7731
|
+
"--yes",
|
|
7176
7732
|
"skills",
|
|
7177
7733
|
"add",
|
|
7178
7734
|
packageUrl,
|
|
@@ -7184,8 +7740,56 @@ function runSkillsInstall(baseUrl) {
|
|
|
7184
7740
|
SDK_SKILL_NAME,
|
|
7185
7741
|
"--full-depth"
|
|
7186
7742
|
];
|
|
7743
|
+
}
|
|
7744
|
+
function buildBunxSkillsInstallArgs(baseUrl) {
|
|
7745
|
+
const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
|
|
7746
|
+
return [
|
|
7747
|
+
"--bun",
|
|
7748
|
+
"skills",
|
|
7749
|
+
"add",
|
|
7750
|
+
packageUrl,
|
|
7751
|
+
"--agents",
|
|
7752
|
+
...SKILL_AGENTS,
|
|
7753
|
+
"--global",
|
|
7754
|
+
"--yes",
|
|
7755
|
+
"--skill",
|
|
7756
|
+
SDK_SKILL_NAME,
|
|
7757
|
+
"--full-depth"
|
|
7758
|
+
];
|
|
7759
|
+
}
|
|
7760
|
+
function hasCommand(command) {
|
|
7761
|
+
const result = spawnSync(command, ["--version"], {
|
|
7762
|
+
stdio: "ignore",
|
|
7763
|
+
shell: process.platform === "win32"
|
|
7764
|
+
});
|
|
7765
|
+
return result.status === 0;
|
|
7766
|
+
}
|
|
7767
|
+
function shellQuote2(arg) {
|
|
7768
|
+
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
7769
|
+
}
|
|
7770
|
+
function resolveSkillsInstallCommands(baseUrl) {
|
|
7771
|
+
const npxArgs = buildSkillsInstallArgs(baseUrl);
|
|
7772
|
+
const npxInstall = {
|
|
7773
|
+
command: "npx",
|
|
7774
|
+
args: npxArgs,
|
|
7775
|
+
manualCommand: `npx ${npxArgs.map(shellQuote2).join(" ")}`
|
|
7776
|
+
};
|
|
7777
|
+
if (hasCommand("bunx")) {
|
|
7778
|
+
const bunxArgs = buildBunxSkillsInstallArgs(baseUrl);
|
|
7779
|
+
return [
|
|
7780
|
+
{
|
|
7781
|
+
command: "bunx",
|
|
7782
|
+
args: bunxArgs,
|
|
7783
|
+
manualCommand: `bunx ${bunxArgs.map(shellQuote2).join(" ")}`
|
|
7784
|
+
},
|
|
7785
|
+
npxInstall
|
|
7786
|
+
];
|
|
7787
|
+
}
|
|
7788
|
+
return [npxInstall];
|
|
7789
|
+
}
|
|
7790
|
+
function runOneSkillsInstall(install) {
|
|
7187
7791
|
return new Promise((resolve8) => {
|
|
7188
|
-
const child = spawn(
|
|
7792
|
+
const child = spawn(install.command, install.args, {
|
|
7189
7793
|
stdio: ["ignore", "ignore", "pipe"],
|
|
7190
7794
|
env: process.env
|
|
7191
7795
|
});
|
|
@@ -7194,37 +7798,63 @@ function runSkillsInstall(baseUrl) {
|
|
|
7194
7798
|
stderr += chunk.toString("utf-8");
|
|
7195
7799
|
});
|
|
7196
7800
|
child.on("error", (error) => {
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7801
|
+
resolve8({
|
|
7802
|
+
ok: false,
|
|
7803
|
+
detail: `failed to start ${install.command}: ${error.message}`,
|
|
7804
|
+
manualCommand: install.manualCommand
|
|
7805
|
+
});
|
|
7200
7806
|
});
|
|
7201
7807
|
child.on("close", (code) => {
|
|
7202
7808
|
if (code === 0) {
|
|
7203
|
-
resolve8(true);
|
|
7809
|
+
resolve8({ ok: true, detail: "", manualCommand: install.manualCommand });
|
|
7204
7810
|
return;
|
|
7205
7811
|
}
|
|
7206
7812
|
const detail = stderr.trim();
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
);
|
|
7212
|
-
resolve8(false);
|
|
7813
|
+
resolve8({
|
|
7814
|
+
ok: false,
|
|
7815
|
+
detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
|
|
7816
|
+
manualCommand: install.manualCommand
|
|
7817
|
+
});
|
|
7213
7818
|
});
|
|
7214
7819
|
});
|
|
7215
7820
|
}
|
|
7821
|
+
async function runSkillsInstall(baseUrl) {
|
|
7822
|
+
const failures = [];
|
|
7823
|
+
for (const install of resolveSkillsInstallCommands(baseUrl)) {
|
|
7824
|
+
const result = await runOneSkillsInstall(install);
|
|
7825
|
+
if (result.ok) return true;
|
|
7826
|
+
failures.push(result);
|
|
7827
|
+
}
|
|
7828
|
+
const details = failures.map((failure) => failure.detail).filter(Boolean).join("\n");
|
|
7829
|
+
const manualCommand = failures.at(-1)?.manualCommand;
|
|
7830
|
+
process.stderr.write(
|
|
7831
|
+
`SDK skills sync failed${details ? `:
|
|
7832
|
+
${details}` : ""}
|
|
7833
|
+
` + (manualCommand ? `Run manually: ${manualCommand}
|
|
7834
|
+
` : "")
|
|
7835
|
+
);
|
|
7836
|
+
return false;
|
|
7837
|
+
}
|
|
7838
|
+
function writeSdkSkillsStatusLine(line) {
|
|
7839
|
+
const progress = getActiveCliProgress();
|
|
7840
|
+
if (progress) {
|
|
7841
|
+
progress.writeLine(line);
|
|
7842
|
+
return;
|
|
7843
|
+
}
|
|
7844
|
+
process.stderr.write(`${line}
|
|
7845
|
+
`);
|
|
7846
|
+
}
|
|
7216
7847
|
async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
7217
7848
|
if (attemptedSync || shouldSkipSkillsSync()) return;
|
|
7218
7849
|
attemptedSync = true;
|
|
7219
7850
|
const localVersion = readLocalSkillsVersion(baseUrl);
|
|
7220
7851
|
const update = await fetchSkillsUpdate(baseUrl, localVersion);
|
|
7221
7852
|
if (!update?.needsUpdate || !update.remoteVersion) return;
|
|
7222
|
-
|
|
7223
|
-
progress?.writeLine("SDK skills changed; syncing deepline-sdk skill...") ?? process.stderr.write("SDK skills changed; syncing deepline-sdk skill...\n");
|
|
7853
|
+
writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-sdk skill...");
|
|
7224
7854
|
const installed = await runSkillsInstall(baseUrl);
|
|
7225
7855
|
if (!installed) return;
|
|
7226
7856
|
writeLocalSkillsVersion(baseUrl, update.remoteVersion);
|
|
7227
|
-
|
|
7857
|
+
writeSdkSkillsStatusLine("SDK skills are up to date.");
|
|
7228
7858
|
}
|
|
7229
7859
|
|
|
7230
7860
|
// src/cli/trace.ts
|
|
@@ -7386,4 +8016,3 @@ Output:
|
|
|
7386
8016
|
process.exit(process.exitCode ?? 0);
|
|
7387
8017
|
}
|
|
7388
8018
|
main();
|
|
7389
|
-
//# sourceMappingURL=index.mjs.map
|