deepline 0.1.109 → 0.1.110
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/dist/cli/index.js +2226 -1271
- package/dist/cli/index.mjs +2303 -1355
- package/dist/index.d.mts +21 -14
- package/dist/index.d.ts +21 -14
- package/dist/index.js +97 -23
- package/dist/index.mjs +97 -23
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +42 -20
- package/dist/repo/apps/play-runner-workers/src/entry.ts +135 -45
- package/dist/repo/apps/play-runner-workers/src/runtime/receipts.ts +18 -27
- package/dist/repo/apps/play-runner-workers/src/workflow-instance-create.ts +44 -0
- package/dist/repo/apps/play-runner-workers/src/workflow-retry.ts +7 -11
- package/dist/repo/sdk/src/client.ts +35 -12
- package/dist/repo/sdk/src/errors.ts +2 -2
- package/dist/repo/sdk/src/http.ts +87 -7
- package/dist/repo/sdk/src/play.ts +1 -1
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +5 -1
- package/dist/repo/sdk/src/release.ts +13 -10
- package/dist/repo/sdk/src/tool-output.ts +2 -2
- package/dist/repo/sdk/src/types.ts +9 -6
- package/dist/repo/shared_libs/play-runtime/fullenrich-batching.ts +229 -0
- package/dist/repo/shared_libs/play-runtime/governor/policy.ts +1 -1
- package/dist/repo/shared_libs/play-runtime/play-runtime-batching-registry.ts +20 -0
- package/dist/repo/shared_libs/play-runtime/run-failure.ts +20 -12
- package/dist/repo/shared_libs/play-runtime/run-ledger.ts +107 -56
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +4 -2
- package/dist/repo/shared_libs/play-runtime/secret-redaction.ts +15 -0
- package/dist/repo/shared_libs/play-runtime/work-receipts.ts +1 -0
- package/dist/repo/shared_libs/plays/bundling/index.ts +69 -11
- package/dist/repo/shared_libs/plays/static-pipeline.ts +1 -3
- package/dist/repo/shared_libs/security/outbound-url-policy.ts +238 -0
- package/dist/repo/shared_libs/security/safe-fetch.ts +118 -0
- package/dist/viewer/viewer.css +617 -0
- package/dist/viewer/viewer.js +1496 -0
- package/package.json +5 -1
package/dist/cli/index.js
CHANGED
|
@@ -23,10 +23,167 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
mod
|
|
24
24
|
));
|
|
25
25
|
|
|
26
|
+
// src/cli/proxy-env.ts
|
|
27
|
+
var import_undici = require("undici");
|
|
28
|
+
function defaultPort(protocol) {
|
|
29
|
+
return protocol === "https:" ? "443" : "80";
|
|
30
|
+
}
|
|
31
|
+
function proxyEnvValue(name) {
|
|
32
|
+
return process.env[name]?.trim() ?? "";
|
|
33
|
+
}
|
|
34
|
+
function firstProxyEnv(...names) {
|
|
35
|
+
for (const name of names) {
|
|
36
|
+
const value = proxyEnvValue(name);
|
|
37
|
+
if (value) {
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
function ipv4ToNumber(value) {
|
|
44
|
+
const parts = value.split(".");
|
|
45
|
+
if (parts.length !== 4) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
let result = 0;
|
|
49
|
+
for (const part of parts) {
|
|
50
|
+
if (!/^\d+$/.test(part)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const parsed = Number(part);
|
|
54
|
+
if (parsed < 0 || parsed > 255) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
result = (result << 8) + parsed;
|
|
58
|
+
}
|
|
59
|
+
return result >>> 0;
|
|
60
|
+
}
|
|
61
|
+
function ipv4MatchesCidr(hostname3, cidr) {
|
|
62
|
+
const [range, bitsRaw] = cidr.split("/");
|
|
63
|
+
const hostValue = ipv4ToNumber(hostname3);
|
|
64
|
+
const rangeValue = range ? ipv4ToNumber(range) : null;
|
|
65
|
+
const bits = Number(bitsRaw);
|
|
66
|
+
if (hostValue === null || rangeValue === null || !Number.isInteger(bits) || bits < 0 || bits > 32) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const mask = bits === 0 ? 0 : 4294967295 << 32 - bits >>> 0;
|
|
70
|
+
return (hostValue & mask) === (rangeValue & mask);
|
|
71
|
+
}
|
|
72
|
+
function splitNoProxyHostPort(entry) {
|
|
73
|
+
const bracketed = entry.match(/^\[([^\]]+)\](?::(\d+))?$/);
|
|
74
|
+
if (bracketed) {
|
|
75
|
+
return { host: bracketed[1].toLowerCase(), port: bracketed[2] ?? null };
|
|
76
|
+
}
|
|
77
|
+
const portMatch = entry.match(/^([^:]+):(\d+)$/);
|
|
78
|
+
if (portMatch) {
|
|
79
|
+
return { host: portMatch[1].toLowerCase(), port: portMatch[2] ?? null };
|
|
80
|
+
}
|
|
81
|
+
return { host: entry.toLowerCase(), port: null };
|
|
82
|
+
}
|
|
83
|
+
function noProxyMatches(url, noProxy) {
|
|
84
|
+
const hostname3 = url.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
85
|
+
const port = url.port || defaultPort(url.protocol);
|
|
86
|
+
for (const rawEntry of noProxy.split(/[,\s]+/)) {
|
|
87
|
+
const entry = rawEntry.trim().toLowerCase();
|
|
88
|
+
if (!entry) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (entry === "*") {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
const { host, port: entryPort } = splitNoProxyHostPort(entry);
|
|
95
|
+
if (entryPort && entryPort !== port) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (host.includes("/") && ipv4MatchesCidr(hostname3, host)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (host.startsWith("*.")) {
|
|
102
|
+
const suffix = host.slice(2);
|
|
103
|
+
if (hostname3.endsWith(`.${suffix}`)) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (host.startsWith(".")) {
|
|
109
|
+
const suffix = host.slice(1);
|
|
110
|
+
if (hostname3 === suffix || hostname3.endsWith(host)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (hostname3 === host || hostname3.endsWith(`.${host}`)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
var EnvProxyDispatcher = class extends import_undici.Dispatcher {
|
|
122
|
+
directAgent = new import_undici.Agent();
|
|
123
|
+
httpProxyAgent;
|
|
124
|
+
httpsProxyAgent;
|
|
125
|
+
noProxy;
|
|
126
|
+
constructor(options) {
|
|
127
|
+
super();
|
|
128
|
+
this.httpProxyAgent = options.httpProxy ? new import_undici.ProxyAgent(options.httpProxy) : null;
|
|
129
|
+
this.httpsProxyAgent = options.httpsProxy ? new import_undici.ProxyAgent(options.httpsProxy) : this.httpProxyAgent;
|
|
130
|
+
this.noProxy = options.noProxy;
|
|
131
|
+
}
|
|
132
|
+
dispatch(options, handler) {
|
|
133
|
+
const origin = new URL(String(options.origin));
|
|
134
|
+
if (this.noProxy && noProxyMatches(origin, this.noProxy)) {
|
|
135
|
+
return this.directAgent.dispatch(options, handler);
|
|
136
|
+
}
|
|
137
|
+
const proxyAgent = origin.protocol === "https:" ? this.httpsProxyAgent : this.httpProxyAgent;
|
|
138
|
+
return (proxyAgent ?? this.directAgent).dispatch(options, handler);
|
|
139
|
+
}
|
|
140
|
+
close(callback) {
|
|
141
|
+
const promise = Promise.all([
|
|
142
|
+
this.directAgent.close(),
|
|
143
|
+
this.httpProxyAgent?.close(),
|
|
144
|
+
this.httpsProxyAgent && this.httpsProxyAgent !== this.httpProxyAgent ? this.httpsProxyAgent.close() : void 0
|
|
145
|
+
]).then(() => void 0);
|
|
146
|
+
if (callback) {
|
|
147
|
+
promise.then(callback, callback);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
return promise;
|
|
151
|
+
}
|
|
152
|
+
destroy(errorOrCallback, callback) {
|
|
153
|
+
const error = typeof errorOrCallback === "function" ? null : errorOrCallback ?? null;
|
|
154
|
+
const done = typeof errorOrCallback === "function" ? errorOrCallback : callback;
|
|
155
|
+
const promise = Promise.all([
|
|
156
|
+
this.directAgent.destroy(error),
|
|
157
|
+
this.httpProxyAgent?.destroy(error),
|
|
158
|
+
this.httpsProxyAgent && this.httpsProxyAgent !== this.httpProxyAgent ? this.httpsProxyAgent.destroy(error) : void 0
|
|
159
|
+
]).then(() => void 0);
|
|
160
|
+
if (done) {
|
|
161
|
+
promise.then(done, done);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
return promise;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
function configureProxyFromEnv() {
|
|
168
|
+
const httpProxy = firstProxyEnv("HTTP_PROXY", "http_proxy");
|
|
169
|
+
const httpsProxy = firstProxyEnv("HTTPS_PROXY", "https_proxy") || httpProxy;
|
|
170
|
+
if (!httpProxy && !httpsProxy) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
(0, import_undici.setGlobalDispatcher)(
|
|
174
|
+
new EnvProxyDispatcher({
|
|
175
|
+
httpProxy,
|
|
176
|
+
httpsProxy,
|
|
177
|
+
noProxy: firstProxyEnv("NO_PROXY", "no_proxy")
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
configureProxyFromEnv();
|
|
182
|
+
|
|
26
183
|
// src/cli/index.ts
|
|
27
184
|
var import_promises7 = require("fs/promises");
|
|
28
|
-
var
|
|
29
|
-
var
|
|
185
|
+
var import_node_path21 = require("path");
|
|
186
|
+
var import_node_os14 = require("os");
|
|
30
187
|
var import_commander3 = require("commander");
|
|
31
188
|
|
|
32
189
|
// src/config.ts
|
|
@@ -246,10 +403,10 @@ var SDK_RELEASE = {
|
|
|
246
403
|
// skill on the sdk sync surface, and the people-search-to-email prebuilt.
|
|
247
404
|
// 0.1.108 ships explicit dataset column/tool recompute policy and removes
|
|
248
405
|
// the SDK enrich generator's one-second stale policy.
|
|
249
|
-
version: "0.1.
|
|
406
|
+
version: "0.1.110",
|
|
250
407
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
251
408
|
supportPolicy: {
|
|
252
|
-
latest: "0.1.
|
|
409
|
+
latest: "0.1.110",
|
|
253
410
|
minimumSupported: "0.1.53",
|
|
254
411
|
deprecatedBelow: "0.1.53",
|
|
255
412
|
commandMinimumSupported: [
|
|
@@ -276,6 +433,7 @@ var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
|
|
|
276
433
|
|
|
277
434
|
// src/http.ts
|
|
278
435
|
var MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
|
|
436
|
+
var COWORK_NETWORK_HINT = "Claude Cowork appears to be running Deepline in a network-restricted sandbox. In Claude Desktop, open Settings > Capabilities, turn on Allow network egress, and set Domain allowlist to All domains for the Cowork session.";
|
|
279
437
|
var HttpClient = class {
|
|
280
438
|
constructor(config) {
|
|
281
439
|
this.config = config;
|
|
@@ -311,6 +469,7 @@ var HttpClient = class {
|
|
|
311
469
|
"User-Agent": `deepline-ts-sdk/${SDK_VERSION}`,
|
|
312
470
|
"X-Deepline-Client-Family": "sdk",
|
|
313
471
|
"X-Deepline-CLI-Family": "sdk",
|
|
472
|
+
"X-Deepline-Agent-Runtime": detectAgentRuntime(),
|
|
314
473
|
"X-Deepline-CLI-Version": SDK_VERSION,
|
|
315
474
|
"X-Deepline-SDK-Version": SDK_VERSION,
|
|
316
475
|
"X-Deepline-API-Contract": SDK_API_CONTRACT,
|
|
@@ -413,12 +572,13 @@ var HttpClient = class {
|
|
|
413
572
|
parsed = body;
|
|
414
573
|
}
|
|
415
574
|
if (!response.ok) {
|
|
575
|
+
const retryableApiError = options?.retryApiErrors === true && isRetryableApiErrorResponse(response.status);
|
|
416
576
|
const htmlError = detectHtmlErrorBody(
|
|
417
577
|
body,
|
|
418
578
|
response.headers.get("content-type")
|
|
419
579
|
);
|
|
420
580
|
if (htmlError) {
|
|
421
|
-
|
|
581
|
+
lastError = new DeeplineError(
|
|
422
582
|
htmlError.message(response.status),
|
|
423
583
|
response.status,
|
|
424
584
|
"API_ERROR",
|
|
@@ -428,12 +588,23 @@ var HttpClient = class {
|
|
|
428
588
|
...htmlError.workerThrewException ? { workerThrewException: true } : {}
|
|
429
589
|
}
|
|
430
590
|
);
|
|
591
|
+
if (retryableApiError && attempt < this.config.maxRetries) {
|
|
592
|
+
retryAfterDelayMs = parseOptionalRetryAfter(response);
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
throw lastError;
|
|
431
596
|
}
|
|
432
597
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
433
598
|
const msg = typeof errorValue === "string" ? errorValue : errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string" ? errorValue.message : typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string" ? parsed.message : `HTTP ${response.status}`;
|
|
434
|
-
|
|
599
|
+
const apiErrorCode = errorValue && typeof errorValue === "object" && typeof errorValue.code === "string" ? errorValue.code : "API_ERROR";
|
|
600
|
+
lastError = new DeeplineError(msg, response.status, apiErrorCode, {
|
|
435
601
|
response: parsed
|
|
436
602
|
});
|
|
603
|
+
if (retryableApiError && attempt < this.config.maxRetries) {
|
|
604
|
+
retryAfterDelayMs = parseOptionalRetryAfter(response);
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
throw lastError;
|
|
437
608
|
}
|
|
438
609
|
return parsed;
|
|
439
610
|
} catch (error) {
|
|
@@ -453,8 +624,8 @@ var HttpClient = class {
|
|
|
453
624
|
if (lastError instanceof DeeplineError) {
|
|
454
625
|
throw lastError;
|
|
455
626
|
}
|
|
456
|
-
const
|
|
457
|
-
throw new DeeplineError(
|
|
627
|
+
const errorMessage4 = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
|
|
628
|
+
throw new DeeplineError(withCoworkNetworkHint(errorMessage4));
|
|
458
629
|
}
|
|
459
630
|
/**
|
|
460
631
|
* Send a GET request.
|
|
@@ -526,7 +697,9 @@ var HttpClient = class {
|
|
|
526
697
|
}
|
|
527
698
|
}
|
|
528
699
|
throw new DeeplineError(
|
|
529
|
-
|
|
700
|
+
withCoworkNetworkHint(
|
|
701
|
+
lastError?.message ? `Unable to stream from ${this.config.baseUrl}. ${lastError.message}` : `Unable to stream from ${this.config.baseUrl}.`
|
|
702
|
+
)
|
|
530
703
|
);
|
|
531
704
|
}
|
|
532
705
|
/**
|
|
@@ -536,11 +709,12 @@ var HttpClient = class {
|
|
|
536
709
|
* @param path - API path
|
|
537
710
|
* @param body - Request body (will be JSON-serialized)
|
|
538
711
|
*/
|
|
539
|
-
async post(path, body, headers) {
|
|
712
|
+
async post(path, body, headers, options) {
|
|
540
713
|
return this.request(path, {
|
|
541
714
|
method: "POST",
|
|
542
715
|
body,
|
|
543
|
-
headers
|
|
716
|
+
headers,
|
|
717
|
+
...options
|
|
544
718
|
});
|
|
545
719
|
}
|
|
546
720
|
async postFormData(path, formData, headers) {
|
|
@@ -581,6 +755,9 @@ function parseResponseBody(body) {
|
|
|
581
755
|
return body;
|
|
582
756
|
}
|
|
583
757
|
}
|
|
758
|
+
function isRetryableApiErrorResponse(status) {
|
|
759
|
+
return status === 408 || status === 425 || status >= 500 && status < 600;
|
|
760
|
+
}
|
|
584
761
|
function detectHtmlErrorBody(body, contentType) {
|
|
585
762
|
const trimmed = body.trim();
|
|
586
763
|
const lower = trimmed.toLowerCase();
|
|
@@ -620,6 +797,9 @@ function apiErrorMessage(parsed, status) {
|
|
|
620
797
|
return `HTTP ${status}`;
|
|
621
798
|
}
|
|
622
799
|
function parseRetryAfter(response) {
|
|
800
|
+
return parseOptionalRetryAfter(response) ?? 5e3;
|
|
801
|
+
}
|
|
802
|
+
function parseOptionalRetryAfter(response) {
|
|
623
803
|
const header = response.headers.get("retry-after");
|
|
624
804
|
if (header) {
|
|
625
805
|
const seconds = Number(header);
|
|
@@ -627,7 +807,7 @@ function parseRetryAfter(response) {
|
|
|
627
807
|
return seconds * 1e3;
|
|
628
808
|
}
|
|
629
809
|
}
|
|
630
|
-
return
|
|
810
|
+
return null;
|
|
631
811
|
}
|
|
632
812
|
function buildCandidateUrls(url) {
|
|
633
813
|
try {
|
|
@@ -684,6 +864,39 @@ function decodeSseFrame(frame) {
|
|
|
684
864
|
function sleep(ms) {
|
|
685
865
|
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
686
866
|
}
|
|
867
|
+
function isTruthyEnv(name) {
|
|
868
|
+
const normalized = process.env[name]?.trim().toLowerCase();
|
|
869
|
+
return ["1", "true", "yes", "on"].includes(normalized ?? "");
|
|
870
|
+
}
|
|
871
|
+
function isCoworkLikeSandbox() {
|
|
872
|
+
const pluginMode = isTruthyEnv("DEEPLINE_PLUGIN_MODE");
|
|
873
|
+
const claudeRemote = isTruthyEnv("CLAUDE_CODE_REMOTE");
|
|
874
|
+
const projectDir = Boolean(process.env.CLAUDE_PROJECT_DIR?.trim());
|
|
875
|
+
const pluginRoot = Boolean(process.env.DEEPLINE_PLUGIN_ROOT?.trim());
|
|
876
|
+
const home = process.env.HOME?.trim() ?? "";
|
|
877
|
+
const sessionHome = home.startsWith("/sessions/");
|
|
878
|
+
return (pluginMode || pluginRoot) && (claudeRemote || projectDir || sessionHome);
|
|
879
|
+
}
|
|
880
|
+
function detectAgentRuntime() {
|
|
881
|
+
if (process.env.CODEX_THREAD_ID?.trim()) return "codex";
|
|
882
|
+
if (isCoworkLikeSandbox()) return "claude_cowork";
|
|
883
|
+
if (process.env.CLAUDECODE?.trim() === "1") return "claude_code";
|
|
884
|
+
if (process.env.CLINE_ACTIVE?.trim().toLowerCase() === "true") return "cline";
|
|
885
|
+
if (process.env.CURSOR_TRACE_ID?.trim() || process.env.CURSOR_AGENT?.trim()) {
|
|
886
|
+
return "cursor";
|
|
887
|
+
}
|
|
888
|
+
if (process.env.WINDSURF?.trim() || process.env.CASCADE?.trim()) {
|
|
889
|
+
return "windsurf";
|
|
890
|
+
}
|
|
891
|
+
return "unknown";
|
|
892
|
+
}
|
|
893
|
+
function withCoworkNetworkHint(message) {
|
|
894
|
+
if (!isCoworkLikeSandbox() || message.includes(COWORK_NETWORK_HINT)) {
|
|
895
|
+
return message;
|
|
896
|
+
}
|
|
897
|
+
return `${message}
|
|
898
|
+
${COWORK_NETWORK_HINT}`;
|
|
899
|
+
}
|
|
687
900
|
|
|
688
901
|
// src/stream-reconnect.ts
|
|
689
902
|
var STREAM_RECONNECT_BASE_DELAY_MS = 500;
|
|
@@ -1960,12 +2173,12 @@ var DeeplineClient = class {
|
|
|
1960
2173
|
* Returns everything from {@link ToolDefinition} plus pricing info, sample
|
|
1961
2174
|
* inputs/outputs, failure modes, and cost estimates.
|
|
1962
2175
|
*
|
|
1963
|
-
* @param toolId - Tool identifier (e.g. `"
|
|
2176
|
+
* @param toolId - Tool identifier (e.g. `"dropleads_search_people"`)
|
|
1964
2177
|
* @returns Full tool metadata
|
|
1965
2178
|
*
|
|
1966
2179
|
* @example
|
|
1967
2180
|
* ```typescript
|
|
1968
|
-
* const meta = await client.getTool('
|
|
2181
|
+
* const meta = await client.getTool('dropleads_search_people');
|
|
1969
2182
|
* console.log(`Cost: ${meta.estimatedCreditsRange} credits`);
|
|
1970
2183
|
* console.log(`Input schema:`, meta.inputSchema);
|
|
1971
2184
|
* ```
|
|
@@ -1997,7 +2210,8 @@ var DeeplineClient = class {
|
|
|
1997
2210
|
return this.http.post(
|
|
1998
2211
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
1999
2212
|
{ payload: input2 },
|
|
2000
|
-
headers
|
|
2213
|
+
headers,
|
|
2214
|
+
{ forbiddenAsApiError: true }
|
|
2001
2215
|
);
|
|
2002
2216
|
}
|
|
2003
2217
|
/**
|
|
@@ -2079,7 +2293,7 @@ var DeeplineClient = class {
|
|
|
2079
2293
|
...request.force ? { force: true } : {},
|
|
2080
2294
|
...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
|
|
2081
2295
|
// Profile selection is the API's job, not the CLI's. The server
|
|
2082
|
-
//
|
|
2296
|
+
// defaults to workers_edge; tests and runtime probes that want a
|
|
2083
2297
|
// different profile pass `request.profile` explicitly.
|
|
2084
2298
|
...request.profile ? { profile: request.profile } : {}
|
|
2085
2299
|
}
|
|
@@ -2144,10 +2358,15 @@ var DeeplineClient = class {
|
|
|
2144
2358
|
sourceFiles: input2.sourceFiles,
|
|
2145
2359
|
artifact: input2.artifact
|
|
2146
2360
|
});
|
|
2147
|
-
return this.http.post(
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2361
|
+
return this.http.post(
|
|
2362
|
+
"/api/v2/plays/artifacts",
|
|
2363
|
+
{
|
|
2364
|
+
...input2,
|
|
2365
|
+
compilerManifest
|
|
2366
|
+
},
|
|
2367
|
+
void 0,
|
|
2368
|
+
{ forbiddenAsApiError: true, retryApiErrors: true }
|
|
2369
|
+
);
|
|
2151
2370
|
}
|
|
2152
2371
|
/**
|
|
2153
2372
|
* Register multiple bundled play artifacts in one request.
|
|
@@ -2157,7 +2376,12 @@ var DeeplineClient = class {
|
|
|
2157
2376
|
*/
|
|
2158
2377
|
async registerPlayArtifacts(artifacts) {
|
|
2159
2378
|
if (artifacts.length === 0) {
|
|
2160
|
-
return this.http.post(
|
|
2379
|
+
return this.http.post(
|
|
2380
|
+
"/api/v2/plays/artifacts",
|
|
2381
|
+
{ artifacts },
|
|
2382
|
+
void 0,
|
|
2383
|
+
{ forbiddenAsApiError: true, retryApiErrors: true }
|
|
2384
|
+
);
|
|
2161
2385
|
}
|
|
2162
2386
|
const compiledArtifacts = await mapWithConcurrency(
|
|
2163
2387
|
artifacts,
|
|
@@ -2175,9 +2399,14 @@ var DeeplineClient = class {
|
|
|
2175
2399
|
const responses = [];
|
|
2176
2400
|
for (const chunk of chunkRegisterPlayArtifacts(compiledArtifacts)) {
|
|
2177
2401
|
responses.push(
|
|
2178
|
-
await this.http.post(
|
|
2179
|
-
artifacts
|
|
2180
|
-
|
|
2402
|
+
await this.http.post(
|
|
2403
|
+
"/api/v2/plays/artifacts",
|
|
2404
|
+
{
|
|
2405
|
+
artifacts: chunk
|
|
2406
|
+
},
|
|
2407
|
+
void 0,
|
|
2408
|
+
{ forbiddenAsApiError: true, retryApiErrors: true }
|
|
2409
|
+
)
|
|
2181
2410
|
);
|
|
2182
2411
|
}
|
|
2183
2412
|
return {
|
|
@@ -2982,7 +3211,9 @@ var DeeplineClient = class {
|
|
|
2982
3211
|
const encodedName = encodeURIComponent(name);
|
|
2983
3212
|
return this.http.post(
|
|
2984
3213
|
`/api/v2/plays/${encodedName}/live`,
|
|
2985
|
-
request
|
|
3214
|
+
request,
|
|
3215
|
+
void 0,
|
|
3216
|
+
{ forbiddenAsApiError: true }
|
|
2986
3217
|
);
|
|
2987
3218
|
}
|
|
2988
3219
|
/**
|
|
@@ -3553,11 +3784,53 @@ function sleep3(ms) {
|
|
|
3553
3784
|
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
3554
3785
|
}
|
|
3555
3786
|
function collectLocalEnvInfo() {
|
|
3556
|
-
|
|
3787
|
+
const info = {
|
|
3557
3788
|
os: `${process.platform} ${process.arch}`,
|
|
3558
3789
|
node_version: process.version,
|
|
3559
|
-
home_dir: (0, import_node_os3.homedir)()
|
|
3790
|
+
home_dir: (0, import_node_os3.homedir)(),
|
|
3791
|
+
agent_runtime: detectAgentRuntime2()
|
|
3560
3792
|
};
|
|
3793
|
+
const env = process.env;
|
|
3794
|
+
const optional = {
|
|
3795
|
+
claude_code_remote: env.CLAUDE_CODE_REMOTE,
|
|
3796
|
+
deepline_plugin_mode: env.DEEPLINE_PLUGIN_MODE
|
|
3797
|
+
};
|
|
3798
|
+
for (const [key, value] of Object.entries(optional)) {
|
|
3799
|
+
if (value?.trim()) info[key] = value.trim();
|
|
3800
|
+
}
|
|
3801
|
+
if (env.CLAUDE_PROJECT_DIR?.trim()) {
|
|
3802
|
+
info.claude_project_dir_present = "true";
|
|
3803
|
+
}
|
|
3804
|
+
if (env.DEEPLINE_PLUGIN_ROOT?.trim()) {
|
|
3805
|
+
info.deepline_plugin_root_present = "true";
|
|
3806
|
+
}
|
|
3807
|
+
if (env.DEEPLINE_PLUGIN_SKILLS_DIR?.trim()) {
|
|
3808
|
+
info.deepline_plugin_skills_dir_present = "true";
|
|
3809
|
+
}
|
|
3810
|
+
if (info.home_dir.startsWith("/sessions/")) {
|
|
3811
|
+
info.home_scope = "sessions";
|
|
3812
|
+
}
|
|
3813
|
+
return info;
|
|
3814
|
+
}
|
|
3815
|
+
function detectAgentRuntime2() {
|
|
3816
|
+
if (process.env.CODEX_THREAD_ID?.trim()) return "codex";
|
|
3817
|
+
const pluginMode = process.env.DEEPLINE_PLUGIN_MODE?.trim().toLowerCase();
|
|
3818
|
+
const claudeRemote = process.env.CLAUDE_CODE_REMOTE?.trim().toLowerCase();
|
|
3819
|
+
const sessionHome = (process.env.HOME?.trim() || (0, import_node_os3.homedir)()).startsWith(
|
|
3820
|
+
"/sessions/"
|
|
3821
|
+
);
|
|
3822
|
+
if (["1", "true", "yes", "on"].includes(pluginMode ?? "") && (["1", "true", "yes", "on"].includes(claudeRemote ?? "") || Boolean(process.env.CLAUDE_PROJECT_DIR?.trim()) || sessionHome)) {
|
|
3823
|
+
return "claude_cowork";
|
|
3824
|
+
}
|
|
3825
|
+
if (process.env.CLAUDECODE?.trim() === "1") return "claude_code";
|
|
3826
|
+
if (process.env.CLINE_ACTIVE?.trim().toLowerCase() === "true") return "cline";
|
|
3827
|
+
if (process.env.CURSOR_TRACE_ID?.trim() || process.env.CURSOR_AGENT?.trim()) {
|
|
3828
|
+
return "cursor";
|
|
3829
|
+
}
|
|
3830
|
+
if (process.env.WINDSURF?.trim() || process.env.CASCADE?.trim()) {
|
|
3831
|
+
return "windsurf";
|
|
3832
|
+
}
|
|
3833
|
+
return "unknown";
|
|
3561
3834
|
}
|
|
3562
3835
|
function readCsvRows(csvPath) {
|
|
3563
3836
|
const raw = (0, import_node_fs3.readFileSync)((0, import_node_path3.resolve)(csvPath), "utf-8");
|
|
@@ -3825,7 +4098,14 @@ function saveEnvValues(values, baseUrl) {
|
|
|
3825
4098
|
}
|
|
3826
4099
|
async function httpJson(method, url, apiKey, body) {
|
|
3827
4100
|
const headers = {
|
|
3828
|
-
"Content-Type": "application/json"
|
|
4101
|
+
"Content-Type": "application/json",
|
|
4102
|
+
"User-Agent": `deepline-ts-sdk/${SDK_VERSION}`,
|
|
4103
|
+
"X-Deepline-Client-Family": "sdk",
|
|
4104
|
+
"X-Deepline-CLI-Family": "sdk",
|
|
4105
|
+
"X-Deepline-Agent-Runtime": detectAgentRuntime2(),
|
|
4106
|
+
"X-Deepline-CLI-Version": SDK_VERSION,
|
|
4107
|
+
"X-Deepline-SDK-Version": SDK_VERSION,
|
|
4108
|
+
"X-Deepline-API-Contract": SDK_API_CONTRACT
|
|
3829
4109
|
};
|
|
3830
4110
|
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
|
3831
4111
|
let response = null;
|
|
@@ -4683,7 +4963,7 @@ async function handlePlans(options) {
|
|
|
4683
4963
|
);
|
|
4684
4964
|
const activePlan = payload.active_plan ?? {};
|
|
4685
4965
|
const plans = Array.isArray(payload.plans) ? payload.plans : [];
|
|
4686
|
-
const
|
|
4966
|
+
const meters = Array.isArray(payload.meters) ? payload.meters : Array.isArray(payload.metrics) ? payload.metrics : [];
|
|
4687
4967
|
const lines = [
|
|
4688
4968
|
`Catalog: ${payload.catalog_id ?? "(unknown)"} (version ${payload.catalog_version ?? "(unknown)"})`,
|
|
4689
4969
|
`Active plan: ${activePlan.public_name ?? "(unknown)"} (${activePlan.plan_version_id ?? "(unknown)"})`,
|
|
@@ -4694,10 +4974,10 @@ async function handlePlans(options) {
|
|
|
4694
4974
|
(plan) => `${plan.public_name ?? "(unknown)"} (${plan.plan_version_id ?? "(unknown)"}) | ${planPriceText(plan.price_usd, plan.price_interval)} | ${plan.monthly_grant_credits ?? 0} credits/cycle | ${planRolloverText(plan.rollover)} | ${plan.acquirable ? "acquirable" : "not acquirable"}`
|
|
4695
4975
|
)
|
|
4696
4976
|
],
|
|
4697
|
-
...
|
|
4977
|
+
...meters.length === 0 ? ["Metrics: none"] : [
|
|
4698
4978
|
"Metrics:",
|
|
4699
|
-
...
|
|
4700
|
-
(
|
|
4979
|
+
...meters.map(
|
|
4980
|
+
(meter) => `${meter.id ?? "(unknown)"} | ${meter.name ?? "(unknown)"}`
|
|
4701
4981
|
)
|
|
4702
4982
|
]
|
|
4703
4983
|
];
|
|
@@ -7133,17 +7413,58 @@ async function analyzeSourceGraph(entryFile, adapter) {
|
|
|
7133
7413
|
}
|
|
7134
7414
|
async function computeWorkersHarnessFingerprintWithAdapter(adapter) {
|
|
7135
7415
|
const { readdir } = await import("fs/promises");
|
|
7416
|
+
const addFilePart = async (parts2, rootDir, filePath) => {
|
|
7417
|
+
const contents = await (0, import_promises3.readFile)(filePath, "utf-8");
|
|
7418
|
+
parts2.push({
|
|
7419
|
+
name: `${(0, import_node_path8.basename)(rootDir)}:${filePath.slice(rootDir.length + 1)}`,
|
|
7420
|
+
hash: sha256(contents)
|
|
7421
|
+
});
|
|
7422
|
+
};
|
|
7423
|
+
const collectTopLevelTsFiles = async (rootDir, parts2) => {
|
|
7424
|
+
if (!await fileExists(rootDir)) return;
|
|
7425
|
+
const entries2 = await readdir(rootDir, { withFileTypes: true });
|
|
7426
|
+
const tsFiles2 = entries2.filter((entry) => entry.isFile() && /\.[cm]?ts$/.test(entry.name)).map((entry) => entry.name).sort();
|
|
7427
|
+
for (const name of tsFiles2) {
|
|
7428
|
+
await addFilePart(parts2, rootDir, (0, import_node_path8.join)(rootDir, name));
|
|
7429
|
+
}
|
|
7430
|
+
};
|
|
7431
|
+
const collectIntegrationBatchingFiles = async (rootDir, parts2) => {
|
|
7432
|
+
if (!await fileExists(rootDir)) return;
|
|
7433
|
+
const entries2 = await readdir(rootDir, { withFileTypes: true });
|
|
7434
|
+
const filePaths = [];
|
|
7435
|
+
for (const entry of entries2) {
|
|
7436
|
+
if (entry.isFile() && (entry.name === "play-runtime-batching-registry.ts" || /^batching.*\.ts$/.test(entry.name))) {
|
|
7437
|
+
filePaths.push((0, import_node_path8.join)(rootDir, entry.name));
|
|
7438
|
+
}
|
|
7439
|
+
if (entry.isDirectory()) {
|
|
7440
|
+
const batchingFile = (0, import_node_path8.join)(rootDir, entry.name, "batching.ts");
|
|
7441
|
+
if (await fileExists(batchingFile)) {
|
|
7442
|
+
filePaths.push(batchingFile);
|
|
7443
|
+
}
|
|
7444
|
+
}
|
|
7445
|
+
}
|
|
7446
|
+
for (const filePath of filePaths.sort()) {
|
|
7447
|
+
await addFilePart(parts2, rootDir, filePath);
|
|
7448
|
+
}
|
|
7449
|
+
};
|
|
7136
7450
|
const entries = await readdir(adapter.workersHarnessFilesDir, {
|
|
7137
7451
|
withFileTypes: true
|
|
7138
7452
|
});
|
|
7139
7453
|
const tsFiles = entries.filter((e) => e.isFile() && /\.[cm]?ts$/.test(e.name)).map((e) => e.name).sort();
|
|
7140
7454
|
const parts = [];
|
|
7141
7455
|
for (const name of tsFiles) {
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7456
|
+
await addFilePart(
|
|
7457
|
+
parts,
|
|
7458
|
+
adapter.workersHarnessFilesDir,
|
|
7459
|
+
(0, import_node_path8.join)(adapter.workersHarnessFilesDir, name)
|
|
7145
7460
|
);
|
|
7146
|
-
|
|
7461
|
+
}
|
|
7462
|
+
for (const dir of adapter.workersRuntimeFingerprintDirs ?? []) {
|
|
7463
|
+
if ((0, import_node_path8.basename)(dir) === "integrations") {
|
|
7464
|
+
await collectIntegrationBatchingFiles(dir, parts);
|
|
7465
|
+
} else {
|
|
7466
|
+
await collectTopLevelTsFiles(dir, parts);
|
|
7467
|
+
}
|
|
7147
7468
|
}
|
|
7148
7469
|
return sha256(JSON.stringify(parts));
|
|
7149
7470
|
}
|
|
@@ -8000,6 +8321,9 @@ function createSdkPlayBundlingAdapter() {
|
|
|
8000
8321
|
sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
|
|
8001
8322
|
workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
|
|
8002
8323
|
workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
|
|
8324
|
+
workersRuntimeFingerprintDirs: [
|
|
8325
|
+
(0, import_node_path10.resolve)(PROJECT_ROOT, "shared_libs", "play-runtime")
|
|
8326
|
+
],
|
|
8003
8327
|
discoverPackagedLocalFiles,
|
|
8004
8328
|
warnAboutNonDevelopmentBundling
|
|
8005
8329
|
};
|
|
@@ -8010,7 +8334,8 @@ async function bundlePlayFile2(filePath, options = {}) {
|
|
|
8010
8334
|
exportName: options.exportName,
|
|
8011
8335
|
adapter: createSdkPlayBundlingAdapter()
|
|
8012
8336
|
});
|
|
8013
|
-
if (result.success)
|
|
8337
|
+
if (result.success)
|
|
8338
|
+
validatePlaySourceFilesHaveNoInlineSecrets(result.sourceFiles);
|
|
8014
8339
|
return result;
|
|
8015
8340
|
}
|
|
8016
8341
|
|
|
@@ -8192,17 +8517,17 @@ function templateExample(template) {
|
|
|
8192
8517
|
case "people-list":
|
|
8193
8518
|
return "deepline plays bootstrap people-list --from provider:dropleads_search_people --out people.play.ts";
|
|
8194
8519
|
case "company-list":
|
|
8195
|
-
return "deepline plays bootstrap company-list --from provider:
|
|
8520
|
+
return "deepline plays bootstrap company-list --from provider:crustdata_companydb_search --out companies.play.ts";
|
|
8196
8521
|
case "people-email":
|
|
8197
8522
|
return "deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --out email-flow.play.ts";
|
|
8198
8523
|
case "people-phone":
|
|
8199
8524
|
return "deepline plays bootstrap people-phone --from csv:data/vp_contacts.csv --using play:prebuilt/person-to-phone --out phone-flow.play.ts";
|
|
8200
8525
|
case "company-people":
|
|
8201
|
-
return "deepline plays bootstrap company-people --from provider:
|
|
8526
|
+
return "deepline plays bootstrap company-people --from provider:crustdata_companydb_search --using play:prebuilt/company-to-contact --out company-people.play.ts";
|
|
8202
8527
|
case "company-people-email":
|
|
8203
|
-
return "deepline plays bootstrap company-people-email --from provider:
|
|
8528
|
+
return "deepline plays bootstrap company-people-email --from provider:crustdata_companydb_search --people play:prebuilt/company-to-contact --email providers:hunter_email_finder,leadmagic_email_finder --out account-emails.play.ts";
|
|
8204
8529
|
case "company-people-phone":
|
|
8205
|
-
return "deepline plays bootstrap company-people-phone --from provider:
|
|
8530
|
+
return "deepline plays bootstrap company-people-phone --from provider:crustdata_companydb_search --people play:prebuilt/company-to-contact --phone providers:ai_ark_mobile_phone_finder --out account-phones.play.ts";
|
|
8206
8531
|
}
|
|
8207
8532
|
}
|
|
8208
8533
|
var PLAY_BOOTSTRAP_STAGE_NAMES = [
|
|
@@ -9504,8 +9829,8 @@ Examples:
|
|
|
9504
9829
|
deepline plays run email-flow.play.ts --input '{"limit":5}' --watch
|
|
9505
9830
|
|
|
9506
9831
|
deepline plays bootstrap people-email --from provider:dropleads_search_people --using providers:hunter_email_finder,leadmagic_email_finder --limit 5 --out prospecting.play.ts
|
|
9507
|
-
deepline plays bootstrap company-people-email --from provider:
|
|
9508
|
-
deepline plays bootstrap company-list --from provider:
|
|
9832
|
+
deepline plays bootstrap company-people-email --from provider:crustdata_companydb_search --people play:prebuilt/company-to-contact --email play:prebuilt/name-and-domain-to-email-waterfall --limit 5 --out account-contacts.play.ts
|
|
9833
|
+
deepline plays bootstrap company-list --from provider:crustdata_companydb_search --limit 5 --out companies.play.ts
|
|
9509
9834
|
`
|
|
9510
9835
|
).option("--name <name>", "Generated play name").option(
|
|
9511
9836
|
"--from <ref>",
|
|
@@ -9684,11 +10009,11 @@ function createCliProgress(enabled) {
|
|
|
9684
10009
|
|
|
9685
10010
|
// src/cli/trace.ts
|
|
9686
10011
|
var cliTraceStartedAt = Date.now();
|
|
9687
|
-
function
|
|
10012
|
+
function isTruthyEnv2(value) {
|
|
9688
10013
|
return value === "1" || value === "true" || value === "yes";
|
|
9689
10014
|
}
|
|
9690
10015
|
function isCliTraceEnabled() {
|
|
9691
|
-
return
|
|
10016
|
+
return isTruthyEnv2(process.env.DEEPLINE_CLI_TRACE);
|
|
9692
10017
|
}
|
|
9693
10018
|
function recordCliTrace(event) {
|
|
9694
10019
|
if (!isCliTraceEnabled()) {
|
|
@@ -12808,7 +13133,7 @@ function writeStartedPlayRun(input2) {
|
|
|
12808
13133
|
);
|
|
12809
13134
|
}
|
|
12810
13135
|
function parsePlayRunOptions(args) {
|
|
12811
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--profile <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--profile <id>] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--full] [--<input> value]\n --profile defaults to workers_edge; hatchet
|
|
13136
|
+
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--profile <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--profile <id>] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--full] [--<input> value]\n --profile defaults to workers_edge; pass hatchet explicitly for Hatchet runtime comparison.\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.dataset guidance.";
|
|
12812
13137
|
let filePath = null;
|
|
12813
13138
|
let playName = null;
|
|
12814
13139
|
let input2 = null;
|
|
@@ -14147,7 +14472,7 @@ function playRunCommand(play, options) {
|
|
|
14147
14472
|
return `deepline plays run ${target} --input '{...}' --watch`;
|
|
14148
14473
|
}
|
|
14149
14474
|
function summarizePlayListItemForCli(play, options) {
|
|
14150
|
-
const aliases = play.aliases
|
|
14475
|
+
const aliases = play.aliases ?? [];
|
|
14151
14476
|
const csvInput = playSchemaMetadata(play.inputSchema, "csvInput");
|
|
14152
14477
|
const rowOutputSchema = playSchemaMetadata(
|
|
14153
14478
|
play.outputSchema,
|
|
@@ -14602,8 +14927,8 @@ Idempotent execution:
|
|
|
14602
14927
|
.dataset('companies_v1', companies)
|
|
14603
14928
|
.withColumn('cto', (row, ctx) => ctx.tools.execute({
|
|
14604
14929
|
id: 'find_cto',
|
|
14605
|
-
tool: '
|
|
14606
|
-
input: {
|
|
14930
|
+
tool: 'dropleads_search_people',
|
|
14931
|
+
input: { filters: { companyDomains: [row.domain], jobTitles: ['CTO'] }, pagination: { page: 1, limit: 1 } },
|
|
14607
14932
|
}))
|
|
14608
14933
|
.run({ key: 'domain' });
|
|
14609
14934
|
|
|
@@ -14749,14 +15074,12 @@ Examples:
|
|
|
14749
15074
|
]);
|
|
14750
15075
|
});
|
|
14751
15076
|
addPlaySearchCommand(play.command("search <query>"));
|
|
14752
|
-
play.command("grep <query>").description(
|
|
14753
|
-
"Literal grep over play names, aliases, schemas, and descriptions."
|
|
14754
|
-
).addHelpText(
|
|
15077
|
+
play.command("grep <query>").description("Literal grep over play names, schemas, and descriptions.").addHelpText(
|
|
14755
15078
|
"after",
|
|
14756
15079
|
`
|
|
14757
15080
|
Notes:
|
|
14758
15081
|
Literal registry filtering. Terms are matched case-insensitively against play
|
|
14759
|
-
names, references, display names,
|
|
15082
|
+
names, references, display names, ownership, and schemas. Use
|
|
14760
15083
|
--mode phrase for exact phrase matching, --mode any for OR, and the default
|
|
14761
15084
|
--mode all for AND.
|
|
14762
15085
|
|
|
@@ -14777,7 +15100,7 @@ Examples:
|
|
|
14777
15100
|
"after",
|
|
14778
15101
|
`
|
|
14779
15102
|
Notes:
|
|
14780
|
-
Compact contract read for schemas,
|
|
15103
|
+
Compact contract read for schemas, examples, ownership, and the run command.
|
|
14781
15104
|
Use \`get\` when you need full metadata, revisions, source fields, or latest runs.
|
|
14782
15105
|
|
|
14783
15106
|
Examples:
|
|
@@ -16801,7 +17124,8 @@ async function buildPlanArgs(args) {
|
|
|
16801
17124
|
"--name",
|
|
16802
17125
|
"--rows",
|
|
16803
17126
|
"--with-force",
|
|
16804
|
-
"--timeout"
|
|
17127
|
+
"--timeout",
|
|
17128
|
+
"--profile"
|
|
16805
17129
|
]);
|
|
16806
17130
|
const localBooleanOptions = /* @__PURE__ */ new Set([
|
|
16807
17131
|
"--dry-run",
|
|
@@ -17941,6 +18265,9 @@ function registerEnrichCommand(program) {
|
|
|
17941
18265
|
).option("--in-place", "Write enriched output back to the input CSV.").option(
|
|
17942
18266
|
"--timeout <SECONDS>",
|
|
17943
18267
|
"API read timeout for enrich status/API requests."
|
|
18268
|
+
).option(
|
|
18269
|
+
"--profile <id>",
|
|
18270
|
+
"Internal/testing: override the execution profile for the generated play run."
|
|
17944
18271
|
).action(async (options, _command) => {
|
|
17945
18272
|
if (options.inPlace && options.dryRun) {
|
|
17946
18273
|
throw new Error("--in-place is not supported with --dry-run.");
|
|
@@ -18011,6 +18338,9 @@ function registerEnrichCommand(program) {
|
|
|
18011
18338
|
JSON.stringify(runtimeInput),
|
|
18012
18339
|
"--watch"
|
|
18013
18340
|
];
|
|
18341
|
+
if (options.profile) {
|
|
18342
|
+
runArgs.push("--profile", options.profile);
|
|
18343
|
+
}
|
|
18014
18344
|
if (options.noOpen) {
|
|
18015
18345
|
runArgs.push("--no-open");
|
|
18016
18346
|
}
|
|
@@ -18157,308 +18487,987 @@ Examples:
|
|
|
18157
18487
|
).argument("<text>", "Feedback text").option("--command <command>", "Command that reproduced the issue").option("--payload <payload>", "JSON or plain-text payload for the repro").option("--json", "Emit JSON output").action(handleFeedback);
|
|
18158
18488
|
}
|
|
18159
18489
|
|
|
18160
|
-
// src/cli/commands/
|
|
18161
|
-
var
|
|
18162
|
-
|
|
18163
|
-
|
|
18164
|
-
|
|
18165
|
-
|
|
18166
|
-
|
|
18167
|
-
|
|
18168
|
-
|
|
18169
|
-
|
|
18170
|
-
|
|
18171
|
-
var
|
|
18172
|
-
|
|
18173
|
-
|
|
18174
|
-
|
|
18175
|
-
|
|
18176
|
-
|
|
18177
|
-
|
|
18178
|
-
|
|
18179
|
-
|
|
18180
|
-
|
|
18181
|
-
input2.family,
|
|
18182
|
-
input2.subcommand
|
|
18183
|
-
].filter(Boolean).join(" ");
|
|
18184
|
-
const subject = input2.family === "session" ? "Legacy Session UI/playground command" : "Legacy local playground backend command";
|
|
18185
|
-
const note = input2.family === "session" ? "The SDK CLI does not manage the legacy Python Session UI. This command is accepted for backwards compatibility and intentionally does nothing." : "The SDK CLI does not start or stop the legacy Python local playground backend. This command is accepted for backwards compatibility and intentionally does nothing.";
|
|
18490
|
+
// src/cli/commands/sessions.ts
|
|
18491
|
+
var import_node_fs11 = require("fs");
|
|
18492
|
+
var import_node_os8 = require("os");
|
|
18493
|
+
var import_node_path14 = require("path");
|
|
18494
|
+
var import_node_zlib = require("zlib");
|
|
18495
|
+
var import_node_crypto4 = require("crypto");
|
|
18496
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
18497
|
+
var UUID_IN_TEXT_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
|
18498
|
+
var MAX_SESSION_UPLOAD_BYTES = 35e5;
|
|
18499
|
+
var MAX_DIRECT_SESSION_DECODED_BYTES = 50 * 1024 * 1024;
|
|
18500
|
+
var CHUNK_SIZE_BYTES = 25e5;
|
|
18501
|
+
var MAX_EVENT_STRING_CHARS = 8e3;
|
|
18502
|
+
var MAX_EVENT_LIST_ITEMS = 40;
|
|
18503
|
+
var MAX_EVENT_OBJECT_KEYS = 80;
|
|
18504
|
+
var TRUNCATION_MARKER = "...[truncated]";
|
|
18505
|
+
var NOISE_EVENT_TYPES = /* @__PURE__ */ new Set(["progress", "file-history-snapshot"]);
|
|
18506
|
+
function homeDir() {
|
|
18507
|
+
return process.env.HOME?.trim() || (0, import_node_os8.homedir)();
|
|
18508
|
+
}
|
|
18509
|
+
function detectShellContext() {
|
|
18510
|
+
const shellPath = process.env.SHELL?.trim() || process.env.ComSpec?.trim() || process.env.COMSPEC?.trim() || "";
|
|
18186
18511
|
return {
|
|
18187
|
-
|
|
18188
|
-
|
|
18189
|
-
|
|
18190
|
-
|
|
18191
|
-
family: "legacy_python_cli",
|
|
18192
|
-
sdk_behavior: "noop"
|
|
18193
|
-
},
|
|
18194
|
-
render: {
|
|
18195
|
-
sections: [
|
|
18196
|
-
{
|
|
18197
|
-
title: subject,
|
|
18198
|
-
lines: [note]
|
|
18199
|
-
}
|
|
18200
|
-
]
|
|
18201
|
-
}
|
|
18512
|
+
shell: shellPath ? (0, import_node_path14.basename)(shellPath).replace(/\.exe$/i, "") : "unknown",
|
|
18513
|
+
shell_path: shellPath || null,
|
|
18514
|
+
os: (0, import_node_os8.platform)(),
|
|
18515
|
+
cwd: process.cwd()
|
|
18202
18516
|
};
|
|
18203
18517
|
}
|
|
18204
|
-
function
|
|
18205
|
-
|
|
18518
|
+
function claudeProjectsRoot() {
|
|
18519
|
+
return (0, import_node_path14.join)(homeDir(), ".claude", "projects");
|
|
18206
18520
|
}
|
|
18207
|
-
function
|
|
18208
|
-
|
|
18209
|
-
const familyIndex = args.indexOf(family);
|
|
18210
|
-
const nextToken = familyIndex >= 0 ? args[familyIndex + 1] : void 0;
|
|
18211
|
-
return nextToken && !nextToken.startsWith("-") ? nextToken : void 0;
|
|
18521
|
+
function codexSessionsRoot() {
|
|
18522
|
+
return (0, import_node_path14.join)(homeDir(), ".codex", "sessions");
|
|
18212
18523
|
}
|
|
18213
|
-
function
|
|
18214
|
-
|
|
18215
|
-
|
|
18216
|
-
|
|
18524
|
+
function listClaudeSessionFiles() {
|
|
18525
|
+
const root = claudeProjectsRoot();
|
|
18526
|
+
if (!(0, import_node_fs11.existsSync)(root)) return [];
|
|
18527
|
+
const projectDirs = readDirectoryNames(root);
|
|
18528
|
+
const files = [];
|
|
18529
|
+
for (const projectDir of projectDirs) {
|
|
18530
|
+
const fullProjectDir = (0, import_node_path14.join)(root, projectDir);
|
|
18531
|
+
for (const fileName of readDirectoryNames(fullProjectDir)) {
|
|
18532
|
+
if (fileName.endsWith(".jsonl")) {
|
|
18533
|
+
const filePath = (0, import_node_path14.join)(fullProjectDir, fileName);
|
|
18534
|
+
const sessionId = sessionIdFromClaudeFilePath(filePath);
|
|
18535
|
+
const stat4 = statIfReadable(filePath);
|
|
18536
|
+
if (sessionId && stat4) {
|
|
18537
|
+
files.push({
|
|
18538
|
+
agent: "claude",
|
|
18539
|
+
sessionId,
|
|
18540
|
+
filePath,
|
|
18541
|
+
mtimeMs: stat4.mtimeMs
|
|
18542
|
+
});
|
|
18543
|
+
}
|
|
18544
|
+
}
|
|
18545
|
+
}
|
|
18546
|
+
}
|
|
18547
|
+
return files;
|
|
18217
18548
|
}
|
|
18218
|
-
function
|
|
18219
|
-
const
|
|
18220
|
-
|
|
18221
|
-
|
|
18222
|
-
|
|
18223
|
-
|
|
18224
|
-
|
|
18225
|
-
|
|
18226
|
-
|
|
18227
|
-
|
|
18228
|
-
transcript workflows.
|
|
18229
|
-
|
|
18230
|
-
Examples:
|
|
18231
|
-
deepline session start --steps '["Inspect CSV","Run pilot"]'
|
|
18232
|
-
deepline session status --message "Running pilot"
|
|
18233
|
-
deepline session output --csv ./results.csv --label "Results"
|
|
18234
|
-
`
|
|
18235
|
-
).action((args, options) => {
|
|
18236
|
-
void args;
|
|
18237
|
-
printLegacyNoop({
|
|
18238
|
-
family: "session",
|
|
18239
|
-
subcommand: legacySubcommandFromArgv("session"),
|
|
18240
|
-
options
|
|
18241
|
-
});
|
|
18242
|
-
});
|
|
18243
|
-
for (const subcommand of SESSION_SUBCOMMANDS) {
|
|
18244
|
-
addLegacyNoopSubcommand(
|
|
18245
|
-
session,
|
|
18246
|
-
"session",
|
|
18247
|
-
subcommand,
|
|
18248
|
-
`Accept legacy "deepline session ${subcommand}" as an SDK no-op.`
|
|
18249
|
-
);
|
|
18250
|
-
}
|
|
18251
|
-
const backend = program.command("backend").description(
|
|
18252
|
-
"Compatibility no-ops for legacy Python local backend commands."
|
|
18253
|
-
).allowUnknownOption(true).allowExcessArguments(true).option("--json", "Emit JSON output").argument("[args...]").addHelpText(
|
|
18254
|
-
"after",
|
|
18255
|
-
`
|
|
18256
|
-
Notes:
|
|
18257
|
-
The SDK CLI uses the configured Deepline host and V2 play runtime. It does not
|
|
18258
|
-
start, stop, or refresh the legacy Python local playground backend.
|
|
18259
|
-
|
|
18260
|
-
Examples:
|
|
18261
|
-
deepline backend start
|
|
18262
|
-
deepline backend stop --just-backend
|
|
18263
|
-
deepline backend status --json
|
|
18264
|
-
`
|
|
18265
|
-
).action((args, options) => {
|
|
18266
|
-
void args;
|
|
18267
|
-
printLegacyNoop({
|
|
18268
|
-
family: "backend",
|
|
18269
|
-
subcommand: legacySubcommandFromArgv("backend"),
|
|
18270
|
-
options
|
|
18271
|
-
});
|
|
18272
|
-
});
|
|
18273
|
-
for (const subcommand of BACKEND_SUBCOMMANDS) {
|
|
18274
|
-
addLegacyNoopSubcommand(
|
|
18275
|
-
backend,
|
|
18276
|
-
"backend",
|
|
18277
|
-
subcommand,
|
|
18278
|
-
`Accept legacy "deepline backend ${subcommand}" as an SDK no-op.`
|
|
18279
|
-
);
|
|
18549
|
+
function listCodexSessionFiles() {
|
|
18550
|
+
const root = codexSessionsRoot();
|
|
18551
|
+
if (!(0, import_node_fs11.existsSync)(root)) return [];
|
|
18552
|
+
const files = [];
|
|
18553
|
+
for (const filePath of listJsonlFilesRecursive(root, 5)) {
|
|
18554
|
+
const stat4 = statIfReadable(filePath);
|
|
18555
|
+
if (!stat4) continue;
|
|
18556
|
+
const sessionId = sessionIdFromCodexFilePath(filePath) ?? readCodexSessionId(filePath);
|
|
18557
|
+
if (!sessionId) continue;
|
|
18558
|
+
files.push({ agent: "codex", sessionId, filePath, mtimeMs: stat4.mtimeMs });
|
|
18280
18559
|
}
|
|
18560
|
+
return files;
|
|
18281
18561
|
}
|
|
18282
|
-
|
|
18283
|
-
|
|
18284
|
-
|
|
18285
|
-
|
|
18562
|
+
function listSessionFiles(agent) {
|
|
18563
|
+
const files = [];
|
|
18564
|
+
if (agent === "auto" || agent === "claude") {
|
|
18565
|
+
files.push(...listClaudeSessionFiles());
|
|
18566
|
+
}
|
|
18567
|
+
if (agent === "auto" || agent === "codex") {
|
|
18568
|
+
files.push(...listCodexSessionFiles());
|
|
18569
|
+
}
|
|
18570
|
+
return files;
|
|
18286
18571
|
}
|
|
18287
|
-
function
|
|
18288
|
-
|
|
18289
|
-
|
|
18290
|
-
|
|
18291
|
-
return
|
|
18292
|
-
}
|
|
18572
|
+
function readDirectoryNames(dir) {
|
|
18573
|
+
try {
|
|
18574
|
+
return (0, import_node_fs11.readdirSync)(dir);
|
|
18575
|
+
} catch {
|
|
18576
|
+
return [];
|
|
18577
|
+
}
|
|
18293
18578
|
}
|
|
18294
|
-
|
|
18295
|
-
const
|
|
18296
|
-
|
|
18297
|
-
|
|
18298
|
-
|
|
18299
|
-
{
|
|
18300
|
-
|
|
18301
|
-
|
|
18302
|
-
|
|
18303
|
-
|
|
18304
|
-
|
|
18305
|
-
|
|
18306
|
-
|
|
18307
|
-
|
|
18579
|
+
function listJsonlFilesRecursive(root, maxDepth) {
|
|
18580
|
+
const files = [];
|
|
18581
|
+
function visit(dir, depth) {
|
|
18582
|
+
if (depth > maxDepth) return;
|
|
18583
|
+
let entries;
|
|
18584
|
+
try {
|
|
18585
|
+
entries = (0, import_node_fs11.readdirSync)(dir, { withFileTypes: true });
|
|
18586
|
+
} catch {
|
|
18587
|
+
return;
|
|
18588
|
+
}
|
|
18589
|
+
for (const entry of entries) {
|
|
18590
|
+
const fullPath = (0, import_node_path14.join)(dir, entry.name);
|
|
18591
|
+
if (entry.isDirectory()) {
|
|
18592
|
+
visit(fullPath, depth + 1);
|
|
18593
|
+
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
18594
|
+
files.push(fullPath);
|
|
18308
18595
|
}
|
|
18309
|
-
}
|
|
18310
|
-
|
|
18311
|
-
);
|
|
18596
|
+
}
|
|
18597
|
+
}
|
|
18598
|
+
visit(root, 0);
|
|
18599
|
+
return files;
|
|
18312
18600
|
}
|
|
18313
|
-
|
|
18314
|
-
|
|
18315
|
-
|
|
18316
|
-
|
|
18317
|
-
|
|
18318
|
-
|
|
18319
|
-
|
|
18320
|
-
|
|
18321
|
-
|
|
18322
|
-
|
|
18323
|
-
|
|
18324
|
-
|
|
18325
|
-
|
|
18326
|
-
|
|
18327
|
-
|
|
18328
|
-
|
|
18329
|
-
|
|
18330
|
-
|
|
18331
|
-
|
|
18332
|
-
|
|
18601
|
+
function statIfReadable(filePath) {
|
|
18602
|
+
try {
|
|
18603
|
+
return (0, import_node_fs11.statSync)(filePath);
|
|
18604
|
+
} catch {
|
|
18605
|
+
return null;
|
|
18606
|
+
}
|
|
18607
|
+
}
|
|
18608
|
+
function newestSessionFile(agent) {
|
|
18609
|
+
let newest = null;
|
|
18610
|
+
for (const candidate of listSessionFiles(agent)) {
|
|
18611
|
+
if (!newest || candidate.mtimeMs > newest.mtimeMs) {
|
|
18612
|
+
newest = candidate;
|
|
18613
|
+
}
|
|
18614
|
+
}
|
|
18615
|
+
return newest;
|
|
18616
|
+
}
|
|
18617
|
+
function sessionIdFromClaudeFilePath(filePath) {
|
|
18618
|
+
return (0, import_node_path14.basename)(filePath, ".jsonl");
|
|
18619
|
+
}
|
|
18620
|
+
function sessionIdFromCodexFilePath(filePath) {
|
|
18621
|
+
const match = (0, import_node_path14.basename)(filePath, ".jsonl").match(UUID_IN_TEXT_RE);
|
|
18622
|
+
return match?.[0] ?? null;
|
|
18623
|
+
}
|
|
18624
|
+
function readCodexSessionId(filePath) {
|
|
18625
|
+
try {
|
|
18626
|
+
for (const line of normalizedJsonLines((0, import_node_fs11.readFileSync)(filePath)).slice(
|
|
18627
|
+
0,
|
|
18628
|
+
20
|
|
18629
|
+
)) {
|
|
18630
|
+
const parsed = parseJsonLine(line);
|
|
18631
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
18632
|
+
const record = parsed;
|
|
18633
|
+
const payload = record.payload && typeof record.payload === "object" ? record.payload : null;
|
|
18634
|
+
const id = record.type === "session_meta" && typeof payload?.id === "string" ? payload.id : null;
|
|
18635
|
+
if (id && UUID_RE.test(id)) return id;
|
|
18636
|
+
}
|
|
18637
|
+
} catch {
|
|
18638
|
+
return null;
|
|
18639
|
+
}
|
|
18640
|
+
return null;
|
|
18641
|
+
}
|
|
18642
|
+
function sessionSearchDescription(agent) {
|
|
18643
|
+
if (agent === "claude") return "~/.claude/projects/*/<session-id>.jsonl";
|
|
18644
|
+
if (agent === "codex") return "~/.codex/sessions/**/*.jsonl";
|
|
18645
|
+
return "~/.claude/projects/*/<session-id>.jsonl or ~/.codex/sessions/**/*.jsonl";
|
|
18646
|
+
}
|
|
18647
|
+
function parseSessionAgent(value) {
|
|
18648
|
+
const normalized = String(value || "auto").trim().toLowerCase();
|
|
18649
|
+
if (normalized === "auto" || normalized === "claude" || normalized === "codex") {
|
|
18650
|
+
return normalized;
|
|
18651
|
+
}
|
|
18652
|
+
throw new Error("Invalid --agent value. Expected auto, claude, or codex.");
|
|
18653
|
+
}
|
|
18654
|
+
function findSessionFile(sessionId, agent) {
|
|
18655
|
+
if (!UUID_RE.test(sessionId)) {
|
|
18656
|
+
throw new Error(
|
|
18657
|
+
"Invalid session ID format. Expected a UUID such as 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca."
|
|
18333
18658
|
);
|
|
18334
|
-
return;
|
|
18335
18659
|
}
|
|
18336
|
-
|
|
18337
|
-
(
|
|
18338
|
-
|
|
18339
|
-
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18660
|
+
for (const candidate of listSessionFiles(agent)) {
|
|
18661
|
+
if (candidate.sessionId === sessionId) {
|
|
18662
|
+
return candidate;
|
|
18663
|
+
}
|
|
18664
|
+
}
|
|
18665
|
+
return null;
|
|
18666
|
+
}
|
|
18667
|
+
function resolveSessionTargets(input2) {
|
|
18668
|
+
const agent = parseSessionAgent(input2.agent);
|
|
18669
|
+
const targets = [];
|
|
18670
|
+
if (input2.currentSession) {
|
|
18671
|
+
const currentFile = newestSessionFile(agent);
|
|
18672
|
+
if (!currentFile) {
|
|
18673
|
+
throw new Error(
|
|
18674
|
+
`No session files found in ${sessionSearchDescription(agent)}.`
|
|
18346
18675
|
);
|
|
18347
18676
|
}
|
|
18677
|
+
targets.push({
|
|
18678
|
+
sessionId: currentFile.sessionId,
|
|
18679
|
+
label: `${currentFile.agent}-session-${currentFile.sessionId}`,
|
|
18680
|
+
filePath: currentFile.filePath,
|
|
18681
|
+
agent: currentFile.agent
|
|
18682
|
+
});
|
|
18348
18683
|
}
|
|
18349
|
-
|
|
18350
|
-
|
|
18684
|
+
for (const [index, sessionId] of (input2.sessionIds ?? []).entries()) {
|
|
18685
|
+
const candidate = findSessionFile(sessionId, agent);
|
|
18686
|
+
if (!candidate) {
|
|
18687
|
+
throw new Error(
|
|
18688
|
+
`Session file not found in ${sessionSearchDescription(agent)}: ${sessionId}`
|
|
18689
|
+
);
|
|
18690
|
+
}
|
|
18691
|
+
targets.push({
|
|
18692
|
+
sessionId,
|
|
18693
|
+
label: input2.labels?.[index] ?? `${candidate.agent}-session-${sessionId}`,
|
|
18694
|
+
filePath: candidate.filePath,
|
|
18695
|
+
agent: candidate.agent
|
|
18696
|
+
});
|
|
18351
18697
|
}
|
|
18352
|
-
if (
|
|
18353
|
-
|
|
18354
|
-
{
|
|
18355
|
-
ok: true,
|
|
18356
|
-
unchanged: true,
|
|
18357
|
-
organization: target,
|
|
18358
|
-
render: {
|
|
18359
|
-
sections: [
|
|
18360
|
-
{ title: "org switch", lines: [`Already on ${target.name}.`] }
|
|
18361
|
-
]
|
|
18362
|
-
}
|
|
18363
|
-
},
|
|
18364
|
-
{ json: options.json }
|
|
18365
|
-
);
|
|
18366
|
-
return;
|
|
18698
|
+
if (targets.length === 0) {
|
|
18699
|
+
throw new Error("One of --session-id or --current-session is required.");
|
|
18367
18700
|
}
|
|
18368
|
-
|
|
18369
|
-
api_key: config.apiKey,
|
|
18370
|
-
org_id: target.org_id
|
|
18371
|
-
});
|
|
18372
|
-
saveHostEnvValues(config.baseUrl, {
|
|
18373
|
-
DEEPLINE_API_KEY: switched.api_key,
|
|
18374
|
-
DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
|
|
18375
|
-
DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
|
|
18376
|
-
});
|
|
18377
|
-
const { api_key: _apiKey, ...publicSwitched } = switched;
|
|
18378
|
-
printCommandEnvelope(
|
|
18379
|
-
{
|
|
18380
|
-
ok: true,
|
|
18381
|
-
host_env_path: hostEnvFilePath(config.baseUrl),
|
|
18382
|
-
...publicSwitched,
|
|
18383
|
-
api_key_saved: true,
|
|
18384
|
-
render: {
|
|
18385
|
-
sections: [
|
|
18386
|
-
{
|
|
18387
|
-
title: "org switch",
|
|
18388
|
-
lines: [
|
|
18389
|
-
`Switched to ${switched.org_name}.`,
|
|
18390
|
-
`Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
|
|
18391
|
-
]
|
|
18392
|
-
}
|
|
18393
|
-
]
|
|
18394
|
-
}
|
|
18395
|
-
},
|
|
18396
|
-
{ json: options.json }
|
|
18397
|
-
);
|
|
18701
|
+
return targets;
|
|
18398
18702
|
}
|
|
18399
|
-
|
|
18400
|
-
|
|
18401
|
-
|
|
18402
|
-
|
|
18403
|
-
|
|
18404
|
-
|
|
18405
|
-
});
|
|
18406
|
-
saveHostEnvValues(config.baseUrl, {
|
|
18407
|
-
DEEPLINE_API_KEY: created.api_key,
|
|
18408
|
-
DEEPLINE_ACTIVE_ORG_ID: created.org_id,
|
|
18409
|
-
DEEPLINE_ACTIVE_ORG_NAME: created.org_name
|
|
18410
|
-
});
|
|
18411
|
-
const { api_key: _apiKey, ...publicCreated } = created;
|
|
18412
|
-
printCommandEnvelope(
|
|
18413
|
-
{
|
|
18414
|
-
ok: true,
|
|
18415
|
-
...publicCreated,
|
|
18416
|
-
api_key_saved: true,
|
|
18417
|
-
switched: true,
|
|
18418
|
-
host_env_path: hostEnvFilePath(config.baseUrl),
|
|
18419
|
-
render: {
|
|
18420
|
-
sections: [
|
|
18421
|
-
{
|
|
18422
|
-
title: "org create",
|
|
18423
|
-
lines: [
|
|
18424
|
-
`Created organization: ${created.org_name}.`,
|
|
18425
|
-
`Switched to ${created.org_name}.`,
|
|
18426
|
-
`Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
|
|
18427
|
-
]
|
|
18428
|
-
}
|
|
18429
|
-
]
|
|
18430
|
-
}
|
|
18431
|
-
},
|
|
18432
|
-
{ json: options.json }
|
|
18433
|
-
);
|
|
18703
|
+
function parseJsonLine(line) {
|
|
18704
|
+
try {
|
|
18705
|
+
return JSON.parse(line);
|
|
18706
|
+
} catch {
|
|
18707
|
+
return null;
|
|
18708
|
+
}
|
|
18434
18709
|
}
|
|
18435
|
-
function
|
|
18436
|
-
|
|
18437
|
-
|
|
18438
|
-
|
|
18439
|
-
|
|
18440
|
-
|
|
18441
|
-
|
|
18442
|
-
|
|
18443
|
-
|
|
18444
|
-
|
|
18445
|
-
|
|
18446
|
-
|
|
18447
|
-
|
|
18448
|
-
`
|
|
18449
|
-
|
|
18450
|
-
|
|
18451
|
-
|
|
18452
|
-
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18710
|
+
function normalizedJsonLines(raw) {
|
|
18711
|
+
return raw.toString("utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
18712
|
+
}
|
|
18713
|
+
function stripNoiseEvents(raw) {
|
|
18714
|
+
const lines = [];
|
|
18715
|
+
for (const line of normalizedJsonLines(raw)) {
|
|
18716
|
+
const parsed = parseJsonLine(line);
|
|
18717
|
+
if (parsed && typeof parsed === "object" && NOISE_EVENT_TYPES.has(String(parsed.type ?? ""))) {
|
|
18718
|
+
continue;
|
|
18719
|
+
}
|
|
18720
|
+
lines.push(line);
|
|
18721
|
+
}
|
|
18722
|
+
return Buffer.from(lines.length > 0 ? `${lines.join("\n")}
|
|
18723
|
+
` : "", "utf8");
|
|
18724
|
+
}
|
|
18725
|
+
function messageContentKey(value) {
|
|
18726
|
+
const message = value.message;
|
|
18727
|
+
if (!message || typeof message !== "object") return null;
|
|
18728
|
+
const content = message.content;
|
|
18729
|
+
if (typeof content === "string") return content;
|
|
18730
|
+
if (!Array.isArray(content)) return null;
|
|
18731
|
+
return content.map((block) => {
|
|
18732
|
+
if (!block || typeof block !== "object") return String(block);
|
|
18733
|
+
const record = block;
|
|
18734
|
+
const type = String(record.type ?? "");
|
|
18735
|
+
if (type === "tool_use") {
|
|
18736
|
+
return `tool_use:${String(record.name ?? "")}:${String(record.id ?? "")}`;
|
|
18737
|
+
}
|
|
18738
|
+
if (type === "tool_result") {
|
|
18739
|
+
return `tool_result:${String(record.tool_use_id ?? "")}`;
|
|
18740
|
+
}
|
|
18741
|
+
return String(record.text ?? type);
|
|
18742
|
+
}).join("\n");
|
|
18743
|
+
}
|
|
18744
|
+
function dedupConsecutiveEvents(raw) {
|
|
18745
|
+
const rawLines = normalizedJsonLines(raw);
|
|
18746
|
+
const parsedEvents = rawLines.map(parseJsonLine);
|
|
18747
|
+
const output2 = [];
|
|
18748
|
+
let index = 0;
|
|
18749
|
+
while (index < parsedEvents.length) {
|
|
18750
|
+
const event = parsedEvents[index];
|
|
18751
|
+
if (!event || typeof event !== "object") {
|
|
18752
|
+
output2.push(rawLines[index] ?? "");
|
|
18753
|
+
index += 1;
|
|
18754
|
+
continue;
|
|
18755
|
+
}
|
|
18756
|
+
const record = event;
|
|
18757
|
+
const eventType = String(record.type ?? "");
|
|
18758
|
+
const eventKey = messageContentKey(record);
|
|
18759
|
+
if (!["user", "assistant"].includes(eventType) || !eventKey) {
|
|
18760
|
+
output2.push(rawLines[index] ?? "");
|
|
18761
|
+
index += 1;
|
|
18762
|
+
continue;
|
|
18763
|
+
}
|
|
18764
|
+
let runCount = 1;
|
|
18765
|
+
let cursor = index + 1;
|
|
18766
|
+
while (cursor < parsedEvents.length) {
|
|
18767
|
+
const next = parsedEvents[cursor];
|
|
18768
|
+
if (!next || typeof next !== "object") break;
|
|
18769
|
+
const nextRecord = next;
|
|
18770
|
+
if (String(nextRecord.type ?? "") !== eventType || messageContentKey(nextRecord) !== eventKey) {
|
|
18771
|
+
break;
|
|
18772
|
+
}
|
|
18773
|
+
runCount += 1;
|
|
18774
|
+
cursor += 1;
|
|
18775
|
+
}
|
|
18776
|
+
if (runCount > 1) {
|
|
18777
|
+
record._repeat_count = runCount;
|
|
18778
|
+
record._repeat_summary = `${runCount} consecutive identical ${eventType} messages collapsed`;
|
|
18779
|
+
output2.push(JSON.stringify(record));
|
|
18780
|
+
index = cursor;
|
|
18781
|
+
continue;
|
|
18782
|
+
}
|
|
18783
|
+
output2.push(rawLines[index] ?? "");
|
|
18784
|
+
index += 1;
|
|
18785
|
+
}
|
|
18786
|
+
return Buffer.from(output2.length > 0 ? `${output2.join("\n")}
|
|
18787
|
+
` : "", "utf8");
|
|
18788
|
+
}
|
|
18789
|
+
function compactEventValue(value) {
|
|
18790
|
+
if (typeof value === "string") {
|
|
18791
|
+
if (value.length <= MAX_EVENT_STRING_CHARS) return value;
|
|
18792
|
+
return `${value.slice(0, MAX_EVENT_STRING_CHARS - TRUNCATION_MARKER.length)}${TRUNCATION_MARKER}`;
|
|
18793
|
+
}
|
|
18794
|
+
if (Array.isArray(value)) {
|
|
18795
|
+
const compacted = value.slice(0, MAX_EVENT_LIST_ITEMS).map(compactEventValue);
|
|
18796
|
+
if (value.length > MAX_EVENT_LIST_ITEMS) {
|
|
18797
|
+
compacted.push(
|
|
18798
|
+
`${TRUNCATION_MARKER} ${value.length - MAX_EVENT_LIST_ITEMS} more item(s)`
|
|
18799
|
+
);
|
|
18800
|
+
}
|
|
18801
|
+
return compacted;
|
|
18802
|
+
}
|
|
18803
|
+
if (value && typeof value === "object") {
|
|
18804
|
+
const entries = Object.entries(value);
|
|
18805
|
+
const compacted = {};
|
|
18806
|
+
for (const [key, item] of entries.slice(0, MAX_EVENT_OBJECT_KEYS)) {
|
|
18807
|
+
compacted[key] = compactEventValue(item);
|
|
18808
|
+
}
|
|
18809
|
+
if (entries.length > MAX_EVENT_OBJECT_KEYS) {
|
|
18810
|
+
compacted._truncated_keys = entries.length - MAX_EVENT_OBJECT_KEYS;
|
|
18811
|
+
}
|
|
18812
|
+
return compacted;
|
|
18813
|
+
}
|
|
18814
|
+
return value;
|
|
18815
|
+
}
|
|
18816
|
+
function selectiveCompactToolResults(raw) {
|
|
18817
|
+
const lines = [];
|
|
18818
|
+
for (const line of normalizedJsonLines(raw)) {
|
|
18819
|
+
const parsed = parseJsonLine(line);
|
|
18820
|
+
if (!parsed || typeof parsed !== "object") {
|
|
18821
|
+
lines.push(line);
|
|
18822
|
+
continue;
|
|
18823
|
+
}
|
|
18824
|
+
const record = parsed;
|
|
18825
|
+
if (record.type === "user") {
|
|
18826
|
+
const message = record.message;
|
|
18827
|
+
const content = message && typeof message === "object" ? message.content : null;
|
|
18828
|
+
if (Array.isArray(content)) {
|
|
18829
|
+
message.content = content.map(
|
|
18830
|
+
(block) => block && typeof block === "object" && block.type === "tool_result" ? compactEventValue(block) : block
|
|
18831
|
+
);
|
|
18832
|
+
}
|
|
18833
|
+
}
|
|
18834
|
+
lines.push(JSON.stringify(record));
|
|
18835
|
+
}
|
|
18836
|
+
return Buffer.from(lines.length > 0 ? `${lines.join("\n")}
|
|
18837
|
+
` : "", "utf8");
|
|
18838
|
+
}
|
|
18839
|
+
function prepareSessionBuffer(raw) {
|
|
18840
|
+
return selectiveCompactToolResults(
|
|
18841
|
+
dedupConsecutiveEvents(stripNoiseEvents(raw))
|
|
18842
|
+
);
|
|
18843
|
+
}
|
|
18844
|
+
function buildSessionUploadContent(raw) {
|
|
18845
|
+
const prepared = prepareSessionBuffer(raw);
|
|
18846
|
+
const encoded = (0, import_node_zlib.gzipSync)(prepared).toString("base64");
|
|
18847
|
+
if (encoded.length <= MAX_SESSION_UPLOAD_BYTES && prepared.length <= MAX_DIRECT_SESSION_DECODED_BYTES) {
|
|
18848
|
+
return { encodedContent: encoded, needsChunking: false };
|
|
18849
|
+
}
|
|
18850
|
+
return { encodedContent: encoded, needsChunking: true };
|
|
18851
|
+
}
|
|
18852
|
+
async function uploadPayload(path, payload) {
|
|
18853
|
+
const { http } = getAuthedHttpClient();
|
|
18854
|
+
return await http.post(path, payload);
|
|
18855
|
+
}
|
|
18856
|
+
async function uploadChunkedSessions(sessions, options) {
|
|
18857
|
+
const uploadId = (0, import_node_crypto4.randomUUID)();
|
|
18858
|
+
for (const session of sessions) {
|
|
18859
|
+
const bytes = Buffer.from(session.encodedContent, "base64");
|
|
18860
|
+
const chunks = [];
|
|
18861
|
+
for (let offset = 0; offset < bytes.length; offset += CHUNK_SIZE_BYTES) {
|
|
18862
|
+
chunks.push(bytes.subarray(offset, offset + CHUNK_SIZE_BYTES));
|
|
18863
|
+
}
|
|
18864
|
+
process.stderr.write(
|
|
18865
|
+
`Uploading ${session.label} in ${chunks.length} chunk(s)...
|
|
18866
|
+
`
|
|
18867
|
+
);
|
|
18868
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
18869
|
+
await uploadPayload("/api/v2/cli/send-session/chunk", {
|
|
18870
|
+
upload_id: uploadId,
|
|
18871
|
+
session_id: session.sessionId,
|
|
18872
|
+
index,
|
|
18873
|
+
total_chunks: chunks.length,
|
|
18874
|
+
data: chunk.toString("base64")
|
|
18875
|
+
});
|
|
18876
|
+
}
|
|
18877
|
+
}
|
|
18878
|
+
const response = await uploadPayload("/api/v2/cli/send-session/finalize", {
|
|
18879
|
+
upload_id: uploadId,
|
|
18880
|
+
session_ids: sessions.map((session) => session.sessionId),
|
|
18881
|
+
labels: sessions.map((session) => session.label),
|
|
18882
|
+
environments: sessions.map(() => detectShellContext())
|
|
18883
|
+
});
|
|
18884
|
+
printCommandEnvelope(
|
|
18885
|
+
{
|
|
18886
|
+
...response,
|
|
18887
|
+
ok: true,
|
|
18888
|
+
uploaded: sessions.length,
|
|
18889
|
+
render: {
|
|
18890
|
+
sections: [
|
|
18891
|
+
{
|
|
18892
|
+
title: "sessions send",
|
|
18893
|
+
lines: ["Session uploaded to #internal-reports (chunked)."]
|
|
18894
|
+
}
|
|
18895
|
+
]
|
|
18896
|
+
}
|
|
18897
|
+
},
|
|
18898
|
+
{ json: options.json }
|
|
18899
|
+
);
|
|
18900
|
+
}
|
|
18901
|
+
async function handleSessionsSend(options) {
|
|
18902
|
+
if (options.file) {
|
|
18903
|
+
const filePath = (0, import_node_path14.resolve)(options.file);
|
|
18904
|
+
if (!(0, import_node_fs11.existsSync)(filePath)) {
|
|
18905
|
+
throw new Error(`File not found: ${options.file}`);
|
|
18906
|
+
}
|
|
18907
|
+
const response2 = await uploadPayload("/api/v2/cli/send-session", {
|
|
18908
|
+
file: (0, import_node_fs11.readFileSync)(filePath).toString("base64"),
|
|
18909
|
+
filename: (0, import_node_path14.basename)(filePath)
|
|
18910
|
+
});
|
|
18911
|
+
printCommandEnvelope(
|
|
18912
|
+
{
|
|
18913
|
+
...response2,
|
|
18914
|
+
ok: true,
|
|
18915
|
+
filename: (0, import_node_path14.basename)(filePath),
|
|
18916
|
+
render: {
|
|
18917
|
+
sections: [
|
|
18918
|
+
{
|
|
18919
|
+
title: "sessions send",
|
|
18920
|
+
lines: [
|
|
18921
|
+
`File '${(0, import_node_path14.basename)(filePath)}' uploaded to #internal-reports.`
|
|
18922
|
+
]
|
|
18923
|
+
}
|
|
18924
|
+
]
|
|
18925
|
+
}
|
|
18926
|
+
},
|
|
18927
|
+
{ json: options.json }
|
|
18928
|
+
);
|
|
18929
|
+
return;
|
|
18930
|
+
}
|
|
18931
|
+
const targets = resolveSessionTargets({
|
|
18932
|
+
sessionIds: options.sessionId,
|
|
18933
|
+
labels: options.label,
|
|
18934
|
+
currentSession: options.currentSession,
|
|
18935
|
+
agent: options.agent
|
|
18936
|
+
});
|
|
18937
|
+
const built = targets.map((target) => {
|
|
18938
|
+
const upload = buildSessionUploadContent((0, import_node_fs11.readFileSync)(target.filePath));
|
|
18939
|
+
return { ...target, ...upload };
|
|
18940
|
+
});
|
|
18941
|
+
if (built.some((session) => session.needsChunking)) {
|
|
18942
|
+
await uploadChunkedSessions(built, options);
|
|
18943
|
+
return;
|
|
18944
|
+
}
|
|
18945
|
+
const response = built.length === 1 && !options.label?.length ? await uploadPayload("/api/v2/cli/send-session", {
|
|
18946
|
+
session_id: built[0]?.sessionId,
|
|
18947
|
+
content: built[0]?.encodedContent,
|
|
18948
|
+
environment: detectShellContext()
|
|
18949
|
+
}) : await uploadPayload("/api/v2/cli/send-session", {
|
|
18950
|
+
sessions: built.map((session) => ({
|
|
18951
|
+
session_id: session.sessionId,
|
|
18952
|
+
content: session.encodedContent,
|
|
18953
|
+
label: session.label,
|
|
18954
|
+
environment: detectShellContext()
|
|
18955
|
+
})),
|
|
18956
|
+
environment: detectShellContext()
|
|
18957
|
+
});
|
|
18958
|
+
printCommandEnvelope(
|
|
18959
|
+
{
|
|
18960
|
+
...response,
|
|
18961
|
+
ok: true,
|
|
18962
|
+
uploaded: built.length,
|
|
18963
|
+
render: {
|
|
18964
|
+
sections: [
|
|
18965
|
+
{
|
|
18966
|
+
title: "sessions send",
|
|
18967
|
+
lines: ["Session uploaded to #internal-reports."]
|
|
18968
|
+
}
|
|
18969
|
+
]
|
|
18970
|
+
}
|
|
18971
|
+
},
|
|
18972
|
+
{ json: options.json }
|
|
18973
|
+
);
|
|
18974
|
+
}
|
|
18975
|
+
function fallbackViewerAssets() {
|
|
18976
|
+
return {
|
|
18977
|
+
css: [
|
|
18978
|
+
"body{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;margin:0;padding:16px;background:#fafafa;color:#111}",
|
|
18979
|
+
".section{background:#fff;border:1px solid #ddd;border-radius:8px;padding:12px;margin-bottom:12px}",
|
|
18980
|
+
".section h2{margin:0 0 8px 0;font-size:14px}",
|
|
18981
|
+
"pre{margin:0;white-space:pre-wrap;word-break:break-word;background:#f6f8fa;border:1px solid #e3e5e8;border-radius:6px;padding:10px}"
|
|
18982
|
+
].join(""),
|
|
18983
|
+
js: [
|
|
18984
|
+
"(() => {",
|
|
18985
|
+
'const root=document.getElementById("main-content");',
|
|
18986
|
+
'const raw=document.getElementById("raw-sessions");',
|
|
18987
|
+
"if(!root||!raw)return;",
|
|
18988
|
+
'let sessions=[];try{sessions=JSON.parse(raw.textContent||"[]")}catch{}',
|
|
18989
|
+
'root.innerHTML="";',
|
|
18990
|
+
"for(const session of sessions){",
|
|
18991
|
+
'const section=document.createElement("section");section.className="section";',
|
|
18992
|
+
'const title=document.createElement("h2");title.textContent=String(session.label||"session");',
|
|
18993
|
+
'const pre=document.createElement("pre");',
|
|
18994
|
+
'pre.textContent=(Array.isArray(session.events)?session.events:[]).map((event)=>JSON.stringify(event)).join("\\n");',
|
|
18995
|
+
"section.append(title,pre);root.appendChild(section);",
|
|
18996
|
+
"}",
|
|
18997
|
+
"})();"
|
|
18998
|
+
].join("")
|
|
18999
|
+
};
|
|
19000
|
+
}
|
|
19001
|
+
function loadViewerAssets() {
|
|
19002
|
+
const cliEntry = process.argv[1]?.trim() ? (0, import_node_path14.resolve)(process.argv[1]) : null;
|
|
19003
|
+
const candidateRoots2 = [
|
|
19004
|
+
...cliEntry ? [
|
|
19005
|
+
(0, import_node_path14.join)((0, import_node_path14.dirname)((0, import_node_path14.dirname)(cliEntry)), "viewer"),
|
|
19006
|
+
(0, import_node_path14.join)(
|
|
19007
|
+
(0, import_node_path14.dirname)((0, import_node_path14.dirname)((0, import_node_path14.dirname)(cliEntry))),
|
|
19008
|
+
"src",
|
|
19009
|
+
"lib",
|
|
19010
|
+
"cli",
|
|
19011
|
+
"viewer"
|
|
19012
|
+
)
|
|
19013
|
+
] : [],
|
|
19014
|
+
(0, import_node_path14.join)(process.cwd(), "src", "lib", "cli", "viewer")
|
|
19015
|
+
];
|
|
19016
|
+
for (const root of candidateRoots2) {
|
|
19017
|
+
try {
|
|
19018
|
+
const cssPath = (0, import_node_path14.join)(root, "viewer.css");
|
|
19019
|
+
const jsPath = (0, import_node_path14.join)(root, "viewer.js");
|
|
19020
|
+
if (!(0, import_node_fs11.existsSync)(cssPath) || !(0, import_node_fs11.existsSync)(jsPath)) continue;
|
|
19021
|
+
return {
|
|
19022
|
+
css: (0, import_node_fs11.readFileSync)(cssPath, "utf8"),
|
|
19023
|
+
js: (0, import_node_fs11.readFileSync)(jsPath, "utf8")
|
|
19024
|
+
};
|
|
19025
|
+
} catch {
|
|
19026
|
+
continue;
|
|
19027
|
+
}
|
|
19028
|
+
}
|
|
19029
|
+
return fallbackViewerAssets();
|
|
19030
|
+
}
|
|
19031
|
+
function parsePreparedEvents(buffer) {
|
|
19032
|
+
return normalizedJsonLines(buffer).map((line) => {
|
|
19033
|
+
const parsed = parseJsonLine(line);
|
|
19034
|
+
return parsed ?? line;
|
|
19035
|
+
});
|
|
19036
|
+
}
|
|
19037
|
+
function escapeJsonForHtmlScript(json) {
|
|
19038
|
+
return json.replace(/</g, "\\u003c");
|
|
19039
|
+
}
|
|
19040
|
+
async function handleSessionsRender(options) {
|
|
19041
|
+
const targets = resolveSessionTargets({
|
|
19042
|
+
sessionIds: options.sessionId,
|
|
19043
|
+
labels: options.label,
|
|
19044
|
+
currentSession: options.currentSession,
|
|
19045
|
+
agent: options.agent
|
|
19046
|
+
});
|
|
19047
|
+
let outputPath = options.output ? (0, import_node_path14.resolve)(options.output) : "";
|
|
19048
|
+
if (!outputPath) {
|
|
19049
|
+
const outputDir = (0, import_node_path14.join)(process.cwd(), "deepline", "data");
|
|
19050
|
+
(0, import_node_fs11.mkdirSync)(outputDir, { recursive: true });
|
|
19051
|
+
outputPath = (0, import_node_path14.join)(
|
|
19052
|
+
outputDir,
|
|
19053
|
+
targets.length > 1 ? "session-viewer.html" : `session-${targets[0]?.sessionId}.html`
|
|
19054
|
+
);
|
|
19055
|
+
} else {
|
|
19056
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path14.dirname)(outputPath), { recursive: true });
|
|
19057
|
+
}
|
|
19058
|
+
const sessions = targets.map((target) => ({
|
|
19059
|
+
label: target.label,
|
|
19060
|
+
events: parsePreparedEvents(
|
|
19061
|
+
prepareSessionBuffer((0, import_node_fs11.readFileSync)(target.filePath))
|
|
19062
|
+
)
|
|
19063
|
+
}));
|
|
19064
|
+
const { css, js } = loadViewerAssets();
|
|
19065
|
+
const refreshMeta = options.autoRefresh ? `<meta http-equiv="refresh" content="${Number.parseInt(options.autoRefresh, 10)}">` : "";
|
|
19066
|
+
const rawJson = escapeJsonForHtmlScript(JSON.stringify(sessions));
|
|
19067
|
+
const html = `<!DOCTYPE html>
|
|
19068
|
+
<html lang="en">
|
|
19069
|
+
<head>
|
|
19070
|
+
<meta charset="UTF-8">
|
|
19071
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
19072
|
+
${refreshMeta}
|
|
19073
|
+
<title>Session Viewer</title>
|
|
19074
|
+
<style>${css}</style>
|
|
19075
|
+
</head>
|
|
19076
|
+
<body>
|
|
19077
|
+
<div class="layout">
|
|
19078
|
+
<div class="main" id="main-content"></div>
|
|
19079
|
+
</div>
|
|
19080
|
+
<script type="application/json" id="raw-sessions">${rawJson}</script>
|
|
19081
|
+
<script>${js}</script>
|
|
19082
|
+
</body>
|
|
19083
|
+
</html>`;
|
|
19084
|
+
(0, import_node_fs11.writeFileSync)(outputPath, html, "utf8");
|
|
19085
|
+
printCommandEnvelope(
|
|
19086
|
+
{
|
|
19087
|
+
ok: true,
|
|
19088
|
+
file: outputPath,
|
|
19089
|
+
session_count: targets.length,
|
|
19090
|
+
render: {
|
|
19091
|
+
sections: [
|
|
19092
|
+
{
|
|
19093
|
+
title: "sessions render",
|
|
19094
|
+
lines: [`Rendered session viewer: ${outputPath}`]
|
|
19095
|
+
}
|
|
19096
|
+
]
|
|
19097
|
+
}
|
|
19098
|
+
},
|
|
19099
|
+
{ json: options.json }
|
|
19100
|
+
);
|
|
19101
|
+
}
|
|
19102
|
+
function collectOption(value, previous) {
|
|
19103
|
+
previous.push(value);
|
|
19104
|
+
return previous;
|
|
19105
|
+
}
|
|
19106
|
+
function registerSessionsCommands(program) {
|
|
19107
|
+
const sessions = program.command("sessions").description("Upload and render local agent session transcripts.").addHelpText(
|
|
19108
|
+
"after",
|
|
19109
|
+
`
|
|
19110
|
+
Notes:
|
|
19111
|
+
Session commands operate on local Claude and Codex JSONL files under
|
|
19112
|
+
~/.claude/projects and ~/.codex/sessions. send uploads a compacted transcript
|
|
19113
|
+
or file to Deepline. render writes a local HTML viewer.
|
|
19114
|
+
|
|
19115
|
+
Examples:
|
|
19116
|
+
deepline sessions send --current-session --json
|
|
19117
|
+
deepline sessions send --agent codex --current-session --json
|
|
19118
|
+
deepline sessions send --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca
|
|
19119
|
+
deepline sessions render --current-session --output session.html
|
|
19120
|
+
`
|
|
19121
|
+
);
|
|
19122
|
+
registerSessionSendRenderCommands(sessions, "sessions");
|
|
19123
|
+
}
|
|
19124
|
+
function registerSessionSendRenderCommands(parent, familyName) {
|
|
19125
|
+
parent.command("send").description("Upload session transcript(s) or a local file to Deepline.").addHelpText(
|
|
19126
|
+
"after",
|
|
19127
|
+
`
|
|
19128
|
+
Examples:
|
|
19129
|
+
deepline ${familyName} send --current-session --json
|
|
19130
|
+
deepline ${familyName} send --agent codex --current-session --json
|
|
19131
|
+
deepline ${familyName} send --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca --label "pilot run"
|
|
19132
|
+
deepline ${familyName} send --file ./debug.log --json
|
|
19133
|
+
`
|
|
19134
|
+
).option(
|
|
19135
|
+
"--session-id <uuid>",
|
|
19136
|
+
"Claude or Codex session UUID. Repeat for multiple sessions.",
|
|
19137
|
+
collectOption,
|
|
19138
|
+
[]
|
|
19139
|
+
).option(
|
|
19140
|
+
"--label <label>",
|
|
19141
|
+
"Label for the preceding session id",
|
|
19142
|
+
collectOption,
|
|
19143
|
+
[]
|
|
19144
|
+
).option(
|
|
19145
|
+
"--agent <auto|claude|codex>",
|
|
19146
|
+
"Limit transcript discovery to one agent runtime",
|
|
19147
|
+
"auto"
|
|
19148
|
+
).option("--current-session", "Use the newest local agent session JSONL").option("--file <path>", "Upload a raw local file instead of a session").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSessionsSend);
|
|
19149
|
+
parent.command("render").description("Render local session transcript(s) to an HTML viewer.").addHelpText(
|
|
19150
|
+
"after",
|
|
19151
|
+
`
|
|
19152
|
+
Examples:
|
|
19153
|
+
deepline ${familyName} render --current-session
|
|
19154
|
+
deepline ${familyName} render --agent codex --current-session
|
|
19155
|
+
deepline ${familyName} render --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca --output session.html
|
|
19156
|
+
deepline ${familyName} render --current-session --auto-refresh 5 --json
|
|
19157
|
+
`
|
|
19158
|
+
).option(
|
|
19159
|
+
"--session-id <uuid>",
|
|
19160
|
+
"Claude or Codex session UUID. Repeat for multiple sessions.",
|
|
19161
|
+
collectOption,
|
|
19162
|
+
[]
|
|
19163
|
+
).option(
|
|
19164
|
+
"--label <label>",
|
|
19165
|
+
"Label for the preceding session id",
|
|
19166
|
+
collectOption,
|
|
19167
|
+
[]
|
|
19168
|
+
).option(
|
|
19169
|
+
"--agent <auto|claude|codex>",
|
|
19170
|
+
"Limit transcript discovery to one agent runtime",
|
|
19171
|
+
"auto"
|
|
19172
|
+
).option("--current-session", "Use the newest local agent session JSONL").option("--auto-refresh <seconds>", "Add a browser auto-refresh interval").option("-o, --output <path>", "Output HTML path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSessionsRender);
|
|
19173
|
+
}
|
|
19174
|
+
|
|
19175
|
+
// src/cli/commands/legacy-noops.ts
|
|
19176
|
+
var SESSION_SUBCOMMANDS = [
|
|
19177
|
+
"start",
|
|
19178
|
+
"status",
|
|
19179
|
+
"output",
|
|
19180
|
+
"alert",
|
|
19181
|
+
"usage",
|
|
19182
|
+
"limit"
|
|
19183
|
+
];
|
|
19184
|
+
var BACKEND_SUBCOMMANDS = [
|
|
19185
|
+
"start",
|
|
19186
|
+
"stop",
|
|
19187
|
+
"status",
|
|
19188
|
+
"refresh-runtime",
|
|
19189
|
+
"sync-runtime"
|
|
19190
|
+
];
|
|
19191
|
+
function legacyNoopEnvelope(input2) {
|
|
19192
|
+
const command = ["deepline", input2.family, input2.subcommand].filter(Boolean).join(" ");
|
|
19193
|
+
const subject = input2.family === "session" ? "Legacy Session UI/playground command" : "Legacy local playground backend command";
|
|
19194
|
+
const note = input2.family === "session" ? "The SDK CLI does not manage the legacy Python Session UI. This command is accepted for backwards compatibility and intentionally does nothing." : "The SDK CLI does not start or stop the legacy Python local playground backend. This command is accepted for backwards compatibility and intentionally does nothing.";
|
|
19195
|
+
return {
|
|
19196
|
+
ok: true,
|
|
19197
|
+
noop: true,
|
|
19198
|
+
command,
|
|
19199
|
+
compatibility: {
|
|
19200
|
+
family: "legacy_python_cli",
|
|
19201
|
+
sdk_behavior: "noop"
|
|
19202
|
+
},
|
|
19203
|
+
render: {
|
|
19204
|
+
sections: [
|
|
19205
|
+
{
|
|
19206
|
+
title: subject,
|
|
19207
|
+
lines: [note]
|
|
19208
|
+
}
|
|
19209
|
+
]
|
|
19210
|
+
}
|
|
19211
|
+
};
|
|
19212
|
+
}
|
|
19213
|
+
function printLegacyNoop(input2) {
|
|
19214
|
+
printCommandEnvelope(legacyNoopEnvelope(input2), { json: input2.options.json });
|
|
19215
|
+
}
|
|
19216
|
+
function legacySubcommandFromArgv(family) {
|
|
19217
|
+
const args = process.argv.slice(2);
|
|
19218
|
+
const familyIndex = args.indexOf(family);
|
|
19219
|
+
const nextToken = familyIndex >= 0 ? args[familyIndex + 1] : void 0;
|
|
19220
|
+
return nextToken && !nextToken.startsWith("-") ? nextToken : void 0;
|
|
19221
|
+
}
|
|
19222
|
+
function addLegacyNoopSubcommand(parent, family, subcommand, description) {
|
|
19223
|
+
parent.command(subcommand).description(description).allowUnknownOption(true).allowExcessArguments(true).option("--json", "Emit JSON output").argument("[args...]").action((_args, options) => {
|
|
19224
|
+
printLegacyNoop({ family, subcommand, options });
|
|
19225
|
+
});
|
|
19226
|
+
}
|
|
19227
|
+
function registerLegacyNoopCommands(program) {
|
|
19228
|
+
const session = program.command("session").description("Compatibility no-ops for legacy Python Session UI commands.").allowUnknownOption(true).allowExcessArguments(true).option("--json", "Emit JSON output").argument("[args...]").addHelpText(
|
|
19229
|
+
"after",
|
|
19230
|
+
`
|
|
19231
|
+
Notes:
|
|
19232
|
+
The SDK CLI accepts singular session commands from older agent skills so they
|
|
19233
|
+
do not fail, but it does not manage the legacy Python Session UI.
|
|
19234
|
+
Use "deepline sessions send" or "deepline sessions render" for real SDK
|
|
19235
|
+
transcript workflows. "deepline session send" and "deepline session render"
|
|
19236
|
+
are accepted aliases for those real SDK workflows.
|
|
19237
|
+
|
|
19238
|
+
Examples:
|
|
19239
|
+
deepline session start --steps '["Inspect CSV","Run pilot"]'
|
|
19240
|
+
deepline session status --message "Running pilot"
|
|
19241
|
+
deepline session output --csv ./results.csv --label "Results"
|
|
19242
|
+
`
|
|
19243
|
+
).action((args, options) => {
|
|
19244
|
+
void args;
|
|
19245
|
+
printLegacyNoop({
|
|
19246
|
+
family: "session",
|
|
19247
|
+
subcommand: legacySubcommandFromArgv("session"),
|
|
19248
|
+
options
|
|
19249
|
+
});
|
|
19250
|
+
});
|
|
19251
|
+
registerSessionSendRenderCommands(session, "session");
|
|
19252
|
+
for (const subcommand of SESSION_SUBCOMMANDS) {
|
|
19253
|
+
addLegacyNoopSubcommand(
|
|
19254
|
+
session,
|
|
19255
|
+
"session",
|
|
19256
|
+
subcommand,
|
|
19257
|
+
`Accept legacy "deepline session ${subcommand}" as an SDK no-op.`
|
|
19258
|
+
);
|
|
19259
|
+
}
|
|
19260
|
+
const backend = program.command("backend").description(
|
|
19261
|
+
"Compatibility no-ops for legacy Python local backend commands."
|
|
19262
|
+
).allowUnknownOption(true).allowExcessArguments(true).option("--json", "Emit JSON output").argument("[args...]").addHelpText(
|
|
19263
|
+
"after",
|
|
19264
|
+
`
|
|
19265
|
+
Notes:
|
|
19266
|
+
The SDK CLI uses the configured Deepline host and V2 play runtime. It does not
|
|
19267
|
+
start, stop, or refresh the legacy Python local playground backend.
|
|
19268
|
+
|
|
19269
|
+
Examples:
|
|
19270
|
+
deepline backend start
|
|
19271
|
+
deepline backend stop --just-backend
|
|
19272
|
+
deepline backend status --json
|
|
19273
|
+
`
|
|
19274
|
+
).action((args, options) => {
|
|
19275
|
+
void args;
|
|
19276
|
+
printLegacyNoop({
|
|
19277
|
+
family: "backend",
|
|
19278
|
+
subcommand: legacySubcommandFromArgv("backend"),
|
|
19279
|
+
options
|
|
19280
|
+
});
|
|
19281
|
+
});
|
|
19282
|
+
for (const subcommand of BACKEND_SUBCOMMANDS) {
|
|
19283
|
+
addLegacyNoopSubcommand(
|
|
19284
|
+
backend,
|
|
19285
|
+
"backend",
|
|
19286
|
+
subcommand,
|
|
19287
|
+
`Accept legacy "deepline backend ${subcommand}" as an SDK no-op.`
|
|
19288
|
+
);
|
|
19289
|
+
}
|
|
19290
|
+
}
|
|
19291
|
+
|
|
19292
|
+
// src/cli/commands/org.ts
|
|
19293
|
+
async function fetchOrganizations(http, apiKey) {
|
|
19294
|
+
return http.post("/api/v2/auth/cli/organizations", { api_key: apiKey });
|
|
19295
|
+
}
|
|
19296
|
+
function orgListLines(orgs) {
|
|
19297
|
+
return orgs.map((org, index) => {
|
|
19298
|
+
const current = org.is_current ? " (current)" : "";
|
|
19299
|
+
const role = org.role ? ` [${org.role}]` : "";
|
|
19300
|
+
return `${index + 1}. ${org.name}${role}${current}`;
|
|
19301
|
+
});
|
|
19302
|
+
}
|
|
19303
|
+
async function handleOrgList(options) {
|
|
19304
|
+
const config = resolveConfig();
|
|
19305
|
+
const http = new HttpClient(config);
|
|
19306
|
+
const payload = await fetchOrganizations(http, config.apiKey);
|
|
19307
|
+
printCommandEnvelope(
|
|
19308
|
+
{
|
|
19309
|
+
...payload,
|
|
19310
|
+
render: {
|
|
19311
|
+
sections: [
|
|
19312
|
+
{
|
|
19313
|
+
title: "Your organizations:",
|
|
19314
|
+
lines: orgListLines(payload.organizations)
|
|
19315
|
+
}
|
|
19316
|
+
]
|
|
19317
|
+
}
|
|
19318
|
+
},
|
|
19319
|
+
{ json: options.json }
|
|
19320
|
+
);
|
|
19321
|
+
}
|
|
19322
|
+
async function handleOrgSwitch(selection, options) {
|
|
19323
|
+
const config = resolveConfig();
|
|
19324
|
+
const http = new HttpClient(config);
|
|
19325
|
+
const payload = await fetchOrganizations(http, config.apiKey);
|
|
19326
|
+
if (!selection && !options.orgId) {
|
|
19327
|
+
printCommandEnvelope(
|
|
19328
|
+
{
|
|
19329
|
+
...payload,
|
|
19330
|
+
next: { switch: "deepline org switch <number>" },
|
|
19331
|
+
render: {
|
|
19332
|
+
sections: [
|
|
19333
|
+
{
|
|
19334
|
+
title: "Your organizations:",
|
|
19335
|
+
lines: orgListLines(payload.organizations)
|
|
19336
|
+
}
|
|
19337
|
+
],
|
|
19338
|
+
actions: [{ label: "Run", command: "deepline org switch <number>" }]
|
|
19339
|
+
}
|
|
19340
|
+
},
|
|
19341
|
+
{ json: options.json }
|
|
19342
|
+
);
|
|
19343
|
+
return;
|
|
19344
|
+
}
|
|
19345
|
+
let target = payload.organizations.find(
|
|
19346
|
+
(org) => org.org_id === options.orgId
|
|
19347
|
+
);
|
|
19348
|
+
if (!target && selection) {
|
|
19349
|
+
const index = Number.parseInt(selection, 10);
|
|
19350
|
+
if (Number.isFinite(index) && index >= 1 && index <= payload.organizations.length) {
|
|
19351
|
+
target = payload.organizations[index - 1];
|
|
19352
|
+
} else {
|
|
19353
|
+
target = payload.organizations.find(
|
|
19354
|
+
(org) => org.name === selection || org.org_id === selection
|
|
19355
|
+
);
|
|
19356
|
+
}
|
|
19357
|
+
}
|
|
19358
|
+
if (!target) {
|
|
19359
|
+
throw new Error("Could not resolve the selected organization.");
|
|
19360
|
+
}
|
|
19361
|
+
if (target.is_current) {
|
|
19362
|
+
printCommandEnvelope(
|
|
19363
|
+
{
|
|
19364
|
+
ok: true,
|
|
19365
|
+
unchanged: true,
|
|
19366
|
+
organization: target,
|
|
19367
|
+
render: {
|
|
19368
|
+
sections: [
|
|
19369
|
+
{ title: "org switch", lines: [`Already on ${target.name}.`] }
|
|
19370
|
+
]
|
|
19371
|
+
}
|
|
19372
|
+
},
|
|
19373
|
+
{ json: options.json }
|
|
19374
|
+
);
|
|
19375
|
+
return;
|
|
19376
|
+
}
|
|
19377
|
+
const switched = await http.post("/api/v2/auth/cli/switch", {
|
|
19378
|
+
api_key: config.apiKey,
|
|
19379
|
+
org_id: target.org_id
|
|
19380
|
+
});
|
|
19381
|
+
saveHostEnvValues(config.baseUrl, {
|
|
19382
|
+
DEEPLINE_API_KEY: switched.api_key,
|
|
19383
|
+
DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
|
|
19384
|
+
DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
|
|
19385
|
+
});
|
|
19386
|
+
const { api_key: _apiKey, ...publicSwitched } = switched;
|
|
19387
|
+
printCommandEnvelope(
|
|
19388
|
+
{
|
|
19389
|
+
ok: true,
|
|
19390
|
+
host_env_path: hostEnvFilePath(config.baseUrl),
|
|
19391
|
+
...publicSwitched,
|
|
19392
|
+
api_key_saved: true,
|
|
19393
|
+
render: {
|
|
19394
|
+
sections: [
|
|
19395
|
+
{
|
|
19396
|
+
title: "org switch",
|
|
19397
|
+
lines: [
|
|
19398
|
+
`Switched to ${switched.org_name}.`,
|
|
19399
|
+
`Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
|
|
19400
|
+
]
|
|
19401
|
+
}
|
|
19402
|
+
]
|
|
19403
|
+
}
|
|
19404
|
+
},
|
|
19405
|
+
{ json: options.json }
|
|
19406
|
+
);
|
|
19407
|
+
}
|
|
19408
|
+
async function handleOrgCreate(name, options) {
|
|
19409
|
+
const config = resolveConfig();
|
|
19410
|
+
const http = new HttpClient(config);
|
|
19411
|
+
const created = await http.post("/api/v2/auth/cli/org-create", {
|
|
19412
|
+
api_key: config.apiKey,
|
|
19413
|
+
name
|
|
19414
|
+
});
|
|
19415
|
+
saveHostEnvValues(config.baseUrl, {
|
|
19416
|
+
DEEPLINE_API_KEY: created.api_key,
|
|
19417
|
+
DEEPLINE_ACTIVE_ORG_ID: created.org_id,
|
|
19418
|
+
DEEPLINE_ACTIVE_ORG_NAME: created.org_name
|
|
19419
|
+
});
|
|
19420
|
+
const { api_key: _apiKey, ...publicCreated } = created;
|
|
19421
|
+
printCommandEnvelope(
|
|
19422
|
+
{
|
|
19423
|
+
ok: true,
|
|
19424
|
+
...publicCreated,
|
|
19425
|
+
api_key_saved: true,
|
|
19426
|
+
switched: true,
|
|
19427
|
+
host_env_path: hostEnvFilePath(config.baseUrl),
|
|
19428
|
+
render: {
|
|
19429
|
+
sections: [
|
|
19430
|
+
{
|
|
19431
|
+
title: "org create",
|
|
19432
|
+
lines: [
|
|
19433
|
+
`Created organization: ${created.org_name}.`,
|
|
19434
|
+
`Switched to ${created.org_name}.`,
|
|
19435
|
+
`Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
|
|
19436
|
+
]
|
|
19437
|
+
}
|
|
19438
|
+
]
|
|
19439
|
+
}
|
|
19440
|
+
},
|
|
19441
|
+
{ json: options.json }
|
|
19442
|
+
);
|
|
19443
|
+
}
|
|
19444
|
+
function registerOrgCommands(program) {
|
|
19445
|
+
const org = program.command("org").description("List, create, and switch organizations.").addHelpText(
|
|
19446
|
+
"after",
|
|
19447
|
+
`
|
|
19448
|
+
Notes:
|
|
19449
|
+
Organizations are workspaces. Switching organizations mutates the saved host
|
|
19450
|
+
auth file so later CLI commands target the selected workspace.
|
|
19451
|
+
|
|
19452
|
+
Examples:
|
|
19453
|
+
deepline org list --json
|
|
19454
|
+
deepline org create Acme --json
|
|
19455
|
+
deepline org switch 2
|
|
19456
|
+
deepline org switch --org-id org_123 --json
|
|
19457
|
+
`
|
|
19458
|
+
);
|
|
19459
|
+
org.command("list").description("List your organizations.").addHelpText(
|
|
19460
|
+
"after",
|
|
19461
|
+
`
|
|
19462
|
+
Notes:
|
|
19463
|
+
Read-only. Marks the active organization when the server returns that metadata.
|
|
19464
|
+
|
|
19465
|
+
Examples:
|
|
19466
|
+
deepline org list
|
|
19467
|
+
deepline org list --json
|
|
19468
|
+
`
|
|
19469
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgList);
|
|
19470
|
+
org.command("create <name>").description("Create a new organization and switch this CLI to it.").addHelpText(
|
|
18462
19471
|
"after",
|
|
18463
19472
|
`
|
|
18464
19473
|
Notes:
|
|
@@ -18490,7 +19499,7 @@ Examples:
|
|
|
18490
19499
|
|
|
18491
19500
|
// src/cli/commands/quickstart.ts
|
|
18492
19501
|
var import_node_child_process = require("child_process");
|
|
18493
|
-
var
|
|
19502
|
+
var import_node_crypto5 = require("crypto");
|
|
18494
19503
|
var import_node_http = require("http");
|
|
18495
19504
|
var EXIT_OK2 = 0;
|
|
18496
19505
|
var EXIT_AUTH2 = 1;
|
|
@@ -18592,657 +19601,317 @@ function startCallbackServer(input2) {
|
|
|
18592
19601
|
server.listen(0, "127.0.0.1", () => {
|
|
18593
19602
|
const address = server.address();
|
|
18594
19603
|
if (!address || typeof address === "string") {
|
|
18595
|
-
reject(new Error("Failed to bind quickstart callback server."));
|
|
18596
|
-
return;
|
|
18597
|
-
}
|
|
18598
|
-
resolve16({ server, port: address.port });
|
|
18599
|
-
});
|
|
18600
|
-
});
|
|
18601
|
-
}
|
|
18602
|
-
async function handleQuickstart(options) {
|
|
18603
|
-
const jsonOutput = shouldEmitJson(options.json === true);
|
|
18604
|
-
const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
|
|
18605
|
-
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
18606
|
-
let apiKey = resolveApiKeyForBaseUrl(baseUrl);
|
|
18607
|
-
if (!apiKey) {
|
|
18608
|
-
if (interactive) {
|
|
18609
|
-
console.error("Not connected yet \u2014 registering this device first.");
|
|
18610
|
-
const registerExit = await handleRegister([]);
|
|
18611
|
-
if (registerExit !== EXIT_OK2) return registerExit;
|
|
18612
|
-
apiKey = resolveApiKeyForBaseUrl(baseUrl);
|
|
18613
|
-
}
|
|
18614
|
-
if (!apiKey) {
|
|
18615
|
-
console.error("Not connected. Run: deepline auth register");
|
|
18616
|
-
return EXIT_AUTH2;
|
|
18617
|
-
}
|
|
18618
|
-
}
|
|
18619
|
-
const state = (0, import_node_crypto4.randomBytes)(32).toString("hex");
|
|
18620
|
-
let resolveSelection;
|
|
18621
|
-
const selectionPromise = new Promise((resolve16) => {
|
|
18622
|
-
resolveSelection = resolve16;
|
|
18623
|
-
});
|
|
18624
|
-
let callback;
|
|
18625
|
-
try {
|
|
18626
|
-
callback = await startCallbackServer({
|
|
18627
|
-
state,
|
|
18628
|
-
onSelection: (selection) => resolveSelection(selection)
|
|
18629
|
-
});
|
|
18630
|
-
} catch (error) {
|
|
18631
|
-
console.error(
|
|
18632
|
-
`Could not start the local quickstart listener: ${error instanceof Error ? error.message : String(error)}`
|
|
18633
|
-
);
|
|
18634
|
-
return EXIT_SERVER3;
|
|
18635
|
-
}
|
|
18636
|
-
const quickstartUrl = `${baseUrl}/cli/quickstart?cb=${callback.port}&state=${state}`;
|
|
18637
|
-
console.error(" Opening the recipe picker in your browser.");
|
|
18638
|
-
console.error(` If it didn't open, cmd+click: ${quickstartUrl}`);
|
|
18639
|
-
if (options.open !== false) {
|
|
18640
|
-
openInBrowser(quickstartUrl);
|
|
18641
|
-
}
|
|
18642
|
-
const timeoutSeconds = Number.parseInt(options.timeout ?? "300", 10);
|
|
18643
|
-
const timeoutMs = (Number.isFinite(timeoutSeconds) && timeoutSeconds > 0 ? timeoutSeconds : 300) * 1e3;
|
|
18644
|
-
const timeoutHandle = setTimeout(
|
|
18645
|
-
() => resolveSelection("timeout"),
|
|
18646
|
-
timeoutMs
|
|
18647
|
-
);
|
|
18648
|
-
const progress = createCliProgress(!jsonOutput);
|
|
18649
|
-
progress.phase("waiting for your pick in the browser (Ctrl+C to skip)");
|
|
18650
|
-
const onSigint = () => resolveSelection("sigint");
|
|
18651
|
-
process.once("SIGINT", onSigint);
|
|
18652
|
-
const outcome = await selectionPromise;
|
|
18653
|
-
clearTimeout(timeoutHandle);
|
|
18654
|
-
process.removeListener("SIGINT", onSigint);
|
|
18655
|
-
callback.server.close();
|
|
18656
|
-
if (outcome === "sigint") {
|
|
18657
|
-
progress.fail();
|
|
18658
|
-
console.error("\nSkipped \u2014 run `deepline quickstart` anytime.");
|
|
18659
|
-
return EXIT_OK2;
|
|
18660
|
-
}
|
|
18661
|
-
if (outcome === "timeout") {
|
|
18662
|
-
progress.fail();
|
|
18663
|
-
console.error(
|
|
18664
|
-
"Timed out waiting for a selection. Run: deepline quickstart"
|
|
18665
|
-
);
|
|
18666
|
-
return EXIT_AUTH2;
|
|
18667
|
-
}
|
|
18668
|
-
progress.complete();
|
|
18669
|
-
const { prompt, workflowId } = outcome;
|
|
18670
|
-
const claudeCommand = `claude ${shellQuote(prompt)}`;
|
|
18671
|
-
const claudeAvailable = hasClaudeBinary();
|
|
18672
|
-
const willLaunch = options.launch !== false && !jsonOutput && interactive && claudeAvailable;
|
|
18673
|
-
printCommandEnvelope(
|
|
18674
|
-
{
|
|
18675
|
-
status: "submitted",
|
|
18676
|
-
workflowId,
|
|
18677
|
-
prompt,
|
|
18678
|
-
claudeCommand,
|
|
18679
|
-
url: quickstartUrl,
|
|
18680
|
-
launched: willLaunch,
|
|
18681
|
-
render: {
|
|
18682
|
-
sections: [
|
|
18683
|
-
{
|
|
18684
|
-
title: "quickstart",
|
|
18685
|
-
lines: [
|
|
18686
|
-
...workflowId ? [`Recipe: ${workflowId}`] : [],
|
|
18687
|
-
willLaunch ? "Launching Claude Code with your recipe..." : claudeAvailable ? "Run the command below to start your recipe." : "Claude Code not found. Install it (https://code.claude.com/docs/en/overview), then run the command below."
|
|
18688
|
-
]
|
|
18689
|
-
}
|
|
18690
|
-
],
|
|
18691
|
-
actions: [{ label: "Run", command: claudeCommand }]
|
|
18692
|
-
}
|
|
18693
|
-
},
|
|
18694
|
-
{ json: jsonOutput }
|
|
18695
|
-
);
|
|
18696
|
-
if (willLaunch) {
|
|
18697
|
-
return launchClaude(prompt);
|
|
18698
|
-
}
|
|
18699
|
-
return EXIT_OK2;
|
|
18700
|
-
}
|
|
18701
|
-
function registerQuickstartCommands(program) {
|
|
18702
|
-
program.command("quickstart").description(
|
|
18703
|
-
"Pick a starter recipe in the browser and launch it with Claude Code."
|
|
18704
|
-
).addHelpText(
|
|
18705
|
-
"after",
|
|
18706
|
-
`
|
|
18707
|
-
Notes:
|
|
18708
|
-
Opens the hosted recipe picker in your browser. The picker sends your
|
|
18709
|
-
selection back to a temporary listener on 127.0.0.1, so the browser must run
|
|
18710
|
-
on the same machine as the CLI. Once a recipe arrives, the CLI prints the
|
|
18711
|
-
matching claude command and, when Claude Code is installed and the shell is
|
|
18712
|
-
interactive, launches it directly. Press Ctrl+C while waiting to skip.
|
|
18713
|
-
|
|
18714
|
-
Examples:
|
|
18715
|
-
deepline quickstart
|
|
18716
|
-
deepline quickstart --no-open
|
|
18717
|
-
deepline quickstart --no-launch --json
|
|
18718
|
-
deepline quickstart --timeout 120
|
|
18719
|
-
`
|
|
18720
|
-
).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--no-open", "Do not open the browser; print the URL only").option("--no-launch", "Print the claude command instead of launching it").option(
|
|
18721
|
-
"--timeout <seconds>",
|
|
18722
|
-
"Maximum seconds to wait for a selection",
|
|
18723
|
-
"300"
|
|
18724
|
-
).action(async (options) => {
|
|
18725
|
-
process.exitCode = await handleQuickstart(options);
|
|
18726
|
-
});
|
|
18727
|
-
}
|
|
18728
|
-
|
|
18729
|
-
// src/cli/commands/secrets.ts
|
|
18730
|
-
var import_node_process = require("process");
|
|
18731
|
-
var hiddenInputBuffer = "";
|
|
18732
|
-
function normalizeSecretName(value) {
|
|
18733
|
-
const normalized = value.trim().toUpperCase();
|
|
18734
|
-
if (!/^[A-Z][A-Z0-9_]{1,63}$/.test(normalized)) {
|
|
18735
|
-
throw new Error(
|
|
18736
|
-
"Secret names must be 2-64 characters and use uppercase letters, numbers, and underscores."
|
|
18737
|
-
);
|
|
18738
|
-
}
|
|
18739
|
-
return normalized;
|
|
18740
|
-
}
|
|
18741
|
-
function renderSecret(secret) {
|
|
18742
|
-
const scope = secret.scope === "play" && secret.playName ? `play:${secret.playName}` : secret.scope;
|
|
18743
|
-
return `${secret.name} (${scope}) - ${secret.status}${secret.hasValue ? ", set" : ", empty"}`;
|
|
18744
|
-
}
|
|
18745
|
-
async function readHiddenLine(prompt) {
|
|
18746
|
-
if (!import_node_process.stdin.isTTY || !import_node_process.stdout.isTTY) {
|
|
18747
|
-
throw new Error(
|
|
18748
|
-
"Secret values must be entered from an interactive TTY. Do not pipe, pass, or script secret values."
|
|
18749
|
-
);
|
|
18750
|
-
}
|
|
18751
|
-
import_node_process.stdout.write(prompt);
|
|
18752
|
-
const previousRawMode = import_node_process.stdin.isRaw;
|
|
18753
|
-
if (typeof import_node_process.stdin.setRawMode === "function") import_node_process.stdin.setRawMode(true);
|
|
18754
|
-
let value = "";
|
|
18755
|
-
import_node_process.stdin.resume();
|
|
18756
|
-
return await new Promise((resolve16, reject) => {
|
|
18757
|
-
let settled = false;
|
|
18758
|
-
const cleanup = () => {
|
|
18759
|
-
import_node_process.stdin.off("data", onData);
|
|
18760
|
-
import_node_process.stdin.off("end", onEnd);
|
|
18761
|
-
import_node_process.stdin.off("error", onError);
|
|
18762
|
-
if (typeof import_node_process.stdin.setRawMode === "function") {
|
|
18763
|
-
import_node_process.stdin.setRawMode(previousRawMode);
|
|
18764
|
-
}
|
|
18765
|
-
};
|
|
18766
|
-
const finish = (line) => {
|
|
18767
|
-
if (settled) return;
|
|
18768
|
-
settled = true;
|
|
18769
|
-
import_node_process.stdout.write("\n");
|
|
18770
|
-
cleanup();
|
|
18771
|
-
resolve16(line);
|
|
18772
|
-
};
|
|
18773
|
-
const fail = (error) => {
|
|
18774
|
-
if (settled) return;
|
|
18775
|
-
settled = true;
|
|
18776
|
-
import_node_process.stdout.write("\n");
|
|
18777
|
-
cleanup();
|
|
18778
|
-
reject(error);
|
|
18779
|
-
};
|
|
18780
|
-
const processText = (text) => {
|
|
18781
|
-
for (let index = 0; index < text.length; index++) {
|
|
18782
|
-
const char = text[index];
|
|
18783
|
-
const code = char.charCodeAt(0);
|
|
18784
|
-
if (char === "\r" || char === "\n") {
|
|
18785
|
-
hiddenInputBuffer = text.slice(index + 1);
|
|
18786
|
-
finish(value);
|
|
18787
|
-
return;
|
|
18788
|
-
}
|
|
18789
|
-
if (code === 3) {
|
|
18790
|
-
fail(new Error("Secret input cancelled."));
|
|
18791
|
-
return;
|
|
18792
|
-
}
|
|
18793
|
-
if (code === 8 || code === 127) {
|
|
18794
|
-
value = value.slice(0, -1);
|
|
18795
|
-
continue;
|
|
18796
|
-
}
|
|
18797
|
-
if (code >= 32) {
|
|
18798
|
-
value += char;
|
|
18799
|
-
}
|
|
18800
|
-
}
|
|
18801
|
-
hiddenInputBuffer = "";
|
|
18802
|
-
};
|
|
18803
|
-
const onData = (chunk) => {
|
|
18804
|
-
processText(chunk.toString());
|
|
18805
|
-
};
|
|
18806
|
-
const onEnd = () => fail(new Error("Secret input ended before a value was entered."));
|
|
18807
|
-
const onError = (error) => fail(error);
|
|
18808
|
-
import_node_process.stdin.on("data", onData);
|
|
18809
|
-
import_node_process.stdin.once("end", onEnd);
|
|
18810
|
-
import_node_process.stdin.once("error", onError);
|
|
18811
|
-
if (hiddenInputBuffer) {
|
|
18812
|
-
const buffered = hiddenInputBuffer;
|
|
18813
|
-
hiddenInputBuffer = "";
|
|
18814
|
-
processText(buffered);
|
|
18815
|
-
}
|
|
19604
|
+
reject(new Error("Failed to bind quickstart callback server."));
|
|
19605
|
+
return;
|
|
19606
|
+
}
|
|
19607
|
+
resolve16({ server, port: address.port });
|
|
19608
|
+
});
|
|
18816
19609
|
});
|
|
18817
19610
|
}
|
|
18818
|
-
async function
|
|
18819
|
-
const
|
|
18820
|
-
|
|
18821
|
-
const
|
|
18822
|
-
|
|
18823
|
-
|
|
19611
|
+
async function handleQuickstart(options) {
|
|
19612
|
+
const jsonOutput = shouldEmitJson(options.json === true);
|
|
19613
|
+
const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
|
|
19614
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
19615
|
+
let apiKey = resolveApiKeyForBaseUrl(baseUrl);
|
|
19616
|
+
if (!apiKey) {
|
|
19617
|
+
if (interactive) {
|
|
19618
|
+
console.error("Not connected yet \u2014 registering this device first.");
|
|
19619
|
+
const registerExit = await handleRegister([]);
|
|
19620
|
+
if (registerExit !== EXIT_OK2) return registerExit;
|
|
19621
|
+
apiKey = resolveApiKeyForBaseUrl(baseUrl);
|
|
19622
|
+
}
|
|
19623
|
+
if (!apiKey) {
|
|
19624
|
+
console.error("Not connected. Run: deepline auth register");
|
|
19625
|
+
return EXIT_AUTH2;
|
|
19626
|
+
}
|
|
18824
19627
|
}
|
|
18825
|
-
|
|
18826
|
-
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
|
|
18830
|
-
|
|
19628
|
+
const state = (0, import_node_crypto5.randomBytes)(32).toString("hex");
|
|
19629
|
+
let resolveSelection;
|
|
19630
|
+
const selectionPromise = new Promise((resolve16) => {
|
|
19631
|
+
resolveSelection = resolve16;
|
|
19632
|
+
});
|
|
19633
|
+
let callback;
|
|
19634
|
+
try {
|
|
19635
|
+
callback = await startCallbackServer({
|
|
19636
|
+
state,
|
|
19637
|
+
onSelection: (selection) => resolveSelection(selection)
|
|
19638
|
+
});
|
|
19639
|
+
} catch (error) {
|
|
19640
|
+
console.error(
|
|
19641
|
+
`Could not start the local quickstart listener: ${error instanceof Error ? error.message : String(error)}`
|
|
18831
19642
|
);
|
|
19643
|
+
return EXIT_SERVER3;
|
|
18832
19644
|
}
|
|
18833
|
-
}
|
|
18834
|
-
|
|
18835
|
-
|
|
18836
|
-
|
|
18837
|
-
|
|
18838
|
-
|
|
18839
|
-
|
|
18840
|
-
|
|
18841
|
-
|
|
18842
|
-
|
|
18843
|
-
|
|
18844
|
-
title: "secrets",
|
|
18845
|
-
lines: secrets.length ? secrets.map(renderSecret) : ["No play secrets are configured."]
|
|
18846
|
-
}
|
|
18847
|
-
]
|
|
18848
|
-
}
|
|
18849
|
-
},
|
|
18850
|
-
{ json: options.json }
|
|
19645
|
+
const quickstartUrl = `${baseUrl}/cli/quickstart?cb=${callback.port}&state=${state}`;
|
|
19646
|
+
console.error(" Opening the recipe picker in your browser.");
|
|
19647
|
+
console.error(` If it didn't open, cmd+click: ${quickstartUrl}`);
|
|
19648
|
+
if (options.open !== false) {
|
|
19649
|
+
openInBrowser(quickstartUrl);
|
|
19650
|
+
}
|
|
19651
|
+
const timeoutSeconds = Number.parseInt(options.timeout ?? "300", 10);
|
|
19652
|
+
const timeoutMs = (Number.isFinite(timeoutSeconds) && timeoutSeconds > 0 ? timeoutSeconds : 300) * 1e3;
|
|
19653
|
+
const timeoutHandle = setTimeout(
|
|
19654
|
+
() => resolveSelection("timeout"),
|
|
19655
|
+
timeoutMs
|
|
18851
19656
|
);
|
|
18852
|
-
|
|
18853
|
-
|
|
18854
|
-
const
|
|
18855
|
-
|
|
18856
|
-
const
|
|
19657
|
+
const progress = createCliProgress(!jsonOutput);
|
|
19658
|
+
progress.phase("waiting for your pick in the browser (Ctrl+C to skip)");
|
|
19659
|
+
const onSigint = () => resolveSelection("sigint");
|
|
19660
|
+
process.once("SIGINT", onSigint);
|
|
19661
|
+
const outcome = await selectionPromise;
|
|
19662
|
+
clearTimeout(timeoutHandle);
|
|
19663
|
+
process.removeListener("SIGINT", onSigint);
|
|
19664
|
+
callback.server.close();
|
|
19665
|
+
if (outcome === "sigint") {
|
|
19666
|
+
progress.fail();
|
|
19667
|
+
console.error("\nSkipped \u2014 run `deepline quickstart` anytime.");
|
|
19668
|
+
return EXIT_OK2;
|
|
19669
|
+
}
|
|
19670
|
+
if (outcome === "timeout") {
|
|
19671
|
+
progress.fail();
|
|
19672
|
+
console.error(
|
|
19673
|
+
"Timed out waiting for a selection. Run: deepline quickstart"
|
|
19674
|
+
);
|
|
19675
|
+
return EXIT_AUTH2;
|
|
19676
|
+
}
|
|
19677
|
+
progress.complete();
|
|
19678
|
+
const { prompt, workflowId } = outcome;
|
|
19679
|
+
const claudeCommand = `claude ${shellQuote(prompt)}`;
|
|
19680
|
+
const claudeAvailable = hasClaudeBinary();
|
|
19681
|
+
const willLaunch = options.launch !== false && !jsonOutput && interactive && claudeAvailable;
|
|
18857
19682
|
printCommandEnvelope(
|
|
18858
19683
|
{
|
|
18859
|
-
|
|
18860
|
-
|
|
18861
|
-
|
|
19684
|
+
status: "submitted",
|
|
19685
|
+
workflowId,
|
|
19686
|
+
prompt,
|
|
19687
|
+
claudeCommand,
|
|
19688
|
+
url: quickstartUrl,
|
|
19689
|
+
launched: willLaunch,
|
|
18862
19690
|
render: {
|
|
18863
19691
|
sections: [
|
|
18864
19692
|
{
|
|
18865
|
-
title: "
|
|
19693
|
+
title: "quickstart",
|
|
18866
19694
|
lines: [
|
|
18867
|
-
|
|
19695
|
+
...workflowId ? [`Recipe: ${workflowId}`] : [],
|
|
19696
|
+
willLaunch ? "Launching Claude Code with your recipe..." : claudeAvailable ? "Run the command below to start your recipe." : "Claude Code not found. Install it (https://code.claude.com/docs/en/overview), then run the command below."
|
|
18868
19697
|
]
|
|
18869
19698
|
}
|
|
18870
|
-
]
|
|
19699
|
+
],
|
|
19700
|
+
actions: [{ label: "Run", command: claudeCommand }]
|
|
18871
19701
|
}
|
|
18872
19702
|
},
|
|
18873
|
-
{ json:
|
|
19703
|
+
{ json: jsonOutput }
|
|
18874
19704
|
);
|
|
18875
|
-
if (
|
|
18876
|
-
|
|
18877
|
-
async function handleSet(nameInput, forbidden, options) {
|
|
18878
|
-
preventShellHistoryLeak(forbidden);
|
|
18879
|
-
const name = normalizeSecretName(nameInput);
|
|
18880
|
-
const scope = options.scope === "play" ? "play" : "org";
|
|
18881
|
-
const playName = options.play?.trim();
|
|
18882
|
-
if (scope === "play" && !playName) {
|
|
18883
|
-
throw new Error("--play <name> is required when --scope play is used.");
|
|
19705
|
+
if (willLaunch) {
|
|
19706
|
+
return launchClaude(prompt);
|
|
18884
19707
|
}
|
|
18885
|
-
|
|
18886
|
-
const { http } = getAuthedHttpClient();
|
|
18887
|
-
const response = await http.post(
|
|
18888
|
-
"/api/v2/secrets",
|
|
18889
|
-
{
|
|
18890
|
-
name,
|
|
18891
|
-
value,
|
|
18892
|
-
scope,
|
|
18893
|
-
...playName ? { playName } : {}
|
|
18894
|
-
}
|
|
18895
|
-
);
|
|
18896
|
-
const secret = response.secret;
|
|
18897
|
-
printCommandEnvelope(
|
|
18898
|
-
{
|
|
18899
|
-
ok: true,
|
|
18900
|
-
secret,
|
|
18901
|
-
render: {
|
|
18902
|
-
sections: [
|
|
18903
|
-
{
|
|
18904
|
-
title: "secret saved",
|
|
18905
|
-
lines: [`${secret.name}: saved (${secret.scope})`]
|
|
18906
|
-
}
|
|
18907
|
-
]
|
|
18908
|
-
}
|
|
18909
|
-
},
|
|
18910
|
-
{ json: options.json }
|
|
18911
|
-
);
|
|
19708
|
+
return EXIT_OK2;
|
|
18912
19709
|
}
|
|
18913
|
-
function
|
|
18914
|
-
|
|
19710
|
+
function registerQuickstartCommands(program) {
|
|
19711
|
+
program.command("quickstart").description(
|
|
19712
|
+
"Pick a starter recipe in the browser and launch it with Claude Code."
|
|
19713
|
+
).addHelpText(
|
|
18915
19714
|
"after",
|
|
18916
19715
|
`
|
|
18917
19716
|
Notes:
|
|
18918
|
-
|
|
18919
|
-
|
|
18920
|
-
|
|
19717
|
+
Opens the hosted recipe picker in your browser. The picker sends your
|
|
19718
|
+
selection back to a temporary listener on 127.0.0.1, so the browser must run
|
|
19719
|
+
on the same machine as the CLI. Once a recipe arrives, the CLI prints the
|
|
19720
|
+
matching claude command and, when Claude Code is installed and the shell is
|
|
19721
|
+
interactive, launches it directly. Press Ctrl+C while waiting to skip.
|
|
18921
19722
|
|
|
18922
19723
|
Examples:
|
|
18923
|
-
deepline
|
|
18924
|
-
deepline
|
|
18925
|
-
deepline
|
|
18926
|
-
|
|
18927
|
-
|
|
18928
|
-
|
|
18929
|
-
|
|
18930
|
-
|
|
18931
|
-
|
|
18932
|
-
|
|
18933
|
-
|
|
18934
|
-
|
|
18935
|
-
async (name, forbidden, options) => {
|
|
18936
|
-
await handleSet(name, forbidden ?? [], options);
|
|
18937
|
-
}
|
|
18938
|
-
);
|
|
18939
|
-
}
|
|
18940
|
-
|
|
18941
|
-
// src/cli/commands/sessions.ts
|
|
18942
|
-
var import_node_fs11 = require("fs");
|
|
18943
|
-
var import_node_os8 = require("os");
|
|
18944
|
-
var import_node_path14 = require("path");
|
|
18945
|
-
var import_node_zlib = require("zlib");
|
|
18946
|
-
var import_node_crypto5 = require("crypto");
|
|
18947
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
18948
|
-
var MAX_SESSION_UPLOAD_BYTES = 35e5;
|
|
18949
|
-
var MAX_DIRECT_SESSION_DECODED_BYTES = 50 * 1024 * 1024;
|
|
18950
|
-
var CHUNK_SIZE_BYTES = 25e5;
|
|
18951
|
-
var MAX_EVENT_STRING_CHARS = 8e3;
|
|
18952
|
-
var MAX_EVENT_LIST_ITEMS = 40;
|
|
18953
|
-
var MAX_EVENT_OBJECT_KEYS = 80;
|
|
18954
|
-
var TRUNCATION_MARKER = "...[truncated]";
|
|
18955
|
-
var NOISE_EVENT_TYPES = /* @__PURE__ */ new Set(["progress", "file-history-snapshot"]);
|
|
18956
|
-
function homeDir() {
|
|
18957
|
-
return process.env.HOME?.trim() || (0, import_node_os8.homedir)();
|
|
18958
|
-
}
|
|
18959
|
-
function detectShellContext() {
|
|
18960
|
-
const shellPath = process.env.SHELL?.trim() || process.env.ComSpec?.trim() || process.env.COMSPEC?.trim() || "";
|
|
18961
|
-
return {
|
|
18962
|
-
shell: shellPath ? (0, import_node_path14.basename)(shellPath).replace(/\.exe$/i, "") : "unknown",
|
|
18963
|
-
shell_path: shellPath || null,
|
|
18964
|
-
os: (0, import_node_os8.platform)(),
|
|
18965
|
-
cwd: process.cwd()
|
|
18966
|
-
};
|
|
18967
|
-
}
|
|
18968
|
-
function claudeProjectsRoot() {
|
|
18969
|
-
return (0, import_node_path14.join)(homeDir(), ".claude", "projects");
|
|
18970
|
-
}
|
|
18971
|
-
function listClaudeSessionFiles() {
|
|
18972
|
-
const root = claudeProjectsRoot();
|
|
18973
|
-
if (!(0, import_node_fs11.existsSync)(root)) return [];
|
|
18974
|
-
const projectDirs = readDirectoryNames(root);
|
|
18975
|
-
const files = [];
|
|
18976
|
-
for (const projectDir of projectDirs) {
|
|
18977
|
-
const fullProjectDir = (0, import_node_path14.join)(root, projectDir);
|
|
18978
|
-
for (const fileName of readDirectoryNames(fullProjectDir)) {
|
|
18979
|
-
if (fileName.endsWith(".jsonl")) {
|
|
18980
|
-
files.push((0, import_node_path14.join)(fullProjectDir, fileName));
|
|
18981
|
-
}
|
|
18982
|
-
}
|
|
18983
|
-
}
|
|
18984
|
-
return files;
|
|
18985
|
-
}
|
|
18986
|
-
function readDirectoryNames(dir) {
|
|
18987
|
-
try {
|
|
18988
|
-
return (0, import_node_fs11.readdirSync)(dir);
|
|
18989
|
-
} catch {
|
|
18990
|
-
return [];
|
|
18991
|
-
}
|
|
18992
|
-
}
|
|
18993
|
-
function newestClaudeSessionFile() {
|
|
18994
|
-
let newest = null;
|
|
18995
|
-
for (const filePath of listClaudeSessionFiles()) {
|
|
18996
|
-
try {
|
|
18997
|
-
const stat4 = (0, import_node_fs11.statSync)(filePath);
|
|
18998
|
-
if (!newest || stat4.mtimeMs > newest.mtimeMs) {
|
|
18999
|
-
newest = { filePath, mtimeMs: stat4.mtimeMs };
|
|
19000
|
-
}
|
|
19001
|
-
} catch {
|
|
19002
|
-
continue;
|
|
19003
|
-
}
|
|
19004
|
-
}
|
|
19005
|
-
return newest?.filePath ?? null;
|
|
19006
|
-
}
|
|
19007
|
-
function sessionIdFromFilePath(filePath) {
|
|
19008
|
-
return (0, import_node_path14.basename)(filePath, ".jsonl");
|
|
19724
|
+
deepline quickstart
|
|
19725
|
+
deepline quickstart --no-open
|
|
19726
|
+
deepline quickstart --no-launch --json
|
|
19727
|
+
deepline quickstart --timeout 120
|
|
19728
|
+
`
|
|
19729
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--no-open", "Do not open the browser; print the URL only").option("--no-launch", "Print the claude command instead of launching it").option(
|
|
19730
|
+
"--timeout <seconds>",
|
|
19731
|
+
"Maximum seconds to wait for a selection",
|
|
19732
|
+
"300"
|
|
19733
|
+
).action(async (options) => {
|
|
19734
|
+
process.exitCode = await handleQuickstart(options);
|
|
19735
|
+
});
|
|
19009
19736
|
}
|
|
19010
|
-
|
|
19011
|
-
|
|
19737
|
+
|
|
19738
|
+
// src/cli/commands/secrets.ts
|
|
19739
|
+
var import_node_process = require("process");
|
|
19740
|
+
var hiddenInputBuffer = "";
|
|
19741
|
+
function normalizeSecretName(value) {
|
|
19742
|
+
const normalized = value.trim().toUpperCase();
|
|
19743
|
+
if (!/^[A-Z][A-Z0-9_]{1,63}$/.test(normalized)) {
|
|
19012
19744
|
throw new Error(
|
|
19013
|
-
"
|
|
19745
|
+
"Secret names must be 2-64 characters and use uppercase letters, numbers, and underscores."
|
|
19014
19746
|
);
|
|
19015
19747
|
}
|
|
19016
|
-
|
|
19017
|
-
if (sessionIdFromFilePath(filePath) === sessionId) {
|
|
19018
|
-
return filePath;
|
|
19019
|
-
}
|
|
19020
|
-
}
|
|
19021
|
-
return null;
|
|
19022
|
-
}
|
|
19023
|
-
function resolveSessionTargets(input2) {
|
|
19024
|
-
const targets = [];
|
|
19025
|
-
if (input2.currentSession) {
|
|
19026
|
-
const currentFile = newestClaudeSessionFile();
|
|
19027
|
-
if (!currentFile) {
|
|
19028
|
-
throw new Error("No session files found in ~/.claude/projects/*/.");
|
|
19029
|
-
}
|
|
19030
|
-
const sessionId = sessionIdFromFilePath(currentFile);
|
|
19031
|
-
targets.push({
|
|
19032
|
-
sessionId,
|
|
19033
|
-
label: `session-${sessionId}`,
|
|
19034
|
-
filePath: currentFile
|
|
19035
|
-
});
|
|
19036
|
-
}
|
|
19037
|
-
for (const [index, sessionId] of (input2.sessionIds ?? []).entries()) {
|
|
19038
|
-
const filePath = findSessionFile(sessionId);
|
|
19039
|
-
if (!filePath) {
|
|
19040
|
-
throw new Error(
|
|
19041
|
-
`Session file not found: ~/.claude/projects/*/${sessionId}.jsonl`
|
|
19042
|
-
);
|
|
19043
|
-
}
|
|
19044
|
-
targets.push({
|
|
19045
|
-
sessionId,
|
|
19046
|
-
label: input2.labels?.[index] ?? `session-${sessionId}`,
|
|
19047
|
-
filePath
|
|
19048
|
-
});
|
|
19049
|
-
}
|
|
19050
|
-
if (targets.length === 0) {
|
|
19051
|
-
throw new Error("One of --session-id or --current-session is required.");
|
|
19052
|
-
}
|
|
19053
|
-
return targets;
|
|
19054
|
-
}
|
|
19055
|
-
function parseJsonLine(line) {
|
|
19056
|
-
try {
|
|
19057
|
-
return JSON.parse(line);
|
|
19058
|
-
} catch {
|
|
19059
|
-
return null;
|
|
19060
|
-
}
|
|
19748
|
+
return normalized;
|
|
19061
19749
|
}
|
|
19062
|
-
function
|
|
19063
|
-
|
|
19750
|
+
function renderSecret(secret) {
|
|
19751
|
+
const scope = secret.scope === "play" && secret.playName ? `play:${secret.playName}` : secret.scope;
|
|
19752
|
+
return `${secret.name} (${scope}) - ${secret.status}${secret.hasValue ? ", set" : ", empty"}`;
|
|
19064
19753
|
}
|
|
19065
|
-
function
|
|
19066
|
-
|
|
19067
|
-
|
|
19068
|
-
|
|
19069
|
-
|
|
19070
|
-
continue;
|
|
19071
|
-
}
|
|
19072
|
-
lines.push(line);
|
|
19754
|
+
async function readHiddenLine(prompt) {
|
|
19755
|
+
if (!import_node_process.stdin.isTTY || !import_node_process.stdout.isTTY) {
|
|
19756
|
+
throw new Error(
|
|
19757
|
+
"Secret values must be entered from an interactive TTY. Do not pipe, pass, or script secret values."
|
|
19758
|
+
);
|
|
19073
19759
|
}
|
|
19074
|
-
|
|
19075
|
-
|
|
19076
|
-
|
|
19077
|
-
|
|
19078
|
-
|
|
19079
|
-
|
|
19080
|
-
|
|
19081
|
-
|
|
19082
|
-
|
|
19083
|
-
|
|
19084
|
-
|
|
19085
|
-
|
|
19086
|
-
|
|
19087
|
-
if (type === "tool_use") {
|
|
19088
|
-
return `tool_use:${String(record.name ?? "")}:${String(record.id ?? "")}`;
|
|
19089
|
-
}
|
|
19090
|
-
if (type === "tool_result") {
|
|
19091
|
-
return `tool_result:${String(record.tool_use_id ?? "")}`;
|
|
19092
|
-
}
|
|
19093
|
-
return String(record.text ?? type);
|
|
19094
|
-
}).join("\n");
|
|
19095
|
-
}
|
|
19096
|
-
function dedupConsecutiveEvents(raw) {
|
|
19097
|
-
const rawLines = normalizedJsonLines(raw);
|
|
19098
|
-
const parsedEvents = rawLines.map(parseJsonLine);
|
|
19099
|
-
const output2 = [];
|
|
19100
|
-
let index = 0;
|
|
19101
|
-
while (index < parsedEvents.length) {
|
|
19102
|
-
const event = parsedEvents[index];
|
|
19103
|
-
if (!event || typeof event !== "object") {
|
|
19104
|
-
output2.push(rawLines[index] ?? "");
|
|
19105
|
-
index += 1;
|
|
19106
|
-
continue;
|
|
19107
|
-
}
|
|
19108
|
-
const record = event;
|
|
19109
|
-
const eventType = String(record.type ?? "");
|
|
19110
|
-
const eventKey = messageContentKey(record);
|
|
19111
|
-
if (!["user", "assistant"].includes(eventType) || !eventKey) {
|
|
19112
|
-
output2.push(rawLines[index] ?? "");
|
|
19113
|
-
index += 1;
|
|
19114
|
-
continue;
|
|
19115
|
-
}
|
|
19116
|
-
let runCount = 1;
|
|
19117
|
-
let cursor = index + 1;
|
|
19118
|
-
while (cursor < parsedEvents.length) {
|
|
19119
|
-
const next = parsedEvents[cursor];
|
|
19120
|
-
if (!next || typeof next !== "object") break;
|
|
19121
|
-
const nextRecord = next;
|
|
19122
|
-
if (String(nextRecord.type ?? "") !== eventType || messageContentKey(nextRecord) !== eventKey) {
|
|
19123
|
-
break;
|
|
19760
|
+
import_node_process.stdout.write(prompt);
|
|
19761
|
+
const previousRawMode = import_node_process.stdin.isRaw;
|
|
19762
|
+
if (typeof import_node_process.stdin.setRawMode === "function") import_node_process.stdin.setRawMode(true);
|
|
19763
|
+
let value = "";
|
|
19764
|
+
import_node_process.stdin.resume();
|
|
19765
|
+
return await new Promise((resolve16, reject) => {
|
|
19766
|
+
let settled = false;
|
|
19767
|
+
const cleanup = () => {
|
|
19768
|
+
import_node_process.stdin.off("data", onData);
|
|
19769
|
+
import_node_process.stdin.off("end", onEnd);
|
|
19770
|
+
import_node_process.stdin.off("error", onError);
|
|
19771
|
+
if (typeof import_node_process.stdin.setRawMode === "function") {
|
|
19772
|
+
import_node_process.stdin.setRawMode(previousRawMode);
|
|
19124
19773
|
}
|
|
19125
|
-
|
|
19126
|
-
|
|
19127
|
-
|
|
19128
|
-
|
|
19129
|
-
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
19133
|
-
|
|
19774
|
+
};
|
|
19775
|
+
const finish = (line) => {
|
|
19776
|
+
if (settled) return;
|
|
19777
|
+
settled = true;
|
|
19778
|
+
import_node_process.stdout.write("\n");
|
|
19779
|
+
cleanup();
|
|
19780
|
+
resolve16(line);
|
|
19781
|
+
};
|
|
19782
|
+
const fail = (error) => {
|
|
19783
|
+
if (settled) return;
|
|
19784
|
+
settled = true;
|
|
19785
|
+
import_node_process.stdout.write("\n");
|
|
19786
|
+
cleanup();
|
|
19787
|
+
reject(error);
|
|
19788
|
+
};
|
|
19789
|
+
const processText = (text) => {
|
|
19790
|
+
for (let index = 0; index < text.length; index++) {
|
|
19791
|
+
const char = text[index];
|
|
19792
|
+
const code = char.charCodeAt(0);
|
|
19793
|
+
if (char === "\r" || char === "\n") {
|
|
19794
|
+
hiddenInputBuffer = text.slice(index + 1);
|
|
19795
|
+
finish(value);
|
|
19796
|
+
return;
|
|
19797
|
+
}
|
|
19798
|
+
if (code === 3) {
|
|
19799
|
+
fail(new Error("Secret input cancelled."));
|
|
19800
|
+
return;
|
|
19801
|
+
}
|
|
19802
|
+
if (code === 8 || code === 127) {
|
|
19803
|
+
value = value.slice(0, -1);
|
|
19804
|
+
continue;
|
|
19805
|
+
}
|
|
19806
|
+
if (code >= 32) {
|
|
19807
|
+
value += char;
|
|
19808
|
+
}
|
|
19809
|
+
}
|
|
19810
|
+
hiddenInputBuffer = "";
|
|
19811
|
+
};
|
|
19812
|
+
const onData = (chunk) => {
|
|
19813
|
+
processText(chunk.toString());
|
|
19814
|
+
};
|
|
19815
|
+
const onEnd = () => fail(new Error("Secret input ended before a value was entered."));
|
|
19816
|
+
const onError = (error) => fail(error);
|
|
19817
|
+
import_node_process.stdin.on("data", onData);
|
|
19818
|
+
import_node_process.stdin.once("end", onEnd);
|
|
19819
|
+
import_node_process.stdin.once("error", onError);
|
|
19820
|
+
if (hiddenInputBuffer) {
|
|
19821
|
+
const buffered = hiddenInputBuffer;
|
|
19822
|
+
hiddenInputBuffer = "";
|
|
19823
|
+
processText(buffered);
|
|
19134
19824
|
}
|
|
19135
|
-
|
|
19136
|
-
index += 1;
|
|
19137
|
-
}
|
|
19138
|
-
return Buffer.from(output2.length > 0 ? `${output2.join("\n")}
|
|
19139
|
-
` : "", "utf8");
|
|
19825
|
+
});
|
|
19140
19826
|
}
|
|
19141
|
-
function
|
|
19142
|
-
|
|
19143
|
-
|
|
19144
|
-
|
|
19145
|
-
|
|
19146
|
-
|
|
19147
|
-
const compacted = value.slice(0, MAX_EVENT_LIST_ITEMS).map(compactEventValue);
|
|
19148
|
-
if (value.length > MAX_EVENT_LIST_ITEMS) {
|
|
19149
|
-
compacted.push(
|
|
19150
|
-
`${TRUNCATION_MARKER} ${value.length - MAX_EVENT_LIST_ITEMS} more item(s)`
|
|
19151
|
-
);
|
|
19152
|
-
}
|
|
19153
|
-
return compacted;
|
|
19827
|
+
async function readSecretValue() {
|
|
19828
|
+
const first = await readHiddenLine("Secret value: ");
|
|
19829
|
+
if (!first) throw new Error("Secret value is required.");
|
|
19830
|
+
const second = await readHiddenLine("Confirm secret value: ");
|
|
19831
|
+
if (first !== second) {
|
|
19832
|
+
throw new Error("Secret values did not match.");
|
|
19154
19833
|
}
|
|
19155
|
-
|
|
19156
|
-
|
|
19157
|
-
|
|
19158
|
-
|
|
19159
|
-
|
|
19160
|
-
|
|
19161
|
-
|
|
19162
|
-
compacted._truncated_keys = entries.length - MAX_EVENT_OBJECT_KEYS;
|
|
19163
|
-
}
|
|
19164
|
-
return compacted;
|
|
19834
|
+
return first;
|
|
19835
|
+
}
|
|
19836
|
+
function preventShellHistoryLeak(forbidden) {
|
|
19837
|
+
if (forbidden.length > 0) {
|
|
19838
|
+
throw new Error(
|
|
19839
|
+
"Do not pass secret values as command arguments. Run `deepline secrets set NAME` and enter the value at the hidden prompt."
|
|
19840
|
+
);
|
|
19165
19841
|
}
|
|
19166
|
-
return value;
|
|
19167
19842
|
}
|
|
19168
|
-
function
|
|
19169
|
-
const
|
|
19170
|
-
|
|
19171
|
-
|
|
19172
|
-
|
|
19173
|
-
|
|
19174
|
-
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
|
|
19182
|
-
(block) => block && typeof block === "object" && block.type === "tool_result" ? compactEventValue(block) : block
|
|
19183
|
-
);
|
|
19843
|
+
async function handleList(options) {
|
|
19844
|
+
const client2 = new DeeplineClient();
|
|
19845
|
+
const secrets = await client2.listSecrets();
|
|
19846
|
+
printCommandEnvelope(
|
|
19847
|
+
{
|
|
19848
|
+
secrets,
|
|
19849
|
+
count: secrets.length,
|
|
19850
|
+
render: {
|
|
19851
|
+
sections: [
|
|
19852
|
+
{
|
|
19853
|
+
title: "secrets",
|
|
19854
|
+
lines: secrets.length ? secrets.map(renderSecret) : ["No play secrets are configured."]
|
|
19855
|
+
}
|
|
19856
|
+
]
|
|
19184
19857
|
}
|
|
19185
|
-
}
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
return Buffer.from(lines.length > 0 ? `${lines.join("\n")}
|
|
19189
|
-
` : "", "utf8");
|
|
19858
|
+
},
|
|
19859
|
+
{ json: options.json }
|
|
19860
|
+
);
|
|
19190
19861
|
}
|
|
19191
|
-
function
|
|
19192
|
-
|
|
19193
|
-
|
|
19862
|
+
async function handleCheck(nameInput, options) {
|
|
19863
|
+
const name = normalizeSecretName(nameInput);
|
|
19864
|
+
const client2 = new DeeplineClient();
|
|
19865
|
+
const secret = await client2.checkSecret(name);
|
|
19866
|
+
printCommandEnvelope(
|
|
19867
|
+
{
|
|
19868
|
+
ok: Boolean(secret),
|
|
19869
|
+
name,
|
|
19870
|
+
secret: secret ?? null,
|
|
19871
|
+
render: {
|
|
19872
|
+
sections: [
|
|
19873
|
+
{
|
|
19874
|
+
title: "secret check",
|
|
19875
|
+
lines: [
|
|
19876
|
+
secret ? `${name}: active` : `${name}: missing, disabled, or empty`
|
|
19877
|
+
]
|
|
19878
|
+
}
|
|
19879
|
+
]
|
|
19880
|
+
}
|
|
19881
|
+
},
|
|
19882
|
+
{ json: options.json }
|
|
19194
19883
|
);
|
|
19884
|
+
if (!secret) process.exitCode = 4;
|
|
19195
19885
|
}
|
|
19196
|
-
function
|
|
19197
|
-
|
|
19198
|
-
const
|
|
19199
|
-
|
|
19200
|
-
|
|
19886
|
+
async function handleSet(nameInput, forbidden, options) {
|
|
19887
|
+
preventShellHistoryLeak(forbidden);
|
|
19888
|
+
const name = normalizeSecretName(nameInput);
|
|
19889
|
+
const scope = options.scope === "play" ? "play" : "org";
|
|
19890
|
+
const playName = options.play?.trim();
|
|
19891
|
+
if (scope === "play" && !playName) {
|
|
19892
|
+
throw new Error("--play <name> is required when --scope play is used.");
|
|
19201
19893
|
}
|
|
19202
|
-
|
|
19203
|
-
}
|
|
19204
|
-
async function uploadPayload(path, payload) {
|
|
19894
|
+
const value = await readSecretValue();
|
|
19205
19895
|
const { http } = getAuthedHttpClient();
|
|
19206
|
-
|
|
19207
|
-
|
|
19208
|
-
|
|
19209
|
-
|
|
19210
|
-
|
|
19211
|
-
|
|
19212
|
-
|
|
19213
|
-
for (let offset = 0; offset < bytes.length; offset += CHUNK_SIZE_BYTES) {
|
|
19214
|
-
chunks.push(bytes.subarray(offset, offset + CHUNK_SIZE_BYTES));
|
|
19215
|
-
}
|
|
19216
|
-
process.stderr.write(
|
|
19217
|
-
`Uploading ${session.label} in ${chunks.length} chunk(s)...
|
|
19218
|
-
`
|
|
19219
|
-
);
|
|
19220
|
-
for (const [index, chunk] of chunks.entries()) {
|
|
19221
|
-
await uploadPayload("/api/v2/cli/send-session/chunk", {
|
|
19222
|
-
upload_id: uploadId,
|
|
19223
|
-
session_id: session.sessionId,
|
|
19224
|
-
index,
|
|
19225
|
-
total_chunks: chunks.length,
|
|
19226
|
-
data: chunk.toString("base64")
|
|
19227
|
-
});
|
|
19896
|
+
const response = await http.post(
|
|
19897
|
+
"/api/v2/secrets",
|
|
19898
|
+
{
|
|
19899
|
+
name,
|
|
19900
|
+
value,
|
|
19901
|
+
scope,
|
|
19902
|
+
...playName ? { playName } : {}
|
|
19228
19903
|
}
|
|
19229
|
-
|
|
19230
|
-
const
|
|
19231
|
-
upload_id: uploadId,
|
|
19232
|
-
session_ids: sessions.map((session) => session.sessionId),
|
|
19233
|
-
labels: sessions.map((session) => session.label),
|
|
19234
|
-
environments: sessions.map(() => detectShellContext())
|
|
19235
|
-
});
|
|
19904
|
+
);
|
|
19905
|
+
const secret = response.secret;
|
|
19236
19906
|
printCommandEnvelope(
|
|
19237
19907
|
{
|
|
19238
|
-
...response,
|
|
19239
19908
|
ok: true,
|
|
19240
|
-
|
|
19909
|
+
secret,
|
|
19241
19910
|
render: {
|
|
19242
19911
|
sections: [
|
|
19243
19912
|
{
|
|
19244
|
-
title: "
|
|
19245
|
-
lines: [
|
|
19913
|
+
title: "secret saved",
|
|
19914
|
+
lines: [`${secret.name}: saved (${secret.scope})`]
|
|
19246
19915
|
}
|
|
19247
19916
|
]
|
|
19248
19917
|
}
|
|
@@ -19250,27 +19919,100 @@ async function uploadChunkedSessions(sessions, options) {
|
|
|
19250
19919
|
{ json: options.json }
|
|
19251
19920
|
);
|
|
19252
19921
|
}
|
|
19253
|
-
|
|
19254
|
-
|
|
19255
|
-
|
|
19256
|
-
|
|
19257
|
-
|
|
19922
|
+
function registerSecretsCommands(program) {
|
|
19923
|
+
const secrets = program.command("secrets").description("Manage play secrets without revealing values.").addHelpText(
|
|
19924
|
+
"after",
|
|
19925
|
+
`
|
|
19926
|
+
Notes:
|
|
19927
|
+
Secret values are never accepted as command arguments, stdin pipes, env vars,
|
|
19928
|
+
or files. Use deepline secrets set NAME and type the value at the hidden TTY
|
|
19929
|
+
prompt. Agents can list/check metadata but should not enter secret values.
|
|
19930
|
+
|
|
19931
|
+
Examples:
|
|
19932
|
+
deepline secrets list
|
|
19933
|
+
deepline secrets check HUBSPOT_TOKEN
|
|
19934
|
+
deepline secrets set HUBSPOT_TOKEN
|
|
19935
|
+
`
|
|
19936
|
+
);
|
|
19937
|
+
secrets.command("list").description("List secret metadata only.").option("--json", "Emit JSON output").action(async (options) => {
|
|
19938
|
+
await handleList(options);
|
|
19939
|
+
});
|
|
19940
|
+
secrets.command("check").description("Check whether a secret exists and is active.").argument("<name>", "Secret name").option("--json", "Emit JSON output").action(async (name, options) => {
|
|
19941
|
+
await handleCheck(name, options);
|
|
19942
|
+
});
|
|
19943
|
+
secrets.command("set").description("Set or rotate a secret through a hidden interactive prompt.").argument("<name>", "Secret name").argument("[forbidden...]", "Do not pass secret values here").option("--json", "Emit JSON output").option("--scope <scope>", "Secret scope: org or play", "org").option("--play <name>", "Play name for play-scoped secrets").action(
|
|
19944
|
+
async (name, forbidden, options) => {
|
|
19945
|
+
await handleSet(name, forbidden ?? [], options);
|
|
19258
19946
|
}
|
|
19259
|
-
|
|
19260
|
-
|
|
19261
|
-
|
|
19262
|
-
|
|
19947
|
+
);
|
|
19948
|
+
}
|
|
19949
|
+
|
|
19950
|
+
// src/cli/commands/switch.ts
|
|
19951
|
+
var import_node_fs12 = require("fs");
|
|
19952
|
+
var import_node_os9 = require("os");
|
|
19953
|
+
var import_node_path15 = require("path");
|
|
19954
|
+
function hostSlugFromBaseUrl(baseUrl) {
|
|
19955
|
+
try {
|
|
19956
|
+
const url = new URL(baseUrl);
|
|
19957
|
+
const port = url.port ? Number.parseInt(url.port, 10) : null;
|
|
19958
|
+
let slug = (url.hostname || "unknown").replace(/[^a-zA-Z0-9]/g, "-");
|
|
19959
|
+
if (port && port !== 80 && port !== 443) {
|
|
19960
|
+
slug = `${slug}-${port}`;
|
|
19961
|
+
}
|
|
19962
|
+
return slug.toLowerCase().replace(/^-+|-+$/g, "") || "unknown";
|
|
19963
|
+
} catch {
|
|
19964
|
+
return "unknown";
|
|
19965
|
+
}
|
|
19966
|
+
}
|
|
19967
|
+
function resolveConfigScope() {
|
|
19968
|
+
const explicit = (process.env.DEEPLINE_CONFIG_SCOPE || "").trim();
|
|
19969
|
+
if (explicit) return explicit;
|
|
19970
|
+
return hostSlugFromBaseUrl(autoDetectBaseUrl());
|
|
19971
|
+
}
|
|
19972
|
+
function activeFamilyPath() {
|
|
19973
|
+
const home = process.env.HOME || process.env.USERPROFILE || (0, import_node_os9.homedir)();
|
|
19974
|
+
return (0, import_node_path15.join)(
|
|
19975
|
+
home,
|
|
19976
|
+
".local",
|
|
19977
|
+
"deepline",
|
|
19978
|
+
resolveConfigScope(),
|
|
19979
|
+
"cli",
|
|
19980
|
+
".active-family"
|
|
19981
|
+
);
|
|
19982
|
+
}
|
|
19983
|
+
function readActiveFamily() {
|
|
19984
|
+
const path = activeFamilyPath();
|
|
19985
|
+
try {
|
|
19986
|
+
return (0, import_node_fs12.readFileSync)(path, "utf-8").trim() || "sdk";
|
|
19987
|
+
} catch {
|
|
19988
|
+
return "sdk";
|
|
19989
|
+
}
|
|
19990
|
+
}
|
|
19991
|
+
function writeActiveFamily(family) {
|
|
19992
|
+
const path = activeFamilyPath();
|
|
19993
|
+
(0, import_node_fs12.mkdirSync)((0, import_node_path15.dirname)(path), { recursive: true });
|
|
19994
|
+
(0, import_node_fs12.writeFileSync)(path, `${family}
|
|
19995
|
+
`, "utf-8");
|
|
19996
|
+
return path;
|
|
19997
|
+
}
|
|
19998
|
+
function handleSwitch(action, options) {
|
|
19999
|
+
const normalized = (action || "status").trim().toLowerCase();
|
|
20000
|
+
if (normalized === "status") {
|
|
20001
|
+
const path = activeFamilyPath();
|
|
20002
|
+
const activeFamily = readActiveFamily();
|
|
19263
20003
|
printCommandEnvelope(
|
|
19264
20004
|
{
|
|
19265
|
-
...response2,
|
|
19266
20005
|
ok: true,
|
|
19267
|
-
|
|
20006
|
+
active_family: activeFamily,
|
|
20007
|
+
active_family_path: path,
|
|
20008
|
+
active_family_file_exists: (0, import_node_fs12.existsSync)(path),
|
|
19268
20009
|
render: {
|
|
19269
20010
|
sections: [
|
|
19270
20011
|
{
|
|
19271
|
-
title: "
|
|
20012
|
+
title: "cli switch",
|
|
19272
20013
|
lines: [
|
|
19273
|
-
`
|
|
20014
|
+
`Active CLI family: ${activeFamily}`,
|
|
20015
|
+
`Active family file: ${path}`
|
|
19274
20016
|
]
|
|
19275
20017
|
}
|
|
19276
20018
|
]
|
|
@@ -19278,213 +20020,99 @@ async function handleSessionsSend(options) {
|
|
|
19278
20020
|
},
|
|
19279
20021
|
{ json: options.json }
|
|
19280
20022
|
);
|
|
19281
|
-
return;
|
|
19282
|
-
}
|
|
19283
|
-
|
|
19284
|
-
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
|
|
19288
|
-
|
|
19289
|
-
|
|
19290
|
-
|
|
19291
|
-
|
|
19292
|
-
|
|
19293
|
-
|
|
19294
|
-
|
|
19295
|
-
|
|
19296
|
-
|
|
19297
|
-
|
|
19298
|
-
|
|
19299
|
-
|
|
19300
|
-
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
}
|
|
19322
|
-
|
|
19323
|
-
|
|
19324
|
-
|
|
19325
|
-
}
|
|
19326
|
-
|
|
19327
|
-
|
|
19328
|
-
|
|
19329
|
-
|
|
19330
|
-
|
|
19331
|
-
|
|
19332
|
-
|
|
19333
|
-
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
'const raw=document.getElementById("raw-sessions");',
|
|
19338
|
-
"if(!root||!raw)return;",
|
|
19339
|
-
'let sessions=[];try{sessions=JSON.parse(raw.textContent||"[]")}catch{}',
|
|
19340
|
-
'root.innerHTML="";',
|
|
19341
|
-
"for(const session of sessions){",
|
|
19342
|
-
'const section=document.createElement("section");section.className="section";',
|
|
19343
|
-
'const title=document.createElement("h2");title.textContent=String(session.label||"session");',
|
|
19344
|
-
'const pre=document.createElement("pre");',
|
|
19345
|
-
'pre.textContent=(Array.isArray(session.events)?session.events:[]).map((event)=>JSON.stringify(event)).join("\\n");',
|
|
19346
|
-
"section.append(title,pre);root.appendChild(section);",
|
|
19347
|
-
"}",
|
|
19348
|
-
"})();"
|
|
19349
|
-
].join("")
|
|
19350
|
-
};
|
|
19351
|
-
}
|
|
19352
|
-
function parsePreparedEvents(buffer) {
|
|
19353
|
-
return normalizedJsonLines(buffer).map((line) => {
|
|
19354
|
-
const parsed = parseJsonLine(line);
|
|
19355
|
-
return parsed ?? line;
|
|
19356
|
-
});
|
|
19357
|
-
}
|
|
19358
|
-
async function handleSessionsRender(options) {
|
|
19359
|
-
const targets = resolveSessionTargets({
|
|
19360
|
-
sessionIds: options.sessionId,
|
|
19361
|
-
labels: options.label,
|
|
19362
|
-
currentSession: options.currentSession
|
|
19363
|
-
});
|
|
19364
|
-
let outputPath = options.output ? (0, import_node_path14.resolve)(options.output) : "";
|
|
19365
|
-
if (!outputPath) {
|
|
19366
|
-
const outputDir = (0, import_node_path14.join)(process.cwd(), "deepline", "data");
|
|
19367
|
-
(0, import_node_fs11.mkdirSync)(outputDir, { recursive: true });
|
|
19368
|
-
outputPath = (0, import_node_path14.join)(
|
|
19369
|
-
outputDir,
|
|
19370
|
-
targets.length > 1 ? "session-viewer.html" : `session-${targets[0]?.sessionId}.html`
|
|
19371
|
-
);
|
|
20023
|
+
return 0;
|
|
20024
|
+
}
|
|
20025
|
+
if (normalized === "python" || normalized === "rollback") {
|
|
20026
|
+
const path = writeActiveFamily("python");
|
|
20027
|
+
printCommandEnvelope(
|
|
20028
|
+
{
|
|
20029
|
+
ok: true,
|
|
20030
|
+
active_family: "python",
|
|
20031
|
+
active_family_path: path,
|
|
20032
|
+
render: {
|
|
20033
|
+
sections: [
|
|
20034
|
+
{
|
|
20035
|
+
title: "cli switch",
|
|
20036
|
+
lines: [
|
|
20037
|
+
"Switched installer-managed `deepline` to the Python CLI."
|
|
20038
|
+
]
|
|
20039
|
+
}
|
|
20040
|
+
]
|
|
20041
|
+
}
|
|
20042
|
+
},
|
|
20043
|
+
{ json: options.json }
|
|
20044
|
+
);
|
|
20045
|
+
return 0;
|
|
20046
|
+
}
|
|
20047
|
+
if (normalized === "sdk") {
|
|
20048
|
+
const path = writeActiveFamily("sdk");
|
|
20049
|
+
printCommandEnvelope(
|
|
20050
|
+
{
|
|
20051
|
+
ok: true,
|
|
20052
|
+
active_family: "sdk",
|
|
20053
|
+
active_family_path: path,
|
|
20054
|
+
render: {
|
|
20055
|
+
sections: [
|
|
20056
|
+
{
|
|
20057
|
+
title: "cli switch",
|
|
20058
|
+
lines: ["Switched installer-managed `deepline` to the SDK CLI."]
|
|
20059
|
+
}
|
|
20060
|
+
]
|
|
20061
|
+
}
|
|
20062
|
+
},
|
|
20063
|
+
{ json: options.json }
|
|
20064
|
+
);
|
|
20065
|
+
return 0;
|
|
20066
|
+
}
|
|
20067
|
+
const message = `Unknown switch target: ${action}. Use one of: status, sdk, python, rollback.`;
|
|
20068
|
+
const envelope = {
|
|
20069
|
+
ok: false,
|
|
20070
|
+
error: message,
|
|
20071
|
+
code: "usage_error",
|
|
20072
|
+
render: {
|
|
20073
|
+
sections: [{ title: "cli switch", lines: [message] }]
|
|
20074
|
+
}
|
|
20075
|
+
};
|
|
20076
|
+
const wantsJson = options.json === true;
|
|
20077
|
+
if (wantsJson) {
|
|
20078
|
+
printCommandEnvelope(envelope, { json: true });
|
|
19372
20079
|
} else {
|
|
19373
|
-
|
|
20080
|
+
process.stderr.write(`${message}
|
|
20081
|
+
`);
|
|
19374
20082
|
}
|
|
19375
|
-
|
|
19376
|
-
label: target.label,
|
|
19377
|
-
events: parsePreparedEvents(
|
|
19378
|
-
prepareSessionBuffer((0, import_node_fs11.readFileSync)(target.filePath))
|
|
19379
|
-
)
|
|
19380
|
-
}));
|
|
19381
|
-
const { css, js } = fallbackViewerAssets();
|
|
19382
|
-
const refreshMeta = options.autoRefresh ? `<meta http-equiv="refresh" content="${Number.parseInt(options.autoRefresh, 10)}">` : "";
|
|
19383
|
-
const rawJson = JSON.stringify(sessions).replace(/<\//g, "<\\/");
|
|
19384
|
-
const html = `<!DOCTYPE html>
|
|
19385
|
-
<html lang="en">
|
|
19386
|
-
<head>
|
|
19387
|
-
<meta charset="UTF-8">
|
|
19388
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
19389
|
-
${refreshMeta}
|
|
19390
|
-
<title>Session Viewer</title>
|
|
19391
|
-
<style>${css}</style>
|
|
19392
|
-
</head>
|
|
19393
|
-
<body>
|
|
19394
|
-
<div class="layout">
|
|
19395
|
-
<div class="main" id="main-content"></div>
|
|
19396
|
-
</div>
|
|
19397
|
-
<script type="application/json" id="raw-sessions">${rawJson}</script>
|
|
19398
|
-
<script>${js}</script>
|
|
19399
|
-
</body>
|
|
19400
|
-
</html>`;
|
|
19401
|
-
(0, import_node_fs11.writeFileSync)(outputPath, html, "utf8");
|
|
19402
|
-
printCommandEnvelope(
|
|
19403
|
-
{
|
|
19404
|
-
ok: true,
|
|
19405
|
-
file: outputPath,
|
|
19406
|
-
session_count: targets.length,
|
|
19407
|
-
render: {
|
|
19408
|
-
sections: [
|
|
19409
|
-
{
|
|
19410
|
-
title: "sessions render",
|
|
19411
|
-
lines: [`Rendered session viewer: ${outputPath}`]
|
|
19412
|
-
}
|
|
19413
|
-
]
|
|
19414
|
-
}
|
|
19415
|
-
},
|
|
19416
|
-
{ json: options.json }
|
|
19417
|
-
);
|
|
19418
|
-
}
|
|
19419
|
-
function collectOption(value, previous) {
|
|
19420
|
-
previous.push(value);
|
|
19421
|
-
return previous;
|
|
20083
|
+
return 2;
|
|
19422
20084
|
}
|
|
19423
|
-
function
|
|
19424
|
-
|
|
20085
|
+
function registerSwitchCommands(program) {
|
|
20086
|
+
program.command("switch [target]").description(
|
|
20087
|
+
"Switch the installer-managed Deepline CLI between SDK and Python families."
|
|
20088
|
+
).option("--json", "Emit JSON output").addHelpText(
|
|
19425
20089
|
"after",
|
|
19426
20090
|
`
|
|
19427
20091
|
Notes:
|
|
19428
|
-
|
|
19429
|
-
|
|
19430
|
-
render writes a local HTML viewer.
|
|
20092
|
+
This command changes only the local installer-managed wrapper state. It does
|
|
20093
|
+
not re-authenticate, reinstall packages, or contact Deepline servers.
|
|
19431
20094
|
|
|
19432
20095
|
Examples:
|
|
19433
|
-
deepline
|
|
19434
|
-
deepline
|
|
19435
|
-
deepline
|
|
19436
|
-
|
|
19437
|
-
);
|
|
19438
|
-
sessions.command("send").description("Upload session transcript(s) or a local file to Deepline.").addHelpText(
|
|
19439
|
-
"after",
|
|
19440
|
-
`
|
|
19441
|
-
Examples:
|
|
19442
|
-
deepline sessions send --current-session --json
|
|
19443
|
-
deepline sessions send --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca --label "pilot run"
|
|
19444
|
-
deepline sessions send --file ./debug.log --json
|
|
19445
|
-
`
|
|
19446
|
-
).option(
|
|
19447
|
-
"--session-id <uuid>",
|
|
19448
|
-
"Claude session UUID. Repeat for multiple sessions.",
|
|
19449
|
-
collectOption,
|
|
19450
|
-
[]
|
|
19451
|
-
).option(
|
|
19452
|
-
"--label <label>",
|
|
19453
|
-
"Label for the preceding session id",
|
|
19454
|
-
collectOption,
|
|
19455
|
-
[]
|
|
19456
|
-
).option("--current-session", "Use the newest local Claude session JSONL").option("--file <path>", "Upload a raw local file instead of a session").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSessionsSend);
|
|
19457
|
-
sessions.command("render").description("Render local session transcript(s) to an HTML viewer.").addHelpText(
|
|
19458
|
-
"after",
|
|
19459
|
-
`
|
|
19460
|
-
Examples:
|
|
19461
|
-
deepline sessions render --current-session
|
|
19462
|
-
deepline sessions render --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca --output session.html
|
|
19463
|
-
deepline sessions render --current-session --auto-refresh 5 --json
|
|
20096
|
+
deepline switch status
|
|
20097
|
+
deepline switch python
|
|
20098
|
+
deepline switch rollback
|
|
20099
|
+
deepline switch sdk
|
|
19464
20100
|
`
|
|
19465
|
-
).
|
|
19466
|
-
|
|
19467
|
-
|
|
19468
|
-
collectOption,
|
|
19469
|
-
[]
|
|
19470
|
-
).option(
|
|
19471
|
-
"--label <label>",
|
|
19472
|
-
"Label for the preceding session id",
|
|
19473
|
-
collectOption,
|
|
19474
|
-
[]
|
|
19475
|
-
).option("--current-session", "Use the newest local Claude session JSONL").option("--auto-refresh <seconds>", "Add a browser auto-refresh interval").option("-o, --output <path>", "Output HTML path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSessionsRender);
|
|
20101
|
+
).action((target, options) => {
|
|
20102
|
+
process.exitCode = handleSwitch(target, options);
|
|
20103
|
+
});
|
|
19476
20104
|
}
|
|
19477
20105
|
|
|
19478
20106
|
// src/cli/commands/tools.ts
|
|
19479
20107
|
var import_commander2 = require("commander");
|
|
20108
|
+
var import_node_fs14 = require("fs");
|
|
20109
|
+
var import_node_os11 = require("os");
|
|
20110
|
+
var import_node_path17 = require("path");
|
|
20111
|
+
|
|
20112
|
+
// src/tool-output.ts
|
|
19480
20113
|
var import_node_fs13 = require("fs");
|
|
19481
20114
|
var import_node_os10 = require("os");
|
|
19482
20115
|
var import_node_path16 = require("path");
|
|
19483
|
-
|
|
19484
|
-
// src/tool-output.ts
|
|
19485
|
-
var import_node_fs12 = require("fs");
|
|
19486
|
-
var import_node_os9 = require("os");
|
|
19487
|
-
var import_node_path15 = require("path");
|
|
19488
20116
|
function isPlainObject(value) {
|
|
19489
20117
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
19490
20118
|
}
|
|
@@ -19580,19 +20208,19 @@ function tryConvertToList(payload, options) {
|
|
|
19580
20208
|
return null;
|
|
19581
20209
|
}
|
|
19582
20210
|
function ensureOutputDir() {
|
|
19583
|
-
const outputDir = (0,
|
|
19584
|
-
(0,
|
|
20211
|
+
const outputDir = (0, import_node_path16.join)((0, import_node_os10.homedir)(), ".local", "share", "deepline", "data");
|
|
20212
|
+
(0, import_node_fs13.mkdirSync)(outputDir, { recursive: true });
|
|
19585
20213
|
return outputDir;
|
|
19586
20214
|
}
|
|
19587
20215
|
function writeJsonOutputFile(payload, stem) {
|
|
19588
20216
|
const outputDir = ensureOutputDir();
|
|
19589
|
-
const outputPath = (0,
|
|
19590
|
-
(0,
|
|
20217
|
+
const outputPath = (0, import_node_path16.join)(outputDir, `${stem}_${Date.now()}.json`);
|
|
20218
|
+
(0, import_node_fs13.writeFileSync)(outputPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
19591
20219
|
return outputPath;
|
|
19592
20220
|
}
|
|
19593
20221
|
function writeCsvOutputFile(rows, stem) {
|
|
19594
20222
|
const outputDir = ensureOutputDir();
|
|
19595
|
-
const outputPath = (0,
|
|
20223
|
+
const outputPath = (0, import_node_path16.join)(outputDir, `${stem}_${Date.now()}.csv`);
|
|
19596
20224
|
const seen = /* @__PURE__ */ new Set();
|
|
19597
20225
|
const columns = [];
|
|
19598
20226
|
for (const row of rows) {
|
|
@@ -19615,7 +20243,7 @@ function writeCsvOutputFile(rows, stem) {
|
|
|
19615
20243
|
for (const row of rows) {
|
|
19616
20244
|
lines.push(columns.map((column) => escapeCell(row[column])).join(","));
|
|
19617
20245
|
}
|
|
19618
|
-
(0,
|
|
20246
|
+
(0, import_node_fs13.writeFileSync)(outputPath, `${lines.join("\n")}
|
|
19619
20247
|
`, "utf-8");
|
|
19620
20248
|
const previewRows = rows.slice(0, 5);
|
|
19621
20249
|
const previewColumns = columns.slice(0, 5);
|
|
@@ -19654,13 +20282,12 @@ var TOOL_COMMAND_TEMPLATES = {
|
|
|
19654
20282
|
};
|
|
19655
20283
|
function toListedTool(tool) {
|
|
19656
20284
|
if (isPlayLikeTool(tool)) {
|
|
19657
|
-
const playReference = playReferenceForTool(tool);
|
|
19658
20285
|
return {
|
|
19659
20286
|
...tool,
|
|
19660
20287
|
description: listedToolDescription(tool),
|
|
19661
20288
|
id: tool.toolId,
|
|
19662
20289
|
type: "play",
|
|
19663
|
-
executeCommand:
|
|
20290
|
+
executeCommand: playRunCommandForTool(tool, tool.toolId)
|
|
19664
20291
|
};
|
|
19665
20292
|
}
|
|
19666
20293
|
return {
|
|
@@ -19874,17 +20501,32 @@ function isPlayLikeTool(tool) {
|
|
|
19874
20501
|
}
|
|
19875
20502
|
function playReferenceForTool(tool) {
|
|
19876
20503
|
const record = tool;
|
|
19877
|
-
const
|
|
19878
|
-
|
|
20504
|
+
const declared = stringField(record, "playReference", "play_reference");
|
|
20505
|
+
if (declared.startsWith("prebuilt/")) {
|
|
20506
|
+
return declared;
|
|
20507
|
+
}
|
|
20508
|
+
return null;
|
|
20509
|
+
}
|
|
20510
|
+
function playSearchCommandForToolId(toolId) {
|
|
20511
|
+
return `deepline plays search ${JSON.stringify(toolId.replace(/_/g, " "))} --json`;
|
|
19879
20512
|
}
|
|
19880
|
-
function
|
|
19881
|
-
const playReference =
|
|
19882
|
-
return
|
|
19883
|
-
|
|
19884
|
-
|
|
20513
|
+
function playRunCommandForTool(tool, toolId, input2) {
|
|
20514
|
+
const playReference = playReferenceForTool(tool);
|
|
20515
|
+
if (!playReference) return playSearchCommandForToolId(toolId);
|
|
20516
|
+
const inputArg = input2 === void 0 ? "'{...}'" : `'${JSON.stringify(input2)}'`;
|
|
20517
|
+
return `deepline plays run ${playReference} --input ${inputArg} --watch`;
|
|
20518
|
+
}
|
|
20519
|
+
function playLikeToolExecuteErrorMessage(toolId, tool) {
|
|
20520
|
+
const playReference = tool ? playReferenceForTool(tool) : null;
|
|
20521
|
+
const nextStep = playReference ? `Use: deepline plays run ${playReference} --input '{...}' --watch` : `Find the maintained workflow with: ${playSearchCommandForToolId(toolId)}`;
|
|
20522
|
+
return [
|
|
20523
|
+
`${toolId} is a workflow/play entry, not an atomic provider tool.`,
|
|
20524
|
+
nextStep,
|
|
20525
|
+
'Or search provider tools only with: deepline tools search "<query>" --json'
|
|
20526
|
+
].join("\n");
|
|
19885
20527
|
}
|
|
19886
|
-
function printPlayLikeToolExecuteError(toolId) {
|
|
19887
|
-
console.error(playLikeToolExecuteErrorMessage(toolId));
|
|
20528
|
+
function printPlayLikeToolExecuteError(toolId, tool) {
|
|
20529
|
+
console.error(playLikeToolExecuteErrorMessage(toolId, tool));
|
|
19888
20530
|
}
|
|
19889
20531
|
function registerToolsCommands(program) {
|
|
19890
20532
|
const tools = program.command("tools").description("Search, describe, and execute atomic provider tools.").addHelpText(
|
|
@@ -20182,13 +20824,14 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
|
|
|
20182
20824
|
"deeplineUsdPerPricingUnit",
|
|
20183
20825
|
"deepline_usd_per_pricing_unit"
|
|
20184
20826
|
);
|
|
20185
|
-
const starterScript = extractedLists.length > 0 ? starterScriptJson(
|
|
20827
|
+
const starterScript = !isPlayLikeTool(tool) && extractedLists.length > 0 ? starterScriptJson(
|
|
20186
20828
|
seedToolListScript({
|
|
20187
20829
|
toolId,
|
|
20188
20830
|
payload: samplePayloadForInputFields(inputFields),
|
|
20189
20831
|
rows: []
|
|
20190
20832
|
})
|
|
20191
20833
|
) : null;
|
|
20834
|
+
const executeCommand = isPlayLikeTool(tool) ? playRunCommandForTool(tool, toolId) : `deepline tools execute ${toolId} --input '{...}' --json`;
|
|
20192
20835
|
return {
|
|
20193
20836
|
schemaVersion: 1,
|
|
20194
20837
|
toolId,
|
|
@@ -20213,7 +20856,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
|
|
|
20213
20856
|
extractedLists,
|
|
20214
20857
|
extractedValues
|
|
20215
20858
|
},
|
|
20216
|
-
executeCommand
|
|
20859
|
+
executeCommand,
|
|
20217
20860
|
...starterScript ? { starterScript } : {}
|
|
20218
20861
|
};
|
|
20219
20862
|
}
|
|
@@ -20325,6 +20968,10 @@ function printToolSchemaOnly(tool, requestedToolId) {
|
|
|
20325
20968
|
}
|
|
20326
20969
|
}
|
|
20327
20970
|
function printToolExamplesOnly(tool, requestedToolId, options = {}) {
|
|
20971
|
+
if (isPlayLikeTool(tool)) {
|
|
20972
|
+
printPlayLikeToolUsage(tool, requestedToolId);
|
|
20973
|
+
return;
|
|
20974
|
+
}
|
|
20328
20975
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
20329
20976
|
const toolId = String(contract.toolId);
|
|
20330
20977
|
const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
|
|
@@ -20356,6 +21003,25 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
|
|
|
20356
21003
|
printSamples(samples);
|
|
20357
21004
|
}
|
|
20358
21005
|
}
|
|
21006
|
+
function printPlayLikeToolUsage(tool, requestedToolId) {
|
|
21007
|
+
const toolId = String(tool.toolId || requestedToolId);
|
|
21008
|
+
const inputFields = toolInputFieldsForDisplay(
|
|
21009
|
+
recordField2(tool, "inputSchema", "input_schema")
|
|
21010
|
+
);
|
|
21011
|
+
const sampleInput = samplePayloadForInputFields(inputFields);
|
|
21012
|
+
const playReference = playReferenceForTool(tool);
|
|
21013
|
+
console.log("Run as a play:");
|
|
21014
|
+
console.log(
|
|
21015
|
+
playRunCommandForTool(
|
|
21016
|
+
tool,
|
|
21017
|
+
toolId,
|
|
21018
|
+
Object.keys(sampleInput).length ? sampleInput : void 0
|
|
21019
|
+
)
|
|
21020
|
+
);
|
|
21021
|
+
if (playReference) {
|
|
21022
|
+
console.log(`deepline plays describe ${playReference} --json`);
|
|
21023
|
+
}
|
|
21024
|
+
}
|
|
20359
21025
|
function printToolGettersOnly(tool, requestedToolId) {
|
|
20360
21026
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
20361
21027
|
const getters = isRecord8(contract.getters) ? contract.getters : {};
|
|
@@ -20442,13 +21108,14 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
|
|
|
20442
21108
|
const extractedLists = extractionContractEntries(
|
|
20443
21109
|
arrayField(toolExecutionResult, "extractedLists", "extracted_lists")
|
|
20444
21110
|
);
|
|
20445
|
-
const starterScript = extractedLists.length > 0 ? starterScriptJson(
|
|
21111
|
+
const starterScript = !isPlayLikeTool(tool) && extractedLists.length > 0 ? starterScriptJson(
|
|
20446
21112
|
seedToolListScript({
|
|
20447
21113
|
toolId,
|
|
20448
21114
|
payload: samplePayloadForInputFields(inputFields),
|
|
20449
21115
|
rows: []
|
|
20450
21116
|
})
|
|
20451
21117
|
) : null;
|
|
21118
|
+
const observedOutputCommand = isPlayLikeTool(tool) ? playRunCommandForTool(tool, toolId) : `deepline tools execute ${toolId} --input '{...}' --json`;
|
|
20452
21119
|
const {
|
|
20453
21120
|
cost: _cost,
|
|
20454
21121
|
deeplineCreditsPerPricingUnit: _deeplineCreditsPerPricingUnit,
|
|
@@ -20474,8 +21141,8 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
|
|
|
20474
21141
|
getterScope: "extractedValues/extractedLists .get() only works for declared Deepline getters listed in usageGuidance.toolExecutionResult.",
|
|
20475
21142
|
rawToolResponse: "Use toolExecutionResult.toolResponse.raw for provider/tool-specific fields, fields in outputSchema that are not declared getters, and debugging.",
|
|
20476
21143
|
invalidGetterHint: "If TypeScript says an extractedValues/extractedLists property does not exist, that field is not a declared Deepline getter.",
|
|
20477
|
-
observeActualShape:
|
|
20478
|
-
observedOutput:
|
|
21144
|
+
observeActualShape: observedOutputCommand,
|
|
21145
|
+
observedOutput: observedOutputCommand,
|
|
20479
21146
|
forPlayGetterBugs: "Run a tiny play, inspect `deepline runs get <run-id> --full --json`, and export returned dataset handles with `deepline runs export`. Backing tables exist only for ctx.map(...).run(...) stages that actually executed.",
|
|
20480
21147
|
executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run output and returned dataset handles."
|
|
20481
21148
|
},
|
|
@@ -20630,11 +21297,11 @@ function normalizeOutputFormat(raw) {
|
|
|
20630
21297
|
}
|
|
20631
21298
|
function resolveAtFilePath(rawPath) {
|
|
20632
21299
|
const trimmed = rawPath.trim();
|
|
20633
|
-
const resolved = (0,
|
|
20634
|
-
if ((0,
|
|
21300
|
+
const resolved = (0, import_node_path17.resolve)(trimmed);
|
|
21301
|
+
if ((0, import_node_fs14.existsSync)(resolved)) return resolved;
|
|
20635
21302
|
if (process.platform !== "win32" && trimmed.includes("\\")) {
|
|
20636
|
-
const normalized = (0,
|
|
20637
|
-
if ((0,
|
|
21303
|
+
const normalized = (0, import_node_path17.resolve)(trimmed.replace(/\\/g, "/"));
|
|
21304
|
+
if ((0, import_node_fs14.existsSync)(normalized)) return normalized;
|
|
20638
21305
|
}
|
|
20639
21306
|
return resolved;
|
|
20640
21307
|
}
|
|
@@ -20645,7 +21312,7 @@ function readJsonArgument(raw, flagName) {
|
|
|
20645
21312
|
throw new Error(`Invalid ${flagName} value: empty @file path.`);
|
|
20646
21313
|
}
|
|
20647
21314
|
try {
|
|
20648
|
-
return (0,
|
|
21315
|
+
return (0, import_node_fs14.readFileSync)(resolveAtFilePath(filePath), "utf8").replace(
|
|
20649
21316
|
/^\uFEFF/,
|
|
20650
21317
|
""
|
|
20651
21318
|
);
|
|
@@ -20747,9 +21414,9 @@ function starterScriptJson(script) {
|
|
|
20747
21414
|
function seedToolListScript(input2) {
|
|
20748
21415
|
const stem = safeFileStem(input2.toolId);
|
|
20749
21416
|
const fileName = `${stem}-workflow-seed-${Date.now()}.play.ts`;
|
|
20750
|
-
const scriptDir = (0,
|
|
20751
|
-
(0,
|
|
20752
|
-
const scriptPath = (0,
|
|
21417
|
+
const scriptDir = (0, import_node_fs14.mkdtempSync)((0, import_node_path17.join)((0, import_node_os11.tmpdir)(), "deepline-workflow-seed-"));
|
|
21418
|
+
(0, import_node_fs14.chmodSync)(scriptDir, 448);
|
|
21419
|
+
const scriptPath = (0, import_node_path17.join)(scriptDir, fileName);
|
|
20753
21420
|
const projectDir = `deepline/projects/${stem}-workflow`;
|
|
20754
21421
|
const playName = `${stem}-workflow`;
|
|
20755
21422
|
const sampleRows = input2.rows.length > 0 ? `${JSON.stringify(input2.rows.slice(0, 2)).replace(/\]$/, "")}, ...]` : "[]";
|
|
@@ -20785,7 +21452,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
20785
21452
|
};
|
|
20786
21453
|
});
|
|
20787
21454
|
`;
|
|
20788
|
-
(0,
|
|
21455
|
+
(0, import_node_fs14.writeFileSync)(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
20789
21456
|
return {
|
|
20790
21457
|
path: scriptPath,
|
|
20791
21458
|
sourceCode: script,
|
|
@@ -20886,9 +21553,19 @@ async function executeTool(args) {
|
|
|
20886
21553
|
}
|
|
20887
21554
|
if (isPlayLikeTool(metadata)) {
|
|
20888
21555
|
if (argsWantJson(args)) {
|
|
20889
|
-
printJsonError(
|
|
21556
|
+
printJsonError(
|
|
21557
|
+
new Error(
|
|
21558
|
+
playLikeToolExecuteErrorMessage(
|
|
21559
|
+
parsed.toolId,
|
|
21560
|
+
metadata
|
|
21561
|
+
)
|
|
21562
|
+
)
|
|
21563
|
+
);
|
|
20890
21564
|
} else {
|
|
20891
|
-
printPlayLikeToolExecuteError(
|
|
21565
|
+
printPlayLikeToolExecuteError(
|
|
21566
|
+
parsed.toolId,
|
|
21567
|
+
metadata
|
|
21568
|
+
);
|
|
20892
21569
|
}
|
|
20893
21570
|
return 2;
|
|
20894
21571
|
}
|
|
@@ -21040,7 +21717,7 @@ async function executeTool(args) {
|
|
|
21040
21717
|
|
|
21041
21718
|
// src/cli/commands/workflow.ts
|
|
21042
21719
|
var import_promises6 = require("fs/promises");
|
|
21043
|
-
var
|
|
21720
|
+
var import_node_path18 = require("path");
|
|
21044
21721
|
|
|
21045
21722
|
// src/cli/workflow-to-play.ts
|
|
21046
21723
|
var import_node_crypto6 = require("crypto");
|
|
@@ -21247,7 +21924,7 @@ function readStatus(payload) {
|
|
|
21247
21924
|
}
|
|
21248
21925
|
async function readJsonOption(payload, file) {
|
|
21249
21926
|
if (file) {
|
|
21250
|
-
const raw = await (0, import_promises6.readFile)((0,
|
|
21927
|
+
const raw = await (0, import_promises6.readFile)((0, import_node_path18.resolve)(file), "utf8");
|
|
21251
21928
|
return JSON.parse(raw);
|
|
21252
21929
|
}
|
|
21253
21930
|
if (payload) {
|
|
@@ -21281,8 +21958,8 @@ async function transformOne(api, workflowId, outDir, publish) {
|
|
|
21281
21958
|
revision.config,
|
|
21282
21959
|
{ workflowName: workflow.name, version: revision.version }
|
|
21283
21960
|
);
|
|
21284
|
-
const file = (0,
|
|
21285
|
-
await (0, import_promises6.mkdir)((0,
|
|
21961
|
+
const file = (0, import_node_path18.join)((0, import_node_path18.resolve)(outDir), `${compiled.playName}.play.ts`);
|
|
21962
|
+
await (0, import_promises6.mkdir)((0, import_node_path18.dirname)(file), { recursive: true });
|
|
21286
21963
|
await (0, import_promises6.writeFile)(file, compiled.sourceCode, "utf8");
|
|
21287
21964
|
let published = false;
|
|
21288
21965
|
if (publish) {
|
|
@@ -21529,8 +22206,8 @@ Notes:
|
|
|
21529
22206
|
|
|
21530
22207
|
// src/cli/commands/update.ts
|
|
21531
22208
|
var import_node_child_process2 = require("child_process");
|
|
21532
|
-
var
|
|
21533
|
-
var
|
|
22209
|
+
var import_node_fs15 = require("fs");
|
|
22210
|
+
var import_node_path19 = require("path");
|
|
21534
22211
|
function posixShellQuote(value) {
|
|
21535
22212
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
21536
22213
|
}
|
|
@@ -21549,19 +22226,19 @@ function buildSourceUpdateCommand(sourceRoot) {
|
|
|
21549
22226
|
return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
|
|
21550
22227
|
}
|
|
21551
22228
|
function findRepoBackedSdkRoot(startPath) {
|
|
21552
|
-
let current = (0,
|
|
22229
|
+
let current = (0, import_node_path19.resolve)(startPath);
|
|
21553
22230
|
while (true) {
|
|
21554
|
-
if ((0,
|
|
22231
|
+
if ((0, import_node_fs15.existsSync)((0, import_node_path19.join)(current, "sdk", "package.json")) && (0, import_node_fs15.existsSync)((0, import_node_path19.join)(current, "sdk", "bin", "deepline-dev.ts"))) {
|
|
21555
22232
|
return current;
|
|
21556
22233
|
}
|
|
21557
|
-
const parent = (0,
|
|
22234
|
+
const parent = (0, import_node_path19.dirname)(current);
|
|
21558
22235
|
if (parent === current) return null;
|
|
21559
22236
|
current = parent;
|
|
21560
22237
|
}
|
|
21561
22238
|
}
|
|
21562
22239
|
function resolveUpdatePlan() {
|
|
21563
|
-
const entrypoint = process.argv[1] ? (0,
|
|
21564
|
-
const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0,
|
|
22240
|
+
const entrypoint = process.argv[1] ? (0, import_node_path19.resolve)(process.argv[1]) : "";
|
|
22241
|
+
const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0, import_node_path19.dirname)(entrypoint)) : null;
|
|
21565
22242
|
if (sourceRoot) {
|
|
21566
22243
|
return {
|
|
21567
22244
|
kind: "source",
|
|
@@ -21728,6 +22405,23 @@ var install_commands_default = {
|
|
|
21728
22405
|
|
|
21729
22406
|
// src/cli/install-commands.ts
|
|
21730
22407
|
var INSTALL_COMMANDS = install_commands_default;
|
|
22408
|
+
var DEFAULT_V1_SKILL_NAMES = [
|
|
22409
|
+
"build-tam",
|
|
22410
|
+
"clay-to-deepline",
|
|
22411
|
+
"deepline-analytics",
|
|
22412
|
+
"deepline-feedback",
|
|
22413
|
+
"deepline-gtm",
|
|
22414
|
+
"deepline-quickstart",
|
|
22415
|
+
"find-qualified-titles",
|
|
22416
|
+
"linkedin-url-lookup",
|
|
22417
|
+
"niche-signal-discovery",
|
|
22418
|
+
"portfolio-prospecting",
|
|
22419
|
+
"workflow-hello-world"
|
|
22420
|
+
];
|
|
22421
|
+
var DEFAULT_SDK_SKILL_NAMES = [
|
|
22422
|
+
...DEFAULT_V1_SKILL_NAMES,
|
|
22423
|
+
"deepline-plays"
|
|
22424
|
+
];
|
|
21731
22425
|
function normalizeBaseUrl2(baseUrl) {
|
|
21732
22426
|
return baseUrl.replace(/\/$/, "");
|
|
21733
22427
|
}
|
|
@@ -21758,10 +22452,7 @@ function buildSkillsAddArgs(baseUrl, skillName, options = {}) {
|
|
|
21758
22452
|
return INSTALL_COMMANDS.skills.default_agents;
|
|
21759
22453
|
}
|
|
21760
22454
|
if (arg === "{skill_name}") {
|
|
21761
|
-
return [
|
|
21762
|
-
value,
|
|
21763
|
-
...extraSkillNames.flatMap((name) => ["--skill", name])
|
|
21764
|
-
];
|
|
22455
|
+
return [value, ...extraSkillNames.flatMap((name) => ["--skill", name])];
|
|
21765
22456
|
}
|
|
21766
22457
|
return value;
|
|
21767
22458
|
}
|
|
@@ -21805,8 +22496,8 @@ function commandCompatibilityHint(currentFamily, commandName, baseUrl) {
|
|
|
21805
22496
|
if (currentFamily === "sdk") {
|
|
21806
22497
|
lines.push(
|
|
21807
22498
|
"",
|
|
21808
|
-
" To stay on the SDK CLI,
|
|
21809
|
-
` ${skillsInstallCommand(baseUrl,
|
|
22499
|
+
" To stay on the SDK CLI, refresh the Deepline agent skills:",
|
|
22500
|
+
` ${skillsInstallCommand(baseUrl, DEFAULT_SDK_SKILL_NAMES)}`,
|
|
21810
22501
|
" To use the legacy Python CLI instead:",
|
|
21811
22502
|
` ${legacyPythonInstallCommand(baseUrl)}`,
|
|
21812
22503
|
" `deepline update` updates this SDK CLI, but it will not switch CLI families."
|
|
@@ -21817,9 +22508,9 @@ function commandCompatibilityHint(currentFamily, commandName, baseUrl) {
|
|
|
21817
22508
|
} else {
|
|
21818
22509
|
lines.push(
|
|
21819
22510
|
"",
|
|
21820
|
-
" To use SDK commands, install the SDK CLI and
|
|
22511
|
+
" To use SDK commands, install the SDK CLI and refresh Deepline agent skills:",
|
|
21821
22512
|
` ${sdkNpmGlobalInstallCommand()}`,
|
|
21822
|
-
` ${skillsInstallCommand(baseUrl,
|
|
22513
|
+
` ${skillsInstallCommand(baseUrl, DEFAULT_SDK_SKILL_NAMES)}`,
|
|
21823
22514
|
" `deepline update` updates this Python CLI and its skills, but it will not switch CLI families."
|
|
21824
22515
|
);
|
|
21825
22516
|
if (compatibility.python_alternative) {
|
|
@@ -21900,16 +22591,11 @@ async function maybeAutoUpdateAndRelaunch(response) {
|
|
|
21900
22591
|
|
|
21901
22592
|
// src/cli/skills-sync.ts
|
|
21902
22593
|
var import_node_child_process4 = require("child_process");
|
|
21903
|
-
var
|
|
21904
|
-
var
|
|
21905
|
-
var
|
|
22594
|
+
var import_node_fs16 = require("fs");
|
|
22595
|
+
var import_node_os12 = require("os");
|
|
22596
|
+
var import_node_path20 = require("path");
|
|
21906
22597
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
21907
|
-
var
|
|
21908
|
-
var SDK_SKILL_NAMES = [
|
|
21909
|
-
SDK_SKILL_NAME,
|
|
21910
|
-
"deepline-plays-feedback",
|
|
21911
|
-
"deepline-plays-quickstart"
|
|
21912
|
-
];
|
|
22598
|
+
var SDK_PLAY_SKILL_NAME = "deepline-plays";
|
|
21913
22599
|
var attemptedSync = false;
|
|
21914
22600
|
function shouldSkipSkillsSync() {
|
|
21915
22601
|
const value = process.env.DEEPLINE_SKIP_SKILLS_SYNC?.trim().toLowerCase();
|
|
@@ -21921,20 +22607,20 @@ function activePluginSkillsDir() {
|
|
|
21921
22607
|
return "";
|
|
21922
22608
|
}
|
|
21923
22609
|
const dir = process.env.DEEPLINE_PLUGIN_SKILLS_DIR?.trim() ?? "";
|
|
21924
|
-
return dir && (0,
|
|
22610
|
+
return dir && (0, import_node_fs16.existsSync)(dir) ? dir : "";
|
|
21925
22611
|
}
|
|
21926
22612
|
function readPluginSkillsVersion() {
|
|
21927
22613
|
const dir = activePluginSkillsDir();
|
|
21928
22614
|
if (!dir) return "";
|
|
21929
22615
|
try {
|
|
21930
|
-
return (0,
|
|
22616
|
+
return (0, import_node_fs16.readFileSync)((0, import_node_path20.join)(dir, ".version"), "utf-8").trim();
|
|
21931
22617
|
} catch {
|
|
21932
22618
|
return "";
|
|
21933
22619
|
}
|
|
21934
22620
|
}
|
|
21935
22621
|
function sdkSkillsVersionPath(baseUrl) {
|
|
21936
|
-
const home = process.env.HOME?.trim() || (0,
|
|
21937
|
-
return (0,
|
|
22622
|
+
const home = process.env.HOME?.trim() || (0, import_node_os12.homedir)();
|
|
22623
|
+
return (0, import_node_path20.join)(
|
|
21938
22624
|
home,
|
|
21939
22625
|
".local",
|
|
21940
22626
|
"deepline",
|
|
@@ -21947,55 +22633,46 @@ function readLocalSkillsVersion(baseUrl) {
|
|
|
21947
22633
|
const pluginVersion = readPluginSkillsVersion();
|
|
21948
22634
|
if (pluginVersion) return pluginVersion;
|
|
21949
22635
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
21950
|
-
if (!(0,
|
|
22636
|
+
if (!(0, import_node_fs16.existsSync)(path)) return "";
|
|
21951
22637
|
try {
|
|
21952
|
-
return (0,
|
|
22638
|
+
return (0, import_node_fs16.readFileSync)(path, "utf-8").trim();
|
|
21953
22639
|
} catch {
|
|
21954
22640
|
return "";
|
|
21955
22641
|
}
|
|
21956
22642
|
}
|
|
21957
22643
|
function writeLocalSkillsVersion(baseUrl, version) {
|
|
21958
22644
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
21959
|
-
(0,
|
|
21960
|
-
(0,
|
|
22645
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path20.dirname)(path), { recursive: true });
|
|
22646
|
+
(0, import_node_fs16.writeFileSync)(path, `${version}
|
|
21961
22647
|
`, "utf-8");
|
|
21962
22648
|
}
|
|
21963
|
-
function
|
|
21964
|
-
|
|
21965
|
-
|
|
21966
|
-
|
|
21967
|
-
|
|
21968
|
-
|
|
21969
|
-
|
|
21970
|
-
const
|
|
21971
|
-
|
|
21972
|
-
|
|
21973
|
-
|
|
21974
|
-
|
|
21975
|
-
|
|
21976
|
-
|
|
21977
|
-
|
|
21978
|
-
|
|
21979
|
-
|
|
21980
|
-
|
|
21981
|
-
|
|
21982
|
-
|
|
21983
|
-
|
|
21984
|
-
|
|
21985
|
-
|
|
21986
|
-
const text = (0, import_node_fs15.readFileSync)(path, "utf-8");
|
|
21987
|
-
if (staleMarkers.some((marker) => text.includes(marker))) return true;
|
|
21988
|
-
}
|
|
21989
|
-
return false;
|
|
21990
|
-
};
|
|
21991
|
-
for (const root of roots) {
|
|
21992
|
-
try {
|
|
21993
|
-
if ((0, import_node_fs15.existsSync)(root) && scan(root)) return true;
|
|
21994
|
-
} catch {
|
|
21995
|
-
continue;
|
|
21996
|
-
}
|
|
22649
|
+
function sortedUniqueSkillNames(names) {
|
|
22650
|
+
return [...new Set(names.map((name) => name.trim()).filter(Boolean))].sort(
|
|
22651
|
+
(a, b) => a.localeCompare(b)
|
|
22652
|
+
);
|
|
22653
|
+
}
|
|
22654
|
+
async function fetchV1SkillNames(baseUrl) {
|
|
22655
|
+
const controller = new AbortController();
|
|
22656
|
+
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
|
|
22657
|
+
try {
|
|
22658
|
+
const response = await fetch(
|
|
22659
|
+
new URL("/.well-known/skills/index.json", baseUrl),
|
|
22660
|
+
{ signal: controller.signal }
|
|
22661
|
+
);
|
|
22662
|
+
if (!response.ok) return [];
|
|
22663
|
+
const data = await response.json().catch(() => null);
|
|
22664
|
+
const names = (data?.skills ?? []).filter((skill) => skill.install_surface === "v1").map((skill) => skill.name).filter(
|
|
22665
|
+
(name) => typeof name === "string" && name.length > 0
|
|
22666
|
+
);
|
|
22667
|
+
return sortedUniqueSkillNames(names);
|
|
22668
|
+
} catch {
|
|
22669
|
+
return [];
|
|
22670
|
+
} finally {
|
|
22671
|
+
clearTimeout(timeout);
|
|
21997
22672
|
}
|
|
21998
|
-
|
|
22673
|
+
}
|
|
22674
|
+
function buildSdkSkillNames(v1SkillNames) {
|
|
22675
|
+
return sortedUniqueSkillNames([...v1SkillNames, SDK_PLAY_SKILL_NAME]);
|
|
21999
22676
|
}
|
|
22000
22677
|
async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
22001
22678
|
const controller = new AbortController();
|
|
@@ -22025,11 +22702,13 @@ async function fetchSkillsUpdate(baseUrl, localVersion) {
|
|
|
22025
22702
|
clearTimeout(timeout);
|
|
22026
22703
|
}
|
|
22027
22704
|
}
|
|
22028
|
-
function buildSkillsInstallArgs(baseUrl) {
|
|
22029
|
-
return buildSkillsAddArgs(baseUrl,
|
|
22705
|
+
function buildSkillsInstallArgs(baseUrl, skillNames = DEFAULT_SDK_SKILL_NAMES) {
|
|
22706
|
+
return buildSkillsAddArgs(baseUrl, sortedUniqueSkillNames(skillNames));
|
|
22030
22707
|
}
|
|
22031
|
-
function buildBunxSkillsInstallArgs(baseUrl) {
|
|
22032
|
-
return buildSkillsAddArgs(baseUrl,
|
|
22708
|
+
function buildBunxSkillsInstallArgs(baseUrl, skillNames) {
|
|
22709
|
+
return buildSkillsAddArgs(baseUrl, sortedUniqueSkillNames(skillNames), {
|
|
22710
|
+
firstArg: "--bun"
|
|
22711
|
+
});
|
|
22033
22712
|
}
|
|
22034
22713
|
function hasCommand(command) {
|
|
22035
22714
|
const result = (0, import_node_child_process4.spawnSync)(command, ["--version"], {
|
|
@@ -22041,15 +22720,15 @@ function hasCommand(command) {
|
|
|
22041
22720
|
function shellQuote4(arg) {
|
|
22042
22721
|
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
22043
22722
|
}
|
|
22044
|
-
function resolveSkillsInstallCommands(baseUrl) {
|
|
22045
|
-
const npxArgs = buildSkillsInstallArgs(baseUrl);
|
|
22723
|
+
function resolveSkillsInstallCommands(baseUrl, skillNames = DEFAULT_SDK_SKILL_NAMES) {
|
|
22724
|
+
const npxArgs = buildSkillsInstallArgs(baseUrl, skillNames);
|
|
22046
22725
|
const npxInstall = {
|
|
22047
22726
|
command: "npx",
|
|
22048
22727
|
args: npxArgs,
|
|
22049
22728
|
manualCommand: `npx ${npxArgs.map(shellQuote4).join(" ")}`
|
|
22050
22729
|
};
|
|
22051
22730
|
if (hasCommand("bunx")) {
|
|
22052
|
-
const bunxArgs = buildBunxSkillsInstallArgs(baseUrl);
|
|
22731
|
+
const bunxArgs = buildBunxSkillsInstallArgs(baseUrl, skillNames);
|
|
22053
22732
|
return [
|
|
22054
22733
|
{
|
|
22055
22734
|
command: "bunx",
|
|
@@ -22092,9 +22771,9 @@ function runOneSkillsInstall(install) {
|
|
|
22092
22771
|
});
|
|
22093
22772
|
});
|
|
22094
22773
|
}
|
|
22095
|
-
async function runSkillsInstall(baseUrl) {
|
|
22774
|
+
async function runSkillsInstall(baseUrl, skillNames) {
|
|
22096
22775
|
const failures = [];
|
|
22097
|
-
for (const install of resolveSkillsInstallCommands(baseUrl)) {
|
|
22776
|
+
for (const install of resolveSkillsInstallCommands(baseUrl, skillNames)) {
|
|
22098
22777
|
const result = await runOneSkillsInstall(install);
|
|
22099
22778
|
if (result.ok) return true;
|
|
22100
22779
|
failures.push(result);
|
|
@@ -22124,26 +22803,289 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
|
|
|
22124
22803
|
const usingPluginSkills = Boolean(activePluginSkillsDir());
|
|
22125
22804
|
const localVersion = readLocalSkillsVersion(baseUrl);
|
|
22126
22805
|
const update = await fetchSkillsUpdate(baseUrl, localVersion);
|
|
22127
|
-
const hasStaleInstalledSkill = installedSdkSkillHasStalePositionalExecuteExamples();
|
|
22128
22806
|
if (usingPluginSkills) {
|
|
22129
22807
|
return;
|
|
22130
22808
|
}
|
|
22131
|
-
if (!update?.needsUpdate
|
|
22809
|
+
if (!update?.needsUpdate || !update.remoteVersion) {
|
|
22132
22810
|
return;
|
|
22133
22811
|
}
|
|
22134
|
-
|
|
22135
|
-
|
|
22812
|
+
const remoteSkillNames = await fetchV1SkillNames(baseUrl);
|
|
22813
|
+
const skillNames = buildSdkSkillNames(
|
|
22814
|
+
remoteSkillNames.length > 0 ? remoteSkillNames : DEFAULT_SDK_SKILL_NAMES
|
|
22136
22815
|
);
|
|
22137
|
-
|
|
22816
|
+
if (skillNames.length === 0) return;
|
|
22817
|
+
writeSdkSkillsStatusLine("Deepline skills changed; syncing agent skills...");
|
|
22818
|
+
const installed = await runSkillsInstall(baseUrl, skillNames);
|
|
22138
22819
|
if (!installed) return;
|
|
22139
|
-
if (installedSdkSkillHasStalePositionalExecuteExamples()) {
|
|
22140
|
-
process.stderr.write(
|
|
22141
|
-
"SDK skills sync completed, but installed deepline-plays docs still contain stale positional ctx.tools.execute examples.\n"
|
|
22142
|
-
);
|
|
22143
|
-
return;
|
|
22144
|
-
}
|
|
22145
22820
|
writeLocalSkillsVersion(baseUrl, update.remoteVersion);
|
|
22146
|
-
writeSdkSkillsStatusLine("
|
|
22821
|
+
writeSdkSkillsStatusLine("Deepline agent skills are up to date.");
|
|
22822
|
+
}
|
|
22823
|
+
|
|
22824
|
+
// src/cli/failure-reporting.ts
|
|
22825
|
+
var import_node_os13 = require("os");
|
|
22826
|
+
var FAILURE_REPORT_DISABLE_ENV = "DEEPLINE_DISABLE_FAILURE_REPORTING";
|
|
22827
|
+
var REPORT_FAILURE_TIMEOUT_MS = 1e4;
|
|
22828
|
+
var MAX_FAILURE_TEXT_CHARS = 4e3;
|
|
22829
|
+
var MAX_COMMAND_TOKENS = 3;
|
|
22830
|
+
var REPORTABLE_EXIT_CODES = /* @__PURE__ */ new Set([4, 5]);
|
|
22831
|
+
var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
|
|
22832
|
+
var UNIX_PATH_RE = /(?:\/Users\/|\/home\/|\/var\/folders\/|\/tmp\/|\/sessions\/)[^"'\n\r`]+/g;
|
|
22833
|
+
var WINDOWS_PATH_RE = /[A-Za-z]:(?:\\{1,2})(?:Users|Temp|tmp)(?:\\{1,2})[^"'\n\r`]+/g;
|
|
22834
|
+
var ASSIGNMENT_SECRET_RE = /\b(access[_-]?token|api[_-]?key|apikey|auth(?:orization)?|bearer|password|secret|session)\b(\s*[:=]\s*)(?!Bearer\s+\[redacted-secret\])([^\s,;]+)/gi;
|
|
22835
|
+
var BEARER_SECRET_RE = /\b(bearer)\s+([A-Za-z0-9._-]+)/gi;
|
|
22836
|
+
var GENERIC_TOKEN_RE = /\b(?:dlp|sk|ghp|xox[baprs])[-_A-Za-z0-9]{8,}\b/g;
|
|
22837
|
+
var SECRET_OPTION_RE = /^--?(?:access[-_]?token|api[-_]?key|apikey|auth(?:orization)?|bearer|password|secret|session|token)$/i;
|
|
22838
|
+
function truthyEnv(name) {
|
|
22839
|
+
return ["1", "true", "yes", "on"].includes(
|
|
22840
|
+
String(process.env[name] ?? "").trim().toLowerCase()
|
|
22841
|
+
);
|
|
22842
|
+
}
|
|
22843
|
+
function isFailureReportingDisabled() {
|
|
22844
|
+
return truthyEnv(FAILURE_REPORT_DISABLE_ENV);
|
|
22845
|
+
}
|
|
22846
|
+
function redactFailureText(value, maxChars = MAX_FAILURE_TEXT_CHARS) {
|
|
22847
|
+
const home = process.env.HOME?.trim();
|
|
22848
|
+
let text = String(value ?? "");
|
|
22849
|
+
if (!text) return "";
|
|
22850
|
+
if (home && home !== "/") {
|
|
22851
|
+
text = text.split(home).join("~");
|
|
22852
|
+
}
|
|
22853
|
+
return text.replace(EMAIL_RE, "[redacted-email]").replace(UNIX_PATH_RE, "[redacted-path]").replace(WINDOWS_PATH_RE, "[redacted-path]").replace(BEARER_SECRET_RE, "$1 [redacted-secret]").replace(ASSIGNMENT_SECRET_RE, "$1$2[redacted-secret]").replace(GENERIC_TOKEN_RE, "[redacted-secret]").slice(0, maxChars);
|
|
22854
|
+
}
|
|
22855
|
+
function sanitizeCommand(argv, prefix = "deepline") {
|
|
22856
|
+
const tokens = [];
|
|
22857
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
22858
|
+
const arg = argv[index];
|
|
22859
|
+
const value = String(arg ?? "").trim();
|
|
22860
|
+
if (!value) continue;
|
|
22861
|
+
if (value.startsWith("-")) {
|
|
22862
|
+
if (!value.includes("=") && SECRET_OPTION_RE.test(value)) {
|
|
22863
|
+
index += 1;
|
|
22864
|
+
}
|
|
22865
|
+
continue;
|
|
22866
|
+
}
|
|
22867
|
+
tokens.push(redactFailureText(value, 200));
|
|
22868
|
+
if (tokens.length >= MAX_COMMAND_TOKENS) break;
|
|
22869
|
+
}
|
|
22870
|
+
return tokens.length > 0 ? [prefix, ...tokens].join(" ") : prefix;
|
|
22871
|
+
}
|
|
22872
|
+
function errorMessage3(error) {
|
|
22873
|
+
if (error instanceof Error) return `${error.name}: ${error.message}`;
|
|
22874
|
+
return String(error ?? "");
|
|
22875
|
+
}
|
|
22876
|
+
function errorStack(error) {
|
|
22877
|
+
if (error instanceof Error && error.stack) {
|
|
22878
|
+
return redactFailureText(error.stack);
|
|
22879
|
+
}
|
|
22880
|
+
return null;
|
|
22881
|
+
}
|
|
22882
|
+
function classifyNetworkFailure(error) {
|
|
22883
|
+
const seen = /* @__PURE__ */ new Set();
|
|
22884
|
+
let current = error;
|
|
22885
|
+
while (current && !seen.has(current)) {
|
|
22886
|
+
seen.add(current);
|
|
22887
|
+
const record = typeof current === "object" && current !== null ? current : {};
|
|
22888
|
+
const code = String(record.code ?? "").toLowerCase();
|
|
22889
|
+
const name = current instanceof Error ? current.name.toLowerCase() : "";
|
|
22890
|
+
const text = String(
|
|
22891
|
+
current instanceof Error ? current.message : current
|
|
22892
|
+
).toLowerCase();
|
|
22893
|
+
const combined = `${code} ${name} ${text}`;
|
|
22894
|
+
if (combined.includes("aborterror") || combined.includes("timeout") || combined.includes("timed out") || combined.includes("etimedout")) {
|
|
22895
|
+
return "network_timeout";
|
|
22896
|
+
}
|
|
22897
|
+
if (combined.includes("enotfound") || combined.includes("eai_again") || combined.includes("name or service not known") || combined.includes("temporary failure in name resolution")) {
|
|
22898
|
+
return "network_dns_resolution_failed";
|
|
22899
|
+
}
|
|
22900
|
+
if (combined.includes("econnrefused") || combined.includes("connection refused")) {
|
|
22901
|
+
return "network_connection_refused";
|
|
22902
|
+
}
|
|
22903
|
+
if (combined.includes("econnreset") || combined.includes("connection reset")) {
|
|
22904
|
+
return "network_connection_reset";
|
|
22905
|
+
}
|
|
22906
|
+
if (combined.includes("incompleteread") || combined.includes("incomplete read")) {
|
|
22907
|
+
return "network_incomplete_read";
|
|
22908
|
+
}
|
|
22909
|
+
if (combined.includes("remotedisconnected") || combined.includes("remote end closed connection") || combined.includes("other side closed") || combined.includes("socket hang up")) {
|
|
22910
|
+
return "network_remote_disconnected";
|
|
22911
|
+
}
|
|
22912
|
+
if (combined.includes("ssl") || combined.includes("tls") || combined.includes("unexpected_eof_while_reading")) {
|
|
22913
|
+
return "network_ssl_error";
|
|
22914
|
+
}
|
|
22915
|
+
current = record.cause ?? record.context;
|
|
22916
|
+
}
|
|
22917
|
+
return "network_error";
|
|
22918
|
+
}
|
|
22919
|
+
function isNetworkFailure(error) {
|
|
22920
|
+
if (!(error instanceof Error)) return false;
|
|
22921
|
+
if (error instanceof DeeplineError && error.statusCode) return false;
|
|
22922
|
+
const code = classifyNetworkFailure(error);
|
|
22923
|
+
return code !== "network_error" || /unable to connect|unable to stream/i.test(error.message);
|
|
22924
|
+
}
|
|
22925
|
+
function detectAgentRuntime3() {
|
|
22926
|
+
if (process.env.CODEX_THREAD_ID?.trim()) return "codex";
|
|
22927
|
+
const pluginMode = truthyEnv("DEEPLINE_PLUGIN_MODE");
|
|
22928
|
+
const claudeRemote = truthyEnv("CLAUDE_CODE_REMOTE");
|
|
22929
|
+
const projectDir = Boolean(process.env.CLAUDE_PROJECT_DIR?.trim());
|
|
22930
|
+
const pluginRoot = Boolean(process.env.DEEPLINE_PLUGIN_ROOT?.trim());
|
|
22931
|
+
const sessionHome = process.env.HOME?.trim().startsWith("/sessions/") ?? false;
|
|
22932
|
+
if ((pluginMode || pluginRoot) && (claudeRemote || projectDir || sessionHome)) {
|
|
22933
|
+
return "claude_cowork";
|
|
22934
|
+
}
|
|
22935
|
+
if (process.env.CLAUDECODE?.trim() === "1") return "claude_code";
|
|
22936
|
+
if (process.env.CLINE_ACTIVE?.trim().toLowerCase() === "true") return "cline";
|
|
22937
|
+
if (process.env.CURSOR_TRACE_ID?.trim() || process.env.CURSOR_AGENT?.trim()) {
|
|
22938
|
+
return "cursor";
|
|
22939
|
+
}
|
|
22940
|
+
if (process.env.WINDSURF?.trim() || process.env.CASCADE?.trim()) {
|
|
22941
|
+
return "windsurf";
|
|
22942
|
+
}
|
|
22943
|
+
return "unknown";
|
|
22944
|
+
}
|
|
22945
|
+
function buildEnvironmentContext() {
|
|
22946
|
+
const context = {
|
|
22947
|
+
os: (0, import_node_os13.platform)(),
|
|
22948
|
+
os_release: (0, import_node_os13.release)(),
|
|
22949
|
+
platform: `${(0, import_node_os13.platform)()}-${(0, import_node_os13.release)()}-${process.arch}`,
|
|
22950
|
+
node_version: process.version,
|
|
22951
|
+
runtime: "Node.js",
|
|
22952
|
+
hostname: (0, import_node_os13.hostname)(),
|
|
22953
|
+
agent_runtime: detectAgentRuntime3()
|
|
22954
|
+
};
|
|
22955
|
+
for (const key of ["CLAUDE_CODE_REMOTE", "DEEPLINE_PLUGIN_MODE"]) {
|
|
22956
|
+
const normalized = process.env[key]?.trim();
|
|
22957
|
+
if (normalized) context[key.toLowerCase()] = normalized;
|
|
22958
|
+
}
|
|
22959
|
+
if (process.env.CLAUDE_PROJECT_DIR?.trim()) {
|
|
22960
|
+
context.claude_project_dir_present = "true";
|
|
22961
|
+
}
|
|
22962
|
+
if (process.env.DEEPLINE_PLUGIN_ROOT?.trim()) {
|
|
22963
|
+
context.deepline_plugin_root_present = "true";
|
|
22964
|
+
}
|
|
22965
|
+
if (process.env.DEEPLINE_PLUGIN_SKILLS_DIR?.trim()) {
|
|
22966
|
+
context.deepline_plugin_skills_dir_present = "true";
|
|
22967
|
+
}
|
|
22968
|
+
if (process.env.HOME?.trim().startsWith("/sessions/")) {
|
|
22969
|
+
context.home_scope = "sessions";
|
|
22970
|
+
}
|
|
22971
|
+
return context;
|
|
22972
|
+
}
|
|
22973
|
+
function subcommandFromArgv(argv) {
|
|
22974
|
+
return argv.find((arg) => arg && !arg.startsWith("-")) ?? null;
|
|
22975
|
+
}
|
|
22976
|
+
function commandTokens(argv) {
|
|
22977
|
+
return argv.map((arg) => String(arg ?? "").trim()).filter((arg) => arg && !arg.startsWith("-"));
|
|
22978
|
+
}
|
|
22979
|
+
function isServerLoggedPlayRunStartFailure(input2) {
|
|
22980
|
+
const [subcommand, command] = commandTokens(input2.argv);
|
|
22981
|
+
return subcommand === "plays" && command === "run" && input2.error instanceof DeeplineError && typeof input2.error.statusCode === "number";
|
|
22982
|
+
}
|
|
22983
|
+
function shouldReport(input2) {
|
|
22984
|
+
if (input2.error !== void 0) return true;
|
|
22985
|
+
return input2.exitCode !== null && REPORTABLE_EXIT_CODES.has(input2.exitCode);
|
|
22986
|
+
}
|
|
22987
|
+
function failureCode(input2) {
|
|
22988
|
+
if (input2.error !== void 0) {
|
|
22989
|
+
if (isNetworkFailure(input2.error))
|
|
22990
|
+
return classifyNetworkFailure(input2.error);
|
|
22991
|
+
if (input2.error instanceof DeeplineError && input2.error.code) {
|
|
22992
|
+
return input2.error.code;
|
|
22993
|
+
}
|
|
22994
|
+
if (input2.error instanceof Error && input2.error.name)
|
|
22995
|
+
return input2.error.name;
|
|
22996
|
+
return "CLI_FAILURE";
|
|
22997
|
+
}
|
|
22998
|
+
return input2.exitCode === 4 ? "network_error" : "command_exit";
|
|
22999
|
+
}
|
|
23000
|
+
function resolvedExitCode(input2) {
|
|
23001
|
+
if (typeof input2.exitCode === "number" && Number.isFinite(input2.exitCode)) {
|
|
23002
|
+
return Math.trunc(input2.exitCode);
|
|
23003
|
+
}
|
|
23004
|
+
if (input2.error === void 0) return null;
|
|
23005
|
+
return isNetworkFailure(input2.error) ? 4 : 5;
|
|
23006
|
+
}
|
|
23007
|
+
function resolveSdkCliFailureExitCode(error) {
|
|
23008
|
+
return isNetworkFailure(error) ? 4 : 1;
|
|
23009
|
+
}
|
|
23010
|
+
function resolvedFailureKind(input2) {
|
|
23011
|
+
return input2.error === void 0 ? "command_exit" : "uncaught_exception";
|
|
23012
|
+
}
|
|
23013
|
+
function buildFailureReport(input2) {
|
|
23014
|
+
const durationMs = Math.max(0, Date.now() - input2.startedAtMs);
|
|
23015
|
+
const failureKind = resolvedFailureKind({
|
|
23016
|
+
exitCode: input2.exitCode,
|
|
23017
|
+
error: input2.error
|
|
23018
|
+
});
|
|
23019
|
+
const code = failureCode({ exitCode: input2.exitCode, error: input2.error });
|
|
23020
|
+
const errorBody = input2.error === void 0 ? `SDK CLI command exited ${input2.exitCode ?? "unknown"}` : errorMessage3(input2.error);
|
|
23021
|
+
return {
|
|
23022
|
+
command: sanitizeCommand(input2.argv),
|
|
23023
|
+
subcommand: subcommandFromArgv(input2.argv),
|
|
23024
|
+
error_status: input2.exitCode,
|
|
23025
|
+
error_body: redactFailureText(errorBody),
|
|
23026
|
+
exit_code: input2.exitCode,
|
|
23027
|
+
duration_ms: durationMs,
|
|
23028
|
+
error_class: input2.error instanceof Error ? input2.error.name : null,
|
|
23029
|
+
stack_trace: errorStack(input2.error),
|
|
23030
|
+
failure_kind: failureKind,
|
|
23031
|
+
failure_code: code,
|
|
23032
|
+
failure_stage: input2.error === void 0 ? "command_exit" : "cli_main",
|
|
23033
|
+
cli_version: SDK_VERSION,
|
|
23034
|
+
context: {
|
|
23035
|
+
base_url: redactFailureText(input2.baseUrl, 400),
|
|
23036
|
+
command_summary: sanitizeCommand(input2.argv),
|
|
23037
|
+
environment: buildEnvironmentContext(),
|
|
23038
|
+
failure_kind: failureKind,
|
|
23039
|
+
failure_code: code,
|
|
23040
|
+
failure_stage: input2.error === void 0 ? "command_exit" : "cli_main",
|
|
23041
|
+
duration_ms: durationMs,
|
|
23042
|
+
...input2.exitCode !== null ? { exit_code: input2.exitCode } : {}
|
|
23043
|
+
}
|
|
23044
|
+
};
|
|
23045
|
+
}
|
|
23046
|
+
async function maybeReportSdkCliFailure(input2) {
|
|
23047
|
+
if (isFailureReportingDisabled()) return false;
|
|
23048
|
+
if (isServerLoggedPlayRunStartFailure(input2)) return false;
|
|
23049
|
+
const exitCode = resolvedExitCode(input2);
|
|
23050
|
+
if (!shouldReport({ exitCode, error: input2.error })) return false;
|
|
23051
|
+
const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
|
|
23052
|
+
const apiKey = resolveApiKeyForBaseUrl(baseUrl);
|
|
23053
|
+
if (!apiKey) return false;
|
|
23054
|
+
const controller = new AbortController();
|
|
23055
|
+
const timeout = setTimeout(
|
|
23056
|
+
() => controller.abort(),
|
|
23057
|
+
REPORT_FAILURE_TIMEOUT_MS
|
|
23058
|
+
);
|
|
23059
|
+
try {
|
|
23060
|
+
await fetch(new URL("/api/v2/cli/report-failure", baseUrl), {
|
|
23061
|
+
method: "POST",
|
|
23062
|
+
headers: {
|
|
23063
|
+
Authorization: `Bearer ${apiKey}`,
|
|
23064
|
+
"Content-Type": "application/json",
|
|
23065
|
+
"User-Agent": `deepline-ts-sdk/${SDK_VERSION}`,
|
|
23066
|
+
"X-Deepline-Client-Family": "sdk",
|
|
23067
|
+
"X-Deepline-CLI-Family": "sdk",
|
|
23068
|
+
"X-Deepline-Agent-Runtime": detectAgentRuntime3(),
|
|
23069
|
+
"X-Deepline-CLI-Version": SDK_VERSION,
|
|
23070
|
+
"X-Deepline-SDK-Version": SDK_VERSION
|
|
23071
|
+
},
|
|
23072
|
+
body: JSON.stringify(
|
|
23073
|
+
buildFailureReport({
|
|
23074
|
+
argv: input2.argv,
|
|
23075
|
+
startedAtMs: input2.startedAtMs,
|
|
23076
|
+
error: input2.error,
|
|
23077
|
+
exitCode,
|
|
23078
|
+
baseUrl
|
|
23079
|
+
})
|
|
23080
|
+
),
|
|
23081
|
+
signal: controller.signal
|
|
23082
|
+
});
|
|
23083
|
+
return true;
|
|
23084
|
+
} catch {
|
|
23085
|
+
return false;
|
|
23086
|
+
} finally {
|
|
23087
|
+
clearTimeout(timeout);
|
|
23088
|
+
}
|
|
22147
23089
|
}
|
|
22148
23090
|
|
|
22149
23091
|
// src/cli/index.ts
|
|
@@ -22187,8 +23129,8 @@ function topLevelCommandKnown(program, commandName) {
|
|
|
22187
23129
|
);
|
|
22188
23130
|
}
|
|
22189
23131
|
async function runPlayRunnerHealthCheck() {
|
|
22190
|
-
const dir = await (0, import_promises7.mkdtemp)((0,
|
|
22191
|
-
const file = (0,
|
|
23132
|
+
const dir = await (0, import_promises7.mkdtemp)((0, import_node_path21.join)((0, import_node_os14.tmpdir)(), "deepline-health-play-"));
|
|
23133
|
+
const file = (0, import_node_path21.join)(dir, "health-check.play.ts");
|
|
22192
23134
|
try {
|
|
22193
23135
|
await (0, import_promises7.writeFile)(
|
|
22194
23136
|
file,
|
|
@@ -22405,7 +23347,7 @@ Exit codes:
|
|
|
22405
23347
|
`
|
|
22406
23348
|
);
|
|
22407
23349
|
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
22408
|
-
if (actionCommand.name() === "version" || actionCommand.name() === "update" || isLegacyNoopInvocation()) {
|
|
23350
|
+
if (actionCommand.name() === "version" || actionCommand.name() === "update" || actionCommand.name() === "switch" || isLegacyNoopInvocation()) {
|
|
22409
23351
|
return;
|
|
22410
23352
|
}
|
|
22411
23353
|
if (printStartupPhase) {
|
|
@@ -22453,6 +23395,7 @@ Exit codes:
|
|
|
22453
23395
|
registerLegacyNoopCommands(program);
|
|
22454
23396
|
registerUpdateCommand(program);
|
|
22455
23397
|
registerQuickstartCommands(program);
|
|
23398
|
+
registerSwitchCommands(program);
|
|
22456
23399
|
program.command("preflight").description("Run compact health, auth, and Deepline billing checks.").option("--json", "Force JSON output.").addHelpText(
|
|
22457
23400
|
"after",
|
|
22458
23401
|
`
|
|
@@ -22572,6 +23515,11 @@ Examples:
|
|
|
22572
23515
|
ms: Date.now() - mainStartedAt,
|
|
22573
23516
|
ok: true
|
|
22574
23517
|
});
|
|
23518
|
+
await maybeReportSdkCliFailure({
|
|
23519
|
+
argv: process.argv.slice(2),
|
|
23520
|
+
startedAtMs: mainStartedAt,
|
|
23521
|
+
exitCode: typeof process.exitCode === "number" ? process.exitCode : null
|
|
23522
|
+
});
|
|
22575
23523
|
} catch (error) {
|
|
22576
23524
|
const commanderError = asCommanderError(error);
|
|
22577
23525
|
recordCliTrace({
|
|
@@ -22607,7 +23555,14 @@ Examples:
|
|
|
22607
23555
|
} else {
|
|
22608
23556
|
console.error(`Error: ${String(error)}`);
|
|
22609
23557
|
}
|
|
22610
|
-
|
|
23558
|
+
const failureExitCode = resolveSdkCliFailureExitCode(error);
|
|
23559
|
+
process.exitCode = failureExitCode;
|
|
23560
|
+
await maybeReportSdkCliFailure({
|
|
23561
|
+
argv: process.argv.slice(2),
|
|
23562
|
+
startedAtMs: mainStartedAt,
|
|
23563
|
+
exitCode: failureExitCode,
|
|
23564
|
+
error
|
|
23565
|
+
});
|
|
22611
23566
|
}
|
|
22612
23567
|
process.exitCode = process.exitCode ?? 0;
|
|
22613
23568
|
}
|