@swestash/agent-lens 0.8.0 → 0.8.1

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
5
- import { dirname as dirname6, join as join12 } from "path";
5
+ import { dirname as dirname6, join as join13 } from "path";
6
6
  import { fileURLToPath as fileURLToPath3 } from "url";
7
7
 
8
8
  // ../../node_modules/.pnpm/cac@6.7.14/node_modules/cac/dist/index.mjs
@@ -1339,14 +1339,72 @@ function resolveWebDist(fromDir, repoRoot = findRepoRoot(fromDir)) {
1339
1339
  return installed;
1340
1340
  }
1341
1341
 
1342
- // ../core/dist/sources.js
1342
+ // ../core/dist/config.js
1343
1343
  import { readFileSync as readFileSync2 } from "fs";
1344
- function readConfig() {
1344
+ import { join as join2 } from "path";
1345
+ var DEFAULT_PORT = 4477;
1346
+ var DEFAULT_HOST = "127.0.0.1";
1347
+ var DEFAULT_DB_NAME = "agent-lens.db";
1348
+ function readConfigFile() {
1345
1349
  const repoRoot = findRepoRoot();
1346
1350
  const dataDir = resolveDataDir(repoRoot);
1347
1351
  const file = resolveConfigFile(repoRoot, dataDir);
1348
1352
  return file ? JSON.parse(readFileSync2(file, "utf8")) : null;
1349
1353
  }
1354
+ function resolveDbPath(override, cfg = readConfigFile()) {
1355
+ if (override)
1356
+ return { path: override, origin: "flag" };
1357
+ if (process.env.AGENT_LENS_DB)
1358
+ return { path: process.env.AGENT_LENS_DB, origin: "env" };
1359
+ if (isSet(cfg?.db))
1360
+ return { path: expandPath(String(cfg.db).trim()), origin: "file" };
1361
+ return { path: join2(resolveDataDir(findRepoRoot()), DEFAULT_DB_NAME), origin: "default" };
1362
+ }
1363
+ function validatePort(value, source) {
1364
+ const n = typeof value === "number" ? value : Number(String(value).trim());
1365
+ if (!Number.isInteger(n) || n < 1 || n > 65535) {
1366
+ throw new Error(`agent-lens: invalid port from ${source}: '${value}' (expected an integer 1-65535)`);
1367
+ }
1368
+ return n;
1369
+ }
1370
+ function isSet(v) {
1371
+ return v !== void 0 && v !== null && String(v).trim() !== "";
1372
+ }
1373
+ function resolveServerConfig(overrides = {}, cfg = readConfigFile()) {
1374
+ let port;
1375
+ let portOrigin;
1376
+ if (isSet(overrides.port)) {
1377
+ port = validatePort(overrides.port, "--port");
1378
+ portOrigin = "flag";
1379
+ } else if (isSet(process.env.AGENT_LENS_PORT)) {
1380
+ port = validatePort(process.env.AGENT_LENS_PORT, "AGENT_LENS_PORT");
1381
+ portOrigin = "env";
1382
+ } else if (isSet(cfg?.server?.port)) {
1383
+ port = validatePort(cfg.server.port, "server.port (config file)");
1384
+ portOrigin = "file";
1385
+ } else {
1386
+ port = DEFAULT_PORT;
1387
+ portOrigin = "default";
1388
+ }
1389
+ let host;
1390
+ let hostOrigin;
1391
+ if (isSet(overrides.host)) {
1392
+ host = String(overrides.host).trim();
1393
+ hostOrigin = "flag";
1394
+ } else if (isSet(process.env.AGENT_LENS_HOST)) {
1395
+ host = process.env.AGENT_LENS_HOST.trim();
1396
+ hostOrigin = "env";
1397
+ } else if (isSet(cfg?.server?.host)) {
1398
+ host = String(cfg.server.host).trim();
1399
+ hostOrigin = "file";
1400
+ } else {
1401
+ host = DEFAULT_HOST;
1402
+ hostOrigin = "default";
1403
+ }
1404
+ return { port, host, portOrigin, hostOrigin };
1405
+ }
1406
+
1407
+ // ../core/dist/sources.js
1350
1408
  function loadSources() {
1351
1409
  if (process.env.CLAUDE_DIR) {
1352
1410
  return [
@@ -1357,7 +1415,7 @@ function loadSources() {
1357
1415
  }
1358
1416
  ];
1359
1417
  }
1360
- const cfg = readConfig() ?? { sources: [{ label: "default", agent: "claude-code", configDir: "$HOME/.claude" }] };
1418
+ const cfg = readConfigFile() ?? { sources: [{ label: "default", agent: "claude-code", configDir: "$HOME/.claude" }] };
1361
1419
  const seen = /* @__PURE__ */ new Set();
1362
1420
  const out = [];
1363
1421
  for (const raw of cfg.sources || []) {
@@ -1377,7 +1435,7 @@ function loadExcludes() {
1377
1435
  const fromEnv = (process.env.AGENT_LENS_EXCLUDE || "").split(",");
1378
1436
  let fromCfg = [];
1379
1437
  if (!process.env.CLAUDE_DIR) {
1380
- const cfg = readConfig();
1438
+ const cfg = readConfigFile();
1381
1439
  if (cfg && Array.isArray(cfg.exclude))
1382
1440
  fromCfg = cfg.exclude;
1383
1441
  }
@@ -1400,7 +1458,7 @@ function encodeProjectPath(p) {
1400
1458
 
1401
1459
  // ../core/dist/collect.js
1402
1460
  import { appendFileSync, closeSync, copyFileSync, existsSync as existsSync2, mkdirSync, openSync, readSync, readdirSync, statSync, unlinkSync, utimesSync, writeSync, chmodSync } from "fs";
1403
- import { basename, dirname as dirname2, join as join2, relative, sep } from "path";
1461
+ import { basename, dirname as dirname2, join as join3, relative, sep } from "path";
1404
1462
  import { platform as platform2 } from "os";
1405
1463
  var POSIX = platform2() !== "win32";
1406
1464
  var DIR_MODE = 448;
@@ -1480,19 +1538,19 @@ function filesEqual(a, b) {
1480
1538
  return prefixEquals(a, b, sa.size);
1481
1539
  }
1482
1540
  function latestSnapshot(archive, relParts) {
1483
- const versRoot = join2(archive, ".versions");
1541
+ const versRoot = join3(archive, ".versions");
1484
1542
  if (!existsSync2(versRoot))
1485
1543
  return null;
1486
1544
  const stamps = readdirSync(versRoot).sort();
1487
1545
  for (let i = stamps.length - 1; i >= 0; i--) {
1488
- const p = join2(versRoot, stamps[i], ...relParts);
1546
+ const p = join3(versRoot, stamps[i], ...relParts);
1489
1547
  if (existsSync2(p))
1490
1548
  return p;
1491
1549
  }
1492
1550
  return null;
1493
1551
  }
1494
1552
  function snapshot(absPath, relParts, versionsDir, stats) {
1495
- const dst = join2(versionsDir, ...relParts);
1553
+ const dst = join3(versionsDir, ...relParts);
1496
1554
  ensureDir(dirname2(dst));
1497
1555
  copyFileSync(absPath, dst);
1498
1556
  syncMeta(dst, statSync(absPath));
@@ -1500,7 +1558,7 @@ function snapshot(absPath, relParts, versionsDir, stats) {
1500
1558
  }
1501
1559
  function walkCollectable(dir, out, inWorkflows = false, inToolResults = false) {
1502
1560
  for (const e of readdirSync(dir, { withFileTypes: true })) {
1503
- const p = join2(dir, e.name);
1561
+ const p = join3(dir, e.name);
1504
1562
  if (e.isDirectory()) {
1505
1563
  walkCollectable(p, out, inWorkflows || e.name === "workflows", inToolResults || e.name === "tool-results");
1506
1564
  } else if (e.isFile() && (e.name.endsWith(".jsonl") || inWorkflows || inToolResults || e.name.endsWith(".meta.json"))) {
@@ -1541,7 +1599,7 @@ function removeCredentials(dir) {
1541
1599
  if (!existsSync2(dir))
1542
1600
  return;
1543
1601
  for (const e of readdirSync(dir, { withFileTypes: true })) {
1544
- const p = join2(dir, e.name);
1602
+ const p = join3(dir, e.name);
1545
1603
  if (e.isDirectory())
1546
1604
  removeCredentials(p);
1547
1605
  else if (e.name === ".credentials.json") {
@@ -1554,19 +1612,19 @@ function removeCredentials(dir) {
1554
1612
  }
1555
1613
  function collectSource(source, archiveBase, excludedDirs, runTs, stats, log) {
1556
1614
  const { label, configDir } = source;
1557
- const archive = join2(archiveBase, label);
1558
- const versionsDir = join2(archive, ".versions", runTs);
1615
+ const archive = join3(archiveBase, label);
1616
+ const versionsDir = join3(archive, ".versions", runTs);
1559
1617
  if (!existsSync2(configDir)) {
1560
1618
  log(`agent-lens: [${label}] source not found: ${configDir} (skipping)`);
1561
1619
  return;
1562
1620
  }
1563
- ensureDir(join2(archive, "projects"));
1564
- ensureDir(join2(archive, "settings"));
1621
+ ensureDir(join3(archive, "projects"));
1622
+ ensureDir(join3(archive, "settings"));
1565
1623
  const files = [];
1566
- const projectsDir = join2(configDir, "projects");
1624
+ const projectsDir = join3(configDir, "projects");
1567
1625
  if (existsSync2(projectsDir))
1568
1626
  walkCollectable(projectsDir, files);
1569
- const historySrc = join2(configDir, "history.jsonl");
1627
+ const historySrc = join3(configDir, "history.jsonl");
1570
1628
  if (existsSync2(historySrc))
1571
1629
  files.push(historySrc);
1572
1630
  const before = { scanned: stats.scanned, snapshots: stats.snapshots };
@@ -1579,13 +1637,13 @@ function collectSource(source, archiveBase, excludedDirs, runTs, stats, log) {
1579
1637
  if (excludedDirs.some((enc) => relPosix.startsWith(`projects/${enc}/`)))
1580
1638
  continue;
1581
1639
  stats.scanned++;
1582
- syncFile(src, join2(archive, ...relParts), relParts, archive, versionsDir, stats, base.endsWith(".jsonl"));
1640
+ syncFile(src, join3(archive, ...relParts), relParts, archive, versionsDir, stats, base.endsWith(".jsonl"));
1583
1641
  }
1584
1642
  for (const f of ["settings.json", "settings.local.json"]) {
1585
- const s = join2(configDir, f);
1643
+ const s = join3(configDir, f);
1586
1644
  if (!existsSync2(s))
1587
1645
  continue;
1588
- const a = join2(archive, "settings", f);
1646
+ const a = join3(archive, "settings", f);
1589
1647
  if (existsSync2(a) && !filesEqual(s, a))
1590
1648
  snapshot(a, ["settings", f], versionsDir, stats);
1591
1649
  ensureDir(dirname2(a));
@@ -1598,13 +1656,13 @@ function collectSource(source, archiveBase, excludedDirs, runTs, stats, log) {
1598
1656
  const line = `${(/* @__PURE__ */ new Date()).toISOString()} run=${runTs} source=${label} scanned=${scanned} snapshots=${snapshots} archive=${archive}`;
1599
1657
  log(`agent-lens: ${line}`);
1600
1658
  try {
1601
- appendFileSync(join2(archiveBase, ".collect.log"), line + "\n");
1659
+ appendFileSync(join3(archiveBase, ".collect.log"), line + "\n");
1602
1660
  } catch {
1603
1661
  }
1604
1662
  }
1605
1663
  function collectAll(opts = {}) {
1606
1664
  const log = opts.log ?? ((m) => console.log(m));
1607
- const archiveBase = opts.archiveBase ?? join2(resolveDataDir(findRepoRoot()), "archive");
1665
+ const archiveBase = opts.archiveBase ?? join3(resolveDataDir(findRepoRoot()), "archive");
1608
1666
  const sources = opts.sources ?? loadSources();
1609
1667
  const excludedDirs = (opts.excludes ?? loadExcludes()).map(encodeProjectPath);
1610
1668
  const runTs = opts.runTimestamp ?? collectTimestamp();
@@ -1665,7 +1723,7 @@ function acquireLock(lockPath, staleMs = 6 * 60 * 60 * 1e3) {
1665
1723
  import { spawnSync } from "child_process";
1666
1724
  import { mkdirSync as mkdirSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
1667
1725
  import { homedir as homedir2, platform as platform3 } from "os";
1668
- import { join as join3 } from "path";
1726
+ import { join as join4 } from "path";
1669
1727
  var DEFAULT_HOURS = [9, 13, 17, 21];
1670
1728
  var SYSTEMD_COLLECTOR_TIMER = "agent-lens-collect.timer";
1671
1729
  var SYSTEMD_COLLECTOR_SERVICE = "agent-lens-collect.service";
@@ -1691,10 +1749,11 @@ function parseHours(spec) {
1691
1749
  }
1692
1750
  function serverEnv() {
1693
1751
  const env = {};
1694
- if (process.env.AGENT_LENS_PORT)
1695
- env.AGENT_LENS_PORT = process.env.AGENT_LENS_PORT;
1696
- if (process.env.AGENT_LENS_HOST)
1697
- env.AGENT_LENS_HOST = process.env.AGENT_LENS_HOST;
1752
+ const { port, host, portOrigin, hostOrigin } = resolveServerConfig();
1753
+ if (portOrigin !== "default")
1754
+ env.AGENT_LENS_PORT = String(port);
1755
+ if (hostOrigin !== "default")
1756
+ env.AGENT_LENS_HOST = host;
1698
1757
  return env;
1699
1758
  }
1700
1759
  function onCalendarHours(hours) {
@@ -1807,7 +1866,7 @@ function have(cmd) {
1807
1866
  return r.status === 0 || r.error === void 0;
1808
1867
  }
1809
1868
  function systemdUnitDir() {
1810
- return join3(process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config"), "systemd", "user");
1869
+ return join4(process.env.XDG_CONFIG_HOME || join4(homedir2(), ".config"), "systemd", "user");
1811
1870
  }
1812
1871
  function linuxInstall(targets, node, cli2, hours) {
1813
1872
  if (!have("systemctl")) {
@@ -1818,11 +1877,11 @@ function linuxInstall(targets, node, cli2, hours) {
1818
1877
  const dir = systemdUnitDir();
1819
1878
  mkdirSync3(dir, { recursive: true });
1820
1879
  if (targets.includes("collector")) {
1821
- writeFileSync2(join3(dir, SYSTEMD_COLLECTOR_SERVICE), systemdCollectorService(node, cli2));
1822
- writeFileSync2(join3(dir, SYSTEMD_COLLECTOR_TIMER), systemdCollectorTimer(hours));
1880
+ writeFileSync2(join4(dir, SYSTEMD_COLLECTOR_SERVICE), systemdCollectorService(node, cli2));
1881
+ writeFileSync2(join4(dir, SYSTEMD_COLLECTOR_TIMER), systemdCollectorTimer(hours));
1823
1882
  }
1824
1883
  if (targets.includes("server")) {
1825
- writeFileSync2(join3(dir, SYSTEMD_SERVER_SERVICE), systemdServerService(node, cli2, serverEnv()));
1884
+ writeFileSync2(join4(dir, SYSTEMD_SERVER_SERVICE), systemdServerService(node, cli2, serverEnv()));
1826
1885
  }
1827
1886
  sh("systemctl", ["--user", "daemon-reload"]);
1828
1887
  if (targets.includes("collector")) {
@@ -1846,12 +1905,12 @@ function linuxUninstall(targets) {
1846
1905
  if (targets.includes("collector")) {
1847
1906
  sh("systemctl", ["--user", "disable", "--now", SYSTEMD_COLLECTOR_TIMER], true);
1848
1907
  for (const u of [SYSTEMD_COLLECTOR_TIMER, SYSTEMD_COLLECTOR_SERVICE])
1849
- rmSync(join3(dir, u), { force: true });
1908
+ rmSync(join4(dir, u), { force: true });
1850
1909
  sh("systemctl", ["--user", "reset-failed", SYSTEMD_COLLECTOR_SERVICE], true);
1851
1910
  }
1852
1911
  if (targets.includes("server")) {
1853
1912
  sh("systemctl", ["--user", "disable", "--now", SYSTEMD_SERVER_SERVICE], true);
1854
- rmSync(join3(dir, SYSTEMD_SERVER_SERVICE), { force: true });
1913
+ rmSync(join4(dir, SYSTEMD_SERVER_SERVICE), { force: true });
1855
1914
  sh("systemctl", ["--user", "reset-failed", SYSTEMD_SERVER_SERVICE], true);
1856
1915
  }
1857
1916
  sh("systemctl", ["--user", "daemon-reload"]);
@@ -1866,7 +1925,7 @@ function linuxStatus(targets) {
1866
1925
  sh("systemctl", ["--user", "status", SYSTEMD_SERVER_SERVICE, "--no-pager"]);
1867
1926
  }
1868
1927
  function launchdPlistPath(label) {
1869
- return join3(homedir2(), "Library", "LaunchAgents", `${label}.plist`);
1928
+ return join4(homedir2(), "Library", "LaunchAgents", `${label}.plist`);
1870
1929
  }
1871
1930
  function macBootstrap(plist) {
1872
1931
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
@@ -1880,18 +1939,18 @@ function macBootout(label, plist) {
1880
1939
  rmSync(plist, { force: true });
1881
1940
  }
1882
1941
  function macInstall(targets, node, cli2, hours) {
1883
- const agents = join3(homedir2(), "Library", "LaunchAgents");
1942
+ const agents = join4(homedir2(), "Library", "LaunchAgents");
1884
1943
  mkdirSync3(agents, { recursive: true });
1885
1944
  const dataDir = resolveDataDir(findRepoRoot());
1886
1945
  if (targets.includes("collector")) {
1887
1946
  const plist = launchdPlistPath(LAUNCHD_COLLECTOR_LABEL);
1888
- writeFileSync2(plist, launchdCollectorPlist(node, cli2, hours, join3(dataDir, "schedule.log")));
1947
+ writeFileSync2(plist, launchdCollectorPlist(node, cli2, hours, join4(dataDir, "schedule.log")));
1889
1948
  macBootstrap(plist);
1890
1949
  console.log(`agent-lens service: installed collector agent ${LAUNCHD_COLLECTOR_LABEL} (hours ${hours.join(",")})`);
1891
1950
  }
1892
1951
  if (targets.includes("server")) {
1893
1952
  const plist = launchdPlistPath(LAUNCHD_SERVER_LABEL);
1894
- writeFileSync2(plist, launchdServerPlist(node, cli2, join3(dataDir, "server.log"), serverEnv()));
1953
+ writeFileSync2(plist, launchdServerPlist(node, cli2, join4(dataDir, "server.log"), serverEnv()));
1895
1954
  macBootstrap(plist);
1896
1955
  console.log(`agent-lens service: installed server agent ${LAUNCHD_SERVER_LABEL}`);
1897
1956
  }
@@ -1982,7 +2041,7 @@ function runService(action, params) {
1982
2041
 
1983
2042
  // ../ingest/dist/run.js
1984
2043
  import { readFileSync as readFileSync7, statSync as statSync5, mkdirSync as mkdirSync4, existsSync as existsSync4 } from "fs";
1985
- import { dirname as dirname4, join as join8 } from "path";
2044
+ import { dirname as dirname4, join as join9 } from "path";
1986
2045
 
1987
2046
  // ../ingest/dist/db.js
1988
2047
  import Database from "better-sqlite3";
@@ -2038,7 +2097,7 @@ function resetSchema(db) {
2038
2097
 
2039
2098
  // ../ingest/dist/adapters/claude-code.js
2040
2099
  import { readdirSync as readdirSync2 } from "fs";
2041
- import { basename as basename2, join as join4 } from "path";
2100
+ import { basename as basename2, join as join5 } from "path";
2042
2101
  function flattenText(content) {
2043
2102
  if (content == null)
2044
2103
  return null;
@@ -2121,7 +2180,7 @@ function recurseJsonl(dir, out) {
2121
2180
  return;
2122
2181
  }
2123
2182
  for (const e of entries) {
2124
- const p = join4(dir, e.name);
2183
+ const p = join5(dir, e.name);
2125
2184
  if (e.isDirectory())
2126
2185
  recurseJsonl(p, out);
2127
2186
  else if (e.isFile() && e.name.endsWith(".jsonl"))
@@ -2152,12 +2211,12 @@ var ClaudeCodeAdapter = class {
2152
2211
  });
2153
2212
  }
2154
2213
  };
2155
- addFrom(join4(archiveRoot, "projects"), false);
2156
- const versionsRoot = join4(archiveRoot, ".versions");
2214
+ addFrom(join5(archiveRoot, "projects"), false);
2215
+ const versionsRoot = join5(archiveRoot, ".versions");
2157
2216
  try {
2158
2217
  for (const ts of readdirSync2(versionsRoot, { withFileTypes: true })) {
2159
2218
  if (ts.isDirectory())
2160
- addFrom(join4(versionsRoot, ts.name, "projects"), true);
2219
+ addFrom(join5(versionsRoot, ts.name, "projects"), true);
2161
2220
  }
2162
2221
  } catch {
2163
2222
  }
@@ -3584,7 +3643,7 @@ function ingestFile(db, stmts, adapter, file, lines, meta, now2, stats) {
3584
3643
 
3585
3644
  // ../ingest/dist/workflows.js
3586
3645
  import { readdirSync as readdirSync3, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
3587
- import { basename as basename3, join as join5 } from "path";
3646
+ import { basename as basename3, join as join6 } from "path";
3588
3647
 
3589
3648
  // ../ingest/dist/redact.js
3590
3649
  import { createHash as createHash4, randomBytes } from "crypto";
@@ -3652,7 +3711,7 @@ function walkSidecars(dir, inWorkflows, out) {
3652
3711
  return;
3653
3712
  }
3654
3713
  for (const e of entries) {
3655
- const p = join5(dir, e.name);
3714
+ const p = join6(dir, e.name);
3656
3715
  if (e.isDirectory())
3657
3716
  walkSidecars(p, inWorkflows || e.name === "workflows", out);
3658
3717
  else if (e.isFile() && inWorkflows && /^wf_.+\.json$/.test(e.name))
@@ -3661,11 +3720,11 @@ function walkSidecars(dir, inWorkflows, out) {
3661
3720
  }
3662
3721
  function discoverSidecars(sourceArchiveDir) {
3663
3722
  const out = [];
3664
- walkSidecars(join5(sourceArchiveDir, "projects"), false, out);
3723
+ walkSidecars(join6(sourceArchiveDir, "projects"), false, out);
3665
3724
  try {
3666
- for (const ts of readdirSync3(join5(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3725
+ for (const ts of readdirSync3(join6(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3667
3726
  if (ts.isDirectory())
3668
- walkSidecars(join5(sourceArchiveDir, ts.name, "projects"), false, out);
3727
+ walkSidecars(join6(sourceArchiveDir, ts.name, "projects"), false, out);
3669
3728
  } catch {
3670
3729
  }
3671
3730
  return out;
@@ -3783,7 +3842,7 @@ function ingestWorkflowResults(db, sourceArchiveDir, sourceId, excludedDirs, now
3783
3842
 
3784
3843
  // ../ingest/dist/meta.js
3785
3844
  import { readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
3786
- import { basename as basename4, join as join6 } from "path";
3845
+ import { basename as basename4, join as join7 } from "path";
3787
3846
  function newMetaStats() {
3788
3847
  return { files: 0, upserted: 0, skipped: 0, malformed: 0 };
3789
3848
  }
@@ -3795,7 +3854,7 @@ function walkMeta(dir, inSubagents, out) {
3795
3854
  return;
3796
3855
  }
3797
3856
  for (const e of entries) {
3798
- const p = join6(dir, e.name);
3857
+ const p = join7(dir, e.name);
3799
3858
  if (e.isDirectory())
3800
3859
  walkMeta(p, inSubagents || e.name === "subagents", out);
3801
3860
  else if (e.isFile() && inSubagents && /^agent-.+\.meta\.json$/.test(e.name))
@@ -3804,11 +3863,11 @@ function walkMeta(dir, inSubagents, out) {
3804
3863
  }
3805
3864
  function discoverMeta(sourceArchiveDir) {
3806
3865
  const out = [];
3807
- walkMeta(join6(sourceArchiveDir, "projects"), false, out);
3866
+ walkMeta(join7(sourceArchiveDir, "projects"), false, out);
3808
3867
  try {
3809
- for (const ts of readdirSync4(join6(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3868
+ for (const ts of readdirSync4(join7(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3810
3869
  if (ts.isDirectory())
3811
- walkMeta(join6(sourceArchiveDir, ts.name, "projects"), false, out);
3870
+ walkMeta(join7(sourceArchiveDir, ts.name, "projects"), false, out);
3812
3871
  } catch {
3813
3872
  }
3814
3873
  return out;
@@ -3882,7 +3941,7 @@ function ingestSubagentMeta(db, sourceArchiveDir, sourceId, excludedDirs, now2,
3882
3941
 
3883
3942
  // ../ingest/dist/toolResults.js
3884
3943
  import { readdirSync as readdirSync5, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
3885
- import { basename as basename5, join as join7 } from "path";
3944
+ import { basename as basename5, join as join8 } from "path";
3886
3945
  function newToolResultStats() {
3887
3946
  return { files: 0, upserted: 0, skipped: 0, malformed: 0 };
3888
3947
  }
@@ -3894,7 +3953,7 @@ function walkResults(dir, inResults, out) {
3894
3953
  return;
3895
3954
  }
3896
3955
  for (const e of entries) {
3897
- const p = join7(dir, e.name);
3956
+ const p = join8(dir, e.name);
3898
3957
  if (e.isDirectory())
3899
3958
  walkResults(p, inResults || e.name === "tool-results", out);
3900
3959
  else if (e.isFile() && inResults && e.name.endsWith(".txt"))
@@ -3903,11 +3962,11 @@ function walkResults(dir, inResults, out) {
3903
3962
  }
3904
3963
  function discoverResults(sourceArchiveDir) {
3905
3964
  const out = [];
3906
- walkResults(join7(sourceArchiveDir, "projects"), false, out);
3965
+ walkResults(join8(sourceArchiveDir, "projects"), false, out);
3907
3966
  try {
3908
- for (const ts of readdirSync5(join7(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3967
+ for (const ts of readdirSync5(join8(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3909
3968
  if (ts.isDirectory())
3910
- walkResults(join7(sourceArchiveDir, ts.name, "projects"), false, out);
3969
+ walkResults(join8(sourceArchiveDir, ts.name, "projects"), false, out);
3911
3970
  } catch {
3912
3971
  }
3913
3972
  return out;
@@ -3984,8 +4043,8 @@ function parseIngestArgs(argv) {
3984
4043
  function runIngest(argv = process.argv.slice(2)) {
3985
4044
  const args = parseIngestArgs(argv);
3986
4045
  const dataDir = resolveDataDir(findRepoRoot());
3987
- const archiveRoot = args.archive || process.env.AGENT_LENS_ARCHIVE || join8(dataDir, "archive");
3988
- const dbPath = args.db || process.env.AGENT_LENS_DB || join8(dataDir, "agent-lens.db");
4046
+ const archiveRoot = args.archive || process.env.AGENT_LENS_ARCHIVE || join9(dataDir, "archive");
4047
+ const { path: dbPath } = resolveDbPath(args.db);
3989
4048
  if (!existsSync4(archiveRoot)) {
3990
4049
  console.error(`agent-lens-ingest: archive not found: ${archiveRoot} (run 'agent-lens collect' first)`);
3991
4050
  process.exit(1);
@@ -4023,7 +4082,7 @@ function runIngest(argv = process.argv.slice(2)) {
4023
4082
  }
4024
4083
  stmts.insAgent.run(adapter.agentId, adapter.agentName);
4025
4084
  stmts.insSource.run({ id: source.label, label: source.label, agent_id: adapter.agentId, config_dir: source.configDir });
4026
- let files = adapter.discover(join8(archiveRoot, source.label), source.label);
4085
+ let files = adapter.discover(join9(archiveRoot, source.label), source.label);
4027
4086
  if (excludedDirs.length)
4028
4087
  files = files.filter((f) => !isExcludedArchivePath(f.path, excludedDirs));
4029
4088
  files.sort((a, b) => Number(a.isVersion) - Number(b.isVersion));
@@ -4055,9 +4114,9 @@ function runIngest(argv = process.argv.slice(2)) {
4055
4114
  ingestFile(db, stmts, adapter, file, lines, { size: st.size, mtimeMs, hash: hash2 }, now2, stats);
4056
4115
  dirty.add(file.sessionId);
4057
4116
  }
4058
- ingestWorkflowResults(db, join8(archiveRoot, source.label), source.label, excludedDirs, now2, wfStats, args.full);
4059
- ingestSubagentMeta(db, join8(archiveRoot, source.label), source.label, excludedDirs, now2, metaStats, args.full);
4060
- ingestToolResults(db, join8(archiveRoot, source.label), excludedDirs, now2, trStats, args.full);
4117
+ ingestWorkflowResults(db, join9(archiveRoot, source.label), source.label, excludedDirs, now2, wfStats, args.full);
4118
+ ingestSubagentMeta(db, join9(archiveRoot, source.label), source.label, excludedDirs, now2, metaStats, args.full);
4119
+ ingestToolResults(db, join9(archiveRoot, source.label), excludedDirs, now2, trStats, args.full);
4061
4120
  }
4062
4121
  const expanded = rebuildDerived(db, args.full ? null : dirty);
4063
4122
  const classified = classify(db, args.full ? null : expanded);
@@ -4095,8 +4154,7 @@ function runMetrics(argv = process.argv.slice(2)) {
4095
4154
  for (let i = 0; i < argv.length; i++)
4096
4155
  if (argv[i] === "--db")
4097
4156
  dbArg = argv[++i] ?? "";
4098
- const dataDir = resolveDataDir(findRepoRoot());
4099
- const dbPath = dbArg || process.env.AGENT_LENS_DB || join8(dataDir, "agent-lens.db");
4157
+ const { path: dbPath } = resolveDbPath(dbArg);
4100
4158
  if (!existsSync4(dbPath)) {
4101
4159
  console.error(`agent-lens-metrics: db not found: ${dbPath} (run ingest first)`);
4102
4160
  process.exit(1);
@@ -4111,7 +4169,7 @@ function runMetrics(argv = process.argv.slice(2)) {
4111
4169
 
4112
4170
  // ../server/dist/run.js
4113
4171
  import { existsSync as existsSync7 } from "fs";
4114
- import { dirname as dirname5, join as join10 } from "path";
4172
+ import { dirname as dirname5, join as join11 } from "path";
4115
4173
  import { fileURLToPath as fileURLToPath2 } from "url";
4116
4174
 
4117
4175
  // ../server/dist/db.js
@@ -4967,7 +5025,7 @@ function dashboardBreakdowns(db, f) {
4967
5025
 
4968
5026
  // ../server/dist/refresh.js
4969
5027
  import { existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
4970
- import { join as join9 } from "path";
5028
+ import { join as join10 } from "path";
4971
5029
  function originAllowed(origin) {
4972
5030
  if (!origin)
4973
5031
  return true;
@@ -4988,12 +5046,12 @@ function writeBlocked(headers) {
4988
5046
  }
4989
5047
  function runRefresh() {
4990
5048
  const dataDir = resolveDataDir(findRepoRoot());
4991
- const lock = acquireLock(join9(dataDir, ".agent-lens.lock"));
5049
+ const lock = acquireLock(join10(dataDir, ".agent-lens.lock"));
4992
5050
  if (!lock)
4993
5051
  return null;
4994
5052
  try {
4995
5053
  const collected = collectAll();
4996
- const archiveRoot = process.env.AGENT_LENS_ARCHIVE || join9(dataDir, "archive");
5054
+ const archiveRoot = process.env.AGENT_LENS_ARCHIVE || join10(dataDir, "archive");
4997
5055
  if (!existsSync5(archiveRoot))
4998
5056
  mkdirSync5(archiveRoot, { recursive: true });
4999
5057
  runIngest([]);
@@ -5309,12 +5367,11 @@ async function createApp(db, opts = {}) {
5309
5367
  }
5310
5368
 
5311
5369
  // ../server/dist/run.js
5312
- async function startServer() {
5370
+ async function startServer(overrides = {}) {
5313
5371
  const here = dirname5(fileURLToPath2(import.meta.url));
5314
5372
  const repoRoot = findRepoRoot(here);
5315
- const dbPath = process.env.AGENT_LENS_DB || join10(resolveDataDir(repoRoot), "agent-lens.db");
5316
- const host = process.env.AGENT_LENS_HOST || "127.0.0.1";
5317
- const port = Number(process.env.AGENT_LENS_PORT || 4477);
5373
+ const { path: dbPath } = resolveDbPath(overrides.db);
5374
+ const { host, port } = resolveServerConfig({ port: overrides.port, host: overrides.host });
5318
5375
  const webDist = resolveWebDist(here, repoRoot);
5319
5376
  if (host !== "127.0.0.1" && host !== "localhost" && !process.env.AGENT_LENS_ALLOW_NONLOCAL) {
5320
5377
  console.error(`agent-lens-server: refusing to bind non-loopback host '${host}' (privacy). Set AGENT_LENS_ALLOW_NONLOCAL=1 to override.`);
@@ -5324,7 +5381,7 @@ async function startServer() {
5324
5381
  console.error(`agent-lens-server: db not found: ${dbPath} (run ingest first)`);
5325
5382
  process.exit(1);
5326
5383
  }
5327
- const triageDbPath = process.env.AGENT_LENS_TRIAGE_DB || join10(dirname5(dbPath), "triage.db");
5384
+ const triageDbPath = process.env.AGENT_LENS_TRIAGE_DB || join11(dirname5(dbPath), "triage.db");
5328
5385
  const db = openReadonly(dbPath);
5329
5386
  const enforceLoopbackHost = host === "127.0.0.1" || host === "localhost";
5330
5387
  const app = await createApp(db, { webDist, triageDbPath, enforceLoopbackHost });
@@ -5341,11 +5398,11 @@ async function startServer() {
5341
5398
 
5342
5399
  // src/watch.ts
5343
5400
  import { existsSync as existsSync8 } from "fs";
5344
- import { join as join11 } from "path";
5401
+ import { join as join12 } from "path";
5345
5402
  import chokidar from "chokidar";
5346
5403
  async function runWatch(opts = {}) {
5347
5404
  const sources = loadSources();
5348
- const lockPath = join11(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5405
+ const lockPath = join12(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5349
5406
  let running = false;
5350
5407
  let pending = false;
5351
5408
  const cycle = () => {
@@ -5378,7 +5435,7 @@ async function runWatch(opts = {}) {
5378
5435
  }
5379
5436
  };
5380
5437
  cycle();
5381
- const watchPaths = sources.map((s) => join11(s.configDir, "projects")).filter((p) => existsSync8(p));
5438
+ const watchPaths = sources.map((s) => join12(s.configDir, "projects")).filter((p) => existsSync8(p));
5382
5439
  if (watchPaths.length === 0) {
5383
5440
  console.error("agent-lens watch: no source 'projects' directories found \u2014 nothing to watch");
5384
5441
  }
@@ -5406,14 +5463,14 @@ async function runWatch(opts = {}) {
5406
5463
  // src/index.ts
5407
5464
  function version() {
5408
5465
  try {
5409
- const pkg = JSON.parse(readFileSync8(join12(dirname6(fileURLToPath3(import.meta.url)), "../package.json"), "utf8"));
5466
+ const pkg = JSON.parse(readFileSync8(join13(dirname6(fileURLToPath3(import.meta.url)), "../package.json"), "utf8"));
5410
5467
  return pkg.version ?? "0.0.0";
5411
5468
  } catch {
5412
5469
  return "0.0.0";
5413
5470
  }
5414
5471
  }
5415
5472
  function withLock(fn) {
5416
- const lockPath = join12(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5473
+ const lockPath = join13(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5417
5474
  const lock = acquireLock(lockPath);
5418
5475
  if (!lock) {
5419
5476
  console.error("agent-lens: another collect/ingest run is in progress \u2014 skipping");
@@ -5435,24 +5492,29 @@ cli.command("collect", "Copy new session traces into the local archive").option(
5435
5492
  if (opts.thenIngest) runIngest([]);
5436
5493
  });
5437
5494
  });
5438
- cli.command("ingest", "Build/update the SQLite projection from the archive").option("--full", "Ignore incremental state and re-read every file").option("--db <path>", "SQLite DB path").option("--archive <path>", "Archive directory").action((opts) => {
5495
+ cli.command("ingest", "Build/update the SQLite projection from the archive").option("--full", "Ignore incremental state and re-read every file").option("--db <path>", "SQLite DB path (overrides AGENT_LENS_DB / config db)").option("--archive <path>", "Archive directory").action((opts) => {
5439
5496
  const argv = [];
5440
5497
  if (opts.full) argv.push("--full");
5441
5498
  if (opts.db) argv.push("--db", opts.db);
5442
5499
  if (opts.archive) argv.push("--archive", opts.archive);
5443
5500
  withLock(() => runIngest(argv));
5444
5501
  });
5445
- cli.command("serve", "Serve the web UI + read-only API on 127.0.0.1").action(async () => {
5446
- await startServer();
5502
+ cli.command("serve", "Serve the web UI + read-only API on 127.0.0.1").option("--port <n>", "HTTP port (overrides AGENT_LENS_PORT / config server.port; default 4477)").option("--host <host>", "Bind host (overrides AGENT_LENS_HOST / config server.host; loopback only unless AGENT_LENS_ALLOW_NONLOCAL=1)").option("--db <path>", "SQLite DB path (overrides AGENT_LENS_DB / config db)").action(async (opts) => {
5503
+ try {
5504
+ await startServer({ port: opts.port, host: opts.host, db: opts.db });
5505
+ } catch (e) {
5506
+ console.error(e instanceof Error ? e.message : String(e));
5507
+ process.exit(1);
5508
+ }
5447
5509
  });
5448
5510
  cli.command("watch", "Watch sources and collect + ingest on change (resident process)").option("--interval <sec>", "Also run on a fixed interval (seconds)").option("--poll", "Use polling \u2014 for network filesystems where fs events don't fire").action(async (opts) => {
5449
5511
  await runWatch({ intervalSec: opts.interval ? Number(opts.interval) : void 0, poll: !!opts.poll });
5450
5512
  });
5451
- cli.command("metrics", "Re-run classification over an already-ingested DB").option("--db <path>", "SQLite DB path").action((opts) => {
5513
+ cli.command("metrics", "Re-run classification over an already-ingested DB").option("--db <path>", "SQLite DB path (overrides AGENT_LENS_DB / config db)").action((opts) => {
5452
5514
  runMetrics(opts.db ? ["--db", opts.db] : []);
5453
5515
  });
5454
- 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").action((sessionId, opts) => {
5455
- const dbPath = opts.db || process.env.AGENT_LENS_DB || join12(resolveDataDir(findRepoRoot()), "agent-lens.db");
5516
+ 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) => {
5517
+ const { path: dbPath } = resolveDbPath(opts.db);
5456
5518
  if (!existsSync9(dbPath)) {
5457
5519
  console.error(`agent-lens: db not found: ${dbPath} (run ingest first)`);
5458
5520
  process.exit(1);
@@ -5478,6 +5540,39 @@ cli.command(
5478
5540
  const hours = action === "install" ? parseHours(opts.times) : void 0;
5479
5541
  runService(action, { cliEntry: fileURLToPath3(import.meta.url), hours, targets });
5480
5542
  });
5543
+ cli.command("config", "Show the effective configuration (sources, paths, server) and where each value came from").action(() => {
5544
+ const repoRoot = findRepoRoot();
5545
+ const dataDir = resolveDataDir(repoRoot);
5546
+ const configFile = resolveConfigFile(repoRoot, dataDir);
5547
+ let server;
5548
+ try {
5549
+ server = resolveServerConfig();
5550
+ } catch (e) {
5551
+ console.error(e instanceof Error ? e.message : String(e));
5552
+ process.exit(1);
5553
+ }
5554
+ const { port, host, portOrigin, hostOrigin } = server;
5555
+ const { path: dbPath, origin: dbOrigin } = resolveDbPath();
5556
+ const archive = process.env.AGENT_LENS_ARCHIVE || join13(dataDir, "archive");
5557
+ const triageDb = process.env.AGENT_LENS_TRIAGE_DB || join13(dirname6(dbPath), "triage.db");
5558
+ const keepDays = process.env.AGENT_LENS_VERSIONS_KEEP_DAYS || "90";
5559
+ const keepOrigin = process.env.AGENT_LENS_VERSIONS_KEEP_DAYS ? "env" : "default";
5560
+ console.log("agent-lens config (precedence: flag > env > config file > default)\n");
5561
+ console.log("Paths");
5562
+ console.log(` config file ${configFile ?? "(none \u2014 using built-in default source)"}`);
5563
+ console.log(` data dir ${dataDir}${process.env.AGENT_LENS_DATA ? " [env]" : ""}`);
5564
+ console.log(` archive ${archive}${process.env.AGENT_LENS_ARCHIVE ? " [env]" : ""}`);
5565
+ console.log(` db ${dbPath} [${dbOrigin}]`);
5566
+ console.log(` triage db ${triageDb}${process.env.AGENT_LENS_TRIAGE_DB ? " [env]" : ""}`);
5567
+ console.log("\nServer");
5568
+ console.log(` host ${host} [${hostOrigin}]`);
5569
+ console.log(` port ${port} [${portOrigin}]`);
5570
+ console.log(` non-local bind ${process.env.AGENT_LENS_ALLOW_NONLOCAL ? "allowed (AGENT_LENS_ALLOW_NONLOCAL)" : "blocked (loopback only)"}`);
5571
+ console.log("\nRetention");
5572
+ console.log(` .versions keep ${keepDays} day(s) [${keepOrigin}]`);
5573
+ console.log("\nSources");
5574
+ for (const s of loadSources()) console.log(` ${s.label.padEnd(14)} ${s.configDir} (${s.agent})`);
5575
+ });
5481
5576
  cli.help();
5482
5577
  cli.version(version());
5483
5578
  cli.parse();