deepline 0.1.32 → 0.1.35
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 +33 -20
- package/dist/cli/index.js +1117 -381
- package/dist/cli/index.mjs +1058 -322
- package/dist/index.d.mts +131 -85
- package/dist/index.d.ts +131 -85
- package/dist/index.js +569 -124
- package/dist/index.mjs +570 -125
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +212 -1
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +129 -2
- package/dist/repo/apps/play-runner-workers/src/entry.ts +147 -64
- package/dist/repo/apps/play-runner-workers/src/workflow-retry.ts +50 -0
- package/dist/repo/sdk/src/client.ts +46 -33
- package/dist/repo/sdk/src/config.ts +109 -233
- package/dist/repo/sdk/src/http.ts +44 -4
- package/dist/repo/sdk/src/index.ts +8 -5
- package/dist/repo/sdk/src/play.ts +124 -45
- package/dist/repo/sdk/src/plays/harness-stub.ts +2 -2
- package/dist/repo/sdk/src/tool-output.ts +26 -7
- package/dist/repo/sdk/src/types.ts +48 -12
- package/dist/repo/sdk/src/version.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/run-failure.ts +49 -0
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +1 -1
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +138 -35
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command as Command2 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/config.ts
|
|
7
|
-
import {
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
8
|
import { homedir } from "os";
|
|
9
9
|
import { dirname, join, resolve } from "path";
|
|
10
10
|
|
|
@@ -44,19 +44,12 @@ var ConfigError = class extends DeeplineError {
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
// src/config.ts
|
|
47
|
+
var HOST_URL_ENV = "DEEPLINE_HOST_URL";
|
|
48
|
+
var API_KEY_ENV = "DEEPLINE_API_KEY";
|
|
47
49
|
var PROD_URL = "https://code.deepline.com";
|
|
48
50
|
var DEFAULT_TIMEOUT = 6e4;
|
|
49
51
|
var DEFAULT_MAX_RETRIES = 3;
|
|
50
|
-
var
|
|
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
|
-
}
|
|
52
|
+
var PROJECT_DEEPLINE_ENV_FILE = ".env.deepline";
|
|
60
53
|
function baseUrlSlug(baseUrl) {
|
|
61
54
|
let url;
|
|
62
55
|
try {
|
|
@@ -65,7 +58,7 @@ function baseUrlSlug(baseUrl) {
|
|
|
65
58
|
return "unknown";
|
|
66
59
|
}
|
|
67
60
|
const host = url.hostname || "unknown";
|
|
68
|
-
const port = url.port ? parseInt(url.port, 10) : null;
|
|
61
|
+
const port = url.port ? Number.parseInt(url.port, 10) : null;
|
|
69
62
|
let slug = host.replace(/[^a-zA-Z0-9]/g, "-");
|
|
70
63
|
if (port && port !== 80 && port !== 443) {
|
|
71
64
|
slug = `${slug}-${port}`;
|
|
@@ -92,79 +85,52 @@ function parseEnvFile(filePath) {
|
|
|
92
85
|
}
|
|
93
86
|
return env;
|
|
94
87
|
}
|
|
95
|
-
function findNearestEnvFile(
|
|
88
|
+
function findNearestEnvFile(name, startDir = process.cwd()) {
|
|
96
89
|
let current = resolve(startDir);
|
|
97
90
|
while (true) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (existsSync(filePath)) return filePath;
|
|
101
|
-
}
|
|
91
|
+
const filePath = join(current, name);
|
|
92
|
+
if (existsSync(filePath)) return filePath;
|
|
102
93
|
const parent = dirname(current);
|
|
103
94
|
if (parent === current) return null;
|
|
104
95
|
current = parent;
|
|
105
96
|
}
|
|
106
97
|
}
|
|
107
|
-
function
|
|
108
|
-
const filePath = findNearestEnvFile(
|
|
98
|
+
function loadProjectDeeplineEnv(startDir = process.cwd()) {
|
|
99
|
+
const filePath = findNearestEnvFile(PROJECT_DEEPLINE_ENV_FILE, startDir);
|
|
109
100
|
return filePath ? parseEnvFile(filePath) : {};
|
|
110
101
|
}
|
|
111
|
-
function
|
|
112
|
-
|
|
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
|
-
}
|
|
141
|
-
function normalizeWorktreeBaseUrl(baseUrl, worktreeEnv = findNearestWorktreeEnv()) {
|
|
142
|
-
const trimmed = baseUrl.trim().replace(/\/$/, "");
|
|
143
|
-
if (!trimmed) return trimmed;
|
|
102
|
+
function normalizeBaseUrl(baseUrl) {
|
|
103
|
+
const trimmed = baseUrl.trim().replace(/\/+$/, "");
|
|
104
|
+
if (!trimmed) return "";
|
|
144
105
|
try {
|
|
145
106
|
const parsed = new URL(trimmed);
|
|
146
|
-
if (parsed.
|
|
147
|
-
|
|
148
|
-
if (port) return `${parsed.protocol}//localhost:${port}`;
|
|
107
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
108
|
+
return "";
|
|
149
109
|
}
|
|
110
|
+
return parsed.toString().replace(/\/+$/, "");
|
|
150
111
|
} catch {
|
|
112
|
+
return "";
|
|
151
113
|
}
|
|
152
|
-
return trimmed;
|
|
153
114
|
}
|
|
154
|
-
function
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return
|
|
115
|
+
function firstNonEmpty(...values) {
|
|
116
|
+
for (const value of values) {
|
|
117
|
+
const trimmed = value?.trim();
|
|
118
|
+
if (trimmed) return trimmed;
|
|
119
|
+
}
|
|
120
|
+
return "";
|
|
160
121
|
}
|
|
161
|
-
function
|
|
122
|
+
function sdkCliConfigDir(baseUrl) {
|
|
162
123
|
const home = process.env.HOME?.trim() || homedir();
|
|
163
|
-
return join(home, ".local", "deepline", baseUrlSlug(baseUrl || PROD_URL)
|
|
124
|
+
return join(home, ".local", "deepline", baseUrlSlug(baseUrl || PROD_URL));
|
|
125
|
+
}
|
|
126
|
+
function sdkCliEnvFilePath(baseUrl) {
|
|
127
|
+
return join(sdkCliConfigDir(baseUrl), ".env");
|
|
164
128
|
}
|
|
165
129
|
function loadCliEnv(baseUrl = PROD_URL) {
|
|
166
|
-
|
|
167
|
-
|
|
130
|
+
return parseEnvFile(sdkCliEnvFilePath(baseUrl));
|
|
131
|
+
}
|
|
132
|
+
function hostConfigDirPath(baseUrl) {
|
|
133
|
+
return sdkCliConfigDir(baseUrl);
|
|
168
134
|
}
|
|
169
135
|
function hostEnvFilePath(baseUrl) {
|
|
170
136
|
return sdkCliEnvFilePath(baseUrl);
|
|
@@ -175,9 +141,10 @@ function saveHostEnvValues(baseUrl, values) {
|
|
|
175
141
|
if (!existsSync(dir)) {
|
|
176
142
|
mkdirSync(dir, { recursive: true });
|
|
177
143
|
}
|
|
178
|
-
const existing =
|
|
144
|
+
const existing = parseEnvFile(filePath);
|
|
179
145
|
const merged = { ...existing, ...values };
|
|
180
|
-
const
|
|
146
|
+
const allowedKeys = /* @__PURE__ */ new Set([HOST_URL_ENV, API_KEY_ENV]);
|
|
147
|
+
const lines = Object.entries(merged).filter(([key, value]) => allowedKeys.has(key) && value !== "").map(([key, value]) => `${key}=${value}`);
|
|
181
148
|
writeFileSync(filePath, `${lines.join("\n")}
|
|
182
149
|
`, "utf-8");
|
|
183
150
|
}
|
|
@@ -185,31 +152,36 @@ function loadGlobalCliEnv() {
|
|
|
185
152
|
return loadCliEnv(PROD_URL);
|
|
186
153
|
}
|
|
187
154
|
function autoDetectBaseUrl() {
|
|
188
|
-
const
|
|
189
|
-
if (envOrigin) return normalizeWorktreeBaseUrl(envOrigin);
|
|
190
|
-
const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
|
|
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);
|
|
196
|
-
const worktreeBaseUrl = resolveWorktreeBaseUrl();
|
|
197
|
-
if (worktreeBaseUrl) return worktreeBaseUrl;
|
|
155
|
+
const projectEnv = loadProjectDeeplineEnv();
|
|
198
156
|
const globalEnv = loadGlobalCliEnv();
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
157
|
+
return normalizeBaseUrl(process.env[HOST_URL_ENV] ?? "") || normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? "") || normalizeBaseUrl(globalEnv[HOST_URL_ENV] ?? "") || PROD_URL;
|
|
158
|
+
}
|
|
159
|
+
function resolveApiKeyForBaseUrl(baseUrl, explicitApiKey) {
|
|
160
|
+
const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
|
|
161
|
+
const projectEnv = loadProjectDeeplineEnv();
|
|
162
|
+
const cliEnv = loadCliEnv(normalizedBaseUrl || baseUrl);
|
|
163
|
+
const projectBaseUrl = normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? "");
|
|
164
|
+
const projectKeyApplies = projectBaseUrl === normalizedBaseUrl;
|
|
165
|
+
return firstNonEmpty(
|
|
166
|
+
explicitApiKey,
|
|
167
|
+
process.env[API_KEY_ENV],
|
|
168
|
+
projectKeyApplies ? projectEnv[API_KEY_ENV] : "",
|
|
169
|
+
cliEnv[API_KEY_ENV]
|
|
170
|
+
);
|
|
202
171
|
}
|
|
203
172
|
function resolveConfig(options) {
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
173
|
+
const baseUrl = normalizeBaseUrl(
|
|
174
|
+
options?.baseUrl?.trim() || autoDetectBaseUrl()
|
|
175
|
+
);
|
|
176
|
+
if (!baseUrl) {
|
|
177
|
+
throw new ConfigError(
|
|
178
|
+
`Invalid ${HOST_URL_ENV}. Expected an http(s) URL such as https://code.deepline.com.`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
const apiKey = resolveApiKeyForBaseUrl(baseUrl, options?.apiKey);
|
|
210
182
|
if (!apiKey) {
|
|
211
183
|
throw new ConfigError(
|
|
212
|
-
`No API key found. Set
|
|
184
|
+
`No API key found. Set ${API_KEY_ENV}, add it to .env.deepline, or run: deepline auth register`
|
|
213
185
|
);
|
|
214
186
|
}
|
|
215
187
|
return {
|
|
@@ -219,32 +191,10 @@ function resolveConfig(options) {
|
|
|
219
191
|
maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
|
|
220
192
|
};
|
|
221
193
|
}
|
|
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
|
-
}
|
|
244
194
|
|
|
245
195
|
// src/version.ts
|
|
246
|
-
var SDK_VERSION = "0.1.
|
|
247
|
-
var SDK_API_CONTRACT = "2026-05-
|
|
196
|
+
var SDK_VERSION = "0.1.35";
|
|
197
|
+
var SDK_API_CONTRACT = "2026-05-v2-tool-result-contract";
|
|
248
198
|
|
|
249
199
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
250
200
|
var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
|
|
@@ -326,7 +276,7 @@ var HttpClient = class {
|
|
|
326
276
|
const response = await fetch(candidateUrl, {
|
|
327
277
|
method,
|
|
328
278
|
headers,
|
|
329
|
-
body: options?.formData !== void 0 ? options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
279
|
+
body: options?.formData !== void 0 ? typeof options.formData === "function" ? options.formData() : options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
330
280
|
signal: controller.signal
|
|
331
281
|
});
|
|
332
282
|
clearTimeout(timeoutId);
|
|
@@ -409,10 +359,13 @@ var HttpClient = class {
|
|
|
409
359
|
throw new AuthError();
|
|
410
360
|
}
|
|
411
361
|
if (!response.ok) {
|
|
362
|
+
const body = await response.text();
|
|
363
|
+
const parsed = parseResponseBody(body);
|
|
412
364
|
throw new DeeplineError(
|
|
413
|
-
|
|
365
|
+
apiErrorMessage(parsed, response.status),
|
|
414
366
|
response.status,
|
|
415
|
-
"API_ERROR"
|
|
367
|
+
"API_ERROR",
|
|
368
|
+
{ response: parsed }
|
|
416
369
|
);
|
|
417
370
|
}
|
|
418
371
|
if (!response.body) {
|
|
@@ -462,6 +415,26 @@ var HttpClient = class {
|
|
|
462
415
|
return this.request(path, { method: "DELETE" });
|
|
463
416
|
}
|
|
464
417
|
};
|
|
418
|
+
function parseResponseBody(body) {
|
|
419
|
+
try {
|
|
420
|
+
return JSON.parse(body);
|
|
421
|
+
} catch {
|
|
422
|
+
return body;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function apiErrorMessage(parsed, status) {
|
|
426
|
+
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
427
|
+
if (typeof errorValue === "string") {
|
|
428
|
+
return errorValue;
|
|
429
|
+
}
|
|
430
|
+
if (errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string") {
|
|
431
|
+
return errorValue.message;
|
|
432
|
+
}
|
|
433
|
+
if (typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string") {
|
|
434
|
+
return parsed.message;
|
|
435
|
+
}
|
|
436
|
+
return `HTTP ${status}`;
|
|
437
|
+
}
|
|
465
438
|
function parseRetryAfter(response) {
|
|
466
439
|
const header = response.headers.get("retry-after");
|
|
467
440
|
if (header) {
|
|
@@ -525,15 +498,17 @@ function decodeSseFrame(frame) {
|
|
|
525
498
|
return parsed;
|
|
526
499
|
}
|
|
527
500
|
function sleep(ms) {
|
|
528
|
-
return new Promise((
|
|
501
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
529
502
|
}
|
|
530
503
|
|
|
531
504
|
// src/client.ts
|
|
532
505
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
533
506
|
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
507
|
+
var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
|
|
508
|
+
var V2_EXECUTE_RESPONSE_CONTRACT = "v2-tool-execution-result";
|
|
534
509
|
var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
|
|
535
510
|
function sleep2(ms) {
|
|
536
|
-
return new Promise((
|
|
511
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
537
512
|
}
|
|
538
513
|
function isTransientCompileManifestError(error) {
|
|
539
514
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
@@ -799,13 +774,16 @@ var DeeplineClient = class {
|
|
|
799
774
|
/**
|
|
800
775
|
* Execute a tool and return the standard execution envelope.
|
|
801
776
|
*
|
|
802
|
-
* The `
|
|
803
|
-
* contains provider
|
|
777
|
+
* The `toolExecutionResult.toolOutput.raw` field contains the raw tool output.
|
|
778
|
+
* `toolExecutionResult.toolOutput.meta` contains tool/provider metadata.
|
|
804
779
|
* Top-level fields such as `status`, `job_id`, and `billing` describe the
|
|
805
|
-
* Deepline execution.
|
|
780
|
+
* Deepline execution envelope.
|
|
806
781
|
*/
|
|
807
782
|
async executeTool(toolId, input, options) {
|
|
808
|
-
const headers =
|
|
783
|
+
const headers = {
|
|
784
|
+
[EXECUTE_RESPONSE_CONTRACT_HEADER]: V2_EXECUTE_RESPONSE_CONTRACT,
|
|
785
|
+
...options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : {}
|
|
786
|
+
};
|
|
809
787
|
return this.http.post(
|
|
810
788
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
811
789
|
{ payload: input },
|
|
@@ -1103,34 +1081,37 @@ var DeeplineClient = class {
|
|
|
1103
1081
|
* ```
|
|
1104
1082
|
*/
|
|
1105
1083
|
async stagePlayFiles(files) {
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1108
|
-
"metadata",
|
|
1109
|
-
JSON.stringify({
|
|
1110
|
-
files: files.map((file, index) => ({
|
|
1111
|
-
index,
|
|
1112
|
-
logicalPath: file.logicalPath,
|
|
1113
|
-
contentHash: file.contentHash,
|
|
1114
|
-
contentType: file.contentType,
|
|
1115
|
-
bytes: file.bytes
|
|
1116
|
-
}))
|
|
1117
|
-
})
|
|
1118
|
-
);
|
|
1119
|
-
for (const [index, file] of files.entries()) {
|
|
1120
|
-
const bytes = decodeBase64Bytes(file.contentBase64);
|
|
1121
|
-
const body = bytes.buffer.slice(
|
|
1122
|
-
bytes.byteOffset,
|
|
1123
|
-
bytes.byteOffset + bytes.byteLength
|
|
1124
|
-
);
|
|
1084
|
+
const buildFormData = () => {
|
|
1085
|
+
const formData = new FormData();
|
|
1125
1086
|
formData.set(
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1087
|
+
"metadata",
|
|
1088
|
+
JSON.stringify({
|
|
1089
|
+
files: files.map((file, index) => ({
|
|
1090
|
+
index,
|
|
1091
|
+
logicalPath: file.logicalPath,
|
|
1092
|
+
contentHash: file.contentHash,
|
|
1093
|
+
contentType: file.contentType,
|
|
1094
|
+
bytes: file.bytes
|
|
1095
|
+
}))
|
|
1096
|
+
})
|
|
1129
1097
|
);
|
|
1130
|
-
|
|
1098
|
+
for (const [index, file] of files.entries()) {
|
|
1099
|
+
const bytes = decodeBase64Bytes(file.contentBase64);
|
|
1100
|
+
const body = bytes.buffer.slice(
|
|
1101
|
+
bytes.byteOffset,
|
|
1102
|
+
bytes.byteOffset + bytes.byteLength
|
|
1103
|
+
);
|
|
1104
|
+
formData.set(
|
|
1105
|
+
`file:${index}`,
|
|
1106
|
+
new Blob([body], { type: file.contentType }),
|
|
1107
|
+
file.logicalPath
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
return formData;
|
|
1111
|
+
};
|
|
1131
1112
|
const response = await this.http.postFormData(
|
|
1132
1113
|
"/api/v2/plays/files/stage",
|
|
1133
|
-
|
|
1114
|
+
buildFormData
|
|
1134
1115
|
);
|
|
1135
1116
|
return response.files;
|
|
1136
1117
|
}
|
|
@@ -1654,7 +1635,7 @@ async function enforceSdkCompatibility(baseUrl) {
|
|
|
1654
1635
|
}
|
|
1655
1636
|
|
|
1656
1637
|
// src/cli/commands/auth.ts
|
|
1657
|
-
import {
|
|
1638
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
1658
1639
|
import { hostname } from "os";
|
|
1659
1640
|
import { dirname as dirname3 } from "path";
|
|
1660
1641
|
|
|
@@ -1668,10 +1649,11 @@ import {
|
|
|
1668
1649
|
import { mkdir, writeFile } from "fs/promises";
|
|
1669
1650
|
import { homedir as homedir2 } from "os";
|
|
1670
1651
|
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
1671
|
-
import
|
|
1652
|
+
import * as childProcess from "child_process";
|
|
1672
1653
|
import { parse } from "csv-parse/sync";
|
|
1673
1654
|
import { stringify } from "csv-stringify/sync";
|
|
1674
1655
|
var BROWSER_FOCUS_COOLDOWN_MS = 3e4;
|
|
1656
|
+
var defaultBrowserCommandRunner = childProcess;
|
|
1675
1657
|
function getAuthedHttpClient() {
|
|
1676
1658
|
const config = resolveConfig();
|
|
1677
1659
|
return { config, http: new HttpClient(config) };
|
|
@@ -1740,9 +1722,9 @@ function browserAppNameFromBundleId(bundleId) {
|
|
|
1740
1722
|
};
|
|
1741
1723
|
return names[bundleId.toLowerCase()] ?? "";
|
|
1742
1724
|
}
|
|
1743
|
-
function readDefaultMacBrowserBundleId() {
|
|
1725
|
+
function readDefaultMacBrowserBundleId(runner = defaultBrowserCommandRunner) {
|
|
1744
1726
|
try {
|
|
1745
|
-
const output = execFileSync(
|
|
1727
|
+
const output = runner.execFileSync(
|
|
1746
1728
|
"/usr/bin/defaults",
|
|
1747
1729
|
[
|
|
1748
1730
|
"read",
|
|
@@ -1778,8 +1760,8 @@ function browserStrategyForBundleId(bundleId) {
|
|
|
1778
1760
|
}
|
|
1779
1761
|
return normalized === "com.apple.safari" ? "safari" : "fallback";
|
|
1780
1762
|
}
|
|
1781
|
-
function runAppleScript(script, args) {
|
|
1782
|
-
const result = spawnSync("osascript", ["-", ...args], {
|
|
1763
|
+
function runAppleScript(script, args, runner = defaultBrowserCommandRunner) {
|
|
1764
|
+
const result = runner.spawnSync("osascript", ["-", ...args], {
|
|
1783
1765
|
input: script,
|
|
1784
1766
|
encoding: "utf-8",
|
|
1785
1767
|
stdio: ["pipe", "ignore", "ignore"],
|
|
@@ -1787,7 +1769,7 @@ function runAppleScript(script, args) {
|
|
|
1787
1769
|
});
|
|
1788
1770
|
return result.status === 0;
|
|
1789
1771
|
}
|
|
1790
|
-
function retargetChromiumMacos(appName, targetUrl, allowFocus) {
|
|
1772
|
+
function retargetChromiumMacos(appName, targetUrl, allowFocus, runner = defaultBrowserCommandRunner) {
|
|
1791
1773
|
const host = extractUrlHost(targetUrl);
|
|
1792
1774
|
if (!host) return false;
|
|
1793
1775
|
const escapedAppName = appName.replace(/"/g, '\\"');
|
|
@@ -1819,9 +1801,9 @@ ${newTabBlock} end if
|
|
|
1819
1801
|
end tell
|
|
1820
1802
|
end run
|
|
1821
1803
|
`;
|
|
1822
|
-
return runAppleScript(script, [targetUrl, host]);
|
|
1804
|
+
return runAppleScript(script, [targetUrl, host], runner);
|
|
1823
1805
|
}
|
|
1824
|
-
function retargetSafariMacos(appName, targetUrl, allowFocus) {
|
|
1806
|
+
function retargetSafariMacos(appName, targetUrl, allowFocus, runner = defaultBrowserCommandRunner) {
|
|
1825
1807
|
const host = extractUrlHost(targetUrl);
|
|
1826
1808
|
if (!host) return false;
|
|
1827
1809
|
const escapedAppName = appName.replace(/"/g, '\\"');
|
|
@@ -1853,30 +1835,38 @@ ${newTabBlock} end if
|
|
|
1853
1835
|
end tell
|
|
1854
1836
|
end run
|
|
1855
1837
|
`;
|
|
1856
|
-
return runAppleScript(script, [targetUrl, host]);
|
|
1838
|
+
return runAppleScript(script, [targetUrl, host], runner);
|
|
1857
1839
|
}
|
|
1858
|
-
function openUrlMacos(targetUrl, allowFocus) {
|
|
1859
|
-
const defaultBundleId = readDefaultMacBrowserBundleId();
|
|
1840
|
+
function openUrlMacos(targetUrl, allowFocus, runner = defaultBrowserCommandRunner) {
|
|
1841
|
+
const defaultBundleId = readDefaultMacBrowserBundleId(runner);
|
|
1860
1842
|
const appName = defaultBundleId ? browserAppNameFromBundleId(defaultBundleId) : "";
|
|
1861
1843
|
const strategy = browserStrategyForBundleId(defaultBundleId);
|
|
1862
|
-
if (appName && strategy === "chromium" && retargetChromiumMacos(appName, targetUrl, allowFocus)) {
|
|
1844
|
+
if (appName && strategy === "chromium" && retargetChromiumMacos(appName, targetUrl, allowFocus, runner)) {
|
|
1863
1845
|
return true;
|
|
1864
1846
|
}
|
|
1865
|
-
if (appName && strategy === "safari" && retargetSafariMacos(appName, targetUrl, allowFocus)) {
|
|
1847
|
+
if (appName && strategy === "safari" && retargetSafariMacos(appName, targetUrl, allowFocus, runner)) {
|
|
1866
1848
|
return true;
|
|
1867
1849
|
}
|
|
1868
|
-
if (!allowFocus) {
|
|
1869
|
-
return false;
|
|
1870
|
-
}
|
|
1871
1850
|
try {
|
|
1872
|
-
execFileSync(
|
|
1851
|
+
runner.execFileSync(
|
|
1852
|
+
"open",
|
|
1853
|
+
[...allowFocus ? [] : ["-g"], targetUrl],
|
|
1854
|
+
{ stdio: "ignore" }
|
|
1855
|
+
);
|
|
1873
1856
|
return true;
|
|
1874
1857
|
} catch {
|
|
1875
1858
|
return false;
|
|
1876
1859
|
}
|
|
1877
1860
|
}
|
|
1861
|
+
function browserOpeningDisabled() {
|
|
1862
|
+
const value = String(
|
|
1863
|
+
process.env.DEEPLINE_NO_BROWSER ?? process.env.PLAYGROUND_HEADLESS ?? ""
|
|
1864
|
+
).trim().toLowerCase();
|
|
1865
|
+
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
1866
|
+
}
|
|
1878
1867
|
function openInBrowser(url) {
|
|
1879
1868
|
try {
|
|
1869
|
+
if (browserOpeningDisabled()) return;
|
|
1880
1870
|
const targetUrl = String(url || "").trim();
|
|
1881
1871
|
if (!targetUrl) return;
|
|
1882
1872
|
const allowFocus = claimBrowserFocus();
|
|
@@ -1886,12 +1876,12 @@ function openInBrowser(url) {
|
|
|
1886
1876
|
}
|
|
1887
1877
|
if (!allowFocus) return;
|
|
1888
1878
|
if (process.platform === "win32") {
|
|
1889
|
-
execFileSync("cmd.exe", ["/c", "start", "", targetUrl], {
|
|
1879
|
+
childProcess.execFileSync("cmd.exe", ["/c", "start", "", targetUrl], {
|
|
1890
1880
|
stdio: "ignore"
|
|
1891
1881
|
});
|
|
1892
1882
|
return;
|
|
1893
1883
|
}
|
|
1894
|
-
execFileSync("xdg-open", [targetUrl], { stdio: "ignore" });
|
|
1884
|
+
childProcess.execFileSync("xdg-open", [targetUrl], { stdio: "ignore" });
|
|
1895
1885
|
} catch {
|
|
1896
1886
|
}
|
|
1897
1887
|
}
|
|
@@ -1915,6 +1905,103 @@ function csvStringFromRows(rows, columns) {
|
|
|
1915
1905
|
...columns?.length ? { columns } : {}
|
|
1916
1906
|
});
|
|
1917
1907
|
}
|
|
1908
|
+
function parseMaybeJsonObject(value) {
|
|
1909
|
+
if (typeof value !== "string") {
|
|
1910
|
+
return value;
|
|
1911
|
+
}
|
|
1912
|
+
const trimmed = value.trim();
|
|
1913
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
|
1914
|
+
return value;
|
|
1915
|
+
}
|
|
1916
|
+
try {
|
|
1917
|
+
return JSON.parse(trimmed);
|
|
1918
|
+
} catch {
|
|
1919
|
+
return value;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
function flattenObjectColumns(row) {
|
|
1923
|
+
const flattened = {};
|
|
1924
|
+
for (const [key, rawValue] of Object.entries(row)) {
|
|
1925
|
+
const value = parseMaybeJsonObject(rawValue);
|
|
1926
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1927
|
+
for (const [nestedKey, nestedValue] of Object.entries(
|
|
1928
|
+
value
|
|
1929
|
+
)) {
|
|
1930
|
+
flattened[`${key}.${nestedKey}`] = nestedValue && typeof nestedValue === "object" ? JSON.stringify(nestedValue) : nestedValue;
|
|
1931
|
+
}
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
flattened[key] = Array.isArray(value) ? JSON.stringify(value) : value;
|
|
1935
|
+
}
|
|
1936
|
+
return flattened;
|
|
1937
|
+
}
|
|
1938
|
+
function recordRows(value) {
|
|
1939
|
+
return value.filter(
|
|
1940
|
+
(row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
function dataExportRows(rows) {
|
|
1944
|
+
return rows.map((row) => flattenObjectColumns(row));
|
|
1945
|
+
}
|
|
1946
|
+
function dataExportColumns(rows, preferredColumns = []) {
|
|
1947
|
+
const discoveredColumns = [
|
|
1948
|
+
...new Set(rows.flatMap((row) => Object.keys(row)))
|
|
1949
|
+
];
|
|
1950
|
+
if (rows.length === 0) {
|
|
1951
|
+
return [...new Set(preferredColumns.filter(Boolean))];
|
|
1952
|
+
}
|
|
1953
|
+
const discovered = new Set(discoveredColumns);
|
|
1954
|
+
const columns = [];
|
|
1955
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1956
|
+
for (const column of preferredColumns) {
|
|
1957
|
+
if (!column) {
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
const expandedColumns = discovered.has(column) ? [column] : discoveredColumns.filter(
|
|
1961
|
+
(discoveredColumn) => discoveredColumn.startsWith(`${column}.`)
|
|
1962
|
+
);
|
|
1963
|
+
for (const expandedColumn of expandedColumns) {
|
|
1964
|
+
if (seen.has(expandedColumn)) {
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1967
|
+
seen.add(expandedColumn);
|
|
1968
|
+
columns.push(expandedColumn);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
for (const column of discoveredColumns) {
|
|
1972
|
+
if (seen.has(column)) {
|
|
1973
|
+
continue;
|
|
1974
|
+
}
|
|
1975
|
+
seen.add(column);
|
|
1976
|
+
columns.push(column);
|
|
1977
|
+
}
|
|
1978
|
+
return columns;
|
|
1979
|
+
}
|
|
1980
|
+
function dataExportCsvString(rows, preferredColumns = []) {
|
|
1981
|
+
const flattenedRows = dataExportRows(rows);
|
|
1982
|
+
return csvStringFromRows(
|
|
1983
|
+
flattenedRows,
|
|
1984
|
+
dataExportColumns(flattenedRows, preferredColumns)
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1987
|
+
function markdownCell(value) {
|
|
1988
|
+
if (value === null || value === void 0) {
|
|
1989
|
+
return "";
|
|
1990
|
+
}
|
|
1991
|
+
const text = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
1992
|
+
return text.replace(/\|/g, "\\|").replace(/\r?\n/g, "<br>");
|
|
1993
|
+
}
|
|
1994
|
+
function markdownTableFromRows(rows, preferredColumns = []) {
|
|
1995
|
+
const flattenedRows = dataExportRows(rows);
|
|
1996
|
+
const columns = dataExportColumns(flattenedRows, preferredColumns);
|
|
1997
|
+
const header = `| ${columns.map(markdownCell).join(" | ")} |`;
|
|
1998
|
+
const separator = `| ${columns.map(() => "---").join(" | ")} |`;
|
|
1999
|
+
const body = flattenedRows.map(
|
|
2000
|
+
(row) => `| ${columns.map((column) => markdownCell(row[column])).join(" | ")} |`
|
|
2001
|
+
);
|
|
2002
|
+
return `${[header, separator, ...body].join("\n")}
|
|
2003
|
+
`;
|
|
2004
|
+
}
|
|
1918
2005
|
function printJson(value) {
|
|
1919
2006
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
1920
2007
|
`);
|
|
@@ -2005,17 +2092,39 @@ var EXIT_SERVER = 2;
|
|
|
2005
2092
|
function envFilePath(baseUrl) {
|
|
2006
2093
|
return hostEnvFilePath(baseUrl);
|
|
2007
2094
|
}
|
|
2008
|
-
function
|
|
2009
|
-
|
|
2095
|
+
function pendingClaimTokenPath(baseUrl) {
|
|
2096
|
+
return `${hostConfigDirPath(baseUrl)}/pending-claim-token`;
|
|
2097
|
+
}
|
|
2098
|
+
function savePendingClaimToken(baseUrl, claimToken) {
|
|
2099
|
+
const filePath = pendingClaimTokenPath(baseUrl);
|
|
2010
2100
|
const dir = dirname3(filePath);
|
|
2011
2101
|
if (!existsSync3(dir)) {
|
|
2012
2102
|
mkdirSync3(dir, { recursive: true });
|
|
2013
2103
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2104
|
+
writeFileSync3(filePath, `${claimToken}
|
|
2105
|
+
`, "utf-8");
|
|
2106
|
+
}
|
|
2107
|
+
function readPendingClaimToken(baseUrl) {
|
|
2108
|
+
const filePath = pendingClaimTokenPath(baseUrl);
|
|
2109
|
+
if (!existsSync3(filePath)) return "";
|
|
2110
|
+
try {
|
|
2111
|
+
return readFileSync3(filePath, "utf-8").trim();
|
|
2112
|
+
} catch {
|
|
2113
|
+
return "";
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
function clearPendingClaimToken(baseUrl) {
|
|
2117
|
+
try {
|
|
2118
|
+
rmSync(pendingClaimTokenPath(baseUrl), { force: true });
|
|
2119
|
+
} catch {
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
function saveEnvValues(values, baseUrl) {
|
|
2123
|
+
const filtered = {
|
|
2124
|
+
...values[HOST_URL_ENV] ? { [HOST_URL_ENV]: values[HOST_URL_ENV] } : {},
|
|
2125
|
+
...values[API_KEY_ENV] ? { [API_KEY_ENV]: values[API_KEY_ENV] } : {}
|
|
2126
|
+
};
|
|
2127
|
+
saveHostEnvValues(baseUrl, filtered);
|
|
2019
2128
|
}
|
|
2020
2129
|
async function httpJson(method, url, apiKey, body) {
|
|
2021
2130
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -2069,7 +2178,7 @@ function buildCandidateUrls2(url) {
|
|
|
2069
2178
|
}
|
|
2070
2179
|
}
|
|
2071
2180
|
function sleep3(ms) {
|
|
2072
|
-
return new Promise((
|
|
2181
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
2073
2182
|
}
|
|
2074
2183
|
function printDeeplineLogo() {
|
|
2075
2184
|
if (process.stdout.isTTY && (process.stdout.columns ?? 80) >= 70) {
|
|
@@ -2121,9 +2230,9 @@ async function handleRegister(args) {
|
|
|
2121
2230
|
const claimUrl = String(data.claim_url || "");
|
|
2122
2231
|
const claimToken = String(data.claim_token || "");
|
|
2123
2232
|
if (claimToken) {
|
|
2233
|
+
savePendingClaimToken(baseUrl, claimToken);
|
|
2124
2234
|
saveEnvValues({
|
|
2125
|
-
|
|
2126
|
-
DEEPLINE_CLAIM_TOKEN: claimToken
|
|
2235
|
+
[HOST_URL_ENV]: baseUrl
|
|
2127
2236
|
}, baseUrl);
|
|
2128
2237
|
}
|
|
2129
2238
|
if (claimUrl) {
|
|
@@ -2147,6 +2256,7 @@ async function handleRegister(args) {
|
|
|
2147
2256
|
{ claim_token: claimToken, reveal: true }
|
|
2148
2257
|
);
|
|
2149
2258
|
if (s === 401 || s === 403) {
|
|
2259
|
+
clearPendingClaimToken(baseUrl);
|
|
2150
2260
|
console.log("Status: unauthorized");
|
|
2151
2261
|
return EXIT_AUTH;
|
|
2152
2262
|
}
|
|
@@ -2163,15 +2273,16 @@ async function handleRegister(args) {
|
|
|
2163
2273
|
const apiKey = String(statusData.api_key || "");
|
|
2164
2274
|
if (apiKey) {
|
|
2165
2275
|
saveEnvValues({
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
DEEPLINE_CLAIM_TOKEN: ""
|
|
2276
|
+
[HOST_URL_ENV]: baseUrl,
|
|
2277
|
+
[API_KEY_ENV]: apiKey
|
|
2169
2278
|
}, baseUrl);
|
|
2279
|
+
clearPendingClaimToken(baseUrl);
|
|
2170
2280
|
printClaimSuccessBanner(statusData);
|
|
2171
2281
|
return EXIT_OK;
|
|
2172
2282
|
}
|
|
2173
2283
|
}
|
|
2174
2284
|
if (state === "expired") {
|
|
2285
|
+
clearPendingClaimToken(baseUrl);
|
|
2175
2286
|
console.log("That approval link expired. Please run: deepline auth register");
|
|
2176
2287
|
return EXIT_AUTH;
|
|
2177
2288
|
}
|
|
@@ -2189,13 +2300,12 @@ async function handleWait(args) {
|
|
|
2189
2300
|
}
|
|
2190
2301
|
}
|
|
2191
2302
|
}
|
|
2192
|
-
const
|
|
2193
|
-
if (env.DEEPLINE_API_KEY?.trim()) {
|
|
2194
|
-
console.log("Already connected.");
|
|
2195
|
-
return EXIT_OK;
|
|
2196
|
-
}
|
|
2197
|
-
const claimToken = env.DEEPLINE_CLAIM_TOKEN?.trim() || "";
|
|
2303
|
+
const claimToken = readPendingClaimToken(baseUrl);
|
|
2198
2304
|
if (!claimToken) {
|
|
2305
|
+
if (resolveApiKeyForBaseUrl(baseUrl)) {
|
|
2306
|
+
console.log("Already connected.");
|
|
2307
|
+
return EXIT_OK;
|
|
2308
|
+
}
|
|
2199
2309
|
console.error("No pending approval. Run: deepline auth register --no-wait");
|
|
2200
2310
|
return EXIT_AUTH;
|
|
2201
2311
|
}
|
|
@@ -2208,6 +2318,7 @@ async function handleWait(args) {
|
|
|
2208
2318
|
{ claim_token: claimToken, reveal: true }
|
|
2209
2319
|
);
|
|
2210
2320
|
if (status === 401 || status === 403) {
|
|
2321
|
+
clearPendingClaimToken(baseUrl);
|
|
2211
2322
|
console.error("Claim is invalid. Run: deepline auth register");
|
|
2212
2323
|
return EXIT_AUTH;
|
|
2213
2324
|
}
|
|
@@ -2224,15 +2335,16 @@ async function handleWait(args) {
|
|
|
2224
2335
|
const apiKey = String(data.api_key || "");
|
|
2225
2336
|
if (apiKey) {
|
|
2226
2337
|
saveEnvValues({
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
DEEPLINE_CLAIM_TOKEN: ""
|
|
2338
|
+
[HOST_URL_ENV]: baseUrl,
|
|
2339
|
+
[API_KEY_ENV]: apiKey
|
|
2230
2340
|
}, baseUrl);
|
|
2341
|
+
clearPendingClaimToken(baseUrl);
|
|
2231
2342
|
printClaimSuccessBanner(data);
|
|
2232
2343
|
return EXIT_OK;
|
|
2233
2344
|
}
|
|
2234
2345
|
}
|
|
2235
2346
|
if (state === "expired") {
|
|
2347
|
+
clearPendingClaimToken(baseUrl);
|
|
2236
2348
|
console.error("That approval link expired. Run: deepline auth register");
|
|
2237
2349
|
return EXIT_AUTH;
|
|
2238
2350
|
}
|
|
@@ -2267,10 +2379,9 @@ async function handleStatus(args) {
|
|
|
2267
2379
|
};
|
|
2268
2380
|
hostLines.push(`Host: ${baseUrl} (unreachable)`);
|
|
2269
2381
|
}
|
|
2270
|
-
const
|
|
2271
|
-
const apiKey = process.env.DEEPLINE_API_KEY?.trim() || env.DEEPLINE_API_KEY || "";
|
|
2382
|
+
const apiKey = resolveApiKeyForBaseUrl(baseUrl);
|
|
2272
2383
|
if (!apiKey) {
|
|
2273
|
-
if (
|
|
2384
|
+
if (readPendingClaimToken(baseUrl)) {
|
|
2274
2385
|
printCommandEnvelope({
|
|
2275
2386
|
...hostStatusPayload ?? { host: baseUrl },
|
|
2276
2387
|
status: "pending",
|
|
@@ -2316,6 +2427,7 @@ async function handleStatus(args) {
|
|
|
2316
2427
|
console.error(`Auth status error (status ${status}).`);
|
|
2317
2428
|
return EXIT_SERVER;
|
|
2318
2429
|
}
|
|
2430
|
+
clearPendingClaimToken(baseUrl);
|
|
2319
2431
|
const payload = {
|
|
2320
2432
|
...hostStatusPayload ?? { host: baseUrl },
|
|
2321
2433
|
status: data.status || "(unknown)",
|
|
@@ -2336,9 +2448,8 @@ async function handleStatus(args) {
|
|
|
2336
2448
|
const apiKeyResp = String(data.api_key || apiKey);
|
|
2337
2449
|
if (apiKeyResp) {
|
|
2338
2450
|
saveEnvValues({
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
DEEPLINE_CLAIM_TOKEN: ""
|
|
2451
|
+
[HOST_URL_ENV]: baseUrl,
|
|
2452
|
+
[API_KEY_ENV]: apiKeyResp
|
|
2342
2453
|
}, baseUrl);
|
|
2343
2454
|
savedApiKeyPath = envFilePath(baseUrl);
|
|
2344
2455
|
}
|
|
@@ -3267,10 +3378,12 @@ function writeCanonicalRowsCsv(rowsInfo, outPath) {
|
|
|
3267
3378
|
rows: rowsInfo.rows,
|
|
3268
3379
|
columns: rowsInfo.columns
|
|
3269
3380
|
});
|
|
3381
|
+
const rows = dataExportRows(sanitized.rows);
|
|
3382
|
+
const columns = dataExportColumns(rows, sanitized.columns);
|
|
3270
3383
|
const resolved = resolve4(outPath);
|
|
3271
3384
|
writeFileSync4(
|
|
3272
3385
|
resolved,
|
|
3273
|
-
csvStringFromRows(
|
|
3386
|
+
csvStringFromRows(rows, columns),
|
|
3274
3387
|
"utf-8"
|
|
3275
3388
|
);
|
|
3276
3389
|
return resolved;
|
|
@@ -3391,6 +3504,14 @@ Examples:
|
|
|
3391
3504
|
}
|
|
3392
3505
|
|
|
3393
3506
|
// src/cli/commands/db.ts
|
|
3507
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
3508
|
+
import { resolve as resolve5 } from "path";
|
|
3509
|
+
var CUSTOMER_DB_QUERY_FORMATS = /* @__PURE__ */ new Set([
|
|
3510
|
+
"table",
|
|
3511
|
+
"json",
|
|
3512
|
+
"csv",
|
|
3513
|
+
"markdown"
|
|
3514
|
+
]);
|
|
3394
3515
|
function parsePositiveInteger(value, flagName) {
|
|
3395
3516
|
const parsed = Number.parseInt(value, 10);
|
|
3396
3517
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
@@ -3404,10 +3525,8 @@ function formatCell(value) {
|
|
|
3404
3525
|
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
|
|
3405
3526
|
}
|
|
3406
3527
|
function tableLines(result) {
|
|
3407
|
-
const rows = result
|
|
3408
|
-
|
|
3409
|
-
);
|
|
3410
|
-
const responseColumns = result.columns.length > 0 ? result.columns.map((column) => column.name) : [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
3528
|
+
const rows = dataExportRows(customerDbRows(result));
|
|
3529
|
+
const responseColumns = dataExportColumns(rows, customerDbColumnNames(result));
|
|
3411
3530
|
const businessColumns = responseColumns.filter((column) => !column.startsWith("_"));
|
|
3412
3531
|
const columns = businessColumns.length > 0 ? businessColumns : responseColumns;
|
|
3413
3532
|
const hiddenColumns = responseColumns.filter((column) => !columns.includes(column));
|
|
@@ -3441,22 +3560,146 @@ function tableLines(result) {
|
|
|
3441
3560
|
}
|
|
3442
3561
|
return lines;
|
|
3443
3562
|
}
|
|
3563
|
+
function customerDbRows(result) {
|
|
3564
|
+
return recordRows(result.rows);
|
|
3565
|
+
}
|
|
3566
|
+
function customerDbColumnNames(result) {
|
|
3567
|
+
return result.columns.map((column) => column.name).filter(Boolean);
|
|
3568
|
+
}
|
|
3569
|
+
function writeCustomerDbCsv(result, outPath) {
|
|
3570
|
+
const resolved = resolve5(outPath);
|
|
3571
|
+
writeFileSync5(
|
|
3572
|
+
resolved,
|
|
3573
|
+
dataExportCsvString(customerDbRows(result), customerDbColumnNames(result)),
|
|
3574
|
+
"utf-8"
|
|
3575
|
+
);
|
|
3576
|
+
return resolved;
|
|
3577
|
+
}
|
|
3578
|
+
function dbQueryExportEnvelope(input) {
|
|
3579
|
+
const destination = input.outPath ?? "stdout";
|
|
3580
|
+
return {
|
|
3581
|
+
command: input.result.command,
|
|
3582
|
+
format: input.format,
|
|
3583
|
+
row_count: input.result.row_count,
|
|
3584
|
+
row_count_returned: input.result.row_count_returned,
|
|
3585
|
+
truncated: input.result.truncated,
|
|
3586
|
+
...input.outPath ? { file: input.outPath, local: { file: input.outPath } } : {},
|
|
3587
|
+
next: { toolEquivalent: input.toolCommand },
|
|
3588
|
+
render: {
|
|
3589
|
+
sections: [
|
|
3590
|
+
{
|
|
3591
|
+
title: "customer db export",
|
|
3592
|
+
lines: [
|
|
3593
|
+
`Rendered ${input.result.row_count_returned} row(s) as ${input.format} to ${destination}`
|
|
3594
|
+
]
|
|
3595
|
+
}
|
|
3596
|
+
],
|
|
3597
|
+
actions: [{ label: "Tool equivalent", command: input.toolCommand }]
|
|
3598
|
+
}
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3444
3601
|
async function handleDbQuery(args) {
|
|
3445
3602
|
const sqlIndex = args.indexOf("--sql");
|
|
3446
3603
|
const sql = sqlIndex >= 0 ? args[sqlIndex + 1]?.trim() : "";
|
|
3447
3604
|
if (!sql) {
|
|
3448
|
-
console.error(
|
|
3605
|
+
console.error(
|
|
3606
|
+
'Usage: deepline db query --sql "select * from table limit 20" [--max-rows N] [--json]'
|
|
3607
|
+
);
|
|
3449
3608
|
return 1;
|
|
3450
3609
|
}
|
|
3451
3610
|
const maxRowsIndex = args.indexOf("--max-rows");
|
|
3452
3611
|
const maxRows = maxRowsIndex >= 0 && args[maxRowsIndex + 1] ? parsePositiveInteger(args[maxRowsIndex + 1], "--max-rows") : void 0;
|
|
3612
|
+
const formatIndex = args.indexOf("--format");
|
|
3613
|
+
const format = formatIndex >= 0 ? args[formatIndex + 1]?.trim().toLowerCase() : "";
|
|
3614
|
+
if (format && !CUSTOMER_DB_QUERY_FORMATS.has(format)) {
|
|
3615
|
+
console.error(
|
|
3616
|
+
'Usage: deepline db query --sql "select * from table limit 20" [--format table|json|csv|markdown] [--out path]'
|
|
3617
|
+
);
|
|
3618
|
+
return 1;
|
|
3619
|
+
}
|
|
3620
|
+
const outIndex = args.indexOf("--out");
|
|
3621
|
+
const outPath = outIndex >= 0 ? args[outIndex + 1]?.trim() : "";
|
|
3622
|
+
if (outIndex >= 0 && !outPath) {
|
|
3623
|
+
console.error("--out requires a path.");
|
|
3624
|
+
return 1;
|
|
3625
|
+
}
|
|
3453
3626
|
const jsonOutput = argsWantJson(args);
|
|
3627
|
+
const explicitJsonOutput = args.includes("--json");
|
|
3454
3628
|
const client = new DeeplineClient();
|
|
3455
3629
|
const result = await client.queryCustomerDb({ sql, maxRows });
|
|
3456
3630
|
const toolCommand = `deepline tools execute query_customer_db --payload ${JSON.stringify({
|
|
3457
3631
|
sql,
|
|
3458
3632
|
...maxRows ? { max_rows: maxRows } : {}
|
|
3459
3633
|
})} --json`;
|
|
3634
|
+
if (format === "csv") {
|
|
3635
|
+
if (outPath) {
|
|
3636
|
+
const exportedPath = writeCustomerDbCsv(result, outPath);
|
|
3637
|
+
printCommandEnvelope(
|
|
3638
|
+
dbQueryExportEnvelope({
|
|
3639
|
+
result,
|
|
3640
|
+
format,
|
|
3641
|
+
outPath: exportedPath,
|
|
3642
|
+
toolCommand
|
|
3643
|
+
}),
|
|
3644
|
+
{
|
|
3645
|
+
json: jsonOutput,
|
|
3646
|
+
text: `Exported ${result.row_count_returned} row(s) to ${exportedPath}
|
|
3647
|
+
`
|
|
3648
|
+
}
|
|
3649
|
+
);
|
|
3650
|
+
return 0;
|
|
3651
|
+
}
|
|
3652
|
+
printCommandEnvelope(
|
|
3653
|
+
dbQueryExportEnvelope({
|
|
3654
|
+
result,
|
|
3655
|
+
format,
|
|
3656
|
+
outPath: null,
|
|
3657
|
+
toolCommand
|
|
3658
|
+
}),
|
|
3659
|
+
{
|
|
3660
|
+
json: explicitJsonOutput,
|
|
3661
|
+
text: dataExportCsvString(customerDbRows(result), customerDbColumnNames(result))
|
|
3662
|
+
}
|
|
3663
|
+
);
|
|
3664
|
+
return 0;
|
|
3665
|
+
}
|
|
3666
|
+
if (format === "markdown") {
|
|
3667
|
+
const content = markdownTableFromRows(
|
|
3668
|
+
customerDbRows(result),
|
|
3669
|
+
customerDbColumnNames(result)
|
|
3670
|
+
);
|
|
3671
|
+
if (outPath) {
|
|
3672
|
+
const exportedPath = resolve5(outPath);
|
|
3673
|
+
writeFileSync5(exportedPath, content, "utf-8");
|
|
3674
|
+
printCommandEnvelope(
|
|
3675
|
+
dbQueryExportEnvelope({
|
|
3676
|
+
result,
|
|
3677
|
+
format,
|
|
3678
|
+
outPath: exportedPath,
|
|
3679
|
+
toolCommand
|
|
3680
|
+
}),
|
|
3681
|
+
{
|
|
3682
|
+
json: jsonOutput,
|
|
3683
|
+
text: `Exported ${result.row_count_returned} row(s) to ${exportedPath}
|
|
3684
|
+
`
|
|
3685
|
+
}
|
|
3686
|
+
);
|
|
3687
|
+
return 0;
|
|
3688
|
+
}
|
|
3689
|
+
printCommandEnvelope(
|
|
3690
|
+
dbQueryExportEnvelope({
|
|
3691
|
+
result,
|
|
3692
|
+
format,
|
|
3693
|
+
outPath: null,
|
|
3694
|
+
toolCommand
|
|
3695
|
+
}),
|
|
3696
|
+
{
|
|
3697
|
+
json: explicitJsonOutput,
|
|
3698
|
+
text: content
|
|
3699
|
+
}
|
|
3700
|
+
);
|
|
3701
|
+
return 0;
|
|
3702
|
+
}
|
|
3460
3703
|
printCommandEnvelope({
|
|
3461
3704
|
...result,
|
|
3462
3705
|
next: { toolEquivalent: toolCommand },
|
|
@@ -3464,7 +3707,7 @@ async function handleDbQuery(args) {
|
|
|
3464
3707
|
sections: [{ title: "customer db query", lines: tableLines(result) }],
|
|
3465
3708
|
actions: [{ label: "Tool equivalent", command: toolCommand }]
|
|
3466
3709
|
}
|
|
3467
|
-
}, { json: jsonOutput });
|
|
3710
|
+
}, { json: jsonOutput || format === "json" });
|
|
3468
3711
|
return 0;
|
|
3469
3712
|
}
|
|
3470
3713
|
function registerDbCommands(program) {
|
|
@@ -3474,11 +3717,14 @@ function registerDbCommands(program) {
|
|
|
3474
3717
|
Notes:
|
|
3475
3718
|
Runs SQL against the active workspace customer database through Deepline APIs.
|
|
3476
3719
|
Results are bounded by the server and --max-rows. Use --json for stable output.
|
|
3720
|
+
Use --format csv or --format markdown for agent-readable exports and display tables.
|
|
3477
3721
|
|
|
3478
3722
|
Examples:
|
|
3479
3723
|
deepline db query --sql "select * from companies limit 20"
|
|
3480
3724
|
deepline db query --sql "select domain, name from companies limit 20" --json
|
|
3481
3725
|
deepline db query --sql "select * from contacts" --max-rows 100 --json
|
|
3726
|
+
deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
|
|
3727
|
+
deepline db query --sql "select domain, name from companies limit 20" --format markdown
|
|
3482
3728
|
`
|
|
3483
3729
|
);
|
|
3484
3730
|
db.command("query").alias("psql").description("Run SQL against the tenant customer database.").addHelpText(
|
|
@@ -3487,17 +3733,23 @@ Examples:
|
|
|
3487
3733
|
Notes:
|
|
3488
3734
|
Requires --sql. Output is a compact table in a terminal and raw JSON with
|
|
3489
3735
|
--json or when stdout is piped. The active auth workspace determines scope.
|
|
3736
|
+
--format csv and --format markdown are explicit data/display formats and can
|
|
3737
|
+
be written directly with --out.
|
|
3490
3738
|
|
|
3491
3739
|
Examples:
|
|
3492
3740
|
deepline db query --sql "select * from companies limit 20"
|
|
3493
3741
|
deepline db query --sql "select domain, name from companies limit 20" --json
|
|
3494
3742
|
deepline db psql --sql "select count(*) from contacts" --json
|
|
3743
|
+
deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
|
|
3744
|
+
deepline db query --sql "select domain, name from companies limit 20" --format markdown
|
|
3495
3745
|
`
|
|
3496
|
-
).requiredOption("--sql <sql>", "SQL statement").option("--max-rows <n>", "Maximum returned rows").option("--json", "Emit raw JSON response. Also automatic when stdout is piped").action(async (options) => {
|
|
3746
|
+
).requiredOption("--sql <sql>", "SQL statement").option("--max-rows <n>", "Maximum returned rows").option("--format <format>", "Output format: table, json, csv, or markdown").option("--out <path>", "Write csv or markdown output to a file").option("--json", "Emit raw JSON response. Also automatic when stdout is piped").action(async (options) => {
|
|
3497
3747
|
process.exitCode = await handleDbQuery([
|
|
3498
3748
|
"--sql",
|
|
3499
3749
|
options.sql,
|
|
3500
3750
|
...options.maxRows ? ["--max-rows", options.maxRows] : [],
|
|
3751
|
+
...options.format ? ["--format", options.format] : [],
|
|
3752
|
+
...options.out ? ["--out", options.out] : [],
|
|
3501
3753
|
...options.json ? ["--json"] : []
|
|
3502
3754
|
]);
|
|
3503
3755
|
});
|
|
@@ -3675,25 +3927,25 @@ Examples:
|
|
|
3675
3927
|
import { createHash as createHash3 } from "crypto";
|
|
3676
3928
|
import {
|
|
3677
3929
|
existsSync as existsSync6,
|
|
3678
|
-
readFileSync as
|
|
3930
|
+
readFileSync as readFileSync5,
|
|
3679
3931
|
readdirSync,
|
|
3680
3932
|
realpathSync,
|
|
3681
|
-
writeFileSync as
|
|
3933
|
+
writeFileSync as writeFileSync6
|
|
3682
3934
|
} from "fs";
|
|
3683
|
-
import { basename as basename3, dirname as dirname8, join as join6, resolve as
|
|
3935
|
+
import { basename as basename3, dirname as dirname8, join as join6, resolve as resolve9 } from "path";
|
|
3684
3936
|
|
|
3685
3937
|
// src/plays/bundle-play-file.ts
|
|
3686
3938
|
import { tmpdir as tmpdir2 } from "os";
|
|
3687
|
-
import { dirname as dirname7, join as join5, resolve as
|
|
3939
|
+
import { dirname as dirname7, join as join5, resolve as resolve8 } from "path";
|
|
3688
3940
|
import { fileURLToPath } from "url";
|
|
3689
3941
|
import { existsSync as existsSync5 } from "fs";
|
|
3690
3942
|
|
|
3691
3943
|
// ../shared_libs/plays/bundling/index.ts
|
|
3692
3944
|
import { createHash } from "crypto";
|
|
3693
|
-
import { existsSync as existsSync4, readFileSync as
|
|
3945
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
3694
3946
|
import { mkdir as mkdir3, readFile, realpath, stat, writeFile as writeFile3 } from "fs/promises";
|
|
3695
3947
|
import { tmpdir } from "os";
|
|
3696
|
-
import { basename, dirname as dirname5, extname, isAbsolute, join as join3, resolve as
|
|
3948
|
+
import { basename, dirname as dirname5, extname, isAbsolute, join as join3, resolve as resolve6 } from "path";
|
|
3697
3949
|
import { builtinModules } from "module";
|
|
3698
3950
|
import { build } from "esbuild";
|
|
3699
3951
|
|
|
@@ -3785,7 +4037,7 @@ async function normalizeLocalPath(filePath) {
|
|
|
3785
4037
|
try {
|
|
3786
4038
|
return await realpath(filePath);
|
|
3787
4039
|
} catch {
|
|
3788
|
-
return
|
|
4040
|
+
return resolve6(filePath);
|
|
3789
4041
|
}
|
|
3790
4042
|
}
|
|
3791
4043
|
function createPlayWorkspace(entryFile) {
|
|
@@ -3944,7 +4196,7 @@ function extractDefinedPlayName(sourceCode) {
|
|
|
3944
4196
|
}
|
|
3945
4197
|
function readPackageVersionFromPackageJson(packageJsonPath, packageName) {
|
|
3946
4198
|
try {
|
|
3947
|
-
const packageJson = JSON.parse(
|
|
4199
|
+
const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
|
|
3948
4200
|
if (packageJson.name === packageName && typeof packageJson.version === "string") {
|
|
3949
4201
|
return packageJson.version;
|
|
3950
4202
|
}
|
|
@@ -3954,7 +4206,7 @@ function readPackageVersionFromPackageJson(packageJsonPath, packageName) {
|
|
|
3954
4206
|
return null;
|
|
3955
4207
|
}
|
|
3956
4208
|
function findPackageJsonPathFrom(startDir, packageName) {
|
|
3957
|
-
let current =
|
|
4209
|
+
let current = resolve6(startDir);
|
|
3958
4210
|
while (true) {
|
|
3959
4211
|
const packageJsonPath = join3(
|
|
3960
4212
|
current,
|
|
@@ -3981,7 +4233,7 @@ function findPackageJsonPath(packageName, fromFile, adapter) {
|
|
|
3981
4233
|
];
|
|
3982
4234
|
const seen = /* @__PURE__ */ new Set();
|
|
3983
4235
|
for (const startDir of startDirs) {
|
|
3984
|
-
const normalized =
|
|
4236
|
+
const normalized = resolve6(startDir);
|
|
3985
4237
|
if (seen.has(normalized)) continue;
|
|
3986
4238
|
seen.add(normalized);
|
|
3987
4239
|
const packageJsonPath = findPackageJsonPathFrom(normalized, packageName);
|
|
@@ -4218,7 +4470,7 @@ async function resolveLocalImport(fromFile, specifier) {
|
|
|
4218
4470
|
if (specifier.startsWith("file:")) {
|
|
4219
4471
|
return normalizeLocalPath(new URL(specifier).pathname);
|
|
4220
4472
|
}
|
|
4221
|
-
const base = isAbsolute(specifier) ?
|
|
4473
|
+
const base = isAbsolute(specifier) ? resolve6(specifier) : resolve6(dirname5(fromFile), specifier);
|
|
4222
4474
|
const candidates = [base];
|
|
4223
4475
|
const explicitExtension = extname(base).toLowerCase();
|
|
4224
4476
|
if (!explicitExtension) {
|
|
@@ -4239,7 +4491,7 @@ function resolvePackageImport(specifier, fromFile, adapter) {
|
|
|
4239
4491
|
const packageName = getPackageName(specifier);
|
|
4240
4492
|
if (packageName === "deepline" && existsSync4(adapter.sdkPackageJson)) {
|
|
4241
4493
|
const packageJson = JSON.parse(
|
|
4242
|
-
|
|
4494
|
+
readFileSync4(adapter.sdkPackageJson, "utf-8")
|
|
4243
4495
|
);
|
|
4244
4496
|
return {
|
|
4245
4497
|
name: "deepline",
|
|
@@ -4416,7 +4668,7 @@ function normalizeSourceMapForRuntime(sourceMapText) {
|
|
|
4416
4668
|
if (sourcePath.startsWith("data:") || sourcePath.startsWith("node:") || sourcePath.startsWith("/") || /^[a-zA-Z]+:\/\//.test(sourcePath)) {
|
|
4417
4669
|
return sourcePath;
|
|
4418
4670
|
}
|
|
4419
|
-
return
|
|
4671
|
+
return resolve6(process.cwd(), sourcePath);
|
|
4420
4672
|
});
|
|
4421
4673
|
parsed.sourceRoot = void 0;
|
|
4422
4674
|
return JSON.stringify(parsed);
|
|
@@ -4738,7 +4990,7 @@ function resolveExecutionProfile(override) {
|
|
|
4738
4990
|
// src/plays/local-file-discovery.ts
|
|
4739
4991
|
import { createHash as createHash2 } from "crypto";
|
|
4740
4992
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
4741
|
-
import { basename as basename2, dirname as dirname6, extname as extname2, isAbsolute as isAbsolute2, join as join4, relative, resolve as
|
|
4993
|
+
import { basename as basename2, dirname as dirname6, extname as extname2, isAbsolute as isAbsolute2, join as join4, relative, resolve as resolve7 } from "path";
|
|
4742
4994
|
var SOURCE_EXTENSIONS2 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
|
|
4743
4995
|
function sha2562(buffer) {
|
|
4744
4996
|
return createHash2("sha256").update(buffer).digest("hex");
|
|
@@ -4939,7 +5191,7 @@ function isPathInsideDirectory2(filePath, directory) {
|
|
|
4939
5191
|
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute2(relativePath);
|
|
4940
5192
|
}
|
|
4941
5193
|
async function resolveLocalImport2(fromFile, specifier) {
|
|
4942
|
-
const base = isAbsolute2(specifier) ?
|
|
5194
|
+
const base = isAbsolute2(specifier) ? resolve7(specifier) : resolve7(dirname6(fromFile), specifier);
|
|
4943
5195
|
const candidates = [base];
|
|
4944
5196
|
const explicitExtension = extname2(base).toLowerCase();
|
|
4945
5197
|
if (!explicitExtension) {
|
|
@@ -4957,13 +5209,13 @@ async function resolveLocalImport2(fromFile, specifier) {
|
|
|
4957
5209
|
throw new Error(`Could not resolve local import "${specifier}" from ${fromFile}`);
|
|
4958
5210
|
}
|
|
4959
5211
|
async function discoverPackagedLocalFiles(entryFile) {
|
|
4960
|
-
const absoluteEntryFile =
|
|
5212
|
+
const absoluteEntryFile = resolve7(entryFile);
|
|
4961
5213
|
const packagingRoot = dirname6(absoluteEntryFile);
|
|
4962
5214
|
const files = /* @__PURE__ */ new Map();
|
|
4963
5215
|
const unresolved = [];
|
|
4964
5216
|
const visitedFiles = /* @__PURE__ */ new Set();
|
|
4965
5217
|
const visitSourceFile = async (filePath) => {
|
|
4966
|
-
const absolutePath =
|
|
5218
|
+
const absolutePath = resolve7(filePath);
|
|
4967
5219
|
if (visitedFiles.has(absolutePath)) {
|
|
4968
5220
|
return;
|
|
4969
5221
|
}
|
|
@@ -4995,7 +5247,7 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
4995
5247
|
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."
|
|
4996
5248
|
});
|
|
4997
5249
|
} else {
|
|
4998
|
-
const absoluteCsvPath =
|
|
5250
|
+
const absoluteCsvPath = resolve7(dirname6(absolutePath), resolvedPath);
|
|
4999
5251
|
if (isAbsolute2(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
|
|
5000
5252
|
unresolved.push({
|
|
5001
5253
|
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
@@ -5035,23 +5287,23 @@ async function discoverPackagedLocalFiles(entryFile) {
|
|
|
5035
5287
|
// src/plays/bundle-play-file.ts
|
|
5036
5288
|
var PLAY_BUNDLE_CACHE_VERSION2 = 30;
|
|
5037
5289
|
var MODULE_DIR = dirname7(fileURLToPath(import.meta.url));
|
|
5038
|
-
var SDK_PACKAGE_ROOT =
|
|
5039
|
-
var SOURCE_REPO_ROOT =
|
|
5290
|
+
var SDK_PACKAGE_ROOT = resolve8(MODULE_DIR, "..", "..");
|
|
5291
|
+
var SOURCE_REPO_ROOT = resolve8(SDK_PACKAGE_ROOT, "..");
|
|
5040
5292
|
var HAS_SOURCE_BUNDLING_SOURCES = existsSync5(
|
|
5041
|
-
|
|
5293
|
+
resolve8(SOURCE_REPO_ROOT, "apps", "play-runner-workers", "src", "entry.ts")
|
|
5042
5294
|
);
|
|
5043
|
-
var PACKAGED_REPO_ROOT =
|
|
5295
|
+
var PACKAGED_REPO_ROOT = resolve8(SDK_PACKAGE_ROOT, "dist", "repo");
|
|
5044
5296
|
var HAS_PACKAGED_BUNDLING_SOURCES = existsSync5(
|
|
5045
|
-
|
|
5297
|
+
resolve8(PACKAGED_REPO_ROOT, "apps", "play-runner-workers", "src", "entry.ts")
|
|
5046
5298
|
);
|
|
5047
|
-
var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED_BUNDLING_SOURCES ? PACKAGED_REPO_ROOT :
|
|
5048
|
-
var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ?
|
|
5049
|
-
var SDK_PACKAGE_JSON =
|
|
5050
|
-
var SDK_ENTRY_FILE =
|
|
5051
|
-
var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE :
|
|
5052
|
-
var SDK_WORKERS_ENTRY_FILE =
|
|
5053
|
-
var WORKERS_HARNESS_ENTRY_FILE =
|
|
5054
|
-
var WORKERS_HARNESS_FILES_DIR =
|
|
5299
|
+
var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED_BUNDLING_SOURCES ? PACKAGED_REPO_ROOT : resolve8(SDK_PACKAGE_ROOT, "..");
|
|
5300
|
+
var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? resolve8(SOURCE_REPO_ROOT, "sdk", "src") : HAS_PACKAGED_BUNDLING_SOURCES ? resolve8(PACKAGED_REPO_ROOT, "sdk", "src") : resolve8(SDK_PACKAGE_ROOT, "src");
|
|
5301
|
+
var SDK_PACKAGE_JSON = resolve8(SDK_PACKAGE_ROOT, "package.json");
|
|
5302
|
+
var SDK_ENTRY_FILE = resolve8(SDK_SOURCE_ROOT, "index.ts");
|
|
5303
|
+
var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : resolve8(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
|
|
5304
|
+
var SDK_WORKERS_ENTRY_FILE = resolve8(SDK_SOURCE_ROOT, "worker-play-entry.ts");
|
|
5305
|
+
var WORKERS_HARNESS_ENTRY_FILE = resolve8(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
|
|
5306
|
+
var WORKERS_HARNESS_FILES_DIR = resolve8(PROJECT_ROOT, "apps", "play-runner-workers", "src");
|
|
5055
5307
|
var hasWarnedAboutNonDevelopmentBundling = false;
|
|
5056
5308
|
function warnAboutNonDevelopmentBundling(filePath) {
|
|
5057
5309
|
if (hasWarnedAboutNonDevelopmentBundling) {
|
|
@@ -5075,7 +5327,7 @@ function defaultPlayBundleTarget() {
|
|
|
5075
5327
|
function createSdkPlayBundlingAdapter() {
|
|
5076
5328
|
return {
|
|
5077
5329
|
projectRoot: PROJECT_ROOT,
|
|
5078
|
-
nodeModulesDir:
|
|
5330
|
+
nodeModulesDir: resolve8(PROJECT_ROOT, "node_modules"),
|
|
5079
5331
|
cacheDir: join5(tmpdir2(), `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`),
|
|
5080
5332
|
sdkSourceRoot: SDK_SOURCE_ROOT,
|
|
5081
5333
|
sdkPackageJson: SDK_PACKAGE_JSON,
|
|
@@ -5313,7 +5565,7 @@ function traceCliSync(phase, fields, run) {
|
|
|
5313
5565
|
}
|
|
5314
5566
|
}
|
|
5315
5567
|
function sleep4(ms) {
|
|
5316
|
-
return new Promise((
|
|
5568
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
5317
5569
|
}
|
|
5318
5570
|
function parseReferencedPlayTarget(target) {
|
|
5319
5571
|
const trimmed = target.trim();
|
|
@@ -5359,7 +5611,7 @@ function formatPlayListReference(play) {
|
|
|
5359
5611
|
function defaultMaterializedPlayPath(reference) {
|
|
5360
5612
|
const playName = parseReferencedPlayTarget(reference).unqualifiedPlayName;
|
|
5361
5613
|
const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
5362
|
-
return
|
|
5614
|
+
return resolve9(`${safeName || "play"}.play.ts`);
|
|
5363
5615
|
}
|
|
5364
5616
|
function materializeRemotePlaySource(input) {
|
|
5365
5617
|
if (isFileTarget(input.target)) {
|
|
@@ -5370,14 +5622,14 @@ function materializeRemotePlaySource(input) {
|
|
|
5370
5622
|
}
|
|
5371
5623
|
const outputPath = input.outPath ?? defaultMaterializedPlayPath(input.playName);
|
|
5372
5624
|
if (existsSync6(outputPath)) {
|
|
5373
|
-
const existingSource =
|
|
5625
|
+
const existingSource = readFileSync5(outputPath, "utf-8");
|
|
5374
5626
|
if (existingSource === input.sourceCode) {
|
|
5375
5627
|
return { path: outputPath, status: "unchanged", created: false };
|
|
5376
5628
|
}
|
|
5377
|
-
|
|
5629
|
+
writeFileSync6(outputPath, input.sourceCode, "utf-8");
|
|
5378
5630
|
return { path: outputPath, status: "updated", created: false };
|
|
5379
5631
|
}
|
|
5380
|
-
|
|
5632
|
+
writeFileSync6(outputPath, input.sourceCode, "utf-8");
|
|
5381
5633
|
return { path: outputPath, status: "created", created: true };
|
|
5382
5634
|
}
|
|
5383
5635
|
function formatLoadedPlayMessage(materializedFile) {
|
|
@@ -5422,7 +5674,7 @@ function extractPlayName(code, filePath) {
|
|
|
5422
5674
|
throw buildMissingDefinePlayError(filePath);
|
|
5423
5675
|
}
|
|
5424
5676
|
function isFileTarget(target) {
|
|
5425
|
-
return existsSync6(
|
|
5677
|
+
return existsSync6(resolve9(target));
|
|
5426
5678
|
}
|
|
5427
5679
|
function looksLikeFilePath(target) {
|
|
5428
5680
|
if (target.trim().toLowerCase().startsWith("prebuilt/")) {
|
|
@@ -5441,7 +5693,7 @@ function parsePositiveInteger2(value, flagName) {
|
|
|
5441
5693
|
return parsed;
|
|
5442
5694
|
}
|
|
5443
5695
|
function parseJsonInput(raw) {
|
|
5444
|
-
const source = raw.startsWith("@") ?
|
|
5696
|
+
const source = raw.startsWith("@") ? readFileSync5(resolve9(raw.slice(1)), "utf-8") : raw;
|
|
5445
5697
|
const parsed = JSON.parse(source);
|
|
5446
5698
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5447
5699
|
throw new Error("--input must be a JSON object.");
|
|
@@ -5542,7 +5794,7 @@ function fileInputBindingsFromStaticPipeline(staticPipeline) {
|
|
|
5542
5794
|
function isLocalFilePathValue(value) {
|
|
5543
5795
|
if (typeof value !== "string" || !value.trim()) return false;
|
|
5544
5796
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
5545
|
-
return existsSync6(
|
|
5797
|
+
return existsSync6(resolve9(value));
|
|
5546
5798
|
}
|
|
5547
5799
|
function inputContainsLocalFilePath(value) {
|
|
5548
5800
|
if (isLocalFilePathValue(value)) {
|
|
@@ -5568,7 +5820,7 @@ async function stageFileInputArgs(input) {
|
|
|
5568
5820
|
const localFiles = uniqueBindings.flatMap((binding) => {
|
|
5569
5821
|
const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
|
|
5570
5822
|
if (!isLocalFilePathValue(value)) return [];
|
|
5571
|
-
const absolutePath =
|
|
5823
|
+
const absolutePath = resolve9(value);
|
|
5572
5824
|
return [{ binding, absolutePath, logicalPath: basename3(absolutePath) }];
|
|
5573
5825
|
});
|
|
5574
5826
|
if (localFiles.length === 0) {
|
|
@@ -5593,7 +5845,7 @@ async function stageFileInputArgs(input) {
|
|
|
5593
5845
|
};
|
|
5594
5846
|
}
|
|
5595
5847
|
function stageFile(logicalPath, absolutePath) {
|
|
5596
|
-
const buffer =
|
|
5848
|
+
const buffer = readFileSync5(absolutePath);
|
|
5597
5849
|
return {
|
|
5598
5850
|
logicalPath,
|
|
5599
5851
|
contentBase64: buffer.toString("base64"),
|
|
@@ -5604,9 +5856,9 @@ function stageFile(logicalPath, absolutePath) {
|
|
|
5604
5856
|
}
|
|
5605
5857
|
function normalizePlayPath(filePath) {
|
|
5606
5858
|
try {
|
|
5607
|
-
return realpathSync.native(
|
|
5859
|
+
return realpathSync.native(resolve9(filePath));
|
|
5608
5860
|
} catch {
|
|
5609
|
-
return
|
|
5861
|
+
return resolve9(filePath);
|
|
5610
5862
|
}
|
|
5611
5863
|
}
|
|
5612
5864
|
function formatBundlingErrors(filePath, errors) {
|
|
@@ -5759,11 +6011,42 @@ function isTransientPlayStreamError(error) {
|
|
|
5759
6011
|
text
|
|
5760
6012
|
);
|
|
5761
6013
|
}
|
|
6014
|
+
function playStatusErrorText(status) {
|
|
6015
|
+
const chunks = [];
|
|
6016
|
+
const progressError = status.progress?.error;
|
|
6017
|
+
if (typeof progressError === "string" && progressError.trim()) {
|
|
6018
|
+
chunks.push(progressError.trim());
|
|
6019
|
+
}
|
|
6020
|
+
const errorValue = status.error;
|
|
6021
|
+
if (typeof errorValue === "string" && errorValue.trim()) {
|
|
6022
|
+
chunks.push(errorValue.trim());
|
|
6023
|
+
}
|
|
6024
|
+
const errors = status.errors;
|
|
6025
|
+
if (Array.isArray(errors)) {
|
|
6026
|
+
for (const error of errors) {
|
|
6027
|
+
if (typeof error === "string" && error.trim()) {
|
|
6028
|
+
chunks.push(error.trim());
|
|
6029
|
+
} else if (error && typeof error === "object") {
|
|
6030
|
+
const message = error.message;
|
|
6031
|
+
if (typeof message === "string" && message.trim()) {
|
|
6032
|
+
chunks.push(message.trim());
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
}
|
|
6037
|
+
return chunks.join("; ") || status.status;
|
|
6038
|
+
}
|
|
6039
|
+
function isRetryablePendingStartFailure(status) {
|
|
6040
|
+
if (status.status !== "failed") return false;
|
|
6041
|
+
if (status.runId && status.runId !== "pending") return false;
|
|
6042
|
+
return isTransientPlayStreamError(new Error(playStatusErrorText(status)));
|
|
6043
|
+
}
|
|
5762
6044
|
var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
|
|
5763
6045
|
"completed",
|
|
5764
6046
|
"failed",
|
|
5765
6047
|
"cancelled"
|
|
5766
6048
|
]);
|
|
6049
|
+
var PLAY_START_TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
|
|
5767
6050
|
function getEventPayload(event) {
|
|
5768
6051
|
return event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
5769
6052
|
}
|
|
@@ -5830,10 +6113,6 @@ function openPlayDashboard(input) {
|
|
|
5830
6113
|
}
|
|
5831
6114
|
openInBrowser(input.dashboardUrl);
|
|
5832
6115
|
}
|
|
5833
|
-
function getDashboardUrlFromLiveEvent(event) {
|
|
5834
|
-
const dashboardUrl = getEventPayload(event).dashboardUrl;
|
|
5835
|
-
return typeof dashboardUrl === "string" && dashboardUrl.trim() ? dashboardUrl.trim() : null;
|
|
5836
|
-
}
|
|
5837
6116
|
function printPlayLogLines(input) {
|
|
5838
6117
|
for (const line of input.lines) {
|
|
5839
6118
|
if (input.emitLogs) {
|
|
@@ -5902,7 +6181,7 @@ async function waitForPlayCompletionByStream(input) {
|
|
|
5902
6181
|
billing: false
|
|
5903
6182
|
});
|
|
5904
6183
|
if (TERMINAL_PLAY_STATUSES2.has(finalStatus.status)) {
|
|
5905
|
-
return finalStatus;
|
|
6184
|
+
return input.dashboardUrl ? { ...finalStatus, dashboardUrl: input.dashboardUrl } : finalStatus;
|
|
5906
6185
|
}
|
|
5907
6186
|
}
|
|
5908
6187
|
}
|
|
@@ -5929,7 +6208,39 @@ async function waitForPlayCompletionByStream(input) {
|
|
|
5929
6208
|
);
|
|
5930
6209
|
}
|
|
5931
6210
|
async function startAndWaitForPlayCompletionByStream(input) {
|
|
6211
|
+
for (let attempt = 0; attempt <= PLAY_START_TRANSIENT_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
6212
|
+
const status = await startAndWaitForPlayCompletionByStreamOnce(input);
|
|
6213
|
+
const retryDelayMs = PLAY_START_TRANSIENT_RETRY_DELAYS_MS[attempt];
|
|
6214
|
+
if (retryDelayMs === void 0 || !isRetryablePendingStartFailure(status)) {
|
|
6215
|
+
return status;
|
|
6216
|
+
}
|
|
6217
|
+
if (!input.jsonOutput) {
|
|
6218
|
+
input.progress.writeLine(
|
|
6219
|
+
`[play watch] start failed before run id with a transient error; retrying (${playStatusErrorText(status)})`
|
|
6220
|
+
);
|
|
6221
|
+
}
|
|
6222
|
+
recordCliTrace({
|
|
6223
|
+
phase: "cli.play_start_stream_retry",
|
|
6224
|
+
ms: 0,
|
|
6225
|
+
ok: true,
|
|
6226
|
+
playName: input.playName,
|
|
6227
|
+
attempt: attempt + 1,
|
|
6228
|
+
reason: playStatusErrorText(status)
|
|
6229
|
+
});
|
|
6230
|
+
await sleep4(retryDelayMs);
|
|
6231
|
+
}
|
|
6232
|
+
throw new DeeplineError(
|
|
6233
|
+
`Play ${input.playName} did not start after retrying transient start failures.`,
|
|
6234
|
+
void 0,
|
|
6235
|
+
"PLAY_START_RETRY_EXHAUSTED"
|
|
6236
|
+
);
|
|
6237
|
+
}
|
|
6238
|
+
async function startAndWaitForPlayCompletionByStreamOnce(input) {
|
|
5932
6239
|
const startedAt = Date.now();
|
|
6240
|
+
const dashboardUrl = buildPlayDashboardUrl(
|
|
6241
|
+
input.client.baseUrl,
|
|
6242
|
+
input.playName
|
|
6243
|
+
);
|
|
5933
6244
|
const state = {
|
|
5934
6245
|
lastLogIndex: 0,
|
|
5935
6246
|
emittedRunnerStarted: false
|
|
@@ -5960,7 +6271,6 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
5960
6271
|
}
|
|
5961
6272
|
const workflowId = lastKnownWorkflowId || "pending";
|
|
5962
6273
|
if (workflowId !== "pending" && !emittedDashboardUrl) {
|
|
5963
|
-
const dashboardUrl = getDashboardUrlFromLiveEvent(event) ?? buildPlayDashboardUrl(input.client.baseUrl, input.playName);
|
|
5964
6274
|
openPlayDashboard({
|
|
5965
6275
|
dashboardUrl,
|
|
5966
6276
|
jsonOutput: input.jsonOutput,
|
|
@@ -6009,7 +6319,7 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
6009
6319
|
firstRunIdMs,
|
|
6010
6320
|
lastPhase
|
|
6011
6321
|
});
|
|
6012
|
-
return finalStatus;
|
|
6322
|
+
return { ...finalStatus, dashboardUrl };
|
|
6013
6323
|
}
|
|
6014
6324
|
}
|
|
6015
6325
|
} catch (error) {
|
|
@@ -6046,6 +6356,7 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
6046
6356
|
return waitForPlayCompletionByStream({
|
|
6047
6357
|
client: input.client,
|
|
6048
6358
|
workflowId: lastKnownWorkflowId,
|
|
6359
|
+
dashboardUrl,
|
|
6049
6360
|
jsonOutput: input.jsonOutput,
|
|
6050
6361
|
emitLogs: input.emitLogs,
|
|
6051
6362
|
waitTimeoutMs: input.waitTimeoutMs,
|
|
@@ -6080,6 +6391,7 @@ async function startAndWaitForPlayCompletionByStream(input) {
|
|
|
6080
6391
|
return waitForPlayCompletionByStream({
|
|
6081
6392
|
client: input.client,
|
|
6082
6393
|
workflowId: lastKnownWorkflowId,
|
|
6394
|
+
dashboardUrl,
|
|
6083
6395
|
jsonOutput: input.jsonOutput,
|
|
6084
6396
|
emitLogs: input.emitLogs,
|
|
6085
6397
|
waitTimeoutMs: input.waitTimeoutMs,
|
|
@@ -6250,12 +6562,25 @@ function buildRunWarnings(status, rowsInfo) {
|
|
|
6250
6562
|
}
|
|
6251
6563
|
return [];
|
|
6252
6564
|
}
|
|
6253
|
-
function buildRunNextCommands(
|
|
6254
|
-
|
|
6565
|
+
function buildRunNextCommands(status) {
|
|
6566
|
+
const runId = status.runId?.trim();
|
|
6567
|
+
if (!runId) {
|
|
6568
|
+
const playName = extractRunPlayName(status);
|
|
6569
|
+
return playName ? {
|
|
6570
|
+
list: `deepline runs list --play ${playName} --json`
|
|
6571
|
+
} : {
|
|
6572
|
+
list: "deepline runs list --json"
|
|
6573
|
+
};
|
|
6574
|
+
}
|
|
6575
|
+
const commands = {
|
|
6255
6576
|
get: `deepline runs get ${runId} --json`,
|
|
6256
6577
|
stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
|
|
6257
6578
|
logs: `deepline runs logs ${runId} --out run.log --json`
|
|
6258
6579
|
};
|
|
6580
|
+
if (status.dashboardUrl) {
|
|
6581
|
+
commands.open = `Open ${status.dashboardUrl} to see results.`;
|
|
6582
|
+
}
|
|
6583
|
+
return commands;
|
|
6259
6584
|
}
|
|
6260
6585
|
var RUN_LOG_PREVIEW_LIMIT = 20;
|
|
6261
6586
|
function getRecordField(value, key) {
|
|
@@ -6276,6 +6601,121 @@ function getTimestampField(value, key) {
|
|
|
6276
6601
|
}
|
|
6277
6602
|
return typeof field === "string" && field.trim() ? field : null;
|
|
6278
6603
|
}
|
|
6604
|
+
function getObjectField(value, key) {
|
|
6605
|
+
const field = getRecordField(value, key);
|
|
6606
|
+
return field && typeof field === "object" && !Array.isArray(field) ? field : null;
|
|
6607
|
+
}
|
|
6608
|
+
function formatCreditAmount(value) {
|
|
6609
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
6610
|
+
return String(value ?? "-");
|
|
6611
|
+
}
|
|
6612
|
+
return Number(value.toFixed(8)).toString();
|
|
6613
|
+
}
|
|
6614
|
+
function extractJsonObjectFromText(text) {
|
|
6615
|
+
const start = text.indexOf("{");
|
|
6616
|
+
if (start < 0) {
|
|
6617
|
+
return null;
|
|
6618
|
+
}
|
|
6619
|
+
const suffix = text.slice(start);
|
|
6620
|
+
try {
|
|
6621
|
+
const parsed = JSON.parse(suffix);
|
|
6622
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
6623
|
+
} catch {
|
|
6624
|
+
let depth = 0;
|
|
6625
|
+
let inString = false;
|
|
6626
|
+
let escaped = false;
|
|
6627
|
+
for (let index = start; index < text.length; index += 1) {
|
|
6628
|
+
const char = text[index];
|
|
6629
|
+
if (inString) {
|
|
6630
|
+
if (escaped) {
|
|
6631
|
+
escaped = false;
|
|
6632
|
+
} else if (char === "\\") {
|
|
6633
|
+
escaped = true;
|
|
6634
|
+
} else if (char === '"') {
|
|
6635
|
+
inString = false;
|
|
6636
|
+
}
|
|
6637
|
+
continue;
|
|
6638
|
+
}
|
|
6639
|
+
if (char === '"') {
|
|
6640
|
+
inString = true;
|
|
6641
|
+
} else if (char === "{") {
|
|
6642
|
+
depth += 1;
|
|
6643
|
+
} else if (char === "}") {
|
|
6644
|
+
depth -= 1;
|
|
6645
|
+
if (depth === 0) {
|
|
6646
|
+
try {
|
|
6647
|
+
const parsed = JSON.parse(text.slice(start, index + 1));
|
|
6648
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
6649
|
+
} catch {
|
|
6650
|
+
return null;
|
|
6651
|
+
}
|
|
6652
|
+
}
|
|
6653
|
+
}
|
|
6654
|
+
}
|
|
6655
|
+
}
|
|
6656
|
+
return null;
|
|
6657
|
+
}
|
|
6658
|
+
function extractBillingFromText(text) {
|
|
6659
|
+
if (!text) {
|
|
6660
|
+
return null;
|
|
6661
|
+
}
|
|
6662
|
+
const parsed = extractJsonObjectFromText(text);
|
|
6663
|
+
return getObjectField(parsed, "billing");
|
|
6664
|
+
}
|
|
6665
|
+
function extractToolIdFromErrorText(text) {
|
|
6666
|
+
if (!text) {
|
|
6667
|
+
return null;
|
|
6668
|
+
}
|
|
6669
|
+
const lowerToolMatch = /\btool\s+([A-Za-z0-9_.:-]+)\s+\d{3}\b/.exec(text);
|
|
6670
|
+
if (lowerToolMatch?.[1]) {
|
|
6671
|
+
return lowerToolMatch[1];
|
|
6672
|
+
}
|
|
6673
|
+
const upperToolMatch = /\bTool\s+([A-Za-z0-9_.:-]+)\s+failed\b/.exec(text);
|
|
6674
|
+
return upperToolMatch?.[1] ?? null;
|
|
6675
|
+
}
|
|
6676
|
+
function isInsufficientCreditsBilling(billing) {
|
|
6677
|
+
return billing?.kind === "insufficient_credits";
|
|
6678
|
+
}
|
|
6679
|
+
function extractBillingForStatus(status, error) {
|
|
6680
|
+
const errorBilling = getObjectField(status, "errorBilling");
|
|
6681
|
+
if (errorBilling) {
|
|
6682
|
+
return errorBilling;
|
|
6683
|
+
}
|
|
6684
|
+
const directErrors = getRecordField(status, "errors");
|
|
6685
|
+
if (Array.isArray(directErrors)) {
|
|
6686
|
+
for (const entry of directErrors) {
|
|
6687
|
+
const billing = getObjectField(entry, "billing");
|
|
6688
|
+
if (billing) {
|
|
6689
|
+
return billing;
|
|
6690
|
+
}
|
|
6691
|
+
}
|
|
6692
|
+
}
|
|
6693
|
+
const progressError = getStringField(status.progress, "error");
|
|
6694
|
+
return extractBillingFromText(error) ?? extractBillingFromText(progressError) ?? getObjectField(status, "billing");
|
|
6695
|
+
}
|
|
6696
|
+
function formatInsufficientCreditsMessage(input) {
|
|
6697
|
+
const operation = getStringField(input.billing, "operation_id") ?? getStringField(input.billing, "operation") ?? extractToolIdFromErrorText(input.error) ?? getStringField(input.billing, "provider") ?? "tool call";
|
|
6698
|
+
const balance = formatCreditAmount(input.billing.balance_credits);
|
|
6699
|
+
const required = formatCreditAmount(input.billing.required_credits);
|
|
6700
|
+
const recommended = formatCreditAmount(
|
|
6701
|
+
input.billing.recommended_add_credits ?? input.billing.needed_credits
|
|
6702
|
+
);
|
|
6703
|
+
const billingUrl = getStringField(input.billing, "billing_url");
|
|
6704
|
+
const workspace = getStringField(input.billing, "workspace_id") ?? getStringField(input.billing, "workspaceId");
|
|
6705
|
+
const workspaceSuffix = workspace ? ` (workspace=${workspace})` : "";
|
|
6706
|
+
const addSuffix = billingUrl && recommended !== "-" ? ` Add >=${recommended} at ${billingUrl}.` : billingUrl ? ` Add credits at ${billingUrl}.` : "";
|
|
6707
|
+
return `Workspace balance ${balance} < required ${required} for ${operation}${workspaceSuffix}.${addSuffix}`;
|
|
6708
|
+
}
|
|
6709
|
+
function formatPlayErrorForDisplay(status, error) {
|
|
6710
|
+
if (!error) {
|
|
6711
|
+
return null;
|
|
6712
|
+
}
|
|
6713
|
+
const billing = extractBillingForStatus(status, error);
|
|
6714
|
+
if (isInsufficientCreditsBilling(billing)) {
|
|
6715
|
+
return formatInsufficientCreditsMessage({ billing, error });
|
|
6716
|
+
}
|
|
6717
|
+
return error;
|
|
6718
|
+
}
|
|
6279
6719
|
function normalizeRunStatusForEnvelope(status) {
|
|
6280
6720
|
const run = status.run ?? null;
|
|
6281
6721
|
return {
|
|
@@ -6326,18 +6766,33 @@ function normalizeErrorsForEnvelope(status, error) {
|
|
|
6326
6766
|
if (Array.isArray(directErrors)) {
|
|
6327
6767
|
return directErrors.filter(
|
|
6328
6768
|
(entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)
|
|
6329
|
-
)
|
|
6769
|
+
).map((entry) => {
|
|
6770
|
+
const message2 = typeof entry.message === "string" && entry.message.trim() ? entry.message : error;
|
|
6771
|
+
const billing2 = getObjectField(entry, "billing");
|
|
6772
|
+
if (!isInsufficientCreditsBilling(billing2) || !message2) {
|
|
6773
|
+
return entry;
|
|
6774
|
+
}
|
|
6775
|
+
return {
|
|
6776
|
+
...entry,
|
|
6777
|
+
message: formatInsufficientCreditsMessage({ billing: billing2, error: message2 }),
|
|
6778
|
+
billing: stripProviderSpendFromBilling(billing2)
|
|
6779
|
+
};
|
|
6780
|
+
});
|
|
6330
6781
|
}
|
|
6331
6782
|
if (!error) {
|
|
6332
6783
|
return [];
|
|
6333
6784
|
}
|
|
6785
|
+
const nextCommands = buildRunNextCommands(status);
|
|
6786
|
+
const billing = extractBillingForStatus(status, error);
|
|
6787
|
+
const message = formatPlayErrorForDisplay(status, error) ?? error;
|
|
6334
6788
|
return [
|
|
6335
6789
|
{
|
|
6336
6790
|
code: getStringField(status, "errorCode") ?? "RUN_FAILED",
|
|
6337
6791
|
phase: getStringField(status, "errorPhase") ?? "runtime",
|
|
6338
|
-
message
|
|
6792
|
+
message,
|
|
6339
6793
|
retryable: typeof getRecordField(status, "retryable") === "boolean" ? getRecordField(status, "retryable") : null,
|
|
6340
|
-
|
|
6794
|
+
...billing ? { billing: stripProviderSpendFromBilling(billing) } : {},
|
|
6795
|
+
nextAction: nextCommands.get ?? nextCommands.list
|
|
6341
6796
|
}
|
|
6342
6797
|
];
|
|
6343
6798
|
}
|
|
@@ -6386,24 +6841,26 @@ function compactPlayStatus(status) {
|
|
|
6386
6841
|
) : null;
|
|
6387
6842
|
const progressError = status.progress?.error;
|
|
6388
6843
|
const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
|
|
6844
|
+
const displayError = formatPlayErrorForDisplay(status, error);
|
|
6389
6845
|
return {
|
|
6390
6846
|
runId: status.runId,
|
|
6391
6847
|
apiVersion: status.apiVersion ?? 1,
|
|
6392
6848
|
...typeof status.name === "string" ? { name: status.name } : {},
|
|
6393
6849
|
...typeof status.playName === "string" ? { playName: status.playName } : {},
|
|
6850
|
+
...status.dashboardUrl ? { dashboardUrl: status.dashboardUrl } : {},
|
|
6394
6851
|
status: status.status,
|
|
6395
6852
|
run: normalizeRunStatusForEnvelope(status),
|
|
6396
6853
|
progress: normalizeProgressForEnvelope(status, rowsInfo),
|
|
6397
6854
|
steps: normalizeStepsForEnvelope(status),
|
|
6398
6855
|
errors: normalizeErrorsForEnvelope(status, error),
|
|
6399
6856
|
logs: normalizeLogsForEnvelope(status),
|
|
6400
|
-
...
|
|
6857
|
+
...displayError ? { error: displayError } : {},
|
|
6401
6858
|
...warnings.length > 0 ? { warnings } : {},
|
|
6402
6859
|
...result !== void 0 ? { result } : {},
|
|
6403
6860
|
...status.resultView ? { resultView: status.resultView } : {},
|
|
6404
6861
|
...datasetStats ? { dataset_stats: datasetStats } : {},
|
|
6405
6862
|
...billing ? { billing } : {},
|
|
6406
|
-
next: buildRunNextCommands(status
|
|
6863
|
+
next: buildRunNextCommands(status)
|
|
6407
6864
|
};
|
|
6408
6865
|
}
|
|
6409
6866
|
function enrichPlayStatusWithDatasetStats(status) {
|
|
@@ -6477,7 +6934,8 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
6477
6934
|
lines.push(...formatDatasetStatsLines(datasetStats));
|
|
6478
6935
|
const progressError = status.progress?.error;
|
|
6479
6936
|
if (progressError && typeof progressError === "string") {
|
|
6480
|
-
|
|
6937
|
+
const displayError = formatPlayErrorForDisplay(status, progressError) ?? progressError;
|
|
6938
|
+
lines.push(` error: ${displayError.slice(0, 200)}`);
|
|
6481
6939
|
}
|
|
6482
6940
|
const renderedServerView = renderServerResultView(status.resultView);
|
|
6483
6941
|
if (result) {
|
|
@@ -6501,8 +6959,11 @@ var RUN_EXPORT_PAGE_SIZE = 5e3;
|
|
|
6501
6959
|
function shellSingleQuote(value) {
|
|
6502
6960
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
6503
6961
|
}
|
|
6962
|
+
function sqlStringLiteral(value) {
|
|
6963
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
6964
|
+
}
|
|
6504
6965
|
function runExportRetryCommand(runId, outPath, datasetPath) {
|
|
6505
|
-
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(
|
|
6966
|
+
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(resolve9(outPath))}`;
|
|
6506
6967
|
}
|
|
6507
6968
|
function extractRunPlayName(status) {
|
|
6508
6969
|
const run = status.run;
|
|
@@ -6519,6 +6980,26 @@ function extractRunPlayName(status) {
|
|
|
6519
6980
|
}
|
|
6520
6981
|
return null;
|
|
6521
6982
|
}
|
|
6983
|
+
function normalizeCustomerDbIdentifier(value) {
|
|
6984
|
+
return value.split("/").pop().replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
6985
|
+
}
|
|
6986
|
+
function buildCustomerDbQueryPlan(input) {
|
|
6987
|
+
const playName = extractRunPlayName(input.status);
|
|
6988
|
+
const tableNamespace = input.rowsInfo.tableNamespace?.trim();
|
|
6989
|
+
if (!playName || !tableNamespace || input.rowsInfo.totalRows <= 0) {
|
|
6990
|
+
return null;
|
|
6991
|
+
}
|
|
6992
|
+
const tableName = `${normalizeCustomerDbIdentifier(playName)}_${normalizeCustomerDbIdentifier(
|
|
6993
|
+
tableNamespace
|
|
6994
|
+
)}`;
|
|
6995
|
+
const sql = `select * from "storage"."${tableName}" where _run_id = ${sqlStringLiteral(input.status.runId)} limit ${input.rowsInfo.totalRows}`;
|
|
6996
|
+
const base = `deepline customer-db query --sql ${shellSingleQuote(sql)} --max-rows ${input.rowsInfo.totalRows}`;
|
|
6997
|
+
return {
|
|
6998
|
+
sql,
|
|
6999
|
+
json: `${base} --json`,
|
|
7000
|
+
csv: `${base} --format csv --out ${shellSingleQuote(resolve9(input.outPath))}`
|
|
7001
|
+
};
|
|
7002
|
+
}
|
|
6522
7003
|
function exportableSheetRow(row) {
|
|
6523
7004
|
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
6524
7005
|
return null;
|
|
@@ -6616,7 +7097,7 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
|
|
|
6616
7097
|
return null;
|
|
6617
7098
|
}
|
|
6618
7099
|
const availableRows = collectSerializedDatasetRowsInfos(status);
|
|
6619
|
-
|
|
7100
|
+
const rowsInfo = options.datasetPath ? availableRows.find((info) => info.source === options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
|
|
6620
7101
|
if (!rowsInfo && options.datasetPath) {
|
|
6621
7102
|
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
6622
7103
|
throw new DeeplineError(
|
|
@@ -6690,6 +7171,60 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
|
|
|
6690
7171
|
}
|
|
6691
7172
|
return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
|
|
6692
7173
|
}
|
|
7174
|
+
function extractActiveRunsFromError(error) {
|
|
7175
|
+
if (!(error instanceof DeeplineError)) {
|
|
7176
|
+
return [];
|
|
7177
|
+
}
|
|
7178
|
+
const response = error.details?.response;
|
|
7179
|
+
if (!response || typeof response !== "object" || Array.isArray(response)) {
|
|
7180
|
+
return [];
|
|
7181
|
+
}
|
|
7182
|
+
const details = response.details;
|
|
7183
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) {
|
|
7184
|
+
return [];
|
|
7185
|
+
}
|
|
7186
|
+
const activeRuns = details.activeRuns;
|
|
7187
|
+
return Array.isArray(activeRuns) ? activeRuns.filter(
|
|
7188
|
+
(run) => Boolean(run) && typeof run === "object" && !Array.isArray(run)
|
|
7189
|
+
) : [];
|
|
7190
|
+
}
|
|
7191
|
+
function activeRunId(run) {
|
|
7192
|
+
return getStringField(run, "workflowId") ?? getStringField(run, "runId");
|
|
7193
|
+
}
|
|
7194
|
+
function formatActiveRunConflictError(input) {
|
|
7195
|
+
const lines = [
|
|
7196
|
+
`Active run exists for ${input.playName}. Use --force to supersede, or inspect/stop the active run first.`
|
|
7197
|
+
];
|
|
7198
|
+
for (const run of input.activeRuns.slice(0, 3)) {
|
|
7199
|
+
const runId = activeRunId(run);
|
|
7200
|
+
if (!runId) {
|
|
7201
|
+
continue;
|
|
7202
|
+
}
|
|
7203
|
+
const status = getStringField(run, "status");
|
|
7204
|
+
const startedAt = getStringField(run, "startedAt") ?? getStringField(run, "startTime");
|
|
7205
|
+
lines.push(
|
|
7206
|
+
` active: ${runId}${status ? ` status=${status}` : ""}${startedAt ? ` startedAt=${startedAt}` : ""}`
|
|
7207
|
+
);
|
|
7208
|
+
lines.push(` get: deepline runs get ${runId} --json`);
|
|
7209
|
+
lines.push(
|
|
7210
|
+
` stop: deepline runs stop ${runId} --reason "stale lock" --json`
|
|
7211
|
+
);
|
|
7212
|
+
}
|
|
7213
|
+
lines.push(` rerun: add --force to the same deepline plays run command`);
|
|
7214
|
+
return lines.join("\n");
|
|
7215
|
+
}
|
|
7216
|
+
function normalizePlayStartError(error, playName) {
|
|
7217
|
+
const activeRuns = extractActiveRunsFromError(error);
|
|
7218
|
+
if (activeRuns.length === 0) {
|
|
7219
|
+
return error;
|
|
7220
|
+
}
|
|
7221
|
+
return new DeeplineError(
|
|
7222
|
+
formatActiveRunConflictError({ playName, activeRuns }),
|
|
7223
|
+
error instanceof DeeplineError ? error.statusCode : 409,
|
|
7224
|
+
"ACTIVE_RUN_EXISTS",
|
|
7225
|
+
error instanceof DeeplineError ? error.details : void 0
|
|
7226
|
+
);
|
|
7227
|
+
}
|
|
6693
7228
|
function renderServerResultView(value) {
|
|
6694
7229
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6695
7230
|
return { lines: [], actions: [] };
|
|
@@ -6925,12 +7460,12 @@ function shouldUseLocalOnlyPlayCheck() {
|
|
|
6925
7460
|
async function handlePlayCheck(args) {
|
|
6926
7461
|
const options = parsePlayCheckOptions(args);
|
|
6927
7462
|
if (!isFileTarget(options.target)) {
|
|
6928
|
-
const resolved =
|
|
7463
|
+
const resolved = resolve9(options.target);
|
|
6929
7464
|
console.error(`File not found: ${resolved}`);
|
|
6930
7465
|
return 1;
|
|
6931
7466
|
}
|
|
6932
|
-
const absolutePlayPath =
|
|
6933
|
-
const sourceCode =
|
|
7467
|
+
const absolutePlayPath = resolve9(options.target);
|
|
7468
|
+
const sourceCode = readFileSync5(absolutePlayPath, "utf-8");
|
|
6934
7469
|
let graph;
|
|
6935
7470
|
try {
|
|
6936
7471
|
graph = await collectBundledPlayGraph(absolutePlayPath);
|
|
@@ -6993,12 +7528,12 @@ async function handleFileBackedRun(options) {
|
|
|
6993
7528
|
}
|
|
6994
7529
|
const client = new DeeplineClient();
|
|
6995
7530
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
6996
|
-
const absolutePlayPath =
|
|
7531
|
+
const absolutePlayPath = resolve9(options.target.path);
|
|
6997
7532
|
progress.phase("compiling play");
|
|
6998
7533
|
const sourceCode = traceCliSync(
|
|
6999
7534
|
"cli.play_file_read_source",
|
|
7000
7535
|
{ targetKind: "file" },
|
|
7001
|
-
() =>
|
|
7536
|
+
() => readFileSync5(absolutePlayPath, "utf-8")
|
|
7002
7537
|
);
|
|
7003
7538
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
7004
7539
|
let graph;
|
|
@@ -7079,6 +7614,8 @@ async function handleFileBackedRun(options) {
|
|
|
7079
7614
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
7080
7615
|
noOpen: options.noOpen,
|
|
7081
7616
|
progress
|
|
7617
|
+
}).catch((error) => {
|
|
7618
|
+
throw normalizePlayStartError(error, playName);
|
|
7082
7619
|
})
|
|
7083
7620
|
);
|
|
7084
7621
|
if (finalStatus.status === "completed") {
|
|
@@ -7097,10 +7634,11 @@ async function handleFileBackedRun(options) {
|
|
|
7097
7634
|
const started = await traceCliSpan(
|
|
7098
7635
|
"cli.play_start_unwatched",
|
|
7099
7636
|
{ targetKind: "file", playName },
|
|
7100
|
-
() => client.startPlayRun(startRequest)
|
|
7637
|
+
() => client.startPlayRun(startRequest).catch((error) => {
|
|
7638
|
+
throw normalizePlayStartError(error, playName);
|
|
7639
|
+
})
|
|
7101
7640
|
);
|
|
7102
|
-
const
|
|
7103
|
-
const resolvedDashboardUrl = started.dashboardUrl ?? dashboardUrl;
|
|
7641
|
+
const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
7104
7642
|
openPlayDashboard({
|
|
7105
7643
|
dashboardUrl: resolvedDashboardUrl,
|
|
7106
7644
|
jsonOutput: options.jsonOutput,
|
|
@@ -7215,6 +7753,8 @@ async function handleNamedRun(options) {
|
|
|
7215
7753
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
7216
7754
|
noOpen: options.noOpen,
|
|
7217
7755
|
progress
|
|
7756
|
+
}).catch((error) => {
|
|
7757
|
+
throw normalizePlayStartError(error, playName);
|
|
7218
7758
|
})
|
|
7219
7759
|
);
|
|
7220
7760
|
if (finalStatus.status === "completed") {
|
|
@@ -7233,13 +7773,11 @@ async function handleNamedRun(options) {
|
|
|
7233
7773
|
const started = await traceCliSpan(
|
|
7234
7774
|
"cli.play_start_unwatched",
|
|
7235
7775
|
{ targetKind: "name", playName },
|
|
7236
|
-
() => client.startPlayRun(startRequest)
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
client.baseUrl,
|
|
7240
|
-
playName
|
|
7776
|
+
() => client.startPlayRun(startRequest).catch((error) => {
|
|
7777
|
+
throw normalizePlayStartError(error, playName);
|
|
7778
|
+
})
|
|
7241
7779
|
);
|
|
7242
|
-
const resolvedDashboardUrl =
|
|
7780
|
+
const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
|
|
7243
7781
|
openPlayDashboard({
|
|
7244
7782
|
dashboardUrl: resolvedDashboardUrl,
|
|
7245
7783
|
jsonOutput: options.jsonOutput,
|
|
@@ -7263,7 +7801,7 @@ async function handlePlayRun(args) {
|
|
|
7263
7801
|
if (isFileTarget(options.target.path)) {
|
|
7264
7802
|
return handleFileBackedRun(options);
|
|
7265
7803
|
}
|
|
7266
|
-
const resolved =
|
|
7804
|
+
const resolved = resolve9(options.target.path);
|
|
7267
7805
|
console.error(`File not found: ${resolved}`);
|
|
7268
7806
|
const dir = dirname8(resolved);
|
|
7269
7807
|
if (existsSync6(dir)) {
|
|
@@ -7410,14 +7948,14 @@ async function handleRunLogs(args) {
|
|
|
7410
7948
|
continue;
|
|
7411
7949
|
}
|
|
7412
7950
|
if (arg === "--out" && args[index + 1]) {
|
|
7413
|
-
outPath =
|
|
7951
|
+
outPath = resolve9(args[++index]);
|
|
7414
7952
|
}
|
|
7415
7953
|
}
|
|
7416
7954
|
const client = new DeeplineClient();
|
|
7417
7955
|
const status = await client.runs.get(runId);
|
|
7418
7956
|
const logs = status.progress?.logs ?? [];
|
|
7419
7957
|
if (outPath) {
|
|
7420
|
-
|
|
7958
|
+
writeFileSync6(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
7421
7959
|
printCommandEnvelope({
|
|
7422
7960
|
runId: status.runId,
|
|
7423
7961
|
log_path: outPath,
|
|
@@ -7473,7 +8011,7 @@ async function handleRunStop(args) {
|
|
|
7473
8011
|
return 0;
|
|
7474
8012
|
}
|
|
7475
8013
|
async function handleRunExport(args) {
|
|
7476
|
-
const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--json]";
|
|
8014
|
+
const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--metadata-out export.json] [--json]";
|
|
7477
8015
|
let runId;
|
|
7478
8016
|
try {
|
|
7479
8017
|
runId = parseRunIdPositional(args, usage);
|
|
@@ -7483,14 +8021,19 @@ async function handleRunExport(args) {
|
|
|
7483
8021
|
}
|
|
7484
8022
|
let outPath = null;
|
|
7485
8023
|
let datasetPath = null;
|
|
8024
|
+
let metadataOutPath = null;
|
|
7486
8025
|
for (let index = 0; index < args.length; index += 1) {
|
|
7487
8026
|
const arg = args[index];
|
|
7488
8027
|
if (arg === "--out" && args[index + 1]) {
|
|
7489
|
-
outPath =
|
|
8028
|
+
outPath = resolve9(args[++index]);
|
|
7490
8029
|
continue;
|
|
7491
8030
|
}
|
|
7492
8031
|
if (arg === "--dataset" && args[index + 1]) {
|
|
7493
8032
|
datasetPath = args[++index];
|
|
8033
|
+
continue;
|
|
8034
|
+
}
|
|
8035
|
+
if (arg === "--metadata-out" && args[index + 1]) {
|
|
8036
|
+
metadataOutPath = resolve9(args[++index]);
|
|
7494
8037
|
}
|
|
7495
8038
|
}
|
|
7496
8039
|
if (!outPath) {
|
|
@@ -7502,15 +8045,59 @@ async function handleRunExport(args) {
|
|
|
7502
8045
|
const exportResult = await exportPlayStatusRows(client, status, outPath, {
|
|
7503
8046
|
datasetPath
|
|
7504
8047
|
});
|
|
7505
|
-
|
|
8048
|
+
const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
|
|
8049
|
+
const queryPlan = exportResult && outPath ? buildCustomerDbQueryPlan({
|
|
8050
|
+
status,
|
|
8051
|
+
rowsInfo: exportResult.rowsInfo,
|
|
8052
|
+
outPath
|
|
8053
|
+
}) : null;
|
|
8054
|
+
const next = {
|
|
8055
|
+
...buildRunNextCommands(status),
|
|
8056
|
+
export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source),
|
|
8057
|
+
...queryPlan ? {
|
|
8058
|
+
queryJson: queryPlan.json,
|
|
8059
|
+
queryCsv: queryPlan.csv
|
|
8060
|
+
} : {}
|
|
8061
|
+
};
|
|
8062
|
+
const payload = {
|
|
7506
8063
|
runId: status.runId,
|
|
7507
8064
|
...datasetPath ? { dataset: datasetPath } : {},
|
|
7508
8065
|
csv_path: exportResult?.path ?? null,
|
|
8066
|
+
source,
|
|
7509
8067
|
rowCount: exportResult?.rowsInfo.totalRows ?? null,
|
|
7510
8068
|
columns: exportResult?.rowsInfo.columns ?? [],
|
|
8069
|
+
...queryPlan ? {
|
|
8070
|
+
query: {
|
|
8071
|
+
sql: queryPlan.sql,
|
|
8072
|
+
json: queryPlan.json,
|
|
8073
|
+
csv: queryPlan.csv
|
|
8074
|
+
}
|
|
8075
|
+
} : {},
|
|
8076
|
+
...metadataOutPath ? { metadata_path: metadataOutPath } : {},
|
|
7511
8077
|
local: { csv_path: exportResult?.path ?? null },
|
|
7512
|
-
|
|
7513
|
-
|
|
8078
|
+
next,
|
|
8079
|
+
render: {
|
|
8080
|
+
sections: [
|
|
8081
|
+
{
|
|
8082
|
+
title: "run export",
|
|
8083
|
+
lines: [
|
|
8084
|
+
`Exported ${status.runId} to ${exportResult?.path ?? outPath}`,
|
|
8085
|
+
...source ? [`source=${source}`] : [],
|
|
8086
|
+
...queryPlan ? [`query=${queryPlan.json}`] : []
|
|
8087
|
+
]
|
|
8088
|
+
}
|
|
8089
|
+
]
|
|
8090
|
+
}
|
|
8091
|
+
};
|
|
8092
|
+
if (metadataOutPath) {
|
|
8093
|
+
writeFileSync6(
|
|
8094
|
+
metadataOutPath,
|
|
8095
|
+
`${JSON.stringify(payload, null, 2)}
|
|
8096
|
+
`,
|
|
8097
|
+
"utf-8"
|
|
8098
|
+
);
|
|
8099
|
+
}
|
|
8100
|
+
printCommandEnvelope(payload, { json: argsWantJson(args) });
|
|
7514
8101
|
return 0;
|
|
7515
8102
|
}
|
|
7516
8103
|
async function handlePlayGet(args) {
|
|
@@ -7527,10 +8114,10 @@ async function handlePlayGet(args) {
|
|
|
7527
8114
|
for (let index = 1; index < args.length; index += 1) {
|
|
7528
8115
|
const arg = args[index];
|
|
7529
8116
|
if (arg === "--out" && args[index + 1]) {
|
|
7530
|
-
outPath =
|
|
8117
|
+
outPath = resolve9(args[++index]);
|
|
7531
8118
|
}
|
|
7532
8119
|
}
|
|
7533
|
-
const playName = isFileTarget(target) ? extractPlayName(
|
|
8120
|
+
const playName = isFileTarget(target) ? extractPlayName(readFileSync5(resolve9(target), "utf-8"), resolve9(target)) : parseReferencedPlayTarget(target).playName;
|
|
7534
8121
|
const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
|
|
7535
8122
|
const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
|
|
7536
8123
|
const materializedFile = outPath ? materializeRemotePlaySource({
|
|
@@ -7822,7 +8409,7 @@ async function handlePlayPublish(args) {
|
|
|
7822
8409
|
}
|
|
7823
8410
|
let graph;
|
|
7824
8411
|
try {
|
|
7825
|
-
graph = await collectBundledPlayGraph(
|
|
8412
|
+
graph = await collectBundledPlayGraph(resolve9(playName));
|
|
7826
8413
|
await compileBundledPlayGraphManifests(client, graph);
|
|
7827
8414
|
await publishImportedPlayDependencies(client, graph);
|
|
7828
8415
|
} catch (error) {
|
|
@@ -8308,30 +8895,34 @@ Examples:
|
|
|
8308
8895
|
Notes:
|
|
8309
8896
|
Writes a returned dataset handle to the requested local CSV path. Use runs get
|
|
8310
8897
|
first to inspect dataset paths like result.rows or result.nested.contacts.
|
|
8898
|
+
--metadata-out writes the same export metadata object returned by --json,
|
|
8899
|
+
including source and follow-on customer-db query commands when available.
|
|
8311
8900
|
|
|
8312
8901
|
Examples:
|
|
8313
8902
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
8314
8903
|
deepline runs export play/my-play/run/20260501t000000-000 --dataset result.rows --out output.csv
|
|
8904
|
+
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --metadata-out output.meta.json
|
|
8315
8905
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --json
|
|
8316
8906
|
`
|
|
8317
|
-
).requiredOption("--out <path>", "Output CSV path").option("--dataset <path>", "Returned dataset handle path, such as result.rows").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
8907
|
+
).requiredOption("--out <path>", "Output CSV path").option("--dataset <path>", "Returned dataset handle path, such as result.rows").option("--metadata-out <path>", "Write export metadata JSON to a file").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
8318
8908
|
process.exitCode = await handleRunExport([
|
|
8319
8909
|
runId,
|
|
8320
8910
|
...options.dataset ? ["--dataset", options.dataset] : [],
|
|
8321
8911
|
"--out",
|
|
8322
8912
|
options.out,
|
|
8913
|
+
...options.metadataOut ? ["--metadata-out", options.metadataOut] : [],
|
|
8323
8914
|
...options.json ? ["--json"] : []
|
|
8324
8915
|
]);
|
|
8325
8916
|
});
|
|
8326
8917
|
}
|
|
8327
8918
|
|
|
8328
8919
|
// src/cli/commands/tools.ts
|
|
8329
|
-
import { chmodSync, mkdtempSync, writeFileSync as
|
|
8920
|
+
import { chmodSync, mkdtempSync, writeFileSync as writeFileSync8 } from "fs";
|
|
8330
8921
|
import { tmpdir as tmpdir3 } from "os";
|
|
8331
8922
|
import { join as join8 } from "path";
|
|
8332
8923
|
|
|
8333
8924
|
// src/tool-output.ts
|
|
8334
|
-
import { mkdirSync as mkdirSync4, writeFileSync as
|
|
8925
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
8335
8926
|
import { homedir as homedir3 } from "os";
|
|
8336
8927
|
import { join as join7 } from "path";
|
|
8337
8928
|
function isPlainObject(value) {
|
|
@@ -8356,6 +8947,25 @@ function normalizeRows(value) {
|
|
|
8356
8947
|
}
|
|
8357
8948
|
function candidateRoots(payload) {
|
|
8358
8949
|
const roots = [{ path: null, value: payload }];
|
|
8950
|
+
if (isPlainObject(payload) && isPlainObject(payload.toolExecutionResult)) {
|
|
8951
|
+
roots.push({ path: "toolExecutionResult", value: payload.toolExecutionResult });
|
|
8952
|
+
const toolOutput = payload.toolExecutionResult.toolOutput;
|
|
8953
|
+
if (isPlainObject(toolOutput)) {
|
|
8954
|
+
roots.push({ path: "toolExecutionResult.toolOutput", value: toolOutput });
|
|
8955
|
+
if (Object.prototype.hasOwnProperty.call(toolOutput, "raw")) {
|
|
8956
|
+
roots.push({
|
|
8957
|
+
path: "toolExecutionResult.toolOutput.raw",
|
|
8958
|
+
value: toolOutput.raw
|
|
8959
|
+
});
|
|
8960
|
+
}
|
|
8961
|
+
}
|
|
8962
|
+
}
|
|
8963
|
+
if (isPlainObject(payload) && isPlainObject(payload.output)) {
|
|
8964
|
+
roots.push({ path: "output", value: payload.output });
|
|
8965
|
+
if (Object.prototype.hasOwnProperty.call(payload.output, "body")) {
|
|
8966
|
+
roots.push({ path: "output.body", value: payload.output.body });
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8359
8969
|
if (isPlainObject(payload) && isPlainObject(payload.result)) {
|
|
8360
8970
|
roots.push({ path: "result", value: payload.result });
|
|
8361
8971
|
if (isPlainObject(payload.result.data)) {
|
|
@@ -8415,7 +9025,7 @@ function ensureOutputDir() {
|
|
|
8415
9025
|
function writeJsonOutputFile(payload, stem) {
|
|
8416
9026
|
const outputDir = ensureOutputDir();
|
|
8417
9027
|
const outputPath = join7(outputDir, `${stem}_${Date.now()}.json`);
|
|
8418
|
-
|
|
9028
|
+
writeFileSync7(outputPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
8419
9029
|
return outputPath;
|
|
8420
9030
|
}
|
|
8421
9031
|
function writeCsvOutputFile(rows, stem) {
|
|
@@ -8443,7 +9053,7 @@ function writeCsvOutputFile(rows, stem) {
|
|
|
8443
9053
|
for (const row of rows) {
|
|
8444
9054
|
lines.push(columns.map((column) => escapeCell(row[column])).join(","));
|
|
8445
9055
|
}
|
|
8446
|
-
|
|
9056
|
+
writeFileSync7(outputPath, `${lines.join("\n")}
|
|
8447
9057
|
`, "utf-8");
|
|
8448
9058
|
const previewRows = rows.slice(0, 5);
|
|
8449
9059
|
const previewColumns = columns.slice(0, 5);
|
|
@@ -8489,8 +9099,7 @@ async function listTools(args) {
|
|
|
8489
9099
|
title: `${items.length} tools available:`,
|
|
8490
9100
|
lines: items.flatMap((item) => {
|
|
8491
9101
|
const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
|
|
8492
|
-
|
|
8493
|
-
return [`${item.toolId}${cats}`, ` ${item.description}${listHint}`];
|
|
9102
|
+
return [`${item.toolId}${cats}`, ` ${item.description}`];
|
|
8494
9103
|
})
|
|
8495
9104
|
}
|
|
8496
9105
|
]
|
|
@@ -8569,7 +9178,7 @@ Common commands:
|
|
|
8569
9178
|
deepline tools execute hunter_email_verifier --input '{"email":"a@b.com"}'
|
|
8570
9179
|
|
|
8571
9180
|
Output:
|
|
8572
|
-
Use describe for tool contracts.
|
|
9181
|
+
Use describe for tool contracts.
|
|
8573
9182
|
Use execute to run a tool. run is accepted as a compatibility alias.
|
|
8574
9183
|
`
|
|
8575
9184
|
);
|
|
@@ -8616,8 +9225,8 @@ Examples:
|
|
|
8616
9225
|
`
|
|
8617
9226
|
Notes:
|
|
8618
9227
|
Shows the tool contract, input schema, output schema, Deepline cost, aliases,
|
|
8619
|
-
and metadata. describe is the
|
|
8620
|
-
|
|
9228
|
+
and metadata. describe is the supported discovery verb. get is removed in
|
|
9229
|
+
the V2 SDK CLI; use describe for the same metadata surface.
|
|
8621
9230
|
|
|
8622
9231
|
Examples:
|
|
8623
9232
|
deepline tools describe hunter_email_verifier
|
|
@@ -8630,7 +9239,22 @@ Examples:
|
|
|
8630
9239
|
...options.json ? ["--json"] : []
|
|
8631
9240
|
]);
|
|
8632
9241
|
});
|
|
8633
|
-
addToolMetadataCommand(tools.command("describe <toolId>")
|
|
9242
|
+
addToolMetadataCommand(tools.command("describe <toolId>"));
|
|
9243
|
+
tools.command("get <toolId>").description("Deprecated. Use tools describe.").addHelpText(
|
|
9244
|
+
"after",
|
|
9245
|
+
`
|
|
9246
|
+
Examples:
|
|
9247
|
+
deepline tools describe hunter_email_verifier --json
|
|
9248
|
+
`
|
|
9249
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (toolId, options) => {
|
|
9250
|
+
const message = `tools get has been removed from the V2 SDK CLI. Use: deepline tools describe ${toolId} --json`;
|
|
9251
|
+
if (options.json || shouldEmitJson()) {
|
|
9252
|
+
printJsonError({ message, code: "TOOLS_GET_REMOVED" });
|
|
9253
|
+
} else {
|
|
9254
|
+
console.error(message);
|
|
9255
|
+
}
|
|
9256
|
+
process.exitCode = 2;
|
|
9257
|
+
});
|
|
8634
9258
|
tools.command("execute <toolId>").alias("run").description("Execute a tool by id.").addHelpText(
|
|
8635
9259
|
"after",
|
|
8636
9260
|
`
|
|
@@ -8711,6 +9335,7 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
8711
9335
|
const stepContributions = arrayField(tool, "stepContributions", "step_contributions");
|
|
8712
9336
|
const playExpansion = recordField(tool, "playExpansion", "play_expansion");
|
|
8713
9337
|
const samples = recordField(tool, "samples");
|
|
9338
|
+
const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
|
|
8714
9339
|
console.log(`Tool: ${toolId}`);
|
|
8715
9340
|
if (displayName) {
|
|
8716
9341
|
console.log(" Display name:");
|
|
@@ -8774,14 +9399,16 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
8774
9399
|
console.log(" Tip: pass --payload with a JSON object.");
|
|
8775
9400
|
}
|
|
8776
9401
|
printSamples(samples);
|
|
9402
|
+
printUsageGuidance(usageGuidance);
|
|
8777
9403
|
if (isPlayTool(tool)) {
|
|
8778
9404
|
console.log(" Play contract:");
|
|
8779
9405
|
console.log(" - This is a deepline-native waterfall; the returned rows are extracted by target getters, not by hand-authored payload shape.");
|
|
8780
9406
|
if (playExpansion && typeof playExpansion.group === "string" && playExpansion.group.trim()) {
|
|
8781
9407
|
console.log(` - Output alias/runtime group is: ${playExpansion.group.trim()}`);
|
|
8782
9408
|
}
|
|
8783
|
-
const
|
|
8784
|
-
const
|
|
9409
|
+
const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult");
|
|
9410
|
+
const extractedValues = arrayField(toolExecutionResult, "extractedValues", "extracted_values");
|
|
9411
|
+
const targets = extractedValues.map((entry) => isRecord3(entry) && typeof entry.name === "string" ? entry.name : "").filter(Boolean).sort();
|
|
8785
9412
|
if (targets.length) {
|
|
8786
9413
|
console.log(` - Built-in extract targets: ${targets.join(", ")}`);
|
|
8787
9414
|
}
|
|
@@ -8800,7 +9427,53 @@ function printToolDetails(tool, requestedToolId) {
|
|
|
8800
9427
|
} else {
|
|
8801
9428
|
console.log(` deepline tools execute ${toolId} --payload '{...}'`);
|
|
8802
9429
|
}
|
|
8803
|
-
console.log(" deepline tools
|
|
9430
|
+
console.log(" deepline tools describe <tool_id> --json");
|
|
9431
|
+
}
|
|
9432
|
+
function printUsageGuidance(usageGuidance) {
|
|
9433
|
+
if (Object.keys(usageGuidance).length === 0) return;
|
|
9434
|
+
const execute = stringField(usageGuidance, "execute");
|
|
9435
|
+
const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult", "tool_execution_result");
|
|
9436
|
+
const toolOutput = recordField(toolExecutionResult, "toolOutput", "tool_output");
|
|
9437
|
+
const extractedLists = extractionEntries(toolExecutionResult, "extractedLists", "extracted_lists");
|
|
9438
|
+
const extractedValues = extractionEntries(toolExecutionResult, "extractedValues", "extracted_values");
|
|
9439
|
+
console.log(" Usage guidance:");
|
|
9440
|
+
if (execute) console.log(` ${execute}`);
|
|
9441
|
+
const raw = pathField(toolOutput, "raw");
|
|
9442
|
+
const meta = pathField(toolOutput, "meta");
|
|
9443
|
+
if (raw) console.log(` Raw tool output: ${raw}`);
|
|
9444
|
+
if (meta) console.log(` Tool output metadata: ${meta}`);
|
|
9445
|
+
printExtractions("Extracted lists", extractedLists);
|
|
9446
|
+
printExtractions("Extracted values", extractedValues);
|
|
9447
|
+
}
|
|
9448
|
+
function pathField(record, key) {
|
|
9449
|
+
const value = record[key];
|
|
9450
|
+
if (typeof value === "string") return value.trim();
|
|
9451
|
+
if (isRecord3(value)) return stringField(value, "path");
|
|
9452
|
+
return "";
|
|
9453
|
+
}
|
|
9454
|
+
function extractionEntries(record, camelKey, snakeKey) {
|
|
9455
|
+
const value = record[camelKey] ?? record[snakeKey];
|
|
9456
|
+
if (Array.isArray(value)) return value;
|
|
9457
|
+
if (!isRecord3(value)) return [];
|
|
9458
|
+
return Object.entries(value).map(
|
|
9459
|
+
([name, entry]) => isRecord3(entry) ? { name, ...entry } : { name }
|
|
9460
|
+
);
|
|
9461
|
+
}
|
|
9462
|
+
function printExtractions(label, entries) {
|
|
9463
|
+
if (!entries.length) return;
|
|
9464
|
+
console.log(` ${label}:`);
|
|
9465
|
+
for (const entry of entries) {
|
|
9466
|
+
if (!isRecord3(entry)) continue;
|
|
9467
|
+
const name = stringField(entry, "name");
|
|
9468
|
+
const expression = stringField(entry, "expression");
|
|
9469
|
+
const details = recordField(entry, "details");
|
|
9470
|
+
const rawToolOutputPaths = arrayField(details, "rawToolOutputPaths", "raw_tool_output_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
|
|
9471
|
+
const candidatePaths = arrayField(details, "candidatePaths", "candidate_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
|
|
9472
|
+
if (!name || !expression) continue;
|
|
9473
|
+
const paths = candidatePaths.length ? candidatePaths : rawToolOutputPaths;
|
|
9474
|
+
const pathSuffix = paths.length ? ` from ${paths.join(", ")}` : "";
|
|
9475
|
+
console.log(` - ${name}: ${expression}${pathSuffix}`);
|
|
9476
|
+
}
|
|
8804
9477
|
}
|
|
8805
9478
|
function printToolCost(input) {
|
|
8806
9479
|
const { cost, billingSource, deeplineCredits, deeplineUsdPerPricingUnit } = input;
|
|
@@ -8868,6 +9541,17 @@ function samplePayload(samples, key) {
|
|
|
8868
9541
|
function commandEnvelopeFromRawResponse(rawResponse) {
|
|
8869
9542
|
return isRecord3(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
|
|
8870
9543
|
}
|
|
9544
|
+
function listExtractorPathsFromUsageGuidance(tool) {
|
|
9545
|
+
const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
|
|
9546
|
+
const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord3(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
|
|
9547
|
+
return extractedLists.flatMap((entry) => {
|
|
9548
|
+
const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
|
|
9549
|
+
if (!Array.isArray(paths)) return [];
|
|
9550
|
+
return paths.map(
|
|
9551
|
+
(path) => path.trim().replace(/^toolExecutionResult\.toolOutput\.raw\.?/, "").replace(/^\./, "")
|
|
9552
|
+
).filter(Boolean);
|
|
9553
|
+
});
|
|
9554
|
+
}
|
|
8871
9555
|
function isPlayTool(tool) {
|
|
8872
9556
|
const provider = typeof tool.provider === "string" ? tool.provider : "";
|
|
8873
9557
|
return provider === "deepline_native" && Boolean(recordField(tool, "playExpansion", "play_expansion"));
|
|
@@ -8941,6 +9625,7 @@ function parseExecuteOptions(args) {
|
|
|
8941
9625
|
const params = {};
|
|
8942
9626
|
let outputFormat = "auto";
|
|
8943
9627
|
let noPreview = false;
|
|
9628
|
+
let fullOutput = false;
|
|
8944
9629
|
for (let index = 1; index < args.length; index += 1) {
|
|
8945
9630
|
const arg = args[index];
|
|
8946
9631
|
if ((arg === "--param" || arg === "-p") && args[index + 1]) {
|
|
@@ -8967,6 +9652,7 @@ function parseExecuteOptions(args) {
|
|
|
8967
9652
|
}
|
|
8968
9653
|
if (arg === "--full-output") {
|
|
8969
9654
|
outputFormat = "json";
|
|
9655
|
+
fullOutput = true;
|
|
8970
9656
|
continue;
|
|
8971
9657
|
}
|
|
8972
9658
|
if (arg === "--no-preview") {
|
|
@@ -8975,7 +9661,7 @@ function parseExecuteOptions(args) {
|
|
|
8975
9661
|
}
|
|
8976
9662
|
throw new Error(`Unknown option: ${arg}`);
|
|
8977
9663
|
}
|
|
8978
|
-
return { toolId, params, outputFormat, noPreview };
|
|
9664
|
+
return { toolId, params, outputFormat, noPreview, fullOutput };
|
|
8979
9665
|
}
|
|
8980
9666
|
function safeFileStem(value) {
|
|
8981
9667
|
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
@@ -9007,7 +9693,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
9007
9693
|
description: ${JSON.stringify(`Seed ${input.toolId} rows for workflow expansion.`)},
|
|
9008
9694
|
});
|
|
9009
9695
|
|
|
9010
|
-
const list = Object.values(result.
|
|
9696
|
+
const list = Object.values(result.extractedLists)[0];
|
|
9011
9697
|
const rows = (list?.get() ?? []).slice(0, 100);
|
|
9012
9698
|
// ${sampleRows}
|
|
9013
9699
|
// columns: ${columns}
|
|
@@ -9024,7 +9710,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
9024
9710
|
};
|
|
9025
9711
|
});
|
|
9026
9712
|
`;
|
|
9027
|
-
|
|
9713
|
+
writeFileSync8(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
9028
9714
|
return {
|
|
9029
9715
|
path: scriptPath,
|
|
9030
9716
|
projectDir,
|
|
@@ -9035,7 +9721,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
9035
9721
|
function buildToolExecuteBaseEnvelope(input) {
|
|
9036
9722
|
const envelope = commandEnvelopeFromRawResponse(input.rawResponse);
|
|
9037
9723
|
const summaryEntries = Object.entries(input.summary);
|
|
9038
|
-
const
|
|
9724
|
+
const outputPreview = input.listConversion ? {
|
|
9039
9725
|
kind: "list",
|
|
9040
9726
|
rowCount: input.listConversion.rows.length,
|
|
9041
9727
|
columns: Object.keys(input.listConversion.rows[0] ?? {}),
|
|
@@ -9046,6 +9732,7 @@ function buildToolExecuteBaseEnvelope(input) {
|
|
|
9046
9732
|
kind: summaryEntries.length > 0 ? "object" : "raw",
|
|
9047
9733
|
summary: input.summary
|
|
9048
9734
|
};
|
|
9735
|
+
const envelopeHasCanonicalOutput = isRecord3(envelope.toolExecutionResult) && isRecord3(envelope.toolExecutionResult.toolOutput) && Object.prototype.hasOwnProperty.call(envelope.toolExecutionResult.toolOutput, "raw");
|
|
9049
9736
|
const actions = input.listConversion ? [
|
|
9050
9737
|
{
|
|
9051
9738
|
label: "next",
|
|
@@ -9054,7 +9741,7 @@ function buildToolExecuteBaseEnvelope(input) {
|
|
|
9054
9741
|
] : [];
|
|
9055
9742
|
return {
|
|
9056
9743
|
...envelope,
|
|
9057
|
-
output,
|
|
9744
|
+
...envelopeHasCanonicalOutput ? { output_preview: outputPreview } : { output: outputPreview },
|
|
9058
9745
|
...summaryEntries.length > 0 ? { summary: input.summary } : {},
|
|
9059
9746
|
next: input.listConversion ? {
|
|
9060
9747
|
expandToPlay: "Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
|
|
@@ -9107,8 +9794,12 @@ async function executeTool(args) {
|
|
|
9107
9794
|
throw error;
|
|
9108
9795
|
}
|
|
9109
9796
|
const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
|
|
9797
|
+
if (parsed.fullOutput) {
|
|
9798
|
+
printJson(rawResponse);
|
|
9799
|
+
return 0;
|
|
9800
|
+
}
|
|
9110
9801
|
const listConversion = tryConvertToList(rawResponse, {
|
|
9111
|
-
listExtractorPaths: metadata
|
|
9802
|
+
listExtractorPaths: listExtractorPathsFromUsageGuidance(metadata)
|
|
9112
9803
|
});
|
|
9113
9804
|
const summary = extractSummaryFields(rawResponse);
|
|
9114
9805
|
const baseEnvelope = buildToolExecuteBaseEnvelope({
|
|
@@ -9240,7 +9931,7 @@ async function executeTool(args) {
|
|
|
9240
9931
|
// src/cli/commands/update.ts
|
|
9241
9932
|
import { spawn } from "child_process";
|
|
9242
9933
|
import { existsSync as existsSync7 } from "fs";
|
|
9243
|
-
import { dirname as dirname9, join as join9, resolve as
|
|
9934
|
+
import { dirname as dirname9, join as join9, resolve as resolve10 } from "path";
|
|
9244
9935
|
function posixShellQuote(value) {
|
|
9245
9936
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
9246
9937
|
}
|
|
@@ -9259,7 +9950,7 @@ function buildSourceUpdateCommand(sourceRoot) {
|
|
|
9259
9950
|
return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
|
|
9260
9951
|
}
|
|
9261
9952
|
function findRepoBackedSdkRoot(startPath) {
|
|
9262
|
-
let current =
|
|
9953
|
+
let current = resolve10(startPath);
|
|
9263
9954
|
while (true) {
|
|
9264
9955
|
if (existsSync7(join9(current, "sdk", "package.json")) && existsSync7(join9(current, "sdk", "bin", "deepline-dev.ts"))) {
|
|
9265
9956
|
return current;
|
|
@@ -9270,7 +9961,7 @@ function findRepoBackedSdkRoot(startPath) {
|
|
|
9270
9961
|
}
|
|
9271
9962
|
}
|
|
9272
9963
|
function resolveUpdatePlan() {
|
|
9273
|
-
const entrypoint = process.argv[1] ?
|
|
9964
|
+
const entrypoint = process.argv[1] ? resolve10(process.argv[1]) : "";
|
|
9274
9965
|
const sourceRoot = entrypoint ? findRepoBackedSdkRoot(dirname9(entrypoint)) : null;
|
|
9275
9966
|
if (sourceRoot) {
|
|
9276
9967
|
return {
|
|
@@ -9363,8 +10054,8 @@ Examples:
|
|
|
9363
10054
|
}
|
|
9364
10055
|
|
|
9365
10056
|
// src/cli/skills-sync.ts
|
|
9366
|
-
import { spawn as spawn2, spawnSync
|
|
9367
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as
|
|
10057
|
+
import { spawn as spawn2, spawnSync } from "child_process";
|
|
10058
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync9 } from "fs";
|
|
9368
10059
|
import { homedir as homedir4 } from "os";
|
|
9369
10060
|
import { dirname as dirname10, join as join10 } from "path";
|
|
9370
10061
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
@@ -9383,7 +10074,7 @@ function readLocalSkillsVersion(baseUrl) {
|
|
|
9383
10074
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
9384
10075
|
if (!existsSync8(path)) return "";
|
|
9385
10076
|
try {
|
|
9386
|
-
return
|
|
10077
|
+
return readFileSync6(path, "utf-8").trim();
|
|
9387
10078
|
} catch {
|
|
9388
10079
|
return "";
|
|
9389
10080
|
}
|
|
@@ -9391,9 +10082,45 @@ function readLocalSkillsVersion(baseUrl) {
|
|
|
9391
10082
|
function writeLocalSkillsVersion(baseUrl, version) {
|
|
9392
10083
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
9393
10084
|
mkdirSync5(dirname10(path), { recursive: true });
|
|
9394
|
-
|
|
10085
|
+
writeFileSync9(path, `${version}
|
|
9395
10086
|
`, "utf-8");
|
|
9396
10087
|
}
|
|
10088
|
+
function installedSdkSkillHasStalePositionalExecuteExamples() {
|
|
10089
|
+
const home = process.env.HOME?.trim() || homedir4();
|
|
10090
|
+
const roots = [
|
|
10091
|
+
join10(home, ".claude", "skills", SDK_SKILL_NAME),
|
|
10092
|
+
join10(home, ".agents", "skills", SDK_SKILL_NAME)
|
|
10093
|
+
];
|
|
10094
|
+
const staleMarkers = [
|
|
10095
|
+
"ctx.tools.execute(key",
|
|
10096
|
+
"ctx.tools.execute('",
|
|
10097
|
+
'ctx.tools.execute("',
|
|
10098
|
+
"rowCtx.tools.execute('",
|
|
10099
|
+
'rowCtx.tools.execute("'
|
|
10100
|
+
];
|
|
10101
|
+
const scan = (dir) => {
|
|
10102
|
+
for (const entry of readdirSync2(dir)) {
|
|
10103
|
+
const path = join10(dir, entry);
|
|
10104
|
+
const stat3 = statSync(path);
|
|
10105
|
+
if (stat3.isDirectory()) {
|
|
10106
|
+
if (scan(path)) return true;
|
|
10107
|
+
continue;
|
|
10108
|
+
}
|
|
10109
|
+
if (!entry.endsWith(".md")) continue;
|
|
10110
|
+
const text = readFileSync6(path, "utf-8");
|
|
10111
|
+
if (staleMarkers.some((marker) => text.includes(marker))) return true;
|
|
10112
|
+
}
|
|
10113
|
+
return false;
|
|
10114
|
+
};
|
|
10115
|
+
for (const root of roots) {
|
|
10116
|
+
try {
|
|
10117
|
+
if (existsSync8(root) && scan(root)) return true;
|
|
10118
|
+
} catch {
|
|
10119
|
+
continue;
|
|
10120
|
+
}
|
|
10121
|
+
}
|
|
10122
|
+
return false;
|
|
10123
|
+
}
|
|
9397
10124
|
async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
9398
10125
|
const controller = new AbortController();
|
|
9399
10126
|
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
|
|
@@ -9429,7 +10156,7 @@ function buildSkillsInstallArgs(baseUrl) {
|
|
|
9429
10156
|
"skills",
|
|
9430
10157
|
"add",
|
|
9431
10158
|
packageUrl,
|
|
9432
|
-
"--
|
|
10159
|
+
"--agent",
|
|
9433
10160
|
...SKILL_AGENTS,
|
|
9434
10161
|
"--global",
|
|
9435
10162
|
"--yes",
|
|
@@ -9445,7 +10172,7 @@ function buildBunxSkillsInstallArgs(baseUrl) {
|
|
|
9445
10172
|
"skills",
|
|
9446
10173
|
"add",
|
|
9447
10174
|
packageUrl,
|
|
9448
|
-
"--
|
|
10175
|
+
"--agent",
|
|
9449
10176
|
...SKILL_AGENTS,
|
|
9450
10177
|
"--global",
|
|
9451
10178
|
"--yes",
|
|
@@ -9455,7 +10182,7 @@ function buildBunxSkillsInstallArgs(baseUrl) {
|
|
|
9455
10182
|
];
|
|
9456
10183
|
}
|
|
9457
10184
|
function hasCommand(command) {
|
|
9458
|
-
const result =
|
|
10185
|
+
const result = spawnSync(command, ["--version"], {
|
|
9459
10186
|
stdio: "ignore",
|
|
9460
10187
|
shell: process.platform === "win32"
|
|
9461
10188
|
});
|
|
@@ -9485,7 +10212,7 @@ function resolveSkillsInstallCommands(baseUrl) {
|
|
|
9485
10212
|
return [npxInstall];
|
|
9486
10213
|
}
|
|
9487
10214
|
function runOneSkillsInstall(install) {
|
|
9488
|
-
return new Promise((
|
|
10215
|
+
return new Promise((resolve11) => {
|
|
9489
10216
|
const child = spawn2(install.command, install.args, {
|
|
9490
10217
|
stdio: ["ignore", "ignore", "pipe"],
|
|
9491
10218
|
env: process.env
|
|
@@ -9495,7 +10222,7 @@ function runOneSkillsInstall(install) {
|
|
|
9495
10222
|
stderr += chunk.toString("utf-8");
|
|
9496
10223
|
});
|
|
9497
10224
|
child.on("error", (error) => {
|
|
9498
|
-
|
|
10225
|
+
resolve11({
|
|
9499
10226
|
ok: false,
|
|
9500
10227
|
detail: `failed to start ${install.command}: ${error.message}`,
|
|
9501
10228
|
manualCommand: install.manualCommand
|
|
@@ -9503,11 +10230,11 @@ function runOneSkillsInstall(install) {
|
|
|
9503
10230
|
});
|
|
9504
10231
|
child.on("close", (code) => {
|
|
9505
10232
|
if (code === 0) {
|
|
9506
|
-
|
|
10233
|
+
resolve11({ ok: true, detail: "", manualCommand: install.manualCommand });
|
|
9507
10234
|
return;
|
|
9508
10235
|
}
|
|
9509
10236
|
const detail = stderr.trim();
|
|
9510
|
-
|
|
10237
|
+
resolve11({
|
|
9511
10238
|
ok: false,
|
|
9512
10239
|
detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
|
|
9513
10240
|
manualCommand: install.manualCommand
|
|
@@ -9546,10 +10273,19 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
|
9546
10273
|
attemptedSync = true;
|
|
9547
10274
|
const localVersion = readLocalSkillsVersion(baseUrl);
|
|
9548
10275
|
const update = await fetchSkillsUpdate(baseUrl, localVersion);
|
|
9549
|
-
|
|
10276
|
+
const hasStaleInstalledSkill = installedSdkSkillHasStalePositionalExecuteExamples();
|
|
10277
|
+
if (!update?.needsUpdate && !hasStaleInstalledSkill || !update?.remoteVersion) {
|
|
10278
|
+
return;
|
|
10279
|
+
}
|
|
9550
10280
|
writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-sdk skill...");
|
|
9551
10281
|
const installed = await runSkillsInstall(baseUrl);
|
|
9552
10282
|
if (!installed) return;
|
|
10283
|
+
if (installedSdkSkillHasStalePositionalExecuteExamples()) {
|
|
10284
|
+
process.stderr.write(
|
|
10285
|
+
"SDK skills sync completed, but installed deepline-sdk docs still contain stale positional ctx.tools.execute examples.\n"
|
|
10286
|
+
);
|
|
10287
|
+
return;
|
|
10288
|
+
}
|
|
9553
10289
|
writeLocalSkillsVersion(baseUrl, update.remoteVersion);
|
|
9554
10290
|
writeSdkSkillsStatusLine("SDK skills are up to date.");
|
|
9555
10291
|
}
|