deepline 0.3.293 → 0.3.295
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/bundling-sources/sdk/src/compat.ts +35 -7
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +269 -267
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +3 -4
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +14 -1
- package/dist/cli/auth-EDTRERZP.mjs +31 -0
- package/dist/cli/auth-TDHKB277.js +31 -0
- package/dist/cli/billing-ELIDPILY.js +1749 -0
- package/dist/cli/billing-EXGXJVAU.mjs +1749 -0
- package/dist/cli/chunk-2S5Y3TZP.js +5688 -0
- package/dist/cli/chunk-3KC45UVF.js +150 -0
- package/dist/cli/chunk-3XX5TEA4.js +15 -0
- package/dist/cli/chunk-4MBOJQPL.mjs +0 -0
- package/dist/cli/chunk-4NEFYBF7.js +2240 -0
- package/dist/cli/chunk-5JLQPVNX.js +3056 -0
- package/dist/cli/chunk-5KDKFHZI.js +1269 -0
- package/dist/cli/chunk-6A3TJUWC.js +76 -0
- package/dist/cli/chunk-6O5YZOVN.mjs +890 -0
- package/dist/cli/chunk-7P5AA3SN.js +344 -0
- package/dist/cli/chunk-AYHLUHE5.js +52 -0
- package/dist/cli/chunk-CBVIJSHF.mjs +560 -0
- package/dist/cli/chunk-CFVVCN4A.js +735 -0
- package/dist/cli/chunk-COGTRVWW.js +30 -0
- package/dist/cli/chunk-D4AZUZO4.js +190 -0
- package/dist/cli/chunk-DLIRSHV6.mjs +28 -0
- package/dist/cli/chunk-EACVZI6N.mjs +1995 -0
- package/dist/cli/chunk-EDWJY6T4.mjs +3056 -0
- package/dist/cli/chunk-EXMLR72R.mjs +0 -0
- package/dist/cli/chunk-F35VOIQL.js +890 -0
- package/dist/cli/chunk-GAMFK2ZP.mjs +148 -0
- package/dist/cli/chunk-GHRQQPW2.mjs +2240 -0
- package/dist/cli/chunk-GONRLVV3.js +3440 -0
- package/dist/cli/chunk-GVUJJJ5G.mjs +76 -0
- package/dist/cli/chunk-H7HIDGZ7.mjs +150 -0
- package/dist/cli/chunk-HUZXWOSK.mjs +503 -0
- package/dist/cli/chunk-I5YZLGMG.js +1 -0
- package/dist/cli/chunk-ITOFSXIU.mjs +15 -0
- package/dist/cli/chunk-J5NA2TTX.js +503 -0
- package/dist/cli/chunk-JCS5TPNY.mjs +3440 -0
- package/dist/cli/chunk-JNNOCMQY.js +47 -0
- package/dist/cli/chunk-KRD6H754.mjs +30 -0
- package/dist/cli/chunk-LA46MMTL.js +560 -0
- package/dist/cli/chunk-LDWT7RJK.mjs +190 -0
- package/dist/cli/chunk-M2V26OR5.js +499 -0
- package/dist/cli/chunk-MIMASXO4.mjs +389 -0
- package/dist/cli/chunk-MROPCKW2.mjs +190 -0
- package/dist/cli/chunk-NXDRDJ4K.js +389 -0
- package/dist/cli/chunk-NXGCO4JB.mjs +344 -0
- package/dist/cli/chunk-OG64RBAC.js +6 -0
- package/dist/cli/chunk-OQYEC4GL.mjs +34 -0
- package/dist/cli/chunk-OWJZUE3Y.mjs +103 -0
- package/dist/cli/chunk-SDK3ZA2C.js +103 -0
- package/dist/cli/chunk-SFLARO4Y.mjs +47 -0
- package/dist/cli/chunk-SVIIIYIQ.js +923 -0
- package/dist/cli/chunk-TAC3JIFE.mjs +354 -0
- package/dist/cli/chunk-U3PI7HBM.js +34 -0
- package/dist/cli/chunk-UFS752TU.mjs +52 -0
- package/dist/cli/chunk-UI37DQE7.mjs +499 -0
- package/dist/cli/chunk-UNEMDKXN.js +1 -0
- package/dist/cli/chunk-WB6Z2Q44.mjs +1622 -0
- package/dist/cli/chunk-WJYPBHTE.js +148 -0
- package/dist/cli/chunk-WOHBA5PV.js +354 -0
- package/dist/cli/chunk-WQICC2B3.js +190 -0
- package/dist/cli/chunk-WZYKRD7T.mjs +6 -0
- package/dist/cli/chunk-XL2FO6CW.mjs +5688 -0
- package/dist/cli/chunk-XNYIZMNQ.js +28 -0
- package/dist/cli/chunk-XRK2TZT5.js +1995 -0
- package/dist/cli/chunk-YJYPL5OK.mjs +1269 -0
- package/dist/cli/chunk-YWNDYCXR.mjs +735 -0
- package/dist/cli/chunk-YWWAF7KZ.mjs +923 -0
- package/dist/cli/chunk-ZDO3TO3M.js +1622 -0
- package/dist/cli/client-4JVOYVC5.js +20 -0
- package/dist/cli/client-EEN2IJN2.mjs +20 -0
- package/dist/cli/csv-BOHBEP2I.mjs +585 -0
- package/dist/cli/csv-S5NYRUEX.js +585 -0
- package/dist/cli/db-CLODWCOB.mjs +486 -0
- package/dist/cli/db-F6PKFOOV.js +486 -0
- package/dist/cli/executions-5TQAKLJU.js +28 -0
- package/dist/cli/executions-MAKW6QOY.mjs +28 -0
- package/dist/cli/feedback-GV4QJFEN.js +107 -0
- package/dist/cli/feedback-XY24TW7H.mjs +107 -0
- package/dist/cli/getting-started.js +18 -76
- package/dist/cli/getting-started.mjs +10 -37
- package/dist/cli/git-6S2C22T5.js +23 -0
- package/dist/cli/git-OZLBD5HH.mjs +23 -0
- package/dist/cli/index.js +291 -54294
- package/dist/cli/index.mjs +209 -54352
- package/dist/cli/legacy-noops-LLCGDPQE.js +157 -0
- package/dist/cli/legacy-noops-TYH3TW2D.mjs +157 -0
- package/dist/cli/monitors-CABMB4DW.js +3251 -0
- package/dist/cli/monitors-TTD55HMK.mjs +3251 -0
- package/dist/cli/org-MSF3RG76.js +849 -0
- package/dist/cli/org-SFJ77MWY.mjs +849 -0
- package/dist/cli/play-NXHJSCKL.mjs +18439 -0
- package/dist/cli/play-OKSFNNO4.js +18439 -0
- package/dist/cli/providers-QC2ERERL.js +95 -0
- package/dist/cli/providers-TIIAZEUA.mjs +95 -0
- package/dist/cli/proxy-env-EBANZHPV.js +161 -0
- package/dist/cli/proxy-env-M4ZHXHRL.mjs +161 -0
- package/dist/cli/quickstart-ICANHLMI.js +69 -0
- package/dist/cli/quickstart-KWUOP3FI.mjs +69 -0
- package/dist/cli/search-U5H2FDOO.mjs +18 -0
- package/dist/cli/search-XQRQOLWS.js +18 -0
- package/dist/cli/secrets-3J6YTWFA.mjs +400 -0
- package/dist/cli/secrets-YNMNZNOM.js +400 -0
- package/dist/cli/sessions-CLEES4L6.mjs +15 -0
- package/dist/cli/sessions-J6VR47FG.js +15 -0
- package/dist/cli/settings-EEKZMTY4.js +846 -0
- package/dist/cli/settings-UVLOQZRH.mjs +846 -0
- package/dist/cli/setup-DLMDXPGJ.mjs +1257 -0
- package/dist/cli/setup-HC4Q7QM4.js +1257 -0
- package/dist/cli/skills-33PHFBSB.mjs +28 -0
- package/dist/cli/skills-LFRY3KPX.js +28 -0
- package/dist/cli/tools-3COEPKML.mjs +2955 -0
- package/dist/cli/tools-QXEVCN45.js +2955 -0
- package/dist/cli/update-LSAC4FF5.mjs +28 -0
- package/dist/cli/update-NXHSPYXA.js +28 -0
- package/dist/cli/workflow-7S44OECF.mjs +1936 -0
- package/dist/cli/workflow-QPZWYSZE.js +1936 -0
- package/dist/helpers.js +6 -32
- package/dist/helpers.mjs +2 -0
- package/dist/index.js +1972 -12825
- package/dist/index.mjs +1910 -12672
- package/dist/install-integrity.json +110 -0
- package/dist/monitor-fleet-contract.js +31 -730
- package/dist/monitor-fleet-contract.mjs +18 -667
- package/dist/monitor-input-contract.js +10 -390
- package/dist/monitor-input-contract.mjs +7 -348
- package/dist/plays/bundle-play-file.mjs +13 -5178
- package/dist/plays/csv-data-input.js +5 -3132
- package/dist/plays/csv-data-input.mjs +5 -3105
- package/dist/plays/run-request.js +56 -664
- package/dist/plays/run-request.mjs +30 -588
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +5 -212
- package/dist/release.mjs +5 -185
- package/package.json +1 -1
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAuthedHttpClient,
|
|
3
|
+
printCommandEnvelope
|
|
4
|
+
} from "./chunk-6O5YZOVN.mjs";
|
|
5
|
+
|
|
6
|
+
// src/cli/commands/sessions.ts
|
|
7
|
+
import {
|
|
8
|
+
existsSync,
|
|
9
|
+
mkdirSync,
|
|
10
|
+
readdirSync,
|
|
11
|
+
readFileSync,
|
|
12
|
+
realpathSync,
|
|
13
|
+
statSync,
|
|
14
|
+
writeFileSync
|
|
15
|
+
} from "fs";
|
|
16
|
+
import { homedir, platform } from "os";
|
|
17
|
+
import { basename, dirname, join, resolve } from "path";
|
|
18
|
+
import { gzipSync } from "zlib";
|
|
19
|
+
import { randomUUID } from "crypto";
|
|
20
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
21
|
+
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;
|
|
22
|
+
var MAX_SESSION_UPLOAD_BYTES = 35e5;
|
|
23
|
+
var MAX_DIRECT_SESSION_DECODED_BYTES = 50 * 1024 * 1024;
|
|
24
|
+
var CHUNK_SIZE_BYTES = 25e5;
|
|
25
|
+
var MAX_EVENT_STRING_CHARS = 8e3;
|
|
26
|
+
var MAX_EVENT_LIST_ITEMS = 40;
|
|
27
|
+
var MAX_EVENT_OBJECT_KEYS = 80;
|
|
28
|
+
var TRUNCATION_MARKER = "...[truncated]";
|
|
29
|
+
var NOISE_EVENT_TYPES = /* @__PURE__ */ new Set(["progress", "file-history-snapshot"]);
|
|
30
|
+
function homeDir() {
|
|
31
|
+
return process.env.HOME?.trim() || homedir();
|
|
32
|
+
}
|
|
33
|
+
function detectShellContext() {
|
|
34
|
+
const shellPath = process.env.SHELL?.trim() || process.env.ComSpec?.trim() || process.env.COMSPEC?.trim() || "";
|
|
35
|
+
return {
|
|
36
|
+
shell: shellPath ? basename(shellPath).replace(/\.exe$/i, "") : "unknown",
|
|
37
|
+
shell_path: shellPath || null,
|
|
38
|
+
os: platform(),
|
|
39
|
+
cwd: process.cwd()
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function claudeProjectsRoot() {
|
|
43
|
+
return join(homeDir(), ".claude", "projects");
|
|
44
|
+
}
|
|
45
|
+
function codexSessionsRoot() {
|
|
46
|
+
return join(homeDir(), ".codex", "sessions");
|
|
47
|
+
}
|
|
48
|
+
function listClaudeSessionFiles() {
|
|
49
|
+
const root = claudeProjectsRoot();
|
|
50
|
+
if (!existsSync(root)) return [];
|
|
51
|
+
const projectDirs = readDirectoryNames(root);
|
|
52
|
+
const files = [];
|
|
53
|
+
for (const projectDir of projectDirs) {
|
|
54
|
+
const fullProjectDir = join(root, projectDir);
|
|
55
|
+
for (const fileName of readDirectoryNames(fullProjectDir)) {
|
|
56
|
+
if (fileName.endsWith(".jsonl")) {
|
|
57
|
+
const filePath = join(fullProjectDir, fileName);
|
|
58
|
+
const sessionId = sessionIdFromClaudeFilePath(filePath);
|
|
59
|
+
const stat = statIfReadable(filePath);
|
|
60
|
+
if (sessionId && stat) {
|
|
61
|
+
files.push({
|
|
62
|
+
agent: "claude",
|
|
63
|
+
sessionId,
|
|
64
|
+
filePath,
|
|
65
|
+
mtimeMs: stat.mtimeMs
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return files;
|
|
72
|
+
}
|
|
73
|
+
function listCodexSessionFiles() {
|
|
74
|
+
const root = codexSessionsRoot();
|
|
75
|
+
if (!existsSync(root)) return [];
|
|
76
|
+
const files = [];
|
|
77
|
+
for (const filePath of listJsonlFilesRecursive(root, 5)) {
|
|
78
|
+
const stat = statIfReadable(filePath);
|
|
79
|
+
if (!stat) continue;
|
|
80
|
+
const sessionId = sessionIdFromCodexFilePath(filePath) ?? readCodexSessionId(filePath);
|
|
81
|
+
if (!sessionId) continue;
|
|
82
|
+
files.push({ agent: "codex", sessionId, filePath, mtimeMs: stat.mtimeMs });
|
|
83
|
+
}
|
|
84
|
+
return files;
|
|
85
|
+
}
|
|
86
|
+
function listSessionFiles(agent) {
|
|
87
|
+
const files = [];
|
|
88
|
+
if (agent === "auto" || agent === "claude") {
|
|
89
|
+
files.push(...listClaudeSessionFiles());
|
|
90
|
+
}
|
|
91
|
+
if (agent === "auto" || agent === "codex") {
|
|
92
|
+
files.push(...listCodexSessionFiles());
|
|
93
|
+
}
|
|
94
|
+
return files;
|
|
95
|
+
}
|
|
96
|
+
function readDirectoryNames(dir) {
|
|
97
|
+
try {
|
|
98
|
+
return readdirSync(dir);
|
|
99
|
+
} catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function listJsonlFilesRecursive(root, maxDepth) {
|
|
104
|
+
const files = [];
|
|
105
|
+
function visit(dir, depth) {
|
|
106
|
+
if (depth > maxDepth) return;
|
|
107
|
+
let entries;
|
|
108
|
+
try {
|
|
109
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
110
|
+
} catch {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
const fullPath = join(dir, entry.name);
|
|
115
|
+
if (entry.isDirectory()) {
|
|
116
|
+
visit(fullPath, depth + 1);
|
|
117
|
+
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
118
|
+
files.push(fullPath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
visit(root, 0);
|
|
123
|
+
return files;
|
|
124
|
+
}
|
|
125
|
+
function statIfReadable(filePath) {
|
|
126
|
+
try {
|
|
127
|
+
return statSync(filePath);
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function newestSessionFile(agent) {
|
|
133
|
+
let newest = null;
|
|
134
|
+
for (const candidate of listSessionFiles(agent)) {
|
|
135
|
+
if (!newest || candidate.mtimeMs > newest.mtimeMs) {
|
|
136
|
+
newest = candidate;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return newest;
|
|
140
|
+
}
|
|
141
|
+
function sessionIdFromClaudeFilePath(filePath) {
|
|
142
|
+
return basename(filePath, ".jsonl");
|
|
143
|
+
}
|
|
144
|
+
function sessionIdFromCodexFilePath(filePath) {
|
|
145
|
+
const match = basename(filePath, ".jsonl").match(UUID_IN_TEXT_RE);
|
|
146
|
+
return match?.[0] ?? null;
|
|
147
|
+
}
|
|
148
|
+
function readCodexSessionId(filePath) {
|
|
149
|
+
try {
|
|
150
|
+
for (const line of normalizedJsonLines(readFileSync(filePath)).slice(
|
|
151
|
+
0,
|
|
152
|
+
20
|
|
153
|
+
)) {
|
|
154
|
+
const parsed = parseJsonLine(line);
|
|
155
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
156
|
+
const record = parsed;
|
|
157
|
+
const payload = record.payload && typeof record.payload === "object" ? record.payload : null;
|
|
158
|
+
const id = record.type === "session_meta" && typeof payload?.id === "string" ? payload.id : null;
|
|
159
|
+
if (id && UUID_RE.test(id)) return id;
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
function sessionSearchDescription(agent) {
|
|
167
|
+
if (agent === "claude") return "~/.claude/projects/*/<session-id>.jsonl";
|
|
168
|
+
if (agent === "codex") return "~/.codex/sessions/**/*.jsonl";
|
|
169
|
+
return "~/.claude/projects/*/<session-id>.jsonl or ~/.codex/sessions/**/*.jsonl";
|
|
170
|
+
}
|
|
171
|
+
function parseSessionAgent(value) {
|
|
172
|
+
const normalized = String(value || "auto").trim().toLowerCase();
|
|
173
|
+
if (normalized === "auto" || normalized === "claude" || normalized === "codex") {
|
|
174
|
+
return normalized;
|
|
175
|
+
}
|
|
176
|
+
throw new Error("Invalid --agent value. Expected auto, claude, or codex.");
|
|
177
|
+
}
|
|
178
|
+
function parseSessionRating(value) {
|
|
179
|
+
if (value === void 0) return void 0;
|
|
180
|
+
const normalized = value.trim().toLowerCase();
|
|
181
|
+
if (normalized === "poor") return "bad";
|
|
182
|
+
if (normalized === "good" || normalized === "bad" || normalized === "neutral") {
|
|
183
|
+
return normalized;
|
|
184
|
+
}
|
|
185
|
+
throw new Error("Invalid --rating value. Expected good, poor, or neutral.");
|
|
186
|
+
}
|
|
187
|
+
function findSessionFile(sessionId, agent) {
|
|
188
|
+
if (!UUID_RE.test(sessionId)) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
"Invalid session ID format. Expected a UUID such as 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca."
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
for (const candidate of listSessionFiles(agent)) {
|
|
194
|
+
if (candidate.sessionId === sessionId) {
|
|
195
|
+
return candidate;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
function resolveSessionTargets(input) {
|
|
201
|
+
const agent = parseSessionAgent(input.agent);
|
|
202
|
+
const targets = [];
|
|
203
|
+
if (input.currentSession) {
|
|
204
|
+
const currentFile = newestSessionFile(agent);
|
|
205
|
+
if (!currentFile) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`No session files found in ${sessionSearchDescription(agent)}.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
targets.push({
|
|
211
|
+
sessionId: currentFile.sessionId,
|
|
212
|
+
label: `${currentFile.agent}-session-${currentFile.sessionId}`,
|
|
213
|
+
filePath: currentFile.filePath,
|
|
214
|
+
agent: currentFile.agent
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
for (const [index, sessionId] of (input.sessionIds ?? []).entries()) {
|
|
218
|
+
const candidate = findSessionFile(sessionId, agent);
|
|
219
|
+
if (!candidate) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Session file not found in ${sessionSearchDescription(agent)}: ${sessionId}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
targets.push({
|
|
225
|
+
sessionId,
|
|
226
|
+
label: input.labels?.[index] ?? `${candidate.agent}-session-${sessionId}`,
|
|
227
|
+
filePath: candidate.filePath,
|
|
228
|
+
agent: candidate.agent
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (targets.length === 0) {
|
|
232
|
+
throw new Error("One of --session-id or --current-session is required.");
|
|
233
|
+
}
|
|
234
|
+
return targets;
|
|
235
|
+
}
|
|
236
|
+
function parseJsonLine(line) {
|
|
237
|
+
try {
|
|
238
|
+
return JSON.parse(line);
|
|
239
|
+
} catch {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function normalizedJsonLines(raw) {
|
|
244
|
+
return raw.toString("utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
245
|
+
}
|
|
246
|
+
function stripNoiseEvents(raw) {
|
|
247
|
+
const lines = [];
|
|
248
|
+
for (const line of normalizedJsonLines(raw)) {
|
|
249
|
+
const parsed = parseJsonLine(line);
|
|
250
|
+
if (parsed && typeof parsed === "object" && NOISE_EVENT_TYPES.has(String(parsed.type ?? ""))) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
lines.push(line);
|
|
254
|
+
}
|
|
255
|
+
return Buffer.from(lines.length > 0 ? `${lines.join("\n")}
|
|
256
|
+
` : "", "utf8");
|
|
257
|
+
}
|
|
258
|
+
function messageContentKey(value) {
|
|
259
|
+
const message = value.message;
|
|
260
|
+
if (!message || typeof message !== "object") return null;
|
|
261
|
+
const content = message.content;
|
|
262
|
+
if (typeof content === "string") return content;
|
|
263
|
+
if (!Array.isArray(content)) return null;
|
|
264
|
+
return content.map((block) => {
|
|
265
|
+
if (!block || typeof block !== "object") return String(block);
|
|
266
|
+
const record = block;
|
|
267
|
+
const type = String(record.type ?? "");
|
|
268
|
+
if (type === "tool_use") {
|
|
269
|
+
return `tool_use:${String(record.name ?? "")}:${String(record.id ?? "")}`;
|
|
270
|
+
}
|
|
271
|
+
if (type === "tool_result") {
|
|
272
|
+
return `tool_result:${String(record.tool_use_id ?? "")}`;
|
|
273
|
+
}
|
|
274
|
+
return String(record.text ?? type);
|
|
275
|
+
}).join("\n");
|
|
276
|
+
}
|
|
277
|
+
function dedupConsecutiveEvents(raw) {
|
|
278
|
+
const rawLines = normalizedJsonLines(raw);
|
|
279
|
+
const parsedEvents = rawLines.map(parseJsonLine);
|
|
280
|
+
const output = [];
|
|
281
|
+
let index = 0;
|
|
282
|
+
while (index < parsedEvents.length) {
|
|
283
|
+
const event = parsedEvents[index];
|
|
284
|
+
if (!event || typeof event !== "object") {
|
|
285
|
+
output.push(rawLines[index] ?? "");
|
|
286
|
+
index += 1;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const record = event;
|
|
290
|
+
const eventType = String(record.type ?? "");
|
|
291
|
+
const eventKey = messageContentKey(record);
|
|
292
|
+
if (!["user", "assistant"].includes(eventType) || !eventKey) {
|
|
293
|
+
output.push(rawLines[index] ?? "");
|
|
294
|
+
index += 1;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
let runCount = 1;
|
|
298
|
+
let cursor = index + 1;
|
|
299
|
+
while (cursor < parsedEvents.length) {
|
|
300
|
+
const next = parsedEvents[cursor];
|
|
301
|
+
if (!next || typeof next !== "object") break;
|
|
302
|
+
const nextRecord = next;
|
|
303
|
+
if (String(nextRecord.type ?? "") !== eventType || messageContentKey(nextRecord) !== eventKey) {
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
runCount += 1;
|
|
307
|
+
cursor += 1;
|
|
308
|
+
}
|
|
309
|
+
if (runCount > 1) {
|
|
310
|
+
record._repeat_count = runCount;
|
|
311
|
+
record._repeat_summary = `${runCount} consecutive identical ${eventType} messages collapsed`;
|
|
312
|
+
output.push(JSON.stringify(record));
|
|
313
|
+
index = cursor;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
output.push(rawLines[index] ?? "");
|
|
317
|
+
index += 1;
|
|
318
|
+
}
|
|
319
|
+
return Buffer.from(output.length > 0 ? `${output.join("\n")}
|
|
320
|
+
` : "", "utf8");
|
|
321
|
+
}
|
|
322
|
+
function compactEventValue(value) {
|
|
323
|
+
if (typeof value === "string") {
|
|
324
|
+
if (value.length <= MAX_EVENT_STRING_CHARS) return value;
|
|
325
|
+
return `${value.slice(0, MAX_EVENT_STRING_CHARS - TRUNCATION_MARKER.length)}${TRUNCATION_MARKER}`;
|
|
326
|
+
}
|
|
327
|
+
if (Array.isArray(value)) {
|
|
328
|
+
const compacted = value.slice(0, MAX_EVENT_LIST_ITEMS).map(compactEventValue);
|
|
329
|
+
if (value.length > MAX_EVENT_LIST_ITEMS) {
|
|
330
|
+
compacted.push(
|
|
331
|
+
`${TRUNCATION_MARKER} ${value.length - MAX_EVENT_LIST_ITEMS} more item(s)`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return compacted;
|
|
335
|
+
}
|
|
336
|
+
if (value && typeof value === "object") {
|
|
337
|
+
const entries = Object.entries(value);
|
|
338
|
+
const compacted = {};
|
|
339
|
+
for (const [key, item] of entries.slice(0, MAX_EVENT_OBJECT_KEYS)) {
|
|
340
|
+
compacted[key] = compactEventValue(item);
|
|
341
|
+
}
|
|
342
|
+
if (entries.length > MAX_EVENT_OBJECT_KEYS) {
|
|
343
|
+
compacted._truncated_keys = entries.length - MAX_EVENT_OBJECT_KEYS;
|
|
344
|
+
}
|
|
345
|
+
return compacted;
|
|
346
|
+
}
|
|
347
|
+
return value;
|
|
348
|
+
}
|
|
349
|
+
function selectiveCompactToolResults(raw) {
|
|
350
|
+
const lines = [];
|
|
351
|
+
for (const line of normalizedJsonLines(raw)) {
|
|
352
|
+
const parsed = parseJsonLine(line);
|
|
353
|
+
if (!parsed || typeof parsed !== "object") {
|
|
354
|
+
lines.push(line);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const record = parsed;
|
|
358
|
+
if (record.type === "user") {
|
|
359
|
+
const message = record.message;
|
|
360
|
+
const content = message && typeof message === "object" ? message.content : null;
|
|
361
|
+
if (Array.isArray(content)) {
|
|
362
|
+
message.content = content.map(
|
|
363
|
+
(block) => block && typeof block === "object" && block.type === "tool_result" ? compactEventValue(block) : block
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
lines.push(JSON.stringify(record));
|
|
368
|
+
}
|
|
369
|
+
return Buffer.from(lines.length > 0 ? `${lines.join("\n")}
|
|
370
|
+
` : "", "utf8");
|
|
371
|
+
}
|
|
372
|
+
function compactCodexEvents(raw) {
|
|
373
|
+
const rawLines = normalizedJsonLines(raw);
|
|
374
|
+
const parsedEvents = rawLines.map(parseJsonLine);
|
|
375
|
+
const isCodex = parsedEvents.some(
|
|
376
|
+
(event) => event && typeof event === "object" && event.type === "session_meta"
|
|
377
|
+
);
|
|
378
|
+
if (!isCodex) return raw;
|
|
379
|
+
let lastTokenCountIndex = -1;
|
|
380
|
+
parsedEvents.forEach((event, index) => {
|
|
381
|
+
if (!event || typeof event !== "object") return;
|
|
382
|
+
const payload = event.payload;
|
|
383
|
+
if (payload && typeof payload === "object" && payload.type === "token_count") {
|
|
384
|
+
lastTokenCountIndex = index;
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
const lines = [];
|
|
388
|
+
parsedEvents.forEach((event, index) => {
|
|
389
|
+
if (!event || typeof event !== "object") {
|
|
390
|
+
lines.push(rawLines[index] ?? "");
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const record = event;
|
|
394
|
+
if (record.type === "world_state") return;
|
|
395
|
+
const payload = record.payload;
|
|
396
|
+
const payloadType = payload && typeof payload === "object" ? String(payload.type ?? "") : "";
|
|
397
|
+
if (payloadType === "token_count" && index !== lastTokenCountIndex) return;
|
|
398
|
+
if (payloadType === "custom_tool_call_output" || payloadType === "function_call_output" || payloadType === "tool_search_output") {
|
|
399
|
+
const record_payload = payload;
|
|
400
|
+
record_payload.output = compactEventValue(record_payload.output);
|
|
401
|
+
lines.push(JSON.stringify(record));
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
lines.push(rawLines[index] ?? "");
|
|
405
|
+
});
|
|
406
|
+
return Buffer.from(lines.length > 0 ? `${lines.join("\n")}
|
|
407
|
+
` : "", "utf8");
|
|
408
|
+
}
|
|
409
|
+
function prepareSessionBuffer(raw) {
|
|
410
|
+
return selectiveCompactToolResults(
|
|
411
|
+
dedupConsecutiveEvents(compactCodexEvents(stripNoiseEvents(raw)))
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
function buildSessionUploadContent(raw) {
|
|
415
|
+
const prepared = prepareSessionBuffer(raw);
|
|
416
|
+
const encoded = gzipSync(prepared).toString("base64");
|
|
417
|
+
if (encoded.length <= MAX_SESSION_UPLOAD_BYTES && prepared.length <= MAX_DIRECT_SESSION_DECODED_BYTES) {
|
|
418
|
+
return { encodedContent: encoded, needsChunking: false };
|
|
419
|
+
}
|
|
420
|
+
return { encodedContent: encoded, needsChunking: true };
|
|
421
|
+
}
|
|
422
|
+
async function uploadPayload(path, payload) {
|
|
423
|
+
const { http } = getAuthedHttpClient();
|
|
424
|
+
return await http.post(path, payload);
|
|
425
|
+
}
|
|
426
|
+
async function uploadChunkedSessions(sessions, options) {
|
|
427
|
+
const uploadId = randomUUID();
|
|
428
|
+
for (const session of sessions) {
|
|
429
|
+
const bytes = Buffer.from(session.encodedContent, "base64");
|
|
430
|
+
const chunks = [];
|
|
431
|
+
for (let offset = 0; offset < bytes.length; offset += CHUNK_SIZE_BYTES) {
|
|
432
|
+
chunks.push(bytes.subarray(offset, offset + CHUNK_SIZE_BYTES));
|
|
433
|
+
}
|
|
434
|
+
process.stderr.write(
|
|
435
|
+
`Uploading ${session.label} in ${chunks.length} chunk(s)...
|
|
436
|
+
`
|
|
437
|
+
);
|
|
438
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
439
|
+
await uploadPayload("/api/v2/cli/send-session/chunk", {
|
|
440
|
+
upload_id: uploadId,
|
|
441
|
+
session_id: session.sessionId,
|
|
442
|
+
index,
|
|
443
|
+
total_chunks: chunks.length,
|
|
444
|
+
data: chunk.toString("base64")
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const response = await uploadPayload("/api/v2/cli/send-session/finalize", {
|
|
449
|
+
upload_id: uploadId,
|
|
450
|
+
session_ids: sessions.map((session) => session.sessionId),
|
|
451
|
+
labels: sessions.map((session) => session.label),
|
|
452
|
+
environments: sessions.map(() => detectShellContext()),
|
|
453
|
+
...options.rating ? { rating: options.rating } : {}
|
|
454
|
+
});
|
|
455
|
+
printCommandEnvelope(
|
|
456
|
+
{
|
|
457
|
+
...response,
|
|
458
|
+
ok: true,
|
|
459
|
+
uploaded: sessions.length,
|
|
460
|
+
render: {
|
|
461
|
+
sections: [
|
|
462
|
+
{
|
|
463
|
+
title: "sessions send",
|
|
464
|
+
lines: ["Session uploaded to #internal-reports (chunked)."]
|
|
465
|
+
}
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
{ json: options.json }
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
async function handleSessionsSend(options) {
|
|
473
|
+
const rating = parseSessionRating(options.rating);
|
|
474
|
+
if (options.file) {
|
|
475
|
+
const filePath = resolve(options.file);
|
|
476
|
+
if (!existsSync(filePath)) {
|
|
477
|
+
throw new Error(`File not found: ${options.file}`);
|
|
478
|
+
}
|
|
479
|
+
const response2 = await uploadPayload("/api/v2/cli/send-session", {
|
|
480
|
+
file: readFileSync(filePath).toString("base64"),
|
|
481
|
+
filename: basename(filePath),
|
|
482
|
+
...rating ? { rating } : {}
|
|
483
|
+
});
|
|
484
|
+
printCommandEnvelope(
|
|
485
|
+
{
|
|
486
|
+
...response2,
|
|
487
|
+
ok: true,
|
|
488
|
+
filename: basename(filePath),
|
|
489
|
+
render: {
|
|
490
|
+
sections: [
|
|
491
|
+
{
|
|
492
|
+
title: "sessions send",
|
|
493
|
+
lines: [
|
|
494
|
+
`File '${basename(filePath)}' uploaded to #internal-reports.`
|
|
495
|
+
]
|
|
496
|
+
}
|
|
497
|
+
]
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
{ json: options.json }
|
|
501
|
+
);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const targets = resolveSessionTargets({
|
|
505
|
+
sessionIds: options.sessionId,
|
|
506
|
+
labels: options.label,
|
|
507
|
+
currentSession: options.currentSession,
|
|
508
|
+
agent: options.agent
|
|
509
|
+
});
|
|
510
|
+
const built = targets.map((target) => {
|
|
511
|
+
const upload = buildSessionUploadContent(readFileSync(target.filePath));
|
|
512
|
+
return { ...target, ...upload };
|
|
513
|
+
});
|
|
514
|
+
if (built.some((session) => session.needsChunking)) {
|
|
515
|
+
await uploadChunkedSessions(built, { json: options.json, rating });
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const response = built.length === 1 && !options.label?.length ? await uploadPayload("/api/v2/cli/send-session", {
|
|
519
|
+
session_id: built[0]?.sessionId,
|
|
520
|
+
content: built[0]?.encodedContent,
|
|
521
|
+
environment: detectShellContext(),
|
|
522
|
+
...rating ? { rating } : {}
|
|
523
|
+
}) : await uploadPayload("/api/v2/cli/send-session", {
|
|
524
|
+
sessions: built.map((session) => ({
|
|
525
|
+
session_id: session.sessionId,
|
|
526
|
+
content: session.encodedContent,
|
|
527
|
+
label: session.label,
|
|
528
|
+
environment: detectShellContext()
|
|
529
|
+
})),
|
|
530
|
+
environment: detectShellContext(),
|
|
531
|
+
...rating ? { rating } : {}
|
|
532
|
+
});
|
|
533
|
+
printCommandEnvelope(
|
|
534
|
+
{
|
|
535
|
+
...response,
|
|
536
|
+
ok: true,
|
|
537
|
+
uploaded: built.length,
|
|
538
|
+
render: {
|
|
539
|
+
sections: [
|
|
540
|
+
{
|
|
541
|
+
title: "sessions send",
|
|
542
|
+
lines: ["Session uploaded to #internal-reports."]
|
|
543
|
+
}
|
|
544
|
+
]
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
{ json: options.json }
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
function loadViewerAssets() {
|
|
551
|
+
const cliEntry = process.argv[1]?.trim() ? realpathSync(resolve(process.argv[1])) : null;
|
|
552
|
+
const candidateRoots = [
|
|
553
|
+
...cliEntry ? [
|
|
554
|
+
join(dirname(dirname(cliEntry)), "viewer"),
|
|
555
|
+
join(
|
|
556
|
+
dirname(dirname(dirname(cliEntry))),
|
|
557
|
+
"src",
|
|
558
|
+
"lib",
|
|
559
|
+
"cli",
|
|
560
|
+
"viewer"
|
|
561
|
+
)
|
|
562
|
+
] : [],
|
|
563
|
+
join(process.cwd(), "packages", "sdk", "assets", "viewer")
|
|
564
|
+
];
|
|
565
|
+
for (const root of candidateRoots) {
|
|
566
|
+
try {
|
|
567
|
+
const cssPath = join(root, "viewer.css");
|
|
568
|
+
const jsPath = join(root, "viewer.js");
|
|
569
|
+
if (!existsSync(cssPath) || !existsSync(jsPath)) continue;
|
|
570
|
+
return {
|
|
571
|
+
css: readFileSync(cssPath, "utf8"),
|
|
572
|
+
js: readFileSync(jsPath, "utf8")
|
|
573
|
+
};
|
|
574
|
+
} catch {
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
throw new Error(
|
|
579
|
+
`Session viewer assets not found. Reinstall the CLI or build its viewer assets. Searched: ${candidateRoots.join(", ")}`
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
function parsePreparedEvents(buffer) {
|
|
583
|
+
return normalizedJsonLines(buffer).map((line) => {
|
|
584
|
+
const parsed = parseJsonLine(line);
|
|
585
|
+
return parsed ?? line;
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
function escapeJsonForHtmlScript(json) {
|
|
589
|
+
return json.replace(/</g, "\\u003c");
|
|
590
|
+
}
|
|
591
|
+
async function handleSessionsRender(options) {
|
|
592
|
+
const targets = resolveSessionTargets({
|
|
593
|
+
sessionIds: options.sessionId,
|
|
594
|
+
labels: options.label,
|
|
595
|
+
currentSession: options.currentSession,
|
|
596
|
+
agent: options.agent
|
|
597
|
+
});
|
|
598
|
+
let outputPath = options.output ? resolve(options.output) : "";
|
|
599
|
+
if (!outputPath) {
|
|
600
|
+
const outputDir = join(process.cwd(), "deepline", "data");
|
|
601
|
+
mkdirSync(outputDir, { recursive: true });
|
|
602
|
+
outputPath = join(
|
|
603
|
+
outputDir,
|
|
604
|
+
targets.length > 1 ? "session-viewer.html" : `session-${targets[0]?.sessionId}.html`
|
|
605
|
+
);
|
|
606
|
+
} else {
|
|
607
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
608
|
+
}
|
|
609
|
+
const sessions = targets.map((target) => ({
|
|
610
|
+
label: target.label,
|
|
611
|
+
events: parsePreparedEvents(
|
|
612
|
+
prepareSessionBuffer(readFileSync(target.filePath))
|
|
613
|
+
)
|
|
614
|
+
}));
|
|
615
|
+
const { css, js } = loadViewerAssets();
|
|
616
|
+
const refreshMeta = options.autoRefresh ? `<meta http-equiv="refresh" content="${Number.parseInt(options.autoRefresh, 10)}">` : "";
|
|
617
|
+
const rawJson = escapeJsonForHtmlScript(JSON.stringify(sessions));
|
|
618
|
+
const html = `<!DOCTYPE html>
|
|
619
|
+
<html lang="en">
|
|
620
|
+
<head>
|
|
621
|
+
<meta charset="UTF-8">
|
|
622
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
623
|
+
${refreshMeta}
|
|
624
|
+
<title>Session Viewer</title>
|
|
625
|
+
<style>${css}</style>
|
|
626
|
+
</head>
|
|
627
|
+
<body>
|
|
628
|
+
<div class="layout">
|
|
629
|
+
<div class="sidebar hidden" id="sidebar"></div>
|
|
630
|
+
<div class="run-sidebar hidden" id="run-sidebar"></div>
|
|
631
|
+
<div class="main" id="main-content"></div>
|
|
632
|
+
</div>
|
|
633
|
+
<script type="application/json" id="raw-sessions">${rawJson}</script>
|
|
634
|
+
<script>${js}</script>
|
|
635
|
+
</body>
|
|
636
|
+
</html>`;
|
|
637
|
+
writeFileSync(outputPath, html, "utf8");
|
|
638
|
+
printCommandEnvelope(
|
|
639
|
+
{
|
|
640
|
+
ok: true,
|
|
641
|
+
file: outputPath,
|
|
642
|
+
session_count: targets.length,
|
|
643
|
+
render: {
|
|
644
|
+
sections: [
|
|
645
|
+
{
|
|
646
|
+
title: "sessions render",
|
|
647
|
+
lines: [`Rendered session viewer: ${outputPath}`]
|
|
648
|
+
}
|
|
649
|
+
]
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
{ json: options.json }
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
function collectOption(value, previous) {
|
|
656
|
+
previous.push(value);
|
|
657
|
+
return previous;
|
|
658
|
+
}
|
|
659
|
+
function registerSessionsCommands(program) {
|
|
660
|
+
const sessions = program.command("sessions").description("Upload and render local agent session transcripts.").addHelpText(
|
|
661
|
+
"after",
|
|
662
|
+
`
|
|
663
|
+
Notes:
|
|
664
|
+
Session commands operate on local Claude and Codex JSONL files under
|
|
665
|
+
~/.claude/projects and ~/.codex/sessions. send uploads a compacted transcript
|
|
666
|
+
or file to Deepline. render writes a local HTML viewer.
|
|
667
|
+
|
|
668
|
+
Examples:
|
|
669
|
+
deepline sessions send --current-session --json
|
|
670
|
+
deepline sessions send --agent codex --current-session --json
|
|
671
|
+
deepline sessions send --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca
|
|
672
|
+
deepline sessions render --current-session --output session.html
|
|
673
|
+
`
|
|
674
|
+
);
|
|
675
|
+
registerSessionSendRenderCommands(sessions, "sessions");
|
|
676
|
+
}
|
|
677
|
+
function registerSessionSendRenderCommands(parent, familyName) {
|
|
678
|
+
parent.command("send").description("Upload session transcript(s) or a local file to Deepline.").addHelpText(
|
|
679
|
+
"after",
|
|
680
|
+
`
|
|
681
|
+
Examples:
|
|
682
|
+
deepline ${familyName} send --current-session --json
|
|
683
|
+
deepline ${familyName} send --agent codex --current-session --json
|
|
684
|
+
deepline ${familyName} send --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca --label "pilot run"
|
|
685
|
+
deepline ${familyName} send --current-session --rating poor --json # tag the session as one that went poorly
|
|
686
|
+
deepline ${familyName} send --file ./debug.log --json
|
|
687
|
+
`
|
|
688
|
+
).option(
|
|
689
|
+
"--session-id <uuid>",
|
|
690
|
+
"Claude or Codex session UUID. Repeat for multiple sessions.",
|
|
691
|
+
collectOption,
|
|
692
|
+
[]
|
|
693
|
+
).option(
|
|
694
|
+
"--label <label>",
|
|
695
|
+
"Label for the preceding session id",
|
|
696
|
+
collectOption,
|
|
697
|
+
[]
|
|
698
|
+
).option(
|
|
699
|
+
"--agent <auto|claude|codex>",
|
|
700
|
+
"Limit transcript discovery to one agent runtime",
|
|
701
|
+
"auto"
|
|
702
|
+
).option("--current-session", "Use the newest local agent session JSONL").option("--file <path>", "Upload a raw local file instead of a session").option(
|
|
703
|
+
"--rating <good|poor|neutral>",
|
|
704
|
+
"Tag the upload by outcome, not tone, so Deepline can filter sessions: good = the user's goal was met without product problems; poor = the goal was missed, or a product failure, bug, or avoidable friction (including a substantial workaround) got in the way; neutral = partly met or unclear."
|
|
705
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSessionsSend);
|
|
706
|
+
parent.command("render").description("Render local session transcript(s) to an HTML viewer.").addHelpText(
|
|
707
|
+
"after",
|
|
708
|
+
`
|
|
709
|
+
Examples:
|
|
710
|
+
deepline ${familyName} render --current-session
|
|
711
|
+
deepline ${familyName} render --agent codex --current-session
|
|
712
|
+
deepline ${familyName} render --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca --output session.html
|
|
713
|
+
deepline ${familyName} render --current-session --auto-refresh 5 --json
|
|
714
|
+
`
|
|
715
|
+
).option(
|
|
716
|
+
"--session-id <uuid>",
|
|
717
|
+
"Claude or Codex session UUID. Repeat for multiple sessions.",
|
|
718
|
+
collectOption,
|
|
719
|
+
[]
|
|
720
|
+
).option(
|
|
721
|
+
"--label <label>",
|
|
722
|
+
"Label for the preceding session id",
|
|
723
|
+
collectOption,
|
|
724
|
+
[]
|
|
725
|
+
).option(
|
|
726
|
+
"--agent <auto|claude|codex>",
|
|
727
|
+
"Limit transcript discovery to one agent runtime",
|
|
728
|
+
"auto"
|
|
729
|
+
).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);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
export {
|
|
733
|
+
registerSessionsCommands,
|
|
734
|
+
registerSessionSendRenderCommands
|
|
735
|
+
};
|