@swestash/agent-lens 0.15.0 → 0.17.0
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/agent-lens.js +167 -49
- package/dist/agent-lens.js.map +1 -1
- package/package.json +1 -1
- package/web/404.html +2 -2
- package/web/assets/AboutView-BiuYVPOe.js +1 -0
- package/web/assets/{Dashboard-fakmS_H4.js → Dashboard-fPsqJm-5.js} +2 -2
- package/web/assets/FileView-CX-Pe5S0.js +1 -0
- package/web/assets/{FilesView-B6_w8jKK.js → FilesView-CPaEQFkv.js} +1 -1
- package/web/assets/SecurityView-D3qd2-q5.js +1 -0
- package/web/assets/SessionView-DtykJhVv.js +19 -0
- package/web/assets/{SkillView-CnOhNPN4.js → SkillView-D72WQ-Xv.js} +1 -1
- package/web/assets/SkillsView-CW8sNMN7.js +1 -0
- package/web/assets/{WorkflowView-Dq9JYCHd.js → WorkflowView-Dek4WLJa.js} +1 -1
- package/web/assets/index-CG2N8ixY.js +69 -0
- package/web/assets/{index-CcEbkJx7.js → index-DLoMRgQ8.js} +1 -1
- package/web/assets/index-DN9qB28e.css +1 -0
- package/web/assets/jsonish-B0CzuJ3V.js +1 -0
- package/web/assets/theme-DiAReGS9.js +1 -0
- package/web/assets/{useResetOn-BZWxJ_cM.js → useResetOn-CoUOqjZU.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/AboutView-BD9cfwC2.js +0 -1
- package/web/assets/FileView-1MkgxkzO.js +0 -1
- package/web/assets/SecurityView-BX-IzE--.js +0 -1
- package/web/assets/SessionView-DY5Xm26K.js +0 -15
- package/web/assets/SkillsView-C76l9UYR.js +0 -1
- package/web/assets/index-B6dWnwqZ.css +0 -1
- package/web/assets/index-CvMppG1z.js +0 -69
- package/web/assets/jsonish-DMAKObPI.js +0 -1
- package/web/assets/theme-ByPiBqSS.js +0 -1
package/dist/agent-lens.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { existsSync as
|
|
4
|
+
import { existsSync as existsSync11, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5
5
|
import { join as join12 } from "node:path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
7
7
|
|
|
@@ -812,9 +812,9 @@ CREATE INDEX IF NOT EXISTS idx_findings_category ON findings(category);
|
|
|
812
812
|
CREATE INDEX IF NOT EXISTS idx_findings_rule ON findings(rule_id);
|
|
813
813
|
CREATE INDEX IF NOT EXISTS idx_findings_tool_call ON findings(tool_call_id);
|
|
814
814
|
|
|
815
|
-
-- File modifications derived from tool calls (ADR-022). One row per (tool_call, file):
|
|
816
|
-
-- Edit/Write/NotebookEdit are 1:1
|
|
817
|
-
--
|
|
815
|
+
-- File modifications derived from tool calls (ADR-022). One row per (tool_call, file):
|
|
816
|
+
-- Edit/Write/NotebookEdit are 1:1; a Bash call can write several files, which the hash id already
|
|
817
|
+
-- accommodated without a schema break. Failed calls (status='error') are excluded
|
|
818
818
|
-- at derivation \u2014 they didn't change the file. Re-runnable like findings: incremental runs DELETE
|
|
819
819
|
-- the dirty sessions' rows and re-INSERT (delete-then-insert). Populated by ingest's
|
|
820
820
|
-- deriveFileChanges (filechanges.ts).
|
|
@@ -826,7 +826,7 @@ CREATE TABLE IF NOT EXISTS file_changes (
|
|
|
826
826
|
event_uuid TEXT, -- jump-to-transcript anchor (#ev-<uuid>)
|
|
827
827
|
project_id TEXT REFERENCES projects(id), -- from the session, for per-project grouping
|
|
828
828
|
file_path TEXT NOT NULL, -- normalized absolute path
|
|
829
|
-
tool_name TEXT NOT NULL, -- Edit | Write | NotebookEdit
|
|
829
|
+
tool_name TEXT NOT NULL, -- Edit | Write | NotebookEdit | Bash
|
|
830
830
|
lines_added INTEGER, -- newline-count delta (NULL when unknowable)
|
|
831
831
|
lines_removed INTEGER,
|
|
832
832
|
timestamp TEXT, -- ISO8601, from the tool call's event
|
|
@@ -3427,8 +3427,98 @@ function classifyErrors(db, dirty) {
|
|
|
3427
3427
|
// ../ingest/dist/filechanges.js
|
|
3428
3428
|
import { createHash as createHash3 } from "node:crypto";
|
|
3429
3429
|
import { posix as path2 } from "node:path";
|
|
3430
|
-
|
|
3431
|
-
|
|
3430
|
+
|
|
3431
|
+
// ../ingest/dist/shellwrites.js
|
|
3432
|
+
var TARGET = `"([^"]+)"|'([^']+)'|([^\\s;|&<>()]+)`;
|
|
3433
|
+
var REDIRECT = new RegExp(`(?:^|[\\s;|&(])\\d?>{1,2}\\s*(?:${TARGET})`, "g");
|
|
3434
|
+
var TEE = new RegExp(`(?:^|[\\s;|&(])tee\\s+(?:-a\\s+)?(?:${TARGET})`, "g");
|
|
3435
|
+
var SED_I = new RegExp(`(?:^|[\\s;|&(])sed\\s+(?:-[a-zA-Z]*i[a-zA-Z]*(?:\\.\\w+)?)\\s+(?:(?:-e\\s+)?(?:'[^']*'|"[^"]*"|\\S+)\\s+)+?(?:${TARGET})`, "g");
|
|
3436
|
+
var CD = new RegExp(`^\\s*cd\\s+(?:${TARGET})`);
|
|
3437
|
+
var HEREDOC = /<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1/g;
|
|
3438
|
+
var REDIRECT_BEFORE_HEREDOC = new RegExp(`\\d?>{1,2}\\s*(?:${TARGET})\\s*$`);
|
|
3439
|
+
function target(m, first) {
|
|
3440
|
+
return m[first] ?? m[first + 1] ?? m[first + 2] ?? null;
|
|
3441
|
+
}
|
|
3442
|
+
function usable(path3) {
|
|
3443
|
+
if (!path3)
|
|
3444
|
+
return false;
|
|
3445
|
+
if (/[$*?`~]/.test(path3))
|
|
3446
|
+
return false;
|
|
3447
|
+
if (path3.startsWith("&"))
|
|
3448
|
+
return false;
|
|
3449
|
+
if (path3 === "/dev/null" || path3.startsWith("/dev/"))
|
|
3450
|
+
return false;
|
|
3451
|
+
return true;
|
|
3452
|
+
}
|
|
3453
|
+
function bodyLines(body) {
|
|
3454
|
+
const trimmed = body.replace(/\n$/, "");
|
|
3455
|
+
return trimmed === "" ? 0 : trimmed.split("\n").length;
|
|
3456
|
+
}
|
|
3457
|
+
function stripHeredocs(command) {
|
|
3458
|
+
const writes = [];
|
|
3459
|
+
let scan = command;
|
|
3460
|
+
HEREDOC.lastIndex = 0;
|
|
3461
|
+
let m;
|
|
3462
|
+
while (m = HEREDOC.exec(scan)) {
|
|
3463
|
+
const delim = m[2];
|
|
3464
|
+
const openerEnd = scan.indexOf("\n", m.index);
|
|
3465
|
+
if (openerEnd === -1)
|
|
3466
|
+
break;
|
|
3467
|
+
const terminator = new RegExp(`^[ \\t]*${delim}[ \\t]*$`, "m");
|
|
3468
|
+
const rest = scan.slice(openerEnd + 1);
|
|
3469
|
+
const end = rest.search(terminator);
|
|
3470
|
+
const bodyEnd = end === -1 ? rest.length : end;
|
|
3471
|
+
const before = scan.slice(0, m.index);
|
|
3472
|
+
const red = before.match(REDIRECT_BEFORE_HEREDOC);
|
|
3473
|
+
if (red) {
|
|
3474
|
+
const p = target(red, 1);
|
|
3475
|
+
if (usable(p))
|
|
3476
|
+
writes.push({ at: red.index ?? m.index, write: { path: p, linesAdded: bodyLines(rest.slice(0, bodyEnd)) } });
|
|
3477
|
+
}
|
|
3478
|
+
const stripEnd = end === -1 ? scan.length : openerEnd + 1 + bodyEnd + delim.length;
|
|
3479
|
+
scan = scan.slice(0, m.index) + " ".repeat(stripEnd - m.index) + scan.slice(stripEnd);
|
|
3480
|
+
HEREDOC.lastIndex = stripEnd;
|
|
3481
|
+
}
|
|
3482
|
+
return { scan, writes };
|
|
3483
|
+
}
|
|
3484
|
+
function parseShellWrites(command) {
|
|
3485
|
+
if (typeof command !== "string" || !command)
|
|
3486
|
+
return { cwd: null, writes: [] };
|
|
3487
|
+
const cdMatch = command.match(CD);
|
|
3488
|
+
const cdPath = cdMatch ? target(cdMatch, 1) : null;
|
|
3489
|
+
const cwd = cdPath && cdPath.startsWith("/") && !/[$*?`~]/.test(cdPath) ? cdPath : null;
|
|
3490
|
+
const { scan, writes: found } = stripHeredocs(command);
|
|
3491
|
+
for (const [re, group] of [
|
|
3492
|
+
[REDIRECT, 1],
|
|
3493
|
+
[TEE, 1],
|
|
3494
|
+
[SED_I, 1]
|
|
3495
|
+
]) {
|
|
3496
|
+
re.lastIndex = 0;
|
|
3497
|
+
let m;
|
|
3498
|
+
while (m = re.exec(scan)) {
|
|
3499
|
+
const p = target(m, group);
|
|
3500
|
+
if (usable(p))
|
|
3501
|
+
found.push({ at: m.index, write: { path: p, linesAdded: null } });
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
3505
|
+
for (const entry of found) {
|
|
3506
|
+
const prev = byPath.get(entry.write.path);
|
|
3507
|
+
if (!prev) {
|
|
3508
|
+
byPath.set(entry.write.path, entry);
|
|
3509
|
+
continue;
|
|
3510
|
+
}
|
|
3511
|
+
byPath.set(entry.write.path, {
|
|
3512
|
+
at: Math.min(prev.at, entry.at),
|
|
3513
|
+
write: { path: entry.write.path, linesAdded: prev.write.linesAdded ?? entry.write.linesAdded }
|
|
3514
|
+
});
|
|
3515
|
+
}
|
|
3516
|
+
return { cwd, writes: [...byPath.values()].sort((a, b) => a.at - b.at).map((e) => e.write) };
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3519
|
+
// ../ingest/dist/filechanges.js
|
|
3520
|
+
var FILECHANGES_VERSION = 2;
|
|
3521
|
+
var FILE_TOOLS = ["Edit", "Write", "NotebookEdit", "Bash"];
|
|
3432
3522
|
function fileChangeId(toolCallId, filePath) {
|
|
3433
3523
|
return createHash3("sha1").update(toolCallId).update("\0").update(filePath).digest("hex").slice(0, 16);
|
|
3434
3524
|
}
|
|
@@ -3489,31 +3579,40 @@ function deriveFileChanges(db, dirty) {
|
|
|
3489
3579
|
if (!input)
|
|
3490
3580
|
continue;
|
|
3491
3581
|
const p = proj.get(row.session_id);
|
|
3492
|
-
const
|
|
3493
|
-
if (
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
} else
|
|
3501
|
-
|
|
3582
|
+
const targets = [];
|
|
3583
|
+
if (row.tool_name === "Bash") {
|
|
3584
|
+
const { cwd, writes } = parseShellWrites(input.command);
|
|
3585
|
+
for (const w of writes) {
|
|
3586
|
+
const filePath = normalizeFilePath(w.path, cwd ?? p?.path ?? null);
|
|
3587
|
+
if (filePath)
|
|
3588
|
+
targets.push({ filePath, added: w.linesAdded, removed: null });
|
|
3589
|
+
}
|
|
3590
|
+
} else {
|
|
3591
|
+
const filePath = normalizeFilePath(input.file_path ?? input.notebook_path, p?.path ?? null);
|
|
3592
|
+
if (filePath) {
|
|
3593
|
+
targets.push({
|
|
3594
|
+
filePath,
|
|
3595
|
+
added: row.tool_name === "Edit" ? lineCount(input.new_string) : row.tool_name === "Write" ? lineCount(input.content) : null,
|
|
3596
|
+
removed: row.tool_name === "Edit" ? lineCount(input.old_string) : null
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
for (const t of targets) {
|
|
3601
|
+
insert.run({
|
|
3602
|
+
id: fileChangeId(row.id, t.filePath),
|
|
3603
|
+
tool_call_id: row.id,
|
|
3604
|
+
session_id: row.session_id,
|
|
3605
|
+
turn_id: row.turn_id,
|
|
3606
|
+
event_uuid: row.event_uuid,
|
|
3607
|
+
project_id: p?.id ?? null,
|
|
3608
|
+
file_path: t.filePath,
|
|
3609
|
+
tool_name: row.tool_name,
|
|
3610
|
+
lines_added: t.added,
|
|
3611
|
+
lines_removed: t.removed,
|
|
3612
|
+
timestamp: row.timestamp,
|
|
3613
|
+
derive_version: FILECHANGES_VERSION
|
|
3614
|
+
});
|
|
3502
3615
|
}
|
|
3503
|
-
insert.run({
|
|
3504
|
-
id: fileChangeId(row.id, filePath),
|
|
3505
|
-
tool_call_id: row.id,
|
|
3506
|
-
session_id: row.session_id,
|
|
3507
|
-
turn_id: row.turn_id,
|
|
3508
|
-
event_uuid: row.event_uuid,
|
|
3509
|
-
project_id: p?.id ?? null,
|
|
3510
|
-
file_path: filePath,
|
|
3511
|
-
tool_name: row.tool_name,
|
|
3512
|
-
lines_added: added,
|
|
3513
|
-
lines_removed: removed,
|
|
3514
|
-
timestamp: row.timestamp,
|
|
3515
|
-
derive_version: FILECHANGES_VERSION
|
|
3516
|
-
});
|
|
3517
3616
|
}
|
|
3518
3617
|
});
|
|
3519
3618
|
tx();
|
|
@@ -3585,15 +3684,15 @@ function canonicalizeProjects(db, opts = {}) {
|
|
|
3585
3684
|
const touched = /* @__PURE__ */ new Set();
|
|
3586
3685
|
transaction(db, () => {
|
|
3587
3686
|
for (const r of rows) {
|
|
3588
|
-
const
|
|
3589
|
-
if (
|
|
3687
|
+
const target2 = canon(r.path);
|
|
3688
|
+
if (target2 === strip(r.path))
|
|
3590
3689
|
continue;
|
|
3591
|
-
const targetId = byPath.get(
|
|
3690
|
+
const targetId = byPath.get(target2)?.id ?? projectId(r.agent_id, target2);
|
|
3592
3691
|
if (targetId === r.id)
|
|
3593
3692
|
continue;
|
|
3594
|
-
if (!byPath.has(
|
|
3595
|
-
upsertProject.run(targetId, r.agent_id,
|
|
3596
|
-
byPath.set(
|
|
3693
|
+
if (!byPath.has(target2)) {
|
|
3694
|
+
upsertProject.run(targetId, r.agent_id, target2);
|
|
3695
|
+
byPath.set(target2, { id: targetId, agent_id: r.agent_id });
|
|
3597
3696
|
}
|
|
3598
3697
|
repointSessions.run(targetId, r.id);
|
|
3599
3698
|
repointFileChanges.run(targetId, r.id);
|
|
@@ -4544,11 +4643,12 @@ function runMetrics(argv = process.argv.slice(2)) {
|
|
|
4544
4643
|
}
|
|
4545
4644
|
|
|
4546
4645
|
// ../server/dist/run.js
|
|
4547
|
-
import { existsSync as
|
|
4646
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
4548
4647
|
import { dirname as dirname7 } from "node:path";
|
|
4549
4648
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
4550
4649
|
|
|
4551
4650
|
// ../server/dist/db.js
|
|
4651
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
4552
4652
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
4553
4653
|
|
|
4554
4654
|
// ../server/dist/sql-util.js
|
|
@@ -5006,6 +5106,17 @@ function getSession(db, id) {
|
|
|
5006
5106
|
file_changes: loadFileChanges(db, id)
|
|
5007
5107
|
};
|
|
5008
5108
|
}
|
|
5109
|
+
function sessionLocation(db, id) {
|
|
5110
|
+
const session = queryGet(db, "SELECT source_id FROM sessions WHERE id = ?", id);
|
|
5111
|
+
if (!session)
|
|
5112
|
+
return null;
|
|
5113
|
+
const files = queryAll(db, "SELECT DISTINCT source_file FROM events WHERE session_id = ? AND source_file IS NOT NULL", id).map((r) => r.source_file);
|
|
5114
|
+
if (files.length === 0)
|
|
5115
|
+
return null;
|
|
5116
|
+
const live = files.filter((f) => !f.includes("/.versions/"));
|
|
5117
|
+
const path3 = live.find((f) => f.endsWith(`/${id}.jsonl`)) ?? live[0] ?? files[0];
|
|
5118
|
+
return { session_id: id, path: path3, exists: existsSync6(path3), source_id: session.source_id };
|
|
5119
|
+
}
|
|
5009
5120
|
function safeJson(s) {
|
|
5010
5121
|
if (!s)
|
|
5011
5122
|
return null;
|
|
@@ -5369,7 +5480,7 @@ function securitySummary(db) {
|
|
|
5369
5480
|
}
|
|
5370
5481
|
|
|
5371
5482
|
// ../server/dist/app.js
|
|
5372
|
-
import { existsSync as
|
|
5483
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
5373
5484
|
import Fastify from "fastify";
|
|
5374
5485
|
import fastifyStatic from "@fastify/static";
|
|
5375
5486
|
|
|
@@ -5857,7 +5968,7 @@ function findingsOverTime(db, w, bucket) {
|
|
|
5857
5968
|
}
|
|
5858
5969
|
|
|
5859
5970
|
// ../server/dist/refresh.js
|
|
5860
|
-
import { existsSync as
|
|
5971
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5 } from "node:fs";
|
|
5861
5972
|
import { join as join10 } from "node:path";
|
|
5862
5973
|
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "[::1]"]);
|
|
5863
5974
|
function originAllowed(origin) {
|
|
@@ -5885,7 +5996,7 @@ function runRefresh() {
|
|
|
5885
5996
|
try {
|
|
5886
5997
|
const collected = collectAll();
|
|
5887
5998
|
const archiveRoot = resolveArchiveDir();
|
|
5888
|
-
if (!
|
|
5999
|
+
if (!existsSync7(archiveRoot))
|
|
5889
6000
|
mkdirSync5(archiveRoot, { recursive: true });
|
|
5890
6001
|
runIngest([]);
|
|
5891
6002
|
return { ok: true, collected };
|
|
@@ -6187,6 +6298,13 @@ async function createApp(db, opts = {}) {
|
|
|
6187
6298
|
return notFound(reply);
|
|
6188
6299
|
return result;
|
|
6189
6300
|
});
|
|
6301
|
+
app.get("/api/sessions/:id/location", async (req, reply) => {
|
|
6302
|
+
const { id } = req.params;
|
|
6303
|
+
const result = sessionLocation(db, id);
|
|
6304
|
+
if (!result)
|
|
6305
|
+
return notFound(reply, "no archive path recorded for this session");
|
|
6306
|
+
return result;
|
|
6307
|
+
});
|
|
6190
6308
|
app.get("/api/workflows/:run_id", async (req, reply) => {
|
|
6191
6309
|
const { run_id } = req.params;
|
|
6192
6310
|
const result = getWorkflow(db, run_id);
|
|
@@ -6306,7 +6424,7 @@ async function createApp(db, opts = {}) {
|
|
|
6306
6424
|
return notFound(reply);
|
|
6307
6425
|
reply.header("content-type", "text/markdown; charset=utf-8").header("content-disposition", `attachment; filename="${out.filename}"`).send(out.markdown);
|
|
6308
6426
|
});
|
|
6309
|
-
if (opts.webDist &&
|
|
6427
|
+
if (opts.webDist && existsSync8(opts.webDist)) {
|
|
6310
6428
|
await app.register(fastifyStatic, { root: opts.webDist });
|
|
6311
6429
|
app.setNotFoundHandler((req, reply) => {
|
|
6312
6430
|
if (req.url.startsWith("/api/"))
|
|
@@ -6331,7 +6449,7 @@ async function startServer(overrides = {}) {
|
|
|
6331
6449
|
console.error(`agent-lens-server: refusing to bind non-loopback host '${host}' (privacy). Set AGENT_LENS_ALLOW_NONLOCAL=1 to override.`);
|
|
6332
6450
|
process.exit(1);
|
|
6333
6451
|
}
|
|
6334
|
-
if (!
|
|
6452
|
+
if (!existsSync9(dbPath)) {
|
|
6335
6453
|
console.error(`agent-lens-server: db not found: ${dbPath} (run ingest first)`);
|
|
6336
6454
|
process.exit(1);
|
|
6337
6455
|
}
|
|
@@ -6347,7 +6465,7 @@ async function startServer(overrides = {}) {
|
|
|
6347
6465
|
try {
|
|
6348
6466
|
await app.listen({ host, port });
|
|
6349
6467
|
console.log(`agent-lens-server: http://${host}:${port} (db: ${dbPath})`);
|
|
6350
|
-
if (!
|
|
6468
|
+
if (!existsSync9(webDist))
|
|
6351
6469
|
console.log(" note: web SPA not built \u2014 run `pnpm --filter @agent-lens/web build`");
|
|
6352
6470
|
} catch (err) {
|
|
6353
6471
|
console.error(err);
|
|
@@ -6356,7 +6474,7 @@ async function startServer(overrides = {}) {
|
|
|
6356
6474
|
}
|
|
6357
6475
|
|
|
6358
6476
|
// src/watch.ts
|
|
6359
|
-
import { existsSync as
|
|
6477
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
6360
6478
|
import { join as join11 } from "node:path";
|
|
6361
6479
|
import chokidar from "chokidar";
|
|
6362
6480
|
async function runWatch(opts = {}) {
|
|
@@ -6394,7 +6512,7 @@ async function runWatch(opts = {}) {
|
|
|
6394
6512
|
}
|
|
6395
6513
|
};
|
|
6396
6514
|
cycle();
|
|
6397
|
-
const watchPaths = sources.map((s) => join11(s.configDir, "projects")).filter((p) =>
|
|
6515
|
+
const watchPaths = sources.map((s) => join11(s.configDir, "projects")).filter((p) => existsSync10(p));
|
|
6398
6516
|
if (watchPaths.length === 0) {
|
|
6399
6517
|
console.error("agent-lens watch: no source 'projects' directories found \u2014 nothing to watch");
|
|
6400
6518
|
}
|
|
@@ -6471,7 +6589,7 @@ cli.command("metrics", "Re-run classification over an already-ingested DB").opti
|
|
|
6471
6589
|
});
|
|
6472
6590
|
cli.command("export <sessionId>", "Write a session to a shareable Markdown file (redacted by default)").option("--out <file>", "Write to this file (default: stdout)").option("--level <level>", "Redaction level: secrets (default) | structure").option("--no-redact", "Verbatim, UNREDACTED output (opt-out)").option("--db <path>", "SQLite DB path (overrides AGENT_LENS_DB / config db)").action((sessionId, opts) => {
|
|
6473
6591
|
const { path: dbPath } = resolveDbPath(opts.db);
|
|
6474
|
-
if (!
|
|
6592
|
+
if (!existsSync11(dbPath)) {
|
|
6475
6593
|
console.error(`agent-lens: db not found: ${dbPath} (run ingest first)`);
|
|
6476
6594
|
process.exit(1);
|
|
6477
6595
|
}
|
|
@@ -6491,8 +6609,8 @@ cli.command("export <sessionId>", "Write a session to a shareable Markdown file
|
|
|
6491
6609
|
cli.command(
|
|
6492
6610
|
"service <action> [target]",
|
|
6493
6611
|
"Install/uninstall/status OS services \u2014 action: install|uninstall|status; target: collector|server|all (default all)"
|
|
6494
|
-
).option("--times <hours>", "Collector cadence: comma-separated hours 0-23 (default: 9,13,17,21)").action((action,
|
|
6495
|
-
const targets = parseTargets(
|
|
6612
|
+
).option("--times <hours>", "Collector cadence: comma-separated hours 0-23 (default: 9,13,17,21)").action((action, target2, opts) => {
|
|
6613
|
+
const targets = parseTargets(target2);
|
|
6496
6614
|
const hours = action === "install" ? parseHours(opts.times) : void 0;
|
|
6497
6615
|
runService(action, { cliEntry: fileURLToPath4(import.meta.url), hours, targets });
|
|
6498
6616
|
});
|