deepline 0.1.12 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -6
- package/dist/cli/index.js +1298 -711
- package/dist/cli/index.mjs +1294 -707
- package/dist/index.d.mts +199 -23
- package/dist/index.d.ts +199 -23
- package/dist/index.js +219 -13
- package/dist/index.mjs +219 -13
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +68 -12
- package/dist/repo/apps/play-runner-workers/src/entry.ts +241 -51
- 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 +10 -2
- 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/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.19";
|
|
247
|
+
var SDK_API_CONTRACT = "2026-05-runs-v2";
|
|
174
248
|
|
|
175
249
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
176
250
|
var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
|
|
@@ -363,8 +437,12 @@ var HttpClient = class {
|
|
|
363
437
|
* @param path - API path
|
|
364
438
|
* @param body - Request body (will be JSON-serialized)
|
|
365
439
|
*/
|
|
366
|
-
async post(path, body) {
|
|
367
|
-
return this.request(path, {
|
|
440
|
+
async post(path, body, headers) {
|
|
441
|
+
return this.request(path, {
|
|
442
|
+
method: "POST",
|
|
443
|
+
body,
|
|
444
|
+
headers
|
|
445
|
+
});
|
|
368
446
|
}
|
|
369
447
|
/**
|
|
370
448
|
* Send a DELETE request.
|
|
@@ -444,6 +522,7 @@ function sleep(ms) {
|
|
|
444
522
|
|
|
445
523
|
// src/client.ts
|
|
446
524
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
525
|
+
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
447
526
|
function isRecord(value) {
|
|
448
527
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
449
528
|
}
|
|
@@ -476,6 +555,7 @@ function mapLegacyTemporalStatus(status) {
|
|
|
476
555
|
var DeeplineClient = class {
|
|
477
556
|
http;
|
|
478
557
|
config;
|
|
558
|
+
runs;
|
|
479
559
|
/**
|
|
480
560
|
* @param options - Optional overrides for API key, base URL, timeout, and retries.
|
|
481
561
|
* @throws {@link ConfigError} if no API key can be resolved from any source.
|
|
@@ -483,6 +563,13 @@ var DeeplineClient = class {
|
|
|
483
563
|
constructor(options) {
|
|
484
564
|
this.config = resolveConfig(options);
|
|
485
565
|
this.http = new HttpClient(this.config);
|
|
566
|
+
this.runs = {
|
|
567
|
+
get: (runId) => this.getRunStatus(runId),
|
|
568
|
+
list: (options2) => this.listRuns(options2),
|
|
569
|
+
tail: (runId, options2) => this.tailRun(runId, options2),
|
|
570
|
+
logs: (runId, options2) => this.getRunLogs(runId, options2),
|
|
571
|
+
stop: (runId, options2) => this.stopRun(runId, options2)
|
|
572
|
+
};
|
|
486
573
|
}
|
|
487
574
|
/** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
|
|
488
575
|
get baseUrl() {
|
|
@@ -571,6 +658,31 @@ var DeeplineClient = class {
|
|
|
571
658
|
);
|
|
572
659
|
return res.tools;
|
|
573
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* Search available tools using Deepline's ranked backend search.
|
|
663
|
+
*
|
|
664
|
+
* This is the same discovery surface used by the legacy CLI: it ranks across
|
|
665
|
+
* tool metadata, categories, agent guidance, and input schema fields.
|
|
666
|
+
*/
|
|
667
|
+
async searchTools(options = {}) {
|
|
668
|
+
const params = new URLSearchParams();
|
|
669
|
+
const query = options.query?.trim() ?? "";
|
|
670
|
+
params.set("q", query);
|
|
671
|
+
params.set(
|
|
672
|
+
"include_search_debug",
|
|
673
|
+
options.includeSearchDebug ? "true" : "false"
|
|
674
|
+
);
|
|
675
|
+
params.set("search_mode", options.searchMode ?? "v2");
|
|
676
|
+
if (options.categories?.trim()) {
|
|
677
|
+
params.set("categories", options.categories.trim());
|
|
678
|
+
}
|
|
679
|
+
if (options.searchTerms?.trim()) {
|
|
680
|
+
params.set("search_terms", options.searchTerms.trim());
|
|
681
|
+
}
|
|
682
|
+
return this.http.get(
|
|
683
|
+
`/api/v2/integrations/list?${params.toString()}`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
574
686
|
/**
|
|
575
687
|
* Get detailed metadata for a single tool.
|
|
576
688
|
*
|
|
@@ -606,12 +718,17 @@ var DeeplineClient = class {
|
|
|
606
718
|
* Top-level fields such as `status`, `job_id`, and `billing` describe the
|
|
607
719
|
* Deepline execution.
|
|
608
720
|
*/
|
|
609
|
-
async executeTool(toolId, input) {
|
|
721
|
+
async executeTool(toolId, input, options) {
|
|
722
|
+
const headers = options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : void 0;
|
|
610
723
|
return this.http.post(
|
|
611
724
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
612
|
-
{ payload: input }
|
|
725
|
+
{ payload: input },
|
|
726
|
+
headers
|
|
613
727
|
);
|
|
614
728
|
}
|
|
729
|
+
async executeToolRaw(toolId, input, options) {
|
|
730
|
+
return this.executeTool(toolId, input, options);
|
|
731
|
+
}
|
|
615
732
|
async queryCustomerDb(input) {
|
|
616
733
|
return this.http.post("/api/v2/db/query", {
|
|
617
734
|
sql: input.sql,
|
|
@@ -660,6 +777,7 @@ var DeeplineClient = class {
|
|
|
660
777
|
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
661
778
|
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
662
779
|
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
780
|
+
...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
|
|
663
781
|
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
664
782
|
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
665
783
|
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
@@ -684,6 +802,7 @@ var DeeplineClient = class {
|
|
|
684
802
|
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
685
803
|
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
686
804
|
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
805
|
+
...request.sourceFiles ? { sourceFiles: request.sourceFiles } : {},
|
|
687
806
|
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
688
807
|
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
689
808
|
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
@@ -720,6 +839,7 @@ var DeeplineClient = class {
|
|
|
720
839
|
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
721
840
|
name: input.name,
|
|
722
841
|
sourceCode: input.sourceCode,
|
|
842
|
+
sourceFiles: input.sourceFiles,
|
|
723
843
|
artifact: input.artifact
|
|
724
844
|
});
|
|
725
845
|
return this.http.post("/api/v2/plays/artifacts", {
|
|
@@ -734,6 +854,7 @@ var DeeplineClient = class {
|
|
|
734
854
|
compilerManifest: artifact.compilerManifest ?? await this.compilePlayManifest({
|
|
735
855
|
name: artifact.name,
|
|
736
856
|
sourceCode: artifact.sourceCode,
|
|
857
|
+
sourceFiles: artifact.sourceFiles,
|
|
737
858
|
artifact: artifact.artifact
|
|
738
859
|
})
|
|
739
860
|
}))
|
|
@@ -760,11 +881,13 @@ var DeeplineClient = class {
|
|
|
760
881
|
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
761
882
|
name: input.name,
|
|
762
883
|
sourceCode: input.sourceCode,
|
|
884
|
+
sourceFiles: input.sourceFiles,
|
|
763
885
|
artifact: input.artifact
|
|
764
886
|
});
|
|
765
887
|
const registeredArtifact = await this.registerPlayArtifact({
|
|
766
888
|
name: input.name,
|
|
767
889
|
sourceCode: input.sourceCode,
|
|
890
|
+
sourceFiles: input.sourceFiles,
|
|
768
891
|
artifact: input.artifact,
|
|
769
892
|
compilerManifest,
|
|
770
893
|
publish: false
|
|
@@ -824,11 +947,13 @@ var DeeplineClient = class {
|
|
|
824
947
|
const compilerManifest = options?.compilerManifest ?? await this.compilePlayManifest({
|
|
825
948
|
name,
|
|
826
949
|
sourceCode,
|
|
950
|
+
sourceFiles: options?.sourceFiles,
|
|
827
951
|
artifact
|
|
828
952
|
});
|
|
829
953
|
const registeredArtifact = await this.registerPlayArtifact({
|
|
830
954
|
name,
|
|
831
955
|
sourceCode,
|
|
956
|
+
sourceFiles: options?.sourceFiles,
|
|
832
957
|
artifact,
|
|
833
958
|
compilerManifest,
|
|
834
959
|
publish: false
|
|
@@ -1012,6 +1137,112 @@ var DeeplineClient = class {
|
|
|
1012
1137
|
);
|
|
1013
1138
|
return response.runs ?? [];
|
|
1014
1139
|
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Get a run by id using the public runs resource model.
|
|
1142
|
+
*
|
|
1143
|
+
* This is the SDK equivalent of:
|
|
1144
|
+
*
|
|
1145
|
+
* ```bash
|
|
1146
|
+
* deepline runs get <run-id> --json
|
|
1147
|
+
* ```
|
|
1148
|
+
*/
|
|
1149
|
+
async getRunStatus(runId) {
|
|
1150
|
+
const response = await this.http.get(
|
|
1151
|
+
`/api/v2/runs/${encodeURIComponent(runId)}`
|
|
1152
|
+
);
|
|
1153
|
+
return normalizePlayStatus(response);
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* List play runs using the public runs resource model.
|
|
1157
|
+
*
|
|
1158
|
+
* This is the SDK equivalent of:
|
|
1159
|
+
*
|
|
1160
|
+
* ```bash
|
|
1161
|
+
* deepline runs list --play <play-name> --status failed --json
|
|
1162
|
+
* ```
|
|
1163
|
+
*/
|
|
1164
|
+
async listRuns(options) {
|
|
1165
|
+
const playName = options.play.trim();
|
|
1166
|
+
if (!playName) {
|
|
1167
|
+
throw new Error("runs.list requires options.play.");
|
|
1168
|
+
}
|
|
1169
|
+
const params = new URLSearchParams({ play: playName });
|
|
1170
|
+
const status = options.status?.trim();
|
|
1171
|
+
if (status) {
|
|
1172
|
+
params.set("status", status);
|
|
1173
|
+
}
|
|
1174
|
+
const response = await this.http.get(
|
|
1175
|
+
`/api/v2/runs?${params.toString()}`
|
|
1176
|
+
);
|
|
1177
|
+
return response.runs ?? [];
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Fetch the lightweight tail status for a run using the public runs resource model.
|
|
1181
|
+
*
|
|
1182
|
+
* This is the SDK equivalent of:
|
|
1183
|
+
*
|
|
1184
|
+
* ```bash
|
|
1185
|
+
* deepline runs tail <run-id> --json
|
|
1186
|
+
* ```
|
|
1187
|
+
*/
|
|
1188
|
+
async tailRun(runId, options) {
|
|
1189
|
+
const afterLogIndex = typeof options?.afterLogIndex === "number" ? options.afterLogIndex : typeof options?.cursor === "number" ? options.cursor : typeof options?.cursor === "string" && options.cursor.trim() ? Number(options.cursor) : void 0;
|
|
1190
|
+
const params = new URLSearchParams();
|
|
1191
|
+
if (Number.isFinite(afterLogIndex)) {
|
|
1192
|
+
params.set("afterLogIndex", String(Number(afterLogIndex)));
|
|
1193
|
+
}
|
|
1194
|
+
if (typeof options?.waitMs === "number") {
|
|
1195
|
+
params.set("waitMs", String(options.waitMs));
|
|
1196
|
+
}
|
|
1197
|
+
if (options?.terminalOnly) {
|
|
1198
|
+
params.set("terminalOnly", "true");
|
|
1199
|
+
}
|
|
1200
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
1201
|
+
const response = await this.http.get(
|
|
1202
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/tail${suffix}`
|
|
1203
|
+
);
|
|
1204
|
+
return normalizePlayStatus(response);
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Fetch persisted logs for a run using the public runs resource model.
|
|
1208
|
+
*
|
|
1209
|
+
* This is the SDK equivalent of:
|
|
1210
|
+
*
|
|
1211
|
+
* ```bash
|
|
1212
|
+
* deepline runs logs <run-id> --limit 200 --json
|
|
1213
|
+
* ```
|
|
1214
|
+
*/
|
|
1215
|
+
async getRunLogs(runId, options) {
|
|
1216
|
+
const status = await this.getRunStatus(runId);
|
|
1217
|
+
const logs = status.progress?.logs ?? [];
|
|
1218
|
+
const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
|
|
1219
|
+
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
1220
|
+
return {
|
|
1221
|
+
runId: status.runId,
|
|
1222
|
+
totalCount: logs.length,
|
|
1223
|
+
returnedCount: entries.length,
|
|
1224
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
1225
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
1226
|
+
truncated: logs.length > entries.length,
|
|
1227
|
+
hasMore: logs.length > entries.length,
|
|
1228
|
+
entries
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Stop a run by id using the public runs resource model.
|
|
1233
|
+
*
|
|
1234
|
+
* This is the SDK equivalent of:
|
|
1235
|
+
*
|
|
1236
|
+
* ```bash
|
|
1237
|
+
* deepline runs stop <run-id> --reason "stale lock" --json
|
|
1238
|
+
* ```
|
|
1239
|
+
*/
|
|
1240
|
+
async stopRun(runId, options) {
|
|
1241
|
+
return this.http.post(
|
|
1242
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/stop`,
|
|
1243
|
+
options?.reason ? { reason: options.reason } : {}
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1015
1246
|
async listPlays() {
|
|
1016
1247
|
const response = await this.http.get(
|
|
1017
1248
|
"/api/v2/plays"
|
|
@@ -1398,6 +1629,7 @@ function saveEnvValues(values, baseUrl) {
|
|
|
1398
1629
|
const merged = { ...existing, ...values };
|
|
1399
1630
|
const lines = Object.entries(merged).filter(([, v]) => v !== "").map(([k, v]) => `${k}=${v}`);
|
|
1400
1631
|
writeFileSync2(filePath, lines.join("\n") + "\n", "utf-8");
|
|
1632
|
+
saveProjectDeeplineEnvValues(baseUrl, values);
|
|
1401
1633
|
}
|
|
1402
1634
|
async function httpJson(method, url, apiKey, body) {
|
|
1403
1635
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -1998,6 +2230,9 @@ function rowArray(value) {
|
|
|
1998
2230
|
function readNumber(value) {
|
|
1999
2231
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
|
|
2000
2232
|
}
|
|
2233
|
+
function numericStat(value) {
|
|
2234
|
+
return readNumber(value) ?? 0;
|
|
2235
|
+
}
|
|
2001
2236
|
function inferColumns(rows) {
|
|
2002
2237
|
const columns = [];
|
|
2003
2238
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2077,6 +2312,31 @@ function extractCanonicalRowsInfo(statusOrResult) {
|
|
|
2077
2312
|
function percentText(numerator, denominator) {
|
|
2078
2313
|
return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
|
|
2079
2314
|
}
|
|
2315
|
+
function isDatasetExecutionStatsInput(value) {
|
|
2316
|
+
return isRecord2(value) && isRecord2(value.columnStats) && Object.values(value.columnStats).every(isRecord2);
|
|
2317
|
+
}
|
|
2318
|
+
function extractDatasetExecutionStats(statusOrResult) {
|
|
2319
|
+
if (!isRecord2(statusOrResult)) {
|
|
2320
|
+
return null;
|
|
2321
|
+
}
|
|
2322
|
+
const direct = statusOrResult.dataset_execution_stats;
|
|
2323
|
+
if (isDatasetExecutionStatsInput(direct)) {
|
|
2324
|
+
return direct;
|
|
2325
|
+
}
|
|
2326
|
+
const nested = isRecord2(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
|
|
2327
|
+
return isDatasetExecutionStatsInput(nested) ? nested : null;
|
|
2328
|
+
}
|
|
2329
|
+
function formatExecutionStats(raw, denominator) {
|
|
2330
|
+
return {
|
|
2331
|
+
queued: percentText(numericStat(raw.queued), denominator),
|
|
2332
|
+
running: percentText(numericStat(raw.running), denominator),
|
|
2333
|
+
"completed:executed": percentText(numericStat(raw.completed), denominator),
|
|
2334
|
+
"completed:reused": percentText(numericStat(raw.cached), denominator),
|
|
2335
|
+
"skipped:condition": percentText(numericStat(raw.skipped), denominator),
|
|
2336
|
+
"skipped:missed": percentText(numericStat(raw.missed), denominator),
|
|
2337
|
+
failed: percentText(numericStat(raw.failed), denominator)
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2080
2340
|
function countPercentText(count, denominator) {
|
|
2081
2341
|
return denominator > 0 ? `${count} (${Math.round(100 * count / denominator)}%)` : "0 (0%)";
|
|
2082
2342
|
}
|
|
@@ -2169,7 +2429,7 @@ function compactCell(value) {
|
|
|
2169
2429
|
}
|
|
2170
2430
|
return compactScalar(parsed, 120);
|
|
2171
2431
|
}
|
|
2172
|
-
function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows)) {
|
|
2432
|
+
function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows), executionStats) {
|
|
2173
2433
|
const sanitized = sanitizeCsvProjectionInfo({ rows, columns });
|
|
2174
2434
|
const columnStats = {};
|
|
2175
2435
|
for (const column of sanitized.columns) {
|
|
@@ -2197,6 +2457,10 @@ function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns
|
|
|
2197
2457
|
non_empty: percentText(nonEmpty, denominator),
|
|
2198
2458
|
unique: valueCounts.size
|
|
2199
2459
|
};
|
|
2460
|
+
const rawExecutionStats = executionStats?.columnStats[column];
|
|
2461
|
+
if (rawExecutionStats) {
|
|
2462
|
+
stat3.execution = formatExecutionStats(rawExecutionStats, totalRows);
|
|
2463
|
+
}
|
|
2200
2464
|
if (sampleValue !== void 0 && sampleValueType) {
|
|
2201
2465
|
stat3.sample_value = sampleValue;
|
|
2202
2466
|
stat3.sample_type = sampleValueType;
|
|
@@ -2549,7 +2813,6 @@ import { tmpdir } from "os";
|
|
|
2549
2813
|
import { basename, dirname as dirname3, extname, isAbsolute, join as join3, resolve as resolve4 } from "path";
|
|
2550
2814
|
import { builtinModules, createRequire } from "module";
|
|
2551
2815
|
import { build } from "esbuild";
|
|
2552
|
-
import ts from "typescript";
|
|
2553
2816
|
|
|
2554
2817
|
// ../shared_libs/play-runtime/backend.ts
|
|
2555
2818
|
var PLAY_RUNTIME_BACKENDS = {
|
|
@@ -2633,56 +2896,6 @@ function formatEsbuildMessage(message) {
|
|
|
2633
2896
|
const location = message.location ? `${message.location.file}:${message.location.line}:${message.location.column}` : null;
|
|
2634
2897
|
return location ? `${location} ${message.text}` : message.text;
|
|
2635
2898
|
}
|
|
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
2899
|
function isLocalSpecifier(specifier) {
|
|
2687
2900
|
return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/") || specifier.startsWith("file:");
|
|
2688
2901
|
}
|
|
@@ -2706,11 +2919,8 @@ function assertWithinPlayWorkspace(input) {
|
|
|
2706
2919
|
if (isPathInsideDirectory(input.resolvedPath, input.workspace.rootDir)) {
|
|
2707
2920
|
return;
|
|
2708
2921
|
}
|
|
2709
|
-
const position = input.sourceFile.getLineAndCharacterOfPosition(
|
|
2710
|
-
input.node.getStart(input.sourceFile)
|
|
2711
|
-
);
|
|
2712
2922
|
throw new Error(
|
|
2713
|
-
`${input.importer}:${
|
|
2923
|
+
`${input.importer}:${input.line}:${input.column} Local play imports must stay inside the play workspace (${input.workspace.rootDir}). Import "${input.specifier}" resolved to ${input.resolvedPath}, which crosses into app/backend code. Use the public SDK/API surface or move shared helpers into the play workspace.`
|
|
2714
2924
|
);
|
|
2715
2925
|
}
|
|
2716
2926
|
function getPackageName(specifier) {
|
|
@@ -2720,72 +2930,135 @@ function getPackageName(specifier) {
|
|
|
2720
2930
|
}
|
|
2721
2931
|
return specifier.split("/")[0] ?? specifier;
|
|
2722
2932
|
}
|
|
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
2933
|
function isPlaySourceFile(filePath) {
|
|
2741
2934
|
return PLAY_SOURCE_FILE_PATTERN.test(filePath);
|
|
2742
2935
|
}
|
|
2743
|
-
function
|
|
2744
|
-
|
|
2745
|
-
|
|
2936
|
+
function stripCommentsToSpaces(source) {
|
|
2937
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
|
|
2938
|
+
/(^|[^:])\/\/.*$/gm,
|
|
2939
|
+
(match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
|
|
2940
|
+
);
|
|
2941
|
+
}
|
|
2942
|
+
function lineAndColumnAt(source, index) {
|
|
2943
|
+
const prefix = source.slice(0, index);
|
|
2944
|
+
const lines = prefix.split("\n");
|
|
2945
|
+
return { line: lines.length, column: lines[lines.length - 1].length + 1 };
|
|
2946
|
+
}
|
|
2947
|
+
function findSourceImportReferences(sourceCode) {
|
|
2948
|
+
const source = stripCommentsToSpaces(sourceCode);
|
|
2949
|
+
const references = [];
|
|
2950
|
+
const addReference = (specifier, specifierIndex, kind) => {
|
|
2951
|
+
if (!specifier) return;
|
|
2952
|
+
const position = lineAndColumnAt(sourceCode, specifierIndex);
|
|
2953
|
+
references.push({
|
|
2954
|
+
specifier,
|
|
2955
|
+
line: position.line,
|
|
2956
|
+
column: position.column,
|
|
2957
|
+
kind
|
|
2958
|
+
});
|
|
2959
|
+
};
|
|
2960
|
+
const staticImportPattern = /\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?(['"])([^'"\n]+)\1/g;
|
|
2961
|
+
for (const match of source.matchAll(staticImportPattern)) {
|
|
2962
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "static");
|
|
2963
|
+
}
|
|
2964
|
+
const dynamicImportPattern = /\bimport\s*\(\s*(['"])([^'"\n]+)\1/g;
|
|
2965
|
+
for (const match of source.matchAll(dynamicImportPattern)) {
|
|
2966
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "dynamic-import");
|
|
2967
|
+
}
|
|
2968
|
+
const requirePattern = /\brequire\s*\(\s*(['"])([^'"\n]+)\1/g;
|
|
2969
|
+
for (const match of source.matchAll(requirePattern)) {
|
|
2970
|
+
addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "require");
|
|
2971
|
+
}
|
|
2972
|
+
const literalDynamicImportIndexes = new Set(
|
|
2973
|
+
[...source.matchAll(dynamicImportPattern)].map((match) => match.index)
|
|
2974
|
+
);
|
|
2975
|
+
for (const match of source.matchAll(/\bimport\s*\(/g)) {
|
|
2976
|
+
if (literalDynamicImportIndexes.has(match.index)) continue;
|
|
2977
|
+
const position = lineAndColumnAt(sourceCode, match.index);
|
|
2978
|
+
throw new Error(
|
|
2979
|
+
`:${position.line}:${position.column} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
2980
|
+
);
|
|
2981
|
+
}
|
|
2982
|
+
const literalRequireIndexes = new Set(
|
|
2983
|
+
[...source.matchAll(requirePattern)].map((match) => match.index)
|
|
2984
|
+
);
|
|
2985
|
+
for (const match of source.matchAll(/\brequire\s*\(/g)) {
|
|
2986
|
+
if (literalRequireIndexes.has(match.index)) continue;
|
|
2987
|
+
const position = lineAndColumnAt(sourceCode, match.index);
|
|
2988
|
+
throw new Error(
|
|
2989
|
+
`:${position.line}:${position.column} Dynamic require() is not allowed in plays. Use static imports or require("literal") only.`
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
return references.sort(
|
|
2993
|
+
(left, right) => left.line === right.line ? left.column - right.column : left.line - right.line
|
|
2994
|
+
);
|
|
2995
|
+
}
|
|
2996
|
+
function unquoteStringLiteral(literal) {
|
|
2997
|
+
const trimmed = literal.trim();
|
|
2998
|
+
const quote = trimmed[0];
|
|
2999
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
3000
|
+
return null;
|
|
3001
|
+
}
|
|
3002
|
+
try {
|
|
3003
|
+
return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
|
|
3004
|
+
} catch {
|
|
3005
|
+
return trimmed.slice(1, -1);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
function findMatchingBrace(source, openIndex) {
|
|
3009
|
+
let depth = 0;
|
|
3010
|
+
let quote = null;
|
|
3011
|
+
let escaped = false;
|
|
3012
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
3013
|
+
const char = source[index];
|
|
3014
|
+
if (quote) {
|
|
3015
|
+
if (escaped) {
|
|
3016
|
+
escaped = false;
|
|
3017
|
+
} else if (char === "\\") {
|
|
3018
|
+
escaped = true;
|
|
3019
|
+
} else if (char === quote) {
|
|
3020
|
+
quote = null;
|
|
3021
|
+
}
|
|
2746
3022
|
continue;
|
|
2747
3023
|
}
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
if (!matches) {
|
|
3024
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3025
|
+
quote = char;
|
|
2751
3026
|
continue;
|
|
2752
3027
|
}
|
|
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
|
-
|
|
3028
|
+
if (char === "{") depth += 1;
|
|
3029
|
+
if (char === "}") {
|
|
3030
|
+
depth -= 1;
|
|
3031
|
+
if (depth === 0) return index;
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
return -1;
|
|
3035
|
+
}
|
|
3036
|
+
function extractDefinedPlayName(sourceCode, _filePath) {
|
|
3037
|
+
const source = stripCommentsToSpaces(sourceCode);
|
|
3038
|
+
const callPattern = /(?:\b[A-Za-z_$][\w$]*\s*\.\s*)?\b(?:definePlay|defineWorkflow)\s*\(/g;
|
|
3039
|
+
for (const match of source.matchAll(callPattern)) {
|
|
3040
|
+
const openParen = match.index + match[0].length - 1;
|
|
3041
|
+
const firstArgStart = openParen + 1;
|
|
3042
|
+
const firstNonSpace = source.slice(firstArgStart).search(/\S/);
|
|
3043
|
+
if (firstNonSpace < 0) continue;
|
|
3044
|
+
const argIndex = firstArgStart + firstNonSpace;
|
|
3045
|
+
const quote = source[argIndex];
|
|
3046
|
+
if (quote === '"' || quote === "'") {
|
|
3047
|
+
const literalMatch = source.slice(argIndex).match(/^(['"])(?:\\.|(?!\1)[\s\S])*\1/);
|
|
3048
|
+
const value = literalMatch ? unquoteStringLiteral(literalMatch[0]) : null;
|
|
3049
|
+
if (value?.trim()) return value.trim();
|
|
3050
|
+
}
|
|
3051
|
+
if (quote === "{") {
|
|
3052
|
+
const closeBrace = findMatchingBrace(source, argIndex);
|
|
3053
|
+
if (closeBrace < 0) continue;
|
|
3054
|
+
const objectSource = source.slice(argIndex + 1, closeBrace);
|
|
3055
|
+
const idMatch = objectSource.match(/(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/);
|
|
3056
|
+
if (idMatch?.[2]?.trim()) {
|
|
3057
|
+
return idMatch[2].trim();
|
|
2783
3058
|
}
|
|
2784
3059
|
}
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
visit(sourceFile);
|
|
2788
|
-
return detectedPlayName;
|
|
3060
|
+
}
|
|
3061
|
+
return null;
|
|
2789
3062
|
}
|
|
2790
3063
|
function getPackageRequireCandidates(fromFile) {
|
|
2791
3064
|
const candidates = [
|
|
@@ -3095,18 +3368,10 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3095
3368
|
if (extname(absolutePath).toLowerCase() === ".json") {
|
|
3096
3369
|
return;
|
|
3097
3370
|
}
|
|
3098
|
-
const
|
|
3099
|
-
absolutePath,
|
|
3100
|
-
sourceCode2,
|
|
3101
|
-
ts.ScriptTarget.Latest,
|
|
3102
|
-
true,
|
|
3103
|
-
scriptKindForFile(absolutePath)
|
|
3104
|
-
);
|
|
3105
|
-
const handleSpecifier = async (specifier, node, kind) => {
|
|
3371
|
+
const handleSpecifier = async (specifier, line, column, kind) => {
|
|
3106
3372
|
if (kind === "dynamic-import") {
|
|
3107
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3108
3373
|
throw new Error(
|
|
3109
|
-
`${absolutePath}:${
|
|
3374
|
+
`${absolutePath}:${line}:${column} Dynamic import() is not allowed in plays. Use static imports instead.`
|
|
3110
3375
|
);
|
|
3111
3376
|
}
|
|
3112
3377
|
if (NODE_BUILTIN_SET.has(specifier)) {
|
|
@@ -3120,16 +3385,15 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3120
3385
|
specifier,
|
|
3121
3386
|
resolvedPath: resolved,
|
|
3122
3387
|
workspace,
|
|
3123
|
-
|
|
3124
|
-
|
|
3388
|
+
line,
|
|
3389
|
+
column
|
|
3125
3390
|
});
|
|
3126
3391
|
if (resolved !== absoluteEntryFile && isPlaySourceFile(resolved)) {
|
|
3127
3392
|
const importedSource = await readFile(resolved, "utf-8");
|
|
3128
3393
|
const importedPlayName = extractDefinedPlayName(importedSource, resolved);
|
|
3129
3394
|
if (!importedPlayName) {
|
|
3130
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3131
3395
|
throw new Error(
|
|
3132
|
-
`${absolutePath}:${
|
|
3396
|
+
`${absolutePath}:${line}:${column} Imported play file "${specifier}" must export definePlay(...) so it can be runtime-composed.`
|
|
3133
3397
|
);
|
|
3134
3398
|
}
|
|
3135
3399
|
importedPlayDependencies.set(resolved, {
|
|
@@ -3142,44 +3406,28 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3142
3406
|
return;
|
|
3143
3407
|
}
|
|
3144
3408
|
if (specifier.includes(":")) {
|
|
3145
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3146
3409
|
throw new Error(
|
|
3147
|
-
`${absolutePath}:${
|
|
3410
|
+
`${absolutePath}:${line}:${column} Unsupported import specifier "${specifier}". Allowed imports are relative files, Node builtins, and installed packages.`
|
|
3148
3411
|
);
|
|
3149
3412
|
}
|
|
3150
3413
|
const packageImport = resolvePackageImport(specifier, absolutePath, adapter);
|
|
3151
3414
|
packages.set(packageImport.name, packageImport.version);
|
|
3152
3415
|
};
|
|
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
|
-
}
|
|
3416
|
+
try {
|
|
3417
|
+
for (const reference of findSourceImportReferences(sourceCode2)) {
|
|
3418
|
+
await handleSpecifier(
|
|
3419
|
+
reference.specifier,
|
|
3420
|
+
reference.line,
|
|
3421
|
+
reference.column,
|
|
3422
|
+
reference.kind
|
|
3423
|
+
);
|
|
3177
3424
|
}
|
|
3178
|
-
|
|
3179
|
-
|
|
3425
|
+
} catch (error) {
|
|
3426
|
+
if (error instanceof Error && error.message.startsWith(":")) {
|
|
3427
|
+
throw new Error(`${absolutePath}${error.message}`);
|
|
3180
3428
|
}
|
|
3181
|
-
|
|
3182
|
-
|
|
3429
|
+
throw error;
|
|
3430
|
+
}
|
|
3183
3431
|
};
|
|
3184
3432
|
await visitFile(absoluteEntryFile);
|
|
3185
3433
|
const sourceCode = localFiles.get(absoluteEntryFile) ?? "";
|
|
@@ -3199,6 +3447,11 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
3199
3447
|
const playName = extractDefinedPlayName(sourceCode, absoluteEntryFile);
|
|
3200
3448
|
return {
|
|
3201
3449
|
sourceCode,
|
|
3450
|
+
sourceFiles: Object.fromEntries(
|
|
3451
|
+
[...localFiles.entries()].sort(
|
|
3452
|
+
(left, right) => left[0].localeCompare(right[0])
|
|
3453
|
+
)
|
|
3454
|
+
),
|
|
3202
3455
|
sourceHash,
|
|
3203
3456
|
graphHash,
|
|
3204
3457
|
importPolicy: {
|
|
@@ -3262,8 +3515,7 @@ function normalizeSourceMapForRuntime(sourceMapText) {
|
|
|
3262
3515
|
parsed.sourceRoot = void 0;
|
|
3263
3516
|
return JSON.stringify(parsed);
|
|
3264
3517
|
}
|
|
3265
|
-
function
|
|
3266
|
-
const bundleBytes = Buffer.byteLength(bundledCode, "utf8");
|
|
3518
|
+
function getBundleSizeErrorForBytes(filePath, bundleBytes, artifactKind) {
|
|
3267
3519
|
if (bundleBytes > MAX_PLAY_BUNDLE_BYTES) {
|
|
3268
3520
|
return `${filePath} Play bundle exceeds the 30 MiB limit (${bundleBytes} bytes > ${MAX_PLAY_BUNDLE_BYTES} bytes).`;
|
|
3269
3521
|
}
|
|
@@ -3274,6 +3526,13 @@ function getBundleSizeError(filePath, bundledCode, artifactKind) {
|
|
|
3274
3526
|
}
|
|
3275
3527
|
return null;
|
|
3276
3528
|
}
|
|
3529
|
+
function getBundleSizeError(filePath, bundledCode, artifactKind) {
|
|
3530
|
+
return getBundleSizeErrorForBytes(
|
|
3531
|
+
filePath,
|
|
3532
|
+
Buffer.byteLength(bundledCode, "utf8"),
|
|
3533
|
+
artifactKind
|
|
3534
|
+
);
|
|
3535
|
+
}
|
|
3277
3536
|
async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter, exportName) {
|
|
3278
3537
|
const sdkAliasPlugin = localSdkAliasPlugin(adapter);
|
|
3279
3538
|
const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
|
|
@@ -3406,6 +3665,23 @@ entry-export:${exportName}`
|
|
|
3406
3665
|
workers-harness:${harnessFingerprint}`
|
|
3407
3666
|
);
|
|
3408
3667
|
}
|
|
3668
|
+
const typecheckErrors = [
|
|
3669
|
+
...await adapter.typecheckPlaySource?.({
|
|
3670
|
+
sourceCode: analysis.sourceCode,
|
|
3671
|
+
sourcePath: absolutePath,
|
|
3672
|
+
importedFilePaths: [
|
|
3673
|
+
...analysis.importPolicy.localFiles,
|
|
3674
|
+
...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
|
|
3675
|
+
]
|
|
3676
|
+
}) ?? []
|
|
3677
|
+
];
|
|
3678
|
+
if (typecheckErrors.length > 0) {
|
|
3679
|
+
return {
|
|
3680
|
+
success: false,
|
|
3681
|
+
filePath: absolutePath,
|
|
3682
|
+
errors: typecheckErrors
|
|
3683
|
+
};
|
|
3684
|
+
}
|
|
3409
3685
|
const cachedArtifact = await readArtifactCache(analysis.graphHash, target, adapter);
|
|
3410
3686
|
const discoveredFiles = await adapter.discoverPackagedLocalFiles(absolutePath);
|
|
3411
3687
|
if (cachedArtifact) {
|
|
@@ -3425,6 +3701,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3425
3701
|
success: true,
|
|
3426
3702
|
artifact: { ...cachedArtifact, cacheHit: true },
|
|
3427
3703
|
sourceCode: analysis.sourceCode,
|
|
3704
|
+
sourceFiles: analysis.sourceFiles,
|
|
3428
3705
|
filePath: absolutePath,
|
|
3429
3706
|
playName: analysis.playName,
|
|
3430
3707
|
packagedFiles: discoveredFiles.files,
|
|
@@ -3432,24 +3709,6 @@ workers-harness:${harnessFingerprint}`
|
|
|
3432
3709
|
importedPlayDependencies: analysis.importedPlayDependencies
|
|
3433
3710
|
};
|
|
3434
3711
|
}
|
|
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
3712
|
const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
|
|
3454
3713
|
if (Array.isArray(buildOutcome)) {
|
|
3455
3714
|
return {
|
|
@@ -3499,6 +3758,7 @@ workers-harness:${harnessFingerprint}`
|
|
|
3499
3758
|
success: true,
|
|
3500
3759
|
artifact,
|
|
3501
3760
|
sourceCode: analysis.sourceCode,
|
|
3761
|
+
sourceFiles: analysis.sourceFiles,
|
|
3502
3762
|
filePath: absolutePath,
|
|
3503
3763
|
playName: analysis.playName,
|
|
3504
3764
|
packagedFiles: discoveredFiles.files,
|
|
@@ -3580,7 +3840,6 @@ function resolveExecutionProfile(override) {
|
|
|
3580
3840
|
import { createHash as createHash2 } from "crypto";
|
|
3581
3841
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
3582
3842
|
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
3843
|
var SOURCE_EXTENSIONS2 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
|
|
3585
3844
|
function sha2562(buffer) {
|
|
3586
3845
|
return createHash2("sha256").update(buffer).digest("hex");
|
|
@@ -3592,94 +3851,181 @@ function contentTypeForFile(filePath) {
|
|
|
3592
3851
|
if (extension === ".txt") return "text/plain";
|
|
3593
3852
|
return "application/octet-stream";
|
|
3594
3853
|
}
|
|
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));
|
|
3854
|
+
function stripCommentsToSpaces2(source) {
|
|
3855
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " ")).replace(
|
|
3856
|
+
/(^|[^:])\/\/.*$/gm,
|
|
3857
|
+
(match, prefix) => prefix + " ".repeat(Math.max(0, match.length - prefix.length))
|
|
3858
|
+
);
|
|
3610
3859
|
}
|
|
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);
|
|
3860
|
+
function unquoteStringLiteral2(literal) {
|
|
3861
|
+
const trimmed = literal.trim();
|
|
3862
|
+
const quote = trimmed[0];
|
|
3863
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
3864
|
+
return null;
|
|
3623
3865
|
}
|
|
3624
|
-
|
|
3625
|
-
return
|
|
3866
|
+
try {
|
|
3867
|
+
return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
|
|
3868
|
+
} catch {
|
|
3869
|
+
return trimmed.slice(1, -1);
|
|
3626
3870
|
}
|
|
3627
|
-
|
|
3628
|
-
|
|
3871
|
+
}
|
|
3872
|
+
function splitTopLevelPlus(expression) {
|
|
3873
|
+
const parts = [];
|
|
3874
|
+
let start = 0;
|
|
3875
|
+
let depth = 0;
|
|
3876
|
+
let quote = null;
|
|
3877
|
+
let escaped = false;
|
|
3878
|
+
for (let index = 0; index < expression.length; index += 1) {
|
|
3879
|
+
const char = expression[index];
|
|
3880
|
+
if (quote) {
|
|
3881
|
+
if (escaped) {
|
|
3882
|
+
escaped = false;
|
|
3883
|
+
} else if (char === "\\") {
|
|
3884
|
+
escaped = true;
|
|
3885
|
+
} else if (char === quote) {
|
|
3886
|
+
quote = null;
|
|
3887
|
+
}
|
|
3888
|
+
continue;
|
|
3889
|
+
}
|
|
3890
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3891
|
+
quote = char;
|
|
3892
|
+
continue;
|
|
3893
|
+
}
|
|
3894
|
+
if (char === "(" || char === "[" || char === "{") depth += 1;
|
|
3895
|
+
if (char === ")" || char === "]" || char === "}") depth -= 1;
|
|
3896
|
+
if (char === "+" && depth === 0) {
|
|
3897
|
+
parts.push(expression.slice(start, index));
|
|
3898
|
+
start = index + 1;
|
|
3899
|
+
}
|
|
3629
3900
|
}
|
|
3630
|
-
|
|
3901
|
+
if (parts.length === 0) return null;
|
|
3902
|
+
parts.push(expression.slice(start));
|
|
3903
|
+
return parts;
|
|
3631
3904
|
}
|
|
3632
|
-
function
|
|
3633
|
-
|
|
3634
|
-
|
|
3905
|
+
function stripOuterParens(expression) {
|
|
3906
|
+
let value = expression.trim();
|
|
3907
|
+
while (value.startsWith("(") && value.endsWith(")")) {
|
|
3908
|
+
value = value.slice(1, -1).trim();
|
|
3635
3909
|
}
|
|
3636
|
-
|
|
3637
|
-
|
|
3910
|
+
return value;
|
|
3911
|
+
}
|
|
3912
|
+
function isRuntimeInputExpression(expression) {
|
|
3913
|
+
return /(^|[^\w$])input([^\w$]|$)/.test(expression);
|
|
3914
|
+
}
|
|
3915
|
+
function resolveStringExpression(expression, constants) {
|
|
3916
|
+
const value = stripOuterParens(expression);
|
|
3917
|
+
if (/^(['"])(?:\\.|(?!\1)[\s\S])*\1$/.test(value)) {
|
|
3918
|
+
return unquoteStringLiteral2(value);
|
|
3638
3919
|
}
|
|
3639
|
-
if (
|
|
3640
|
-
return
|
|
3920
|
+
if (/^`(?:\\.|[^`$]|\$(?!\{))*`$/.test(value)) {
|
|
3921
|
+
return value.slice(1, -1);
|
|
3641
3922
|
}
|
|
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;
|
|
3923
|
+
if (/^[A-Za-z_$][\w$]*$/.test(value)) {
|
|
3924
|
+
return constants.get(value) ?? null;
|
|
3652
3925
|
}
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
const
|
|
3656
|
-
|
|
3657
|
-
return null;
|
|
3658
|
-
}
|
|
3659
|
-
return left + right;
|
|
3926
|
+
const parts = splitTopLevelPlus(value);
|
|
3927
|
+
if (parts) {
|
|
3928
|
+
const resolved = parts.map((part) => resolveStringExpression(part, constants));
|
|
3929
|
+
return resolved.every((part) => part != null) ? resolved.join("") : null;
|
|
3660
3930
|
}
|
|
3661
3931
|
return null;
|
|
3662
3932
|
}
|
|
3663
|
-
function collectTopLevelStringConstants(
|
|
3933
|
+
function collectTopLevelStringConstants(sourceCode) {
|
|
3664
3934
|
const constants = /* @__PURE__ */ new Map();
|
|
3665
|
-
|
|
3666
|
-
|
|
3935
|
+
const source = stripCommentsToSpaces2(sourceCode);
|
|
3936
|
+
for (const match of source.matchAll(/(?:^|\n)\s*const\s+([A-Za-z_$][\w$]*)\s*=\s*([^;\n]+)/g)) {
|
|
3937
|
+
const resolved = resolveStringExpression(match[2], constants);
|
|
3938
|
+
if (resolved != null) {
|
|
3939
|
+
constants.set(match[1], resolved);
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
return constants;
|
|
3943
|
+
}
|
|
3944
|
+
function findMatchingGenericEnd(source, openIndex) {
|
|
3945
|
+
let depth = 0;
|
|
3946
|
+
let quote = null;
|
|
3947
|
+
let escaped = false;
|
|
3948
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
3949
|
+
const char = source[index];
|
|
3950
|
+
if (quote) {
|
|
3951
|
+
if (escaped) {
|
|
3952
|
+
escaped = false;
|
|
3953
|
+
} else if (char === "\\") {
|
|
3954
|
+
escaped = true;
|
|
3955
|
+
} else if (char === quote) {
|
|
3956
|
+
quote = null;
|
|
3957
|
+
}
|
|
3667
3958
|
continue;
|
|
3668
3959
|
}
|
|
3669
|
-
if (
|
|
3960
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3961
|
+
quote = char;
|
|
3670
3962
|
continue;
|
|
3671
3963
|
}
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3964
|
+
if (char === "<") depth += 1;
|
|
3965
|
+
if (char === ">") {
|
|
3966
|
+
depth -= 1;
|
|
3967
|
+
if (depth === 0) return index;
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
return -1;
|
|
3971
|
+
}
|
|
3972
|
+
function findCallOpenParen(source, afterCsvIndex) {
|
|
3973
|
+
let index = afterCsvIndex;
|
|
3974
|
+
while (/\s/.test(source[index] ?? "")) index += 1;
|
|
3975
|
+
if (source[index] === "<") {
|
|
3976
|
+
const genericEnd = findMatchingGenericEnd(source, index);
|
|
3977
|
+
if (genericEnd < 0) return -1;
|
|
3978
|
+
index = genericEnd + 1;
|
|
3979
|
+
while (/\s/.test(source[index] ?? "")) index += 1;
|
|
3980
|
+
}
|
|
3981
|
+
return source[index] === "(" ? index : -1;
|
|
3982
|
+
}
|
|
3983
|
+
function firstCallArgument(source, openParen) {
|
|
3984
|
+
let depth = 0;
|
|
3985
|
+
let quote = null;
|
|
3986
|
+
let escaped = false;
|
|
3987
|
+
const start = openParen + 1;
|
|
3988
|
+
for (let index = start; index < source.length; index += 1) {
|
|
3989
|
+
const char = source[index];
|
|
3990
|
+
if (quote) {
|
|
3991
|
+
if (escaped) {
|
|
3992
|
+
escaped = false;
|
|
3993
|
+
} else if (char === "\\") {
|
|
3994
|
+
escaped = true;
|
|
3995
|
+
} else if (char === quote) {
|
|
3996
|
+
quote = null;
|
|
3679
3997
|
}
|
|
3998
|
+
continue;
|
|
3999
|
+
}
|
|
4000
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
4001
|
+
quote = char;
|
|
4002
|
+
continue;
|
|
4003
|
+
}
|
|
4004
|
+
if (char === "(" || char === "[" || char === "{") depth += 1;
|
|
4005
|
+
if (char === ")" && depth === 0) {
|
|
4006
|
+
const text = source.slice(start, index).trim();
|
|
4007
|
+
return text ? { text, start, end: index } : null;
|
|
3680
4008
|
}
|
|
4009
|
+
if (char === "," && depth === 0) {
|
|
4010
|
+
const text = source.slice(start, index).trim();
|
|
4011
|
+
return text ? { text, start, end: index } : null;
|
|
4012
|
+
}
|
|
4013
|
+
if (char === ")" || char === "]" || char === "}") depth -= 1;
|
|
3681
4014
|
}
|
|
3682
|
-
return
|
|
4015
|
+
return null;
|
|
4016
|
+
}
|
|
4017
|
+
function localImportSpecifiers(sourceCode) {
|
|
4018
|
+
const source = stripCommentsToSpaces2(sourceCode);
|
|
4019
|
+
const specifiers = [];
|
|
4020
|
+
for (const match of source.matchAll(
|
|
4021
|
+
/\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?['"]([^'"]+)['"]/g
|
|
4022
|
+
)) {
|
|
4023
|
+
if (match[1]?.startsWith(".")) specifiers.push(match[1]);
|
|
4024
|
+
}
|
|
4025
|
+
for (const match of source.matchAll(/\brequire\s*\(\s*(['"])(\.[^'"]*)\1\s*\)/g)) {
|
|
4026
|
+
specifiers.push(match[2]);
|
|
4027
|
+
}
|
|
4028
|
+
return specifiers;
|
|
3683
4029
|
}
|
|
3684
4030
|
async function fileExists2(filePath) {
|
|
3685
4031
|
try {
|
|
@@ -3724,69 +4070,60 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
3724
4070
|
}
|
|
3725
4071
|
visitedFiles.add(absolutePath);
|
|
3726
4072
|
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);
|
|
4073
|
+
const scanSource = stripCommentsToSpaces2(sourceCode);
|
|
4074
|
+
const constants = collectTopLevelStringConstants(sourceCode);
|
|
3735
4075
|
const childVisits = [];
|
|
3736
|
-
const
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
4076
|
+
for (const match of scanSource.matchAll(/\b([A-Za-z_$][\w$]*)\s*\.\s*csv\b/g)) {
|
|
4077
|
+
const target = match[1];
|
|
4078
|
+
if (target !== "ctx" && !target.endsWith("Ctx")) {
|
|
4079
|
+
continue;
|
|
4080
|
+
}
|
|
4081
|
+
const openParen = findCallOpenParen(scanSource, match.index + match[0].length);
|
|
4082
|
+
if (openParen < 0) {
|
|
4083
|
+
continue;
|
|
4084
|
+
}
|
|
4085
|
+
const argument = firstCallArgument(scanSource, openParen);
|
|
4086
|
+
if (!argument) {
|
|
4087
|
+
unresolved.push({
|
|
4088
|
+
sourceFragment: "ctx.csv()",
|
|
4089
|
+
message: "ctx.csv() requires a file path string or input reference."
|
|
4090
|
+
});
|
|
4091
|
+
} else if (!isRuntimeInputExpression(argument.text)) {
|
|
4092
|
+
const resolvedPath = resolveStringExpression(argument.text, constants);
|
|
4093
|
+
if (resolvedPath == null) {
|
|
3740
4094
|
unresolved.push({
|
|
3741
|
-
sourceFragment:
|
|
3742
|
-
message: "ctx.csv()
|
|
4095
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4096
|
+
message: "Could not resolve this ctx.csv(...) path at submit time. Use a string literal, a top-level const string, or pass a runtime input like input.file."
|
|
3743
4097
|
});
|
|
3744
|
-
} else
|
|
3745
|
-
const
|
|
3746
|
-
if (resolvedPath
|
|
4098
|
+
} else {
|
|
4099
|
+
const absoluteCsvPath = resolve5(dirname4(absolutePath), resolvedPath);
|
|
4100
|
+
if (isAbsolute2(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
|
|
3747
4101
|
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)
|
|
4102
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4103
|
+
message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
|
|
3769
4104
|
});
|
|
4105
|
+
continue;
|
|
3770
4106
|
}
|
|
4107
|
+
const buffer = await readFile2(absoluteCsvPath);
|
|
4108
|
+
const stats = await stat2(absoluteCsvPath);
|
|
4109
|
+
files.set(absoluteCsvPath, {
|
|
4110
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
4111
|
+
logicalPath: resolvedPath,
|
|
4112
|
+
absolutePath: absoluteCsvPath,
|
|
4113
|
+
bytes: stats.size,
|
|
4114
|
+
contentHash: sha2562(buffer),
|
|
4115
|
+
contentType: contentTypeForFile(absoluteCsvPath)
|
|
4116
|
+
});
|
|
3771
4117
|
}
|
|
3772
4118
|
}
|
|
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);
|
|
4119
|
+
}
|
|
4120
|
+
for (const specifier of localImportSpecifiers(sourceCode)) {
|
|
4121
|
+
childVisits.push(
|
|
4122
|
+
resolveLocalImport2(absolutePath, specifier).then(
|
|
4123
|
+
(resolvedImport) => visitSourceFile(resolvedImport)
|
|
4124
|
+
)
|
|
4125
|
+
);
|
|
4126
|
+
}
|
|
3790
4127
|
await Promise.all(childVisits);
|
|
3791
4128
|
};
|
|
3792
4129
|
await visitSourceFile(absoluteEntryFile);
|
|
@@ -3797,7 +4134,7 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
3797
4134
|
}
|
|
3798
4135
|
|
|
3799
4136
|
// src/plays/bundle-play-file.ts
|
|
3800
|
-
var PLAY_BUNDLE_CACHE_VERSION2 =
|
|
4137
|
+
var PLAY_BUNDLE_CACHE_VERSION2 = 26;
|
|
3801
4138
|
var MODULE_DIR = dirname5(fileURLToPath(import.meta.url));
|
|
3802
4139
|
var SDK_PACKAGE_ROOT = resolve6(MODULE_DIR, "..", "..");
|
|
3803
4140
|
var SOURCE_REPO_ROOT = resolve6(SDK_PACKAGE_ROOT, "..");
|
|
@@ -3812,7 +4149,7 @@ var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED
|
|
|
3812
4149
|
var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? resolve6(SOURCE_REPO_ROOT, "sdk", "src") : HAS_PACKAGED_BUNDLING_SOURCES ? resolve6(PACKAGED_REPO_ROOT, "sdk", "src") : resolve6(SDK_PACKAGE_ROOT, "src");
|
|
3813
4150
|
var SDK_PACKAGE_JSON = resolve6(SDK_PACKAGE_ROOT, "package.json");
|
|
3814
4151
|
var SDK_ENTRY_FILE = resolve6(SDK_SOURCE_ROOT, "index.ts");
|
|
3815
|
-
var SDK_TYPES_ENTRY_FILE = resolve6(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
4152
|
+
var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : resolve6(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
3816
4153
|
var SDK_WORKERS_ENTRY_FILE = resolve6(SDK_SOURCE_ROOT, "worker-play-entry.ts");
|
|
3817
4154
|
var WORKERS_HARNESS_ENTRY_FILE = resolve6(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
|
|
3818
4155
|
var WORKERS_HARNESS_FILES_DIR = resolve6(PROJECT_ROOT, "apps", "play-runner-workers", "src");
|
|
@@ -3844,7 +4181,7 @@ function createSdkPlayBundlingAdapter() {
|
|
|
3844
4181
|
sdkSourceRoot: SDK_SOURCE_ROOT,
|
|
3845
4182
|
sdkPackageJson: SDK_PACKAGE_JSON,
|
|
3846
4183
|
sdkEntryFile: SDK_ENTRY_FILE,
|
|
3847
|
-
sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES
|
|
4184
|
+
sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES || !existsSync3(SDK_TYPES_ENTRY_FILE) ? SDK_ENTRY_FILE : SDK_TYPES_ENTRY_FILE,
|
|
3848
4185
|
sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
|
|
3849
4186
|
workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
|
|
3850
4187
|
workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
|
|
@@ -4081,13 +4418,15 @@ function formatLoadedPlayMessage(materializedFile) {
|
|
|
4081
4418
|
return `Loaded play here: ${materializedFile.path}`;
|
|
4082
4419
|
}
|
|
4083
4420
|
function buildReadonlyPrebuiltPlayError(reference) {
|
|
4421
|
+
const localName = reference.split("/").slice(1).join("/") || "custom-play";
|
|
4084
4422
|
return new Error(
|
|
4085
4423
|
`Cannot edit or push ${reference} because Deepline prebuilt plays are read-only.
|
|
4086
4424
|
To make your own version:
|
|
4087
|
-
1.
|
|
4088
|
-
2. Change definePlay('${
|
|
4089
|
-
3. Run: deepline plays
|
|
4090
|
-
4.
|
|
4425
|
+
1. Run: deepline plays get ${reference} --source --out ./${localName}.play.ts
|
|
4426
|
+
2. Change definePlay('${localName}', ...) to a new play name you own.
|
|
4427
|
+
3. Run: deepline plays check ./${localName}.play.ts
|
|
4428
|
+
4. Run: deepline plays publish ./${localName}.play.ts
|
|
4429
|
+
5. Your play will then live under your workspace namespace.`
|
|
4091
4430
|
);
|
|
4092
4431
|
}
|
|
4093
4432
|
async function ensureEditableRemotePlay(client, target) {
|
|
@@ -4228,6 +4567,15 @@ function fileInputBindingsFromStaticPipeline(staticPipeline) {
|
|
|
4228
4567
|
);
|
|
4229
4568
|
return inputField ? [{ inputPath: inputField }] : [];
|
|
4230
4569
|
}
|
|
4570
|
+
function applyCsvShortcutInput(input) {
|
|
4571
|
+
const csvValue = getDottedInputValue(input.runtimeInput, "csv");
|
|
4572
|
+
if (csvValue == null || csvValue === "") return;
|
|
4573
|
+
const candidate = input.bindings.find((binding) => binding.inputPath !== "csv")?.inputPath ?? input.fallbackInputPath ?? null;
|
|
4574
|
+
if (!candidate || candidate === "csv") return;
|
|
4575
|
+
const existing = getDottedInputValue(input.runtimeInput, candidate);
|
|
4576
|
+
if (existing != null && existing !== "") return;
|
|
4577
|
+
setDottedInputValue(input.runtimeInput, candidate, csvValue);
|
|
4578
|
+
}
|
|
4231
4579
|
function isLocalFilePathValue(value) {
|
|
4232
4580
|
if (typeof value !== "string" || !value.trim()) return false;
|
|
4233
4581
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
@@ -4351,6 +4699,7 @@ async function compileBundledPlayGraphManifests(client, graph) {
|
|
|
4351
4699
|
node.compilerManifest = await client.compilePlayManifest({
|
|
4352
4700
|
name,
|
|
4353
4701
|
sourceCode: node.sourceCode,
|
|
4702
|
+
sourceFiles: node.sourceFiles,
|
|
4354
4703
|
artifact: node.artifact,
|
|
4355
4704
|
importedPlayDependencies: node.importedPlayDependencies.map(
|
|
4356
4705
|
(dependency) => {
|
|
@@ -4400,6 +4749,7 @@ async function publishImportedPlayDependencies(client, graph) {
|
|
|
4400
4749
|
await client.registerPlayArtifact({
|
|
4401
4750
|
name: node.playName,
|
|
4402
4751
|
sourceCode: node.sourceCode,
|
|
4752
|
+
sourceFiles: node.sourceFiles,
|
|
4403
4753
|
artifact: node.artifact,
|
|
4404
4754
|
compilerManifest: requireCompilerManifest(node),
|
|
4405
4755
|
publish: true
|
|
@@ -4417,67 +4767,6 @@ function formatTimestamp(value) {
|
|
|
4417
4767
|
function formatRunLine(run) {
|
|
4418
4768
|
return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)}`;
|
|
4419
4769
|
}
|
|
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
4770
|
function isTransientPlayStatusPollError(error) {
|
|
4482
4771
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
4483
4772
|
return error.statusCode >= 500 && error.statusCode < 600;
|
|
@@ -4581,7 +4870,7 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
4581
4870
|
if (input.waitTimeoutMs !== null && Date.now() - input.startedAt >= input.waitTimeoutMs) {
|
|
4582
4871
|
const hasRealRunId = input.workflowId.length > 0 && input.workflowId !== "pending";
|
|
4583
4872
|
const phaseSuffix = input.lastPhase && input.lastPhase.trim() ? ` (last observed phase: ${input.lastPhase.trim()})` : "";
|
|
4584
|
-
const tailHint = hasRealRunId ? ` Run 'deepline
|
|
4873
|
+
const tailHint = hasRealRunId ? ` Run 'deepline runs tail ${input.workflowId} --json' to inspect it, or rerun with a larger --tail-timeout-ms.` : ` The run never reported a workflow id \u2014 the start request likely failed before reaching the scheduler. Check server logs and rerun with a larger --tail-timeout-ms.`;
|
|
4585
4874
|
throw new DeeplineError(
|
|
4586
4875
|
`Timed out waiting for play ${hasRealRunId ? input.workflowId : "<no run id>"} after ${Math.ceil(input.waitTimeoutMs / 1e3)}s${phaseSuffix}.${tailHint}`,
|
|
4587
4876
|
void 0,
|
|
@@ -4594,66 +4883,6 @@ function assertPlayWaitNotTimedOut(input) {
|
|
|
4594
4883
|
);
|
|
4595
4884
|
}
|
|
4596
4885
|
}
|
|
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
4886
|
async function startAndWaitForPlayCompletionByStream(input) {
|
|
4658
4887
|
const startedAt = Date.now();
|
|
4659
4888
|
const state = {
|
|
@@ -4733,10 +4962,12 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4733
4962
|
clearTimeout(timeout);
|
|
4734
4963
|
}
|
|
4735
4964
|
const reason = error instanceof Error ? error.message : String(error);
|
|
4736
|
-
|
|
4737
|
-
|
|
4965
|
+
if (!input.jsonOutput) {
|
|
4966
|
+
process.stderr.write(
|
|
4967
|
+
`[play watch] start stream failed after run ${lastKnownWorkflowId}; falling back to polling (${reason})
|
|
4738
4968
|
`
|
|
4739
|
-
|
|
4969
|
+
);
|
|
4970
|
+
}
|
|
4740
4971
|
return waitForPlayCompletionByPolling({
|
|
4741
4972
|
client: input.client,
|
|
4742
4973
|
workflowId: lastKnownWorkflowId,
|
|
@@ -4755,6 +4986,24 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
4755
4986
|
clearTimeout(timeout);
|
|
4756
4987
|
}
|
|
4757
4988
|
}
|
|
4989
|
+
if (lastKnownWorkflowId) {
|
|
4990
|
+
if (!input.jsonOutput) {
|
|
4991
|
+
input.progress.writeLine(
|
|
4992
|
+
`[play watch] start stream ended after run ${lastKnownWorkflowId}; falling back to polling`
|
|
4993
|
+
);
|
|
4994
|
+
}
|
|
4995
|
+
return waitForPlayCompletionByPolling({
|
|
4996
|
+
client: input.client,
|
|
4997
|
+
workflowId: lastKnownWorkflowId,
|
|
4998
|
+
pollIntervalMs: 500,
|
|
4999
|
+
jsonOutput: input.jsonOutput,
|
|
5000
|
+
emitLogs: input.emitLogs,
|
|
5001
|
+
waitTimeoutMs: input.waitTimeoutMs,
|
|
5002
|
+
startedAt,
|
|
5003
|
+
state,
|
|
5004
|
+
progress: input.progress
|
|
5005
|
+
});
|
|
5006
|
+
}
|
|
4758
5007
|
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
4759
5008
|
const idSuffix = lastKnownWorkflowId ? ` runId=${lastKnownWorkflowId}` : "";
|
|
4760
5009
|
throw new DeeplineError(
|
|
@@ -4832,38 +5081,6 @@ async function waitForPlayCompletionByPolling(input) {
|
|
|
4832
5081
|
}
|
|
4833
5082
|
}
|
|
4834
5083
|
}
|
|
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
5084
|
function formatInteger(value) {
|
|
4868
5085
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
|
|
4869
5086
|
}
|
|
@@ -4992,17 +5209,153 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
4992
5209
|
}
|
|
4993
5210
|
function buildRunNextCommands(runId) {
|
|
4994
5211
|
return {
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
5212
|
+
get: `deepline runs get ${runId} --json`,
|
|
5213
|
+
tail: `deepline runs tail ${runId} --json`,
|
|
5214
|
+
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
5215
|
+
logs: `deepline runs logs ${runId} --out run.log --json`,
|
|
5216
|
+
exportCsv: `deepline runs export ${runId} --out output.csv`
|
|
5217
|
+
};
|
|
5218
|
+
}
|
|
5219
|
+
var RUN_LOG_PREVIEW_LIMIT = 20;
|
|
5220
|
+
function getRecordField(value, key) {
|
|
5221
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value[key] : void 0;
|
|
5222
|
+
}
|
|
5223
|
+
function getNumericField(value, key) {
|
|
5224
|
+
const field = getRecordField(value, key);
|
|
5225
|
+
return typeof field === "number" && Number.isFinite(field) ? field : null;
|
|
5226
|
+
}
|
|
5227
|
+
function getStringField(value, key) {
|
|
5228
|
+
const field = getRecordField(value, key);
|
|
5229
|
+
return typeof field === "string" && field.trim() ? field : null;
|
|
5230
|
+
}
|
|
5231
|
+
function normalizeRunStatusForEnvelope(status) {
|
|
5232
|
+
const run = status.run ?? null;
|
|
5233
|
+
return {
|
|
5234
|
+
id: status.runId,
|
|
5235
|
+
playName: status.playName ?? status.name ?? getStringField(run, "playName") ?? null,
|
|
5236
|
+
status: status.status,
|
|
5237
|
+
runtime: getStringField(status, "runtime") ?? getStringField(status, "runtimeBackend") ?? getStringField(run, "runtime") ?? null,
|
|
5238
|
+
startedAt: getStringField(run, "startTime") ?? getStringField(run, "startedAt") ?? null,
|
|
5239
|
+
updatedAt: getStringField(status, "updatedAt") ?? getStringField(run, "updatedAt") ?? null,
|
|
5240
|
+
finishedAt: getStringField(run, "closeTime") ?? getStringField(run, "finishedAt") ?? null,
|
|
5241
|
+
source: getRecordField(status, "source") ?? getRecordField(status, "artifact") ?? null
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
5244
|
+
function normalizeProgressForEnvelope(status, rowsInfo) {
|
|
5245
|
+
const progress = status.progress;
|
|
5246
|
+
const total = getNumericField(progress, "totalRows") ?? getNumericField(progress, "total") ?? rowsInfo?.totalRows ?? null;
|
|
5247
|
+
const failed = getNumericField(progress, "failed") ?? getNumericField(progress, "failedRows") ?? null;
|
|
5248
|
+
const completed = getNumericField(progress, "completed") ?? getNumericField(progress, "completedRows") ?? (status.status === "completed" ? total : null);
|
|
5249
|
+
const pending = getNumericField(progress, "pending") ?? (typeof total === "number" && typeof completed === "number" && typeof failed === "number" ? Math.max(0, total - completed - failed) : null);
|
|
5250
|
+
return {
|
|
5251
|
+
total,
|
|
5252
|
+
completed,
|
|
5253
|
+
pending,
|
|
5254
|
+
failed,
|
|
5255
|
+
executed: getNumericField(progress, "executed"),
|
|
5256
|
+
reused: getNumericField(progress, "reused"),
|
|
5257
|
+
skipped: getNumericField(progress, "skipped"),
|
|
5258
|
+
retried: getNumericField(progress, "retried"),
|
|
5259
|
+
degraded: typeof getRecordField(progress, "degraded") === "boolean" ? getRecordField(progress, "degraded") : null,
|
|
5260
|
+
duplicates: getRecordField(progress, "duplicates") ?? null,
|
|
5261
|
+
active: getStringField(progress, "status") ?? getStringField(status, "activeStep") ?? getStringField(status, "activeNodeId") ?? null,
|
|
5262
|
+
wait: status.wait ?? null
|
|
5263
|
+
};
|
|
5264
|
+
}
|
|
5265
|
+
function normalizeOutputsForEnvelope(rowsInfo, exportedPath) {
|
|
5266
|
+
if (!rowsInfo) {
|
|
5267
|
+
return exportedPath ? [{ name: "output", kind: "file", path: exportedPath }] : [];
|
|
5268
|
+
}
|
|
5269
|
+
return [
|
|
5270
|
+
{
|
|
5271
|
+
name: "rows",
|
|
5272
|
+
kind: "dataset",
|
|
5273
|
+
rowCount: rowsInfo.totalRows,
|
|
5274
|
+
columns: rowsInfo.columns,
|
|
5275
|
+
preview: rowsInfo.rows.slice(0, 5),
|
|
5276
|
+
previewRowCount: Math.min(rowsInfo.rows.length, 5),
|
|
5277
|
+
previewLimit: 5,
|
|
5278
|
+
complete: rowsInfo.complete,
|
|
5279
|
+
source: rowsInfo.source,
|
|
5280
|
+
...exportedPath ? { csv_path: exportedPath } : {}
|
|
5281
|
+
}
|
|
5282
|
+
];
|
|
5283
|
+
}
|
|
5284
|
+
function normalizeStepsForEnvelope(status) {
|
|
5285
|
+
const directSteps = getRecordField(status, "steps");
|
|
5286
|
+
if (Array.isArray(directSteps)) {
|
|
5287
|
+
return directSteps;
|
|
5288
|
+
}
|
|
5289
|
+
const timeline = getRecordField(status, "timeline");
|
|
5290
|
+
if (Array.isArray(timeline)) {
|
|
5291
|
+
return timeline;
|
|
5292
|
+
}
|
|
5293
|
+
return [];
|
|
5294
|
+
}
|
|
5295
|
+
function normalizeErrorsForEnvelope(status, error) {
|
|
5296
|
+
const directErrors = getRecordField(status, "errors");
|
|
5297
|
+
if (Array.isArray(directErrors)) {
|
|
5298
|
+
return directErrors.filter(
|
|
5299
|
+
(entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)
|
|
5300
|
+
);
|
|
5301
|
+
}
|
|
5302
|
+
if (!error) {
|
|
5303
|
+
return [];
|
|
5304
|
+
}
|
|
5305
|
+
return [
|
|
5306
|
+
{
|
|
5307
|
+
code: getStringField(status, "errorCode") ?? "RUN_FAILED",
|
|
5308
|
+
phase: getStringField(status, "errorPhase") ?? "runtime",
|
|
5309
|
+
message: error,
|
|
5310
|
+
retryable: typeof getRecordField(status, "retryable") === "boolean" ? getRecordField(status, "retryable") : null,
|
|
5311
|
+
nextAction: `deepline runs get ${status.runId} --json`
|
|
5312
|
+
}
|
|
5313
|
+
];
|
|
5314
|
+
}
|
|
5315
|
+
function normalizeLogsForEnvelope(status) {
|
|
5316
|
+
const logs = Array.isArray(status.progress?.logs) ? status.progress.logs : [];
|
|
5317
|
+
const offset = typeof status.progress?.logOffset === "number" && Number.isFinite(status.progress.logOffset) ? Math.max(0, Math.trunc(status.progress.logOffset)) : 0;
|
|
5318
|
+
const totalCount = offset + logs.length;
|
|
5319
|
+
const entries = logs.slice(Math.max(0, logs.length - RUN_LOG_PREVIEW_LIMIT));
|
|
5320
|
+
const firstSequence = entries.length === 0 ? null : offset + logs.length - entries.length + 1;
|
|
5321
|
+
const lastSequence = totalCount === 0 ? null : totalCount;
|
|
5322
|
+
return {
|
|
5323
|
+
totalCount,
|
|
5324
|
+
returnedCount: entries.length,
|
|
5325
|
+
firstSequence,
|
|
5326
|
+
lastSequence,
|
|
5327
|
+
truncated: totalCount > entries.length,
|
|
5328
|
+
hasMore: totalCount > entries.length,
|
|
5329
|
+
entries,
|
|
5330
|
+
nextCursor: lastSequence
|
|
4998
5331
|
};
|
|
4999
5332
|
}
|
|
5333
|
+
function stripProviderSpendFromBilling(value) {
|
|
5334
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5335
|
+
return value;
|
|
5336
|
+
}
|
|
5337
|
+
const next = {};
|
|
5338
|
+
for (const [key, item] of Object.entries(value)) {
|
|
5339
|
+
if (key === "providerCostUsd" || key === "totalProviderCostUsd") {
|
|
5340
|
+
continue;
|
|
5341
|
+
}
|
|
5342
|
+
next[key] = item;
|
|
5343
|
+
}
|
|
5344
|
+
return next;
|
|
5345
|
+
}
|
|
5000
5346
|
function compactPlayStatus(status, options) {
|
|
5001
5347
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
5002
5348
|
const result = status && typeof status === "object" ? status.result : null;
|
|
5003
5349
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
5004
|
-
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5005
|
-
|
|
5350
|
+
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5351
|
+
rowsInfo.rows,
|
|
5352
|
+
rowsInfo.totalRows,
|
|
5353
|
+
rowsInfo.columns,
|
|
5354
|
+
extractDatasetExecutionStats(status)
|
|
5355
|
+
) : null;
|
|
5356
|
+
const billing = status && typeof status === "object" ? stripProviderSpendFromBilling(
|
|
5357
|
+
status.billing
|
|
5358
|
+
) : null;
|
|
5006
5359
|
const progressError = status.progress?.error;
|
|
5007
5360
|
const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
|
|
5008
5361
|
return {
|
|
@@ -5011,6 +5364,12 @@ function compactPlayStatus(status, options) {
|
|
|
5011
5364
|
...typeof status.name === "string" ? { name: status.name } : {},
|
|
5012
5365
|
...typeof status.playName === "string" ? { playName: status.playName } : {},
|
|
5013
5366
|
status: status.status,
|
|
5367
|
+
run: normalizeRunStatusForEnvelope(status),
|
|
5368
|
+
progress: normalizeProgressForEnvelope(status, rowsInfo),
|
|
5369
|
+
outputs: normalizeOutputsForEnvelope(rowsInfo, options?.exportedPath),
|
|
5370
|
+
steps: normalizeStepsForEnvelope(status),
|
|
5371
|
+
errors: normalizeErrorsForEnvelope(status, error),
|
|
5372
|
+
logs: normalizeLogsForEnvelope(status),
|
|
5014
5373
|
...error ? { error } : {},
|
|
5015
5374
|
...warnings.length > 0 ? { warnings } : {},
|
|
5016
5375
|
output: buildOutputSummary(rowsInfo, options?.exportedPath) ?? result ?? null,
|
|
@@ -5019,7 +5378,6 @@ function compactPlayStatus(status, options) {
|
|
|
5019
5378
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
5020
5379
|
...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
|
|
5021
5380
|
...billing ? { billing } : {},
|
|
5022
|
-
...status.run ? { run: status.run } : {},
|
|
5023
5381
|
next: buildRunNextCommands(status.runId)
|
|
5024
5382
|
};
|
|
5025
5383
|
}
|
|
@@ -5033,7 +5391,8 @@ function enrichPlayStatusWithDatasetStats(status) {
|
|
|
5033
5391
|
dataset_stats: buildDatasetStats(
|
|
5034
5392
|
rowsInfo.rows,
|
|
5035
5393
|
rowsInfo.totalRows,
|
|
5036
|
-
rowsInfo.columns
|
|
5394
|
+
rowsInfo.columns,
|
|
5395
|
+
extractDatasetExecutionStats(status)
|
|
5037
5396
|
)
|
|
5038
5397
|
};
|
|
5039
5398
|
}
|
|
@@ -5048,8 +5407,9 @@ function formatDatasetStatsLines(datasetStats) {
|
|
|
5048
5407
|
)) {
|
|
5049
5408
|
const topValues = stat3.top_values ? `, top_values=${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
|
|
5050
5409
|
const sample = stat3.sample_value !== void 0 ? `, sample_value=${JSON.stringify(stat3.sample_value)}` : "";
|
|
5410
|
+
const execution = stat3.execution ? `, execution=${Object.entries(stat3.execution).map(([bucket, count]) => `${bucket}=${count}`).join(", ")}` : "";
|
|
5051
5411
|
lines.push(
|
|
5052
|
-
` ${column}: non_empty=${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}`
|
|
5412
|
+
` ${column}: non_empty=${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}${execution}`
|
|
5053
5413
|
);
|
|
5054
5414
|
}
|
|
5055
5415
|
return lines;
|
|
@@ -5072,7 +5432,12 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
5072
5432
|
lines.push(`${success ? "\u2713" : "\u2717"} ${publicStatus} ${runId}`);
|
|
5073
5433
|
const rowsInfo = extractCanonicalRowsInfo(status);
|
|
5074
5434
|
const warnings = buildRunWarnings(status, rowsInfo);
|
|
5075
|
-
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5435
|
+
const datasetStats = rowsInfo && rowsInfo.complete ? buildDatasetStats(
|
|
5436
|
+
rowsInfo.rows,
|
|
5437
|
+
rowsInfo.totalRows,
|
|
5438
|
+
rowsInfo.columns,
|
|
5439
|
+
extractDatasetExecutionStats(status)
|
|
5440
|
+
) : null;
|
|
5076
5441
|
const outputSummary = buildOutputSummary(rowsInfo, options?.exportedPath);
|
|
5077
5442
|
if (outputSummary) {
|
|
5078
5443
|
const columns = Array.isArray(outputSummary.columns) ? outputSummary.columns.length : 0;
|
|
@@ -5191,10 +5556,10 @@ function writeStartedPlayRun(input) {
|
|
|
5191
5556
|
const lines = [
|
|
5192
5557
|
`Started ${input.playName}`,
|
|
5193
5558
|
` run id: ${input.runId}`,
|
|
5194
|
-
`
|
|
5195
|
-
` tail logs: deepline
|
|
5196
|
-
` stop run: deepline
|
|
5197
|
-
` result JSON: deepline
|
|
5559
|
+
` get status: deepline runs get ${input.runId} --json`,
|
|
5560
|
+
` tail logs: deepline runs tail ${input.runId} --json`,
|
|
5561
|
+
` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`,
|
|
5562
|
+
` result JSON: deepline runs get ${input.runId} --json`
|
|
5198
5563
|
];
|
|
5199
5564
|
if (input.dashboardUrl) {
|
|
5200
5565
|
lines.push(` play page: ${input.dashboardUrl}`);
|
|
@@ -5341,6 +5706,10 @@ function parsePlayCheckOptions(args) {
|
|
|
5341
5706
|
const jsonOutput = argsWantJson(args);
|
|
5342
5707
|
return { target, jsonOutput };
|
|
5343
5708
|
}
|
|
5709
|
+
function shouldUseLocalOnlyPlayCheck() {
|
|
5710
|
+
const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
|
|
5711
|
+
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
5712
|
+
}
|
|
5344
5713
|
async function handlePlayCheck(args) {
|
|
5345
5714
|
const options = parsePlayCheckOptions(args);
|
|
5346
5715
|
if (!isFileTarget(options.target)) {
|
|
@@ -5366,10 +5735,28 @@ async function handlePlayCheck(args) {
|
|
|
5366
5735
|
return 1;
|
|
5367
5736
|
}
|
|
5368
5737
|
const playName = graph.root.playName ?? extractPlayName(sourceCode, absolutePlayPath);
|
|
5738
|
+
if (shouldUseLocalOnlyPlayCheck()) {
|
|
5739
|
+
const result2 = {
|
|
5740
|
+
valid: true,
|
|
5741
|
+
errors: [],
|
|
5742
|
+
staticPipeline: graph.root.compilerManifest?.staticPipeline ?? null,
|
|
5743
|
+
artifactHash: graph.root.artifact.artifactHash,
|
|
5744
|
+
graphHash: graph.root.artifact.graphHash
|
|
5745
|
+
};
|
|
5746
|
+
if (options.jsonOutput) {
|
|
5747
|
+
process.stdout.write(`${JSON.stringify({ name: playName, ...result2 })}
|
|
5748
|
+
`);
|
|
5749
|
+
} else {
|
|
5750
|
+
console.log(`\u2713 ${playName} passed local play check`);
|
|
5751
|
+
console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
|
|
5752
|
+
}
|
|
5753
|
+
return 0;
|
|
5754
|
+
}
|
|
5369
5755
|
const client = new DeeplineClient();
|
|
5370
5756
|
const result = await client.checkPlayArtifact({
|
|
5371
5757
|
name: playName,
|
|
5372
5758
|
sourceCode: graph.root.sourceCode,
|
|
5759
|
+
sourceFiles: graph.root.sourceFiles,
|
|
5373
5760
|
artifact: graph.root.artifact
|
|
5374
5761
|
});
|
|
5375
5762
|
if (options.jsonOutput) {
|
|
@@ -5421,17 +5808,24 @@ async function handleFileBackedRun(options) {
|
|
|
5421
5808
|
const packagedFileUploads = bundleResult.packagedFiles.map(
|
|
5422
5809
|
(file) => stageFile(file.logicalPath, file.absolutePath)
|
|
5423
5810
|
);
|
|
5811
|
+
const fileInputBindings = fileInputBindingsFromStaticPipeline(
|
|
5812
|
+
requireCompilerManifest(bundleResult).staticPipeline
|
|
5813
|
+
);
|
|
5814
|
+
applyCsvShortcutInput({
|
|
5815
|
+
runtimeInput,
|
|
5816
|
+
bindings: fileInputBindings,
|
|
5817
|
+
fallbackInputPath: "file"
|
|
5818
|
+
});
|
|
5424
5819
|
const stagedFileInputs = await stageFileInputArgs({
|
|
5425
5820
|
client,
|
|
5426
5821
|
runtimeInput,
|
|
5427
|
-
bindings:
|
|
5428
|
-
requireCompilerManifest(bundleResult).staticPipeline
|
|
5429
|
-
),
|
|
5822
|
+
bindings: fileInputBindings,
|
|
5430
5823
|
progress
|
|
5431
5824
|
});
|
|
5432
5825
|
const startRequest = {
|
|
5433
5826
|
name: playName,
|
|
5434
5827
|
sourceCode: bundleResult.sourceCode,
|
|
5828
|
+
sourceFiles: bundleResult.sourceFiles,
|
|
5435
5829
|
runtimeArtifact: bundleResult.artifact,
|
|
5436
5830
|
compilerManifest: requireCompilerManifest(bundleResult),
|
|
5437
5831
|
packagedFileUploads,
|
|
@@ -5506,13 +5900,18 @@ async function handleNamedRun(options) {
|
|
|
5506
5900
|
selector: options.revisionSelector
|
|
5507
5901
|
});
|
|
5508
5902
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
5903
|
+
const fileInputBindings = [
|
|
5904
|
+
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
5905
|
+
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
5906
|
+
];
|
|
5907
|
+
applyCsvShortcutInput({
|
|
5908
|
+
runtimeInput,
|
|
5909
|
+
bindings: fileInputBindings
|
|
5910
|
+
});
|
|
5509
5911
|
const stagedFileInputs = await stageFileInputArgs({
|
|
5510
5912
|
client,
|
|
5511
5913
|
runtimeInput,
|
|
5512
|
-
bindings:
|
|
5513
|
-
...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
|
|
5514
|
-
...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
|
|
5515
|
-
],
|
|
5914
|
+
bindings: fileInputBindings,
|
|
5516
5915
|
progress
|
|
5517
5916
|
});
|
|
5518
5917
|
const startRequest = {
|
|
@@ -5590,80 +5989,101 @@ async function handlePlayRun(args) {
|
|
|
5590
5989
|
}
|
|
5591
5990
|
return handleNamedRun(options);
|
|
5592
5991
|
}
|
|
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;
|
|
5992
|
+
function parseRunIdPositional(args, usage) {
|
|
5606
5993
|
for (let index = 0; index < args.length; index += 1) {
|
|
5607
5994
|
const arg = args[index];
|
|
5608
|
-
if (
|
|
5609
|
-
|
|
5995
|
+
if (arg === "--json" || arg === "--full" || arg === "--logs" || arg === "--compact" || arg === "--limit") {
|
|
5996
|
+
if (arg === "--limit" && args[index + 1]) {
|
|
5997
|
+
index += 1;
|
|
5998
|
+
}
|
|
5999
|
+
continue;
|
|
6000
|
+
}
|
|
6001
|
+
if ((arg === "--out" || arg === "--cursor" || arg === "--reason" || arg === "--interval-ms" || arg === "--poll-interval-ms") && args[index + 1]) {
|
|
6002
|
+
index += 1;
|
|
6003
|
+
continue;
|
|
6004
|
+
}
|
|
6005
|
+
if (!arg.startsWith("--")) {
|
|
6006
|
+
return arg;
|
|
5610
6007
|
}
|
|
5611
6008
|
}
|
|
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;
|
|
6009
|
+
throw new DeeplineError(usage);
|
|
5631
6010
|
}
|
|
5632
|
-
async function
|
|
5633
|
-
const usage = "Usage: deepline
|
|
5634
|
-
let
|
|
6011
|
+
async function handleRunGet(args) {
|
|
6012
|
+
const usage = "Usage: deepline runs get <run-id> [--json] [--full]";
|
|
6013
|
+
let runId;
|
|
5635
6014
|
try {
|
|
5636
|
-
|
|
6015
|
+
runId = parseRunIdPositional(args, usage);
|
|
5637
6016
|
} catch (error) {
|
|
5638
6017
|
console.error(error instanceof Error ? error.message : usage);
|
|
5639
6018
|
return 1;
|
|
5640
6019
|
}
|
|
5641
6020
|
const client = new DeeplineClient();
|
|
5642
|
-
const
|
|
5643
|
-
const status = await client.getPlayStatus(workflowId);
|
|
6021
|
+
const status = await client.runs.get(runId);
|
|
5644
6022
|
writePlayResult(status, argsWantJson(args), {
|
|
5645
6023
|
fullJson: args.includes("--full")
|
|
5646
6024
|
});
|
|
5647
6025
|
return 0;
|
|
5648
6026
|
}
|
|
5649
|
-
function
|
|
6027
|
+
async function handleRunsList(args) {
|
|
6028
|
+
const usage = "Usage: deepline runs list --play <play-name> [--status <status>] [--json]";
|
|
6029
|
+
let playName = null;
|
|
6030
|
+
let statusFilter = null;
|
|
5650
6031
|
for (let index = 0; index < args.length; index += 1) {
|
|
5651
6032
|
const arg = args[index];
|
|
5652
|
-
if (arg === "--
|
|
6033
|
+
if ((arg === "--play" || arg === "--name") && args[index + 1]) {
|
|
6034
|
+
playName = parseReferencedPlayTarget(args[++index]).playName;
|
|
5653
6035
|
continue;
|
|
5654
6036
|
}
|
|
5655
|
-
if (arg === "--
|
|
5656
|
-
|
|
6037
|
+
if (arg === "--status" && args[index + 1]) {
|
|
6038
|
+
statusFilter = args[++index].trim().toLowerCase();
|
|
5657
6039
|
continue;
|
|
5658
6040
|
}
|
|
5659
|
-
if (
|
|
5660
|
-
|
|
6041
|
+
if (arg === "--json" || arg === "--compact") {
|
|
6042
|
+
continue;
|
|
5661
6043
|
}
|
|
5662
6044
|
}
|
|
5663
|
-
|
|
6045
|
+
if (!playName) {
|
|
6046
|
+
console.error(usage);
|
|
6047
|
+
return 1;
|
|
6048
|
+
}
|
|
6049
|
+
const client = new DeeplineClient();
|
|
6050
|
+
const runs = (await client.runs.list({
|
|
6051
|
+
play: playName,
|
|
6052
|
+
...statusFilter ? { status: statusFilter } : {}
|
|
6053
|
+
})).map((run) => ({
|
|
6054
|
+
runId: run.workflowId,
|
|
6055
|
+
workflowId: run.workflowId,
|
|
6056
|
+
temporalRunId: run.runId,
|
|
6057
|
+
status: String(run.status ?? "").toLowerCase(),
|
|
6058
|
+
startedAt: run.startTime,
|
|
6059
|
+
finishedAt: run.closeTime,
|
|
6060
|
+
executionTime: run.executionTime,
|
|
6061
|
+
playName: run.memo?.playName ?? playName
|
|
6062
|
+
}));
|
|
6063
|
+
if (argsWantJson(args)) {
|
|
6064
|
+
process.stdout.write(
|
|
6065
|
+
`${JSON.stringify({
|
|
6066
|
+
runs,
|
|
6067
|
+
count: runs.length,
|
|
6068
|
+
next: {
|
|
6069
|
+
get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
|
|
6070
|
+
}
|
|
6071
|
+
})}
|
|
6072
|
+
`
|
|
6073
|
+
);
|
|
6074
|
+
} else {
|
|
6075
|
+
if (runs.length === 0) {
|
|
6076
|
+
console.log(`No runs found for ${playName}.`);
|
|
6077
|
+
} else {
|
|
6078
|
+
for (const run of runs) {
|
|
6079
|
+
console.log(`${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`);
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
return 0;
|
|
5664
6084
|
}
|
|
5665
|
-
async function
|
|
5666
|
-
const usage = "Usage: deepline runs
|
|
6085
|
+
async function handleRunTail(args) {
|
|
6086
|
+
const usage = "Usage: deepline runs tail <run-id> [--json] [--compact] [--cursor <cursor>]";
|
|
5667
6087
|
let runId;
|
|
5668
6088
|
try {
|
|
5669
6089
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -5672,14 +6092,24 @@ async function handleRunStatus(args) {
|
|
|
5672
6092
|
return 1;
|
|
5673
6093
|
}
|
|
5674
6094
|
const client = new DeeplineClient();
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
6095
|
+
let afterLogIndex;
|
|
6096
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6097
|
+
const arg = args[index];
|
|
6098
|
+
if (arg === "--cursor" && args[index + 1]) {
|
|
6099
|
+
const parsed = Number(args[++index]);
|
|
6100
|
+
if (Number.isInteger(parsed) && parsed >= 0) {
|
|
6101
|
+
afterLogIndex = parsed;
|
|
6102
|
+
}
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
const status = await client.runs.tail(runId, {
|
|
6106
|
+
...afterLogIndex !== void 0 ? { afterLogIndex } : {}
|
|
5678
6107
|
});
|
|
5679
|
-
|
|
6108
|
+
writePlayResult(status, argsWantJson(args));
|
|
6109
|
+
return status.status === "failed" ? 1 : 0;
|
|
5680
6110
|
}
|
|
5681
6111
|
async function handleRunLogs(args) {
|
|
5682
|
-
const usage = "Usage: deepline runs logs <run-id> [--json]";
|
|
6112
|
+
const usage = "Usage: deepline runs logs <run-id> [--limit 200] [--out run.log] [--json]";
|
|
5683
6113
|
let runId;
|
|
5684
6114
|
try {
|
|
5685
6115
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -5687,14 +6117,86 @@ async function handleRunLogs(args) {
|
|
|
5687
6117
|
console.error(error instanceof Error ? error.message : usage);
|
|
5688
6118
|
return 1;
|
|
5689
6119
|
}
|
|
6120
|
+
let limit = 200;
|
|
6121
|
+
let outPath = null;
|
|
6122
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6123
|
+
const arg = args[index];
|
|
6124
|
+
if (arg === "--limit" && args[index + 1]) {
|
|
6125
|
+
limit = parsePositiveInteger2(args[++index], "--limit");
|
|
6126
|
+
continue;
|
|
6127
|
+
}
|
|
6128
|
+
if (arg === "--out" && args[index + 1]) {
|
|
6129
|
+
outPath = resolve7(args[++index]);
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
5690
6132
|
const client = new DeeplineClient();
|
|
5691
|
-
const status = await client.
|
|
6133
|
+
const status = await client.runs.get(runId);
|
|
5692
6134
|
const logs = status.progress?.logs ?? [];
|
|
6135
|
+
if (outPath) {
|
|
6136
|
+
writeFileSync4(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
6137
|
+
if (argsWantJson(args)) {
|
|
6138
|
+
process.stdout.write(
|
|
6139
|
+
`${JSON.stringify({
|
|
6140
|
+
runId: status.runId,
|
|
6141
|
+
log_path: outPath,
|
|
6142
|
+
lineCount: logs.length
|
|
6143
|
+
})}
|
|
6144
|
+
`
|
|
6145
|
+
);
|
|
6146
|
+
} else {
|
|
6147
|
+
console.log(`Wrote ${logs.length} log lines to ${outPath}`);
|
|
6148
|
+
}
|
|
6149
|
+
return 0;
|
|
6150
|
+
}
|
|
6151
|
+
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
6152
|
+
if (argsWantJson(args)) {
|
|
6153
|
+
process.stdout.write(
|
|
6154
|
+
`${JSON.stringify({
|
|
6155
|
+
runId: status.runId,
|
|
6156
|
+
totalCount: logs.length,
|
|
6157
|
+
returnedCount: entries.length,
|
|
6158
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
6159
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
6160
|
+
truncated: logs.length > entries.length,
|
|
6161
|
+
hasMore: logs.length > entries.length,
|
|
6162
|
+
entries,
|
|
6163
|
+
next: {
|
|
6164
|
+
export: `deepline runs logs ${status.runId} --out run.log --json`
|
|
6165
|
+
}
|
|
6166
|
+
})}
|
|
6167
|
+
`
|
|
6168
|
+
);
|
|
6169
|
+
} else {
|
|
6170
|
+
process.stdout.write(`${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`);
|
|
6171
|
+
}
|
|
6172
|
+
return 0;
|
|
6173
|
+
}
|
|
6174
|
+
async function handleRunStop(args) {
|
|
6175
|
+
const usage = 'Usage: deepline runs stop <run-id> [--reason "text"] [--json]';
|
|
6176
|
+
let runId;
|
|
6177
|
+
try {
|
|
6178
|
+
runId = parseRunIdPositional(args, usage);
|
|
6179
|
+
} catch (error) {
|
|
6180
|
+
console.error(error instanceof Error ? error.message : usage);
|
|
6181
|
+
return 1;
|
|
6182
|
+
}
|
|
6183
|
+
let reason;
|
|
6184
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6185
|
+
const arg = args[index];
|
|
6186
|
+
if (arg === "--reason" && args[index + 1]) {
|
|
6187
|
+
reason = args[++index];
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
const client = new DeeplineClient();
|
|
6191
|
+
const result = await client.runs.stop(runId, { reason });
|
|
5693
6192
|
if (argsWantJson(args)) {
|
|
5694
|
-
process.stdout.write(`${JSON.stringify(
|
|
6193
|
+
process.stdout.write(`${JSON.stringify(result)}
|
|
5695
6194
|
`);
|
|
5696
6195
|
} else {
|
|
5697
|
-
|
|
6196
|
+
console.log(`Stopped ${result.runId}`);
|
|
6197
|
+
if (result.hitlCancelledCount > 0) {
|
|
6198
|
+
console.log(` cancelled HITL waits: ${result.hitlCancelledCount}`);
|
|
6199
|
+
}
|
|
5698
6200
|
}
|
|
5699
6201
|
return 0;
|
|
5700
6202
|
}
|
|
@@ -5737,37 +6239,6 @@ async function handleRunExport(args) {
|
|
|
5737
6239
|
}
|
|
5738
6240
|
return 0;
|
|
5739
6241
|
}
|
|
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
6242
|
async function handlePlayGet(args) {
|
|
5772
6243
|
const target = args[0];
|
|
5773
6244
|
if (!target) {
|
|
@@ -5855,33 +6326,6 @@ async function handlePlayGet(args) {
|
|
|
5855
6326
|
}
|
|
5856
6327
|
return 0;
|
|
5857
6328
|
}
|
|
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
6329
|
function formatVersionLine(version) {
|
|
5886
6330
|
const revisionLabel = version.artifactHash?.slice(0, 12) ?? "unknown-revision";
|
|
5887
6331
|
return `v${version.version} ${revisionLabel} ${formatTimestamp(version.createdAt)}`;
|
|
@@ -6013,6 +6457,11 @@ function printPlayDescription(play) {
|
|
|
6013
6457
|
}
|
|
6014
6458
|
}
|
|
6015
6459
|
console.log(` Run: ${play.runCommand}`);
|
|
6460
|
+
if (play.origin === "prebuilt" && !play.canEdit) {
|
|
6461
|
+
console.log(
|
|
6462
|
+
` Customize: deepline plays get ${reference} --source --out ./my-play.play.ts`
|
|
6463
|
+
);
|
|
6464
|
+
}
|
|
6016
6465
|
}
|
|
6017
6466
|
async function handlePlaySearch(args) {
|
|
6018
6467
|
let options;
|
|
@@ -6110,6 +6559,7 @@ async function handlePlayPublish(args) {
|
|
|
6110
6559
|
const published = await client.registerPlayArtifact({
|
|
6111
6560
|
name: rootPlayName,
|
|
6112
6561
|
sourceCode: graph.root.sourceCode,
|
|
6562
|
+
sourceFiles: graph.root.sourceFiles,
|
|
6113
6563
|
artifact: graph.root.artifact,
|
|
6114
6564
|
compilerManifest: requireCompilerManifest(graph.root),
|
|
6115
6565
|
publish: true
|
|
@@ -6330,50 +6780,12 @@ Examples:
|
|
|
6330
6780
|
...options.json ? ["--json"] : []
|
|
6331
6781
|
]);
|
|
6332
6782
|
});
|
|
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
6783
|
play.command("versions").description("List revisions for a named play.").option("--name <name>", "Saved play name").option("--json", "Emit JSON output").action(async (options) => {
|
|
6340
6784
|
process.exitCode = await handlePlayVersions([
|
|
6341
6785
|
...options.name ? ["--name", options.name] : [],
|
|
6342
6786
|
...options.json ? ["--json"] : []
|
|
6343
6787
|
]);
|
|
6344
6788
|
});
|
|
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
6789
|
play.command("publish <target>").description("Bundle, validate, save, and publish a play.").option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option("--json", "Emit JSON output").action(async (target, options) => {
|
|
6378
6790
|
process.exitCode = await handlePlayPublish([
|
|
6379
6791
|
target,
|
|
@@ -6389,38 +6801,72 @@ Examples:
|
|
|
6389
6801
|
...options.json ? ["--json"] : []
|
|
6390
6802
|
]);
|
|
6391
6803
|
});
|
|
6392
|
-
const runs = program.command("runs").description("Inspect and export play runs.").addHelpText(
|
|
6804
|
+
const runs = program.command("runs").description("Inspect, tail, stop, and export play runs.").addHelpText(
|
|
6393
6805
|
"after",
|
|
6394
6806
|
`
|
|
6395
6807
|
Examples:
|
|
6396
|
-
deepline runs
|
|
6808
|
+
deepline runs get play/my-play/run/20260501t000000-000 --json
|
|
6809
|
+
deepline runs tail play/my-play/run/20260501t000000-000
|
|
6810
|
+
deepline runs logs play/my-play/run/20260501t000000-000 --out run.log --json
|
|
6811
|
+
deepline runs list --play my-play --status failed --json
|
|
6812
|
+
deepline runs stop play/my-play/run/20260501t000000-000 --reason "stale lock" --json
|
|
6397
6813
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
6398
|
-
deepline runs logs play/my-play/run/20260501t000000-000
|
|
6399
6814
|
`
|
|
6400
6815
|
);
|
|
6401
|
-
runs.command("
|
|
6402
|
-
process.exitCode = await
|
|
6816
|
+
runs.command("get <runId>").description("Get status, progress, outputs, errors, and recovery metadata for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--full", "Debug only: with --json, emit the raw status payload").action(async (runId, options) => {
|
|
6817
|
+
process.exitCode = await handleRunGet([
|
|
6403
6818
|
runId,
|
|
6404
6819
|
...options.json ? ["--json"] : [],
|
|
6405
6820
|
...options.full ? ["--full"] : []
|
|
6406
6821
|
]);
|
|
6407
6822
|
});
|
|
6408
|
-
runs.command("
|
|
6409
|
-
process.exitCode = await
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
options.
|
|
6823
|
+
runs.command("list").description("List play runs.").requiredOption("--play <name>", "Play name to filter runs").option("--status <status>", "Filter by run status").option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
|
|
6824
|
+
process.exitCode = await handleRunsList([
|
|
6825
|
+
"--play",
|
|
6826
|
+
options.play,
|
|
6827
|
+
...options.status ? ["--status", options.status] : [],
|
|
6828
|
+
...options.compact ? ["--compact"] : [],
|
|
6413
6829
|
...options.json ? ["--json"] : []
|
|
6414
6830
|
]);
|
|
6415
6831
|
});
|
|
6416
|
-
runs.command("
|
|
6832
|
+
runs.command("tail <runId>").description("Tail or fetch recent live events for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--compact", "Drop verbose fields from JSON output").option("--cursor <cursor>", "Resume after a previously returned event cursor").action(async (runId, options) => {
|
|
6833
|
+
process.exitCode = await handleRunTail([
|
|
6834
|
+
runId,
|
|
6835
|
+
...options.json ? ["--json"] : [],
|
|
6836
|
+
...options.compact ? ["--compact"] : [],
|
|
6837
|
+
...options.cursor ? ["--cursor", options.cursor] : []
|
|
6838
|
+
]);
|
|
6839
|
+
});
|
|
6840
|
+
runs.command("logs <runId>").description("Fetch persisted logs for a play run.").option("--limit <count>", "Maximum recent log lines to print without --out", "200").option("--out <path>", "Write the full persisted log stream to a file").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
6417
6841
|
process.exitCode = await handleRunLogs([
|
|
6418
6842
|
runId,
|
|
6843
|
+
...options.limit ? ["--limit", options.limit] : [],
|
|
6844
|
+
...options.out ? ["--out", options.out] : [],
|
|
6845
|
+
...options.json ? ["--json"] : []
|
|
6846
|
+
]);
|
|
6847
|
+
});
|
|
6848
|
+
runs.command("stop <runId>").description("Stop a play run.").option("--reason <text>", "Reason to include with the stop request").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
6849
|
+
process.exitCode = await handleRunStop([
|
|
6850
|
+
runId,
|
|
6851
|
+
...options.reason ? ["--reason", options.reason] : [],
|
|
6852
|
+
...options.json ? ["--json"] : []
|
|
6853
|
+
]);
|
|
6854
|
+
});
|
|
6855
|
+
runs.command("export <runId>").description("Export the completed row output for a play run to CSV.").requiredOption("--out <path>", "Output CSV path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
6856
|
+
process.exitCode = await handleRunExport([
|
|
6857
|
+
runId,
|
|
6858
|
+
"--out",
|
|
6859
|
+
options.out,
|
|
6419
6860
|
...options.json ? ["--json"] : []
|
|
6420
6861
|
]);
|
|
6421
6862
|
});
|
|
6422
6863
|
}
|
|
6423
6864
|
|
|
6865
|
+
// src/cli/commands/tools.ts
|
|
6866
|
+
import { chmodSync, mkdtempSync, writeFileSync as writeFileSync6 } from "fs";
|
|
6867
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
6868
|
+
import { join as join8 } from "path";
|
|
6869
|
+
|
|
6424
6870
|
// src/tool-output.ts
|
|
6425
6871
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
6426
6872
|
import { homedir as homedir3 } from "os";
|
|
@@ -6589,31 +7035,23 @@ async function listTools(args) {
|
|
|
6589
7035
|
}
|
|
6590
7036
|
return 0;
|
|
6591
7037
|
}
|
|
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();
|
|
7038
|
+
async function searchTools(queryInput, options = {}) {
|
|
7039
|
+
const query = queryInput.trim();
|
|
6608
7040
|
if (!query) {
|
|
6609
7041
|
console.error("Usage: deepline tools search <query> [--json]");
|
|
6610
7042
|
return 1;
|
|
6611
7043
|
}
|
|
6612
|
-
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
6613
7044
|
const client = new DeeplineClient();
|
|
6614
|
-
const
|
|
6615
|
-
|
|
6616
|
-
|
|
7045
|
+
const result = await client.searchTools({
|
|
7046
|
+
query,
|
|
7047
|
+
categories: options.categories,
|
|
7048
|
+
searchTerms: options.searchTerms,
|
|
7049
|
+
searchMode: options.searchMode,
|
|
7050
|
+
includeSearchDebug: options.includeSearchDebug
|
|
7051
|
+
});
|
|
7052
|
+
const items = result.tools.map(toListedTool);
|
|
7053
|
+
if (options.json || shouldEmitJson()) {
|
|
7054
|
+
process.stdout.write(`${JSON.stringify({ ...result, tools: items })}
|
|
6617
7055
|
`);
|
|
6618
7056
|
return 0;
|
|
6619
7057
|
}
|
|
@@ -6667,11 +7105,14 @@ Common commands:
|
|
|
6667
7105
|
...options.json ? ["--json"] : []
|
|
6668
7106
|
]);
|
|
6669
7107
|
});
|
|
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
|
-
|
|
7108
|
+
tools.command("search <query>").description("Search available tools.").option("--categories <categories>", "Comma-separated categories to filter ranked search").option("--search_terms <terms>", "Structured search terms for ranked search").option("--search-terms <terms>", "Structured search terms for ranked search").option("--search-mode <mode>", "Ranked search mode: v1 or v2").option("--include-search-debug", "Include ranked search debug metadata").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
7109
|
+
process.exitCode = await searchTools(query, {
|
|
7110
|
+
json: options.json,
|
|
7111
|
+
categories: options.categories,
|
|
7112
|
+
searchTerms: options.searchTerms ?? options.search_terms,
|
|
7113
|
+
searchMode: options.searchMode === "v1" || options.searchMode === "v2" ? options.searchMode : void 0,
|
|
7114
|
+
includeSearchDebug: Boolean(options.includeSearchDebug)
|
|
7115
|
+
});
|
|
6675
7116
|
});
|
|
6676
7117
|
tools.command("get <toolId>").alias("describe").description("Show metadata for a tool.").addHelpText(
|
|
6677
7118
|
"after",
|
|
@@ -7029,6 +7470,61 @@ function parseExecuteOptions(args) {
|
|
|
7029
7470
|
}
|
|
7030
7471
|
return { toolId, params, outputFormat, noPreview };
|
|
7031
7472
|
}
|
|
7473
|
+
function safeFileStem(value) {
|
|
7474
|
+
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
7475
|
+
}
|
|
7476
|
+
function shellQuote(value) {
|
|
7477
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
7478
|
+
}
|
|
7479
|
+
function powerShellQuote(value) {
|
|
7480
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
7481
|
+
}
|
|
7482
|
+
function seedToolListScript(input) {
|
|
7483
|
+
const stem = safeFileStem(input.toolId);
|
|
7484
|
+
const fileName = `${stem}-workflow-seed-${Date.now()}.play.ts`;
|
|
7485
|
+
const scriptDir = mkdtempSync(join8(tmpdir3(), "deepline-workflow-seed-"));
|
|
7486
|
+
chmodSync(scriptDir, 448);
|
|
7487
|
+
const scriptPath = join8(scriptDir, fileName);
|
|
7488
|
+
const projectDir = `deepline/projects/${stem}-workflow`;
|
|
7489
|
+
const playName = `${stem}-workflow`;
|
|
7490
|
+
const sampleRows = input.rows.length > 0 ? `${JSON.stringify(input.rows.slice(0, 2)).replace(/\]$/, "")}, ...]` : "[]";
|
|
7491
|
+
const columns = Object.keys(input.rows[0] ?? {}).join(", ");
|
|
7492
|
+
const rowKey = Object.prototype.hasOwnProperty.call(input.rows[0] ?? {}, "id") ? '"id"' : "(row) => JSON.stringify(row)";
|
|
7493
|
+
const script = `import { definePlay } from 'deepline';
|
|
7494
|
+
|
|
7495
|
+
export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
7496
|
+
const result = await ctx.tools.execute({
|
|
7497
|
+
id: ${JSON.stringify(input.toolId)},
|
|
7498
|
+
tool: ${JSON.stringify(input.toolId)},
|
|
7499
|
+
input: ${JSON.stringify(input.payload)},
|
|
7500
|
+
description: ${JSON.stringify(`Seed ${input.toolId} rows for workflow expansion.`)},
|
|
7501
|
+
});
|
|
7502
|
+
|
|
7503
|
+
const list = Object.values(result.lists)[0];
|
|
7504
|
+
const rows = (list?.get() ?? []).slice(0, 100);
|
|
7505
|
+
// ${sampleRows}
|
|
7506
|
+
// columns: ${columns}
|
|
7507
|
+
// .step('email_waterfall', (row, rowCtx) => rowCtx.runPlay('name_domain_email', 'name-and-domain-to-email', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), domain: String(row.domain ?? '') }, { description: 'Resolve email.' }))
|
|
7508
|
+
// .step('phone_waterfall', (row, rowCtx) => rowCtx.runPlay('contact_phone', 'contact-to-phone', { first_name: String(row.first_name ?? ''), last_name: String(row.last_name ?? ''), email: String(row.email ?? '') }, { description: 'Resolve phone.' }))
|
|
7509
|
+
// ctx.map is idempotent by map key + row key; reruns reuse completed rows.
|
|
7510
|
+
const enrichedData = await ctx
|
|
7511
|
+
.map('enriched_data', rows, { key: ${rowKey} })
|
|
7512
|
+
.run({ description: 'Enrich seeded rows.' });
|
|
7513
|
+
|
|
7514
|
+
return {
|
|
7515
|
+
rows: enrichedData,
|
|
7516
|
+
count: await enrichedData.count(),
|
|
7517
|
+
};
|
|
7518
|
+
});
|
|
7519
|
+
`;
|
|
7520
|
+
writeFileSync6(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
7521
|
+
return {
|
|
7522
|
+
path: scriptPath,
|
|
7523
|
+
projectDir,
|
|
7524
|
+
macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
|
|
7525
|
+
windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
|
|
7526
|
+
};
|
|
7527
|
+
}
|
|
7032
7528
|
async function executeTool(args) {
|
|
7033
7529
|
let parsed;
|
|
7034
7530
|
try {
|
|
@@ -7082,6 +7578,11 @@ async function executeTool(args) {
|
|
|
7082
7578
|
return 0;
|
|
7083
7579
|
}
|
|
7084
7580
|
const csv = writeCsvOutputFile(listConversion.rows, `${parsed.toolId}_output`);
|
|
7581
|
+
const seededScript = seedToolListScript({
|
|
7582
|
+
toolId: parsed.toolId,
|
|
7583
|
+
payload: parsed.params,
|
|
7584
|
+
rows: listConversion.rows
|
|
7585
|
+
});
|
|
7085
7586
|
if (parsed.outputFormat === "csv_file") {
|
|
7086
7587
|
process.stdout.write(`${JSON.stringify({
|
|
7087
7588
|
extracted_csv: csv.path,
|
|
@@ -7090,6 +7591,12 @@ async function executeTool(args) {
|
|
|
7090
7591
|
preview: csv.preview,
|
|
7091
7592
|
list_strategy: listConversion.strategy,
|
|
7092
7593
|
list_source_path: listConversion.sourcePath,
|
|
7594
|
+
starter_script: seededScript.path,
|
|
7595
|
+
project_dir: seededScript.projectDir,
|
|
7596
|
+
copy_to_project: {
|
|
7597
|
+
macos_linux: seededScript.macCopyCommand,
|
|
7598
|
+
windows_powershell: seededScript.windowsCopyCommand
|
|
7599
|
+
},
|
|
7093
7600
|
summary
|
|
7094
7601
|
})}
|
|
7095
7602
|
`);
|
|
@@ -7107,14 +7614,20 @@ async function executeTool(args) {
|
|
|
7107
7614
|
console.log(`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`);
|
|
7108
7615
|
}
|
|
7109
7616
|
console.log(`preview: ${JSON.stringify(csv.preview)}`);
|
|
7617
|
+
console.log(`starter script: ${seededScript.path}`);
|
|
7618
|
+
console.log(
|
|
7619
|
+
"next: Move the script into a project folder and expand it into a Deepline play. Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
|
|
7620
|
+
);
|
|
7621
|
+
console.log(`macOS/Linux: ${seededScript.macCopyCommand}`);
|
|
7622
|
+
console.log(`Windows PowerShell: ${seededScript.windowsCopyCommand}`);
|
|
7110
7623
|
return 0;
|
|
7111
7624
|
}
|
|
7112
7625
|
|
|
7113
7626
|
// src/cli/skills-sync.ts
|
|
7114
|
-
import { spawn } from "child_process";
|
|
7115
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as
|
|
7627
|
+
import { spawn, spawnSync } from "child_process";
|
|
7628
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
7116
7629
|
import { homedir as homedir4 } from "os";
|
|
7117
|
-
import { dirname as dirname7, join as
|
|
7630
|
+
import { dirname as dirname7, join as join9 } from "path";
|
|
7118
7631
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
7119
7632
|
var SDK_SKILL_NAME = "deepline-sdk";
|
|
7120
7633
|
var SKILL_AGENTS = ["codex", "claude-code", "cursor"];
|
|
@@ -7125,7 +7638,7 @@ function shouldSkipSkillsSync() {
|
|
|
7125
7638
|
}
|
|
7126
7639
|
function sdkSkillsVersionPath(baseUrl) {
|
|
7127
7640
|
const home = process.env.HOME?.trim() || homedir4();
|
|
7128
|
-
return
|
|
7641
|
+
return join9(home, ".local", "deepline", baseUrlSlug(baseUrl), "sdk-skills", ".version");
|
|
7129
7642
|
}
|
|
7130
7643
|
function readLocalSkillsVersion(baseUrl) {
|
|
7131
7644
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
@@ -7139,7 +7652,7 @@ function readLocalSkillsVersion(baseUrl) {
|
|
|
7139
7652
|
function writeLocalSkillsVersion(baseUrl, version) {
|
|
7140
7653
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
7141
7654
|
mkdirSync4(dirname7(path), { recursive: true });
|
|
7142
|
-
|
|
7655
|
+
writeFileSync7(path, `${version}
|
|
7143
7656
|
`, "utf-8");
|
|
7144
7657
|
}
|
|
7145
7658
|
async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
@@ -7170,9 +7683,26 @@ async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
|
7170
7683
|
clearTimeout(timeout);
|
|
7171
7684
|
}
|
|
7172
7685
|
}
|
|
7173
|
-
function
|
|
7686
|
+
function buildSkillsInstallArgs(baseUrl) {
|
|
7687
|
+
const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
|
|
7688
|
+
return [
|
|
7689
|
+
"--yes",
|
|
7690
|
+
"skills",
|
|
7691
|
+
"add",
|
|
7692
|
+
packageUrl,
|
|
7693
|
+
"--agents",
|
|
7694
|
+
...SKILL_AGENTS,
|
|
7695
|
+
"--global",
|
|
7696
|
+
"--yes",
|
|
7697
|
+
"--skill",
|
|
7698
|
+
SDK_SKILL_NAME,
|
|
7699
|
+
"--full-depth"
|
|
7700
|
+
];
|
|
7701
|
+
}
|
|
7702
|
+
function buildBunxSkillsInstallArgs(baseUrl) {
|
|
7174
7703
|
const packageUrl = new URL("/.well-known/skills/index.json", baseUrl).toString();
|
|
7175
|
-
|
|
7704
|
+
return [
|
|
7705
|
+
"--bun",
|
|
7176
7706
|
"skills",
|
|
7177
7707
|
"add",
|
|
7178
7708
|
packageUrl,
|
|
@@ -7184,8 +7714,40 @@ function runSkillsInstall(baseUrl) {
|
|
|
7184
7714
|
SDK_SKILL_NAME,
|
|
7185
7715
|
"--full-depth"
|
|
7186
7716
|
];
|
|
7717
|
+
}
|
|
7718
|
+
function hasCommand(command) {
|
|
7719
|
+
const result = spawnSync(command, ["--version"], {
|
|
7720
|
+
stdio: "ignore",
|
|
7721
|
+
shell: process.platform === "win32"
|
|
7722
|
+
});
|
|
7723
|
+
return result.status === 0;
|
|
7724
|
+
}
|
|
7725
|
+
function shellQuote2(arg) {
|
|
7726
|
+
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
7727
|
+
}
|
|
7728
|
+
function resolveSkillsInstallCommands(baseUrl) {
|
|
7729
|
+
const npxArgs = buildSkillsInstallArgs(baseUrl);
|
|
7730
|
+
const npxInstall = {
|
|
7731
|
+
command: "npx",
|
|
7732
|
+
args: npxArgs,
|
|
7733
|
+
manualCommand: `npx ${npxArgs.map(shellQuote2).join(" ")}`
|
|
7734
|
+
};
|
|
7735
|
+
if (hasCommand("bunx")) {
|
|
7736
|
+
const bunxArgs = buildBunxSkillsInstallArgs(baseUrl);
|
|
7737
|
+
return [
|
|
7738
|
+
{
|
|
7739
|
+
command: "bunx",
|
|
7740
|
+
args: bunxArgs,
|
|
7741
|
+
manualCommand: `bunx ${bunxArgs.map(shellQuote2).join(" ")}`
|
|
7742
|
+
},
|
|
7743
|
+
npxInstall
|
|
7744
|
+
];
|
|
7745
|
+
}
|
|
7746
|
+
return [npxInstall];
|
|
7747
|
+
}
|
|
7748
|
+
function runOneSkillsInstall(install) {
|
|
7187
7749
|
return new Promise((resolve8) => {
|
|
7188
|
-
const child = spawn(
|
|
7750
|
+
const child = spawn(install.command, install.args, {
|
|
7189
7751
|
stdio: ["ignore", "ignore", "pipe"],
|
|
7190
7752
|
env: process.env
|
|
7191
7753
|
});
|
|
@@ -7194,37 +7756,63 @@ function runSkillsInstall(baseUrl) {
|
|
|
7194
7756
|
stderr += chunk.toString("utf-8");
|
|
7195
7757
|
});
|
|
7196
7758
|
child.on("error", (error) => {
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7759
|
+
resolve8({
|
|
7760
|
+
ok: false,
|
|
7761
|
+
detail: `failed to start ${install.command}: ${error.message}`,
|
|
7762
|
+
manualCommand: install.manualCommand
|
|
7763
|
+
});
|
|
7200
7764
|
});
|
|
7201
7765
|
child.on("close", (code) => {
|
|
7202
7766
|
if (code === 0) {
|
|
7203
|
-
resolve8(true);
|
|
7767
|
+
resolve8({ ok: true, detail: "", manualCommand: install.manualCommand });
|
|
7204
7768
|
return;
|
|
7205
7769
|
}
|
|
7206
7770
|
const detail = stderr.trim();
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
);
|
|
7212
|
-
resolve8(false);
|
|
7771
|
+
resolve8({
|
|
7772
|
+
ok: false,
|
|
7773
|
+
detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
|
|
7774
|
+
manualCommand: install.manualCommand
|
|
7775
|
+
});
|
|
7213
7776
|
});
|
|
7214
7777
|
});
|
|
7215
7778
|
}
|
|
7779
|
+
async function runSkillsInstall(baseUrl) {
|
|
7780
|
+
const failures = [];
|
|
7781
|
+
for (const install of resolveSkillsInstallCommands(baseUrl)) {
|
|
7782
|
+
const result = await runOneSkillsInstall(install);
|
|
7783
|
+
if (result.ok) return true;
|
|
7784
|
+
failures.push(result);
|
|
7785
|
+
}
|
|
7786
|
+
const details = failures.map((failure) => failure.detail).filter(Boolean).join("\n");
|
|
7787
|
+
const manualCommand = failures.at(-1)?.manualCommand;
|
|
7788
|
+
process.stderr.write(
|
|
7789
|
+
`SDK skills sync failed${details ? `:
|
|
7790
|
+
${details}` : ""}
|
|
7791
|
+
` + (manualCommand ? `Run manually: ${manualCommand}
|
|
7792
|
+
` : "")
|
|
7793
|
+
);
|
|
7794
|
+
return false;
|
|
7795
|
+
}
|
|
7796
|
+
function writeSdkSkillsStatusLine(line) {
|
|
7797
|
+
const progress = getActiveCliProgress();
|
|
7798
|
+
if (progress) {
|
|
7799
|
+
progress.writeLine(line);
|
|
7800
|
+
return;
|
|
7801
|
+
}
|
|
7802
|
+
process.stderr.write(`${line}
|
|
7803
|
+
`);
|
|
7804
|
+
}
|
|
7216
7805
|
async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
7217
7806
|
if (attemptedSync || shouldSkipSkillsSync()) return;
|
|
7218
7807
|
attemptedSync = true;
|
|
7219
7808
|
const localVersion = readLocalSkillsVersion(baseUrl);
|
|
7220
7809
|
const update = await fetchSkillsUpdate(baseUrl, localVersion);
|
|
7221
7810
|
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");
|
|
7811
|
+
writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-sdk skill...");
|
|
7224
7812
|
const installed = await runSkillsInstall(baseUrl);
|
|
7225
7813
|
if (!installed) return;
|
|
7226
7814
|
writeLocalSkillsVersion(baseUrl, update.remoteVersion);
|
|
7227
|
-
|
|
7815
|
+
writeSdkSkillsStatusLine("SDK skills are up to date.");
|
|
7228
7816
|
}
|
|
7229
7817
|
|
|
7230
7818
|
// src/cli/trace.ts
|
|
@@ -7386,4 +7974,3 @@ Output:
|
|
|
7386
7974
|
process.exit(process.exitCode ?? 0);
|
|
7387
7975
|
}
|
|
7388
7976
|
main();
|
|
7389
|
-
//# sourceMappingURL=index.mjs.map
|