@swestash/agent-lens 0.8.0 → 0.8.2

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();
@@ -1664,9 +1722,16 @@ function acquireLock(lockPath, staleMs = 6 * 60 * 60 * 1e3) {
1664
1722
  // ../core/dist/service.js
1665
1723
  import { spawnSync } from "child_process";
1666
1724
  import { mkdirSync as mkdirSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
1667
- import { homedir as homedir2, platform as platform3 } from "os";
1668
- import { join as join3 } from "path";
1725
+ import { homedir as homedir2, platform as platform3, userInfo } from "os";
1726
+ import { join as join4 } from "path";
1669
1727
  var DEFAULT_HOURS = [9, 13, 17, 21];
1728
+ function currentUser() {
1729
+ try {
1730
+ return userInfo().username;
1731
+ } catch {
1732
+ return process.env.USER || process.env.LOGNAME || "";
1733
+ }
1734
+ }
1670
1735
  var SYSTEMD_COLLECTOR_TIMER = "agent-lens-collect.timer";
1671
1736
  var SYSTEMD_COLLECTOR_SERVICE = "agent-lens-collect.service";
1672
1737
  var SYSTEMD_SERVER_SERVICE = "agent-lens-server.service";
@@ -1691,10 +1756,11 @@ function parseHours(spec) {
1691
1756
  }
1692
1757
  function serverEnv() {
1693
1758
  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;
1759
+ const { port, host, portOrigin, hostOrigin } = resolveServerConfig();
1760
+ if (portOrigin !== "default")
1761
+ env.AGENT_LENS_PORT = String(port);
1762
+ if (hostOrigin !== "default")
1763
+ env.AGENT_LENS_HOST = host;
1698
1764
  return env;
1699
1765
  }
1700
1766
  function onCalendarHours(hours) {
@@ -1807,7 +1873,7 @@ function have(cmd) {
1807
1873
  return r.status === 0 || r.error === void 0;
1808
1874
  }
1809
1875
  function systemdUnitDir() {
1810
- return join3(process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config"), "systemd", "user");
1876
+ return join4(process.env.XDG_CONFIG_HOME || join4(homedir2(), ".config"), "systemd", "user");
1811
1877
  }
1812
1878
  function linuxInstall(targets, node, cli2, hours) {
1813
1879
  if (!have("systemctl")) {
@@ -1818,11 +1884,11 @@ function linuxInstall(targets, node, cli2, hours) {
1818
1884
  const dir = systemdUnitDir();
1819
1885
  mkdirSync3(dir, { recursive: true });
1820
1886
  if (targets.includes("collector")) {
1821
- writeFileSync2(join3(dir, SYSTEMD_COLLECTOR_SERVICE), systemdCollectorService(node, cli2));
1822
- writeFileSync2(join3(dir, SYSTEMD_COLLECTOR_TIMER), systemdCollectorTimer(hours));
1887
+ writeFileSync2(join4(dir, SYSTEMD_COLLECTOR_SERVICE), systemdCollectorService(node, cli2));
1888
+ writeFileSync2(join4(dir, SYSTEMD_COLLECTOR_TIMER), systemdCollectorTimer(hours));
1823
1889
  }
1824
1890
  if (targets.includes("server")) {
1825
- writeFileSync2(join3(dir, SYSTEMD_SERVER_SERVICE), systemdServerService(node, cli2, serverEnv()));
1891
+ writeFileSync2(join4(dir, SYSTEMD_SERVER_SERVICE), systemdServerService(node, cli2, serverEnv()));
1826
1892
  }
1827
1893
  sh("systemctl", ["--user", "daemon-reload"]);
1828
1894
  if (targets.includes("collector")) {
@@ -1835,8 +1901,9 @@ function linuxInstall(targets, node, cli2, hours) {
1835
1901
  console.log("agent-lens service: installed server daemon");
1836
1902
  sh("systemctl", ["--user", "status", SYSTEMD_SERVER_SERVICE, "--no-pager"]);
1837
1903
  }
1838
- if (have("loginctl") && !sh("loginctl", ["enable-linger", process.env.USER || ""], true)) {
1839
- console.log(`agent-lens: could not enable linger automatically; run: sudo loginctl enable-linger ${process.env.USER}`);
1904
+ const user = currentUser();
1905
+ if (user && have("loginctl") && !sh("loginctl", ["enable-linger", user], true)) {
1906
+ console.log(`agent-lens: could not enable linger automatically; run: sudo loginctl enable-linger ${user}`);
1840
1907
  }
1841
1908
  }
1842
1909
  function linuxUninstall(targets) {
@@ -1846,12 +1913,12 @@ function linuxUninstall(targets) {
1846
1913
  if (targets.includes("collector")) {
1847
1914
  sh("systemctl", ["--user", "disable", "--now", SYSTEMD_COLLECTOR_TIMER], true);
1848
1915
  for (const u of [SYSTEMD_COLLECTOR_TIMER, SYSTEMD_COLLECTOR_SERVICE])
1849
- rmSync(join3(dir, u), { force: true });
1916
+ rmSync(join4(dir, u), { force: true });
1850
1917
  sh("systemctl", ["--user", "reset-failed", SYSTEMD_COLLECTOR_SERVICE], true);
1851
1918
  }
1852
1919
  if (targets.includes("server")) {
1853
1920
  sh("systemctl", ["--user", "disable", "--now", SYSTEMD_SERVER_SERVICE], true);
1854
- rmSync(join3(dir, SYSTEMD_SERVER_SERVICE), { force: true });
1921
+ rmSync(join4(dir, SYSTEMD_SERVER_SERVICE), { force: true });
1855
1922
  sh("systemctl", ["--user", "reset-failed", SYSTEMD_SERVER_SERVICE], true);
1856
1923
  }
1857
1924
  sh("systemctl", ["--user", "daemon-reload"]);
@@ -1866,7 +1933,7 @@ function linuxStatus(targets) {
1866
1933
  sh("systemctl", ["--user", "status", SYSTEMD_SERVER_SERVICE, "--no-pager"]);
1867
1934
  }
1868
1935
  function launchdPlistPath(label) {
1869
- return join3(homedir2(), "Library", "LaunchAgents", `${label}.plist`);
1936
+ return join4(homedir2(), "Library", "LaunchAgents", `${label}.plist`);
1870
1937
  }
1871
1938
  function macBootstrap(plist) {
1872
1939
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
@@ -1880,18 +1947,18 @@ function macBootout(label, plist) {
1880
1947
  rmSync(plist, { force: true });
1881
1948
  }
1882
1949
  function macInstall(targets, node, cli2, hours) {
1883
- const agents = join3(homedir2(), "Library", "LaunchAgents");
1950
+ const agents = join4(homedir2(), "Library", "LaunchAgents");
1884
1951
  mkdirSync3(agents, { recursive: true });
1885
1952
  const dataDir = resolveDataDir(findRepoRoot());
1886
1953
  if (targets.includes("collector")) {
1887
1954
  const plist = launchdPlistPath(LAUNCHD_COLLECTOR_LABEL);
1888
- writeFileSync2(plist, launchdCollectorPlist(node, cli2, hours, join3(dataDir, "schedule.log")));
1955
+ writeFileSync2(plist, launchdCollectorPlist(node, cli2, hours, join4(dataDir, "schedule.log")));
1889
1956
  macBootstrap(plist);
1890
1957
  console.log(`agent-lens service: installed collector agent ${LAUNCHD_COLLECTOR_LABEL} (hours ${hours.join(",")})`);
1891
1958
  }
1892
1959
  if (targets.includes("server")) {
1893
1960
  const plist = launchdPlistPath(LAUNCHD_SERVER_LABEL);
1894
- writeFileSync2(plist, launchdServerPlist(node, cli2, join3(dataDir, "server.log"), serverEnv()));
1961
+ writeFileSync2(plist, launchdServerPlist(node, cli2, join4(dataDir, "server.log"), serverEnv()));
1895
1962
  macBootstrap(plist);
1896
1963
  console.log(`agent-lens service: installed server agent ${LAUNCHD_SERVER_LABEL}`);
1897
1964
  }
@@ -1982,7 +2049,7 @@ function runService(action, params) {
1982
2049
 
1983
2050
  // ../ingest/dist/run.js
1984
2051
  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";
2052
+ import { dirname as dirname4, join as join9 } from "path";
1986
2053
 
1987
2054
  // ../ingest/dist/db.js
1988
2055
  import Database from "better-sqlite3";
@@ -2038,7 +2105,7 @@ function resetSchema(db) {
2038
2105
 
2039
2106
  // ../ingest/dist/adapters/claude-code.js
2040
2107
  import { readdirSync as readdirSync2 } from "fs";
2041
- import { basename as basename2, join as join4 } from "path";
2108
+ import { basename as basename2, join as join5 } from "path";
2042
2109
  function flattenText(content) {
2043
2110
  if (content == null)
2044
2111
  return null;
@@ -2121,7 +2188,7 @@ function recurseJsonl(dir, out) {
2121
2188
  return;
2122
2189
  }
2123
2190
  for (const e of entries) {
2124
- const p = join4(dir, e.name);
2191
+ const p = join5(dir, e.name);
2125
2192
  if (e.isDirectory())
2126
2193
  recurseJsonl(p, out);
2127
2194
  else if (e.isFile() && e.name.endsWith(".jsonl"))
@@ -2152,12 +2219,12 @@ var ClaudeCodeAdapter = class {
2152
2219
  });
2153
2220
  }
2154
2221
  };
2155
- addFrom(join4(archiveRoot, "projects"), false);
2156
- const versionsRoot = join4(archiveRoot, ".versions");
2222
+ addFrom(join5(archiveRoot, "projects"), false);
2223
+ const versionsRoot = join5(archiveRoot, ".versions");
2157
2224
  try {
2158
2225
  for (const ts of readdirSync2(versionsRoot, { withFileTypes: true })) {
2159
2226
  if (ts.isDirectory())
2160
- addFrom(join4(versionsRoot, ts.name, "projects"), true);
2227
+ addFrom(join5(versionsRoot, ts.name, "projects"), true);
2161
2228
  }
2162
2229
  } catch {
2163
2230
  }
@@ -3584,7 +3651,7 @@ function ingestFile(db, stmts, adapter, file, lines, meta, now2, stats) {
3584
3651
 
3585
3652
  // ../ingest/dist/workflows.js
3586
3653
  import { readdirSync as readdirSync3, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
3587
- import { basename as basename3, join as join5 } from "path";
3654
+ import { basename as basename3, join as join6 } from "path";
3588
3655
 
3589
3656
  // ../ingest/dist/redact.js
3590
3657
  import { createHash as createHash4, randomBytes } from "crypto";
@@ -3652,7 +3719,7 @@ function walkSidecars(dir, inWorkflows, out) {
3652
3719
  return;
3653
3720
  }
3654
3721
  for (const e of entries) {
3655
- const p = join5(dir, e.name);
3722
+ const p = join6(dir, e.name);
3656
3723
  if (e.isDirectory())
3657
3724
  walkSidecars(p, inWorkflows || e.name === "workflows", out);
3658
3725
  else if (e.isFile() && inWorkflows && /^wf_.+\.json$/.test(e.name))
@@ -3661,11 +3728,11 @@ function walkSidecars(dir, inWorkflows, out) {
3661
3728
  }
3662
3729
  function discoverSidecars(sourceArchiveDir) {
3663
3730
  const out = [];
3664
- walkSidecars(join5(sourceArchiveDir, "projects"), false, out);
3731
+ walkSidecars(join6(sourceArchiveDir, "projects"), false, out);
3665
3732
  try {
3666
- for (const ts of readdirSync3(join5(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3733
+ for (const ts of readdirSync3(join6(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3667
3734
  if (ts.isDirectory())
3668
- walkSidecars(join5(sourceArchiveDir, ts.name, "projects"), false, out);
3735
+ walkSidecars(join6(sourceArchiveDir, ts.name, "projects"), false, out);
3669
3736
  } catch {
3670
3737
  }
3671
3738
  return out;
@@ -3783,7 +3850,7 @@ function ingestWorkflowResults(db, sourceArchiveDir, sourceId, excludedDirs, now
3783
3850
 
3784
3851
  // ../ingest/dist/meta.js
3785
3852
  import { readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
3786
- import { basename as basename4, join as join6 } from "path";
3853
+ import { basename as basename4, join as join7 } from "path";
3787
3854
  function newMetaStats() {
3788
3855
  return { files: 0, upserted: 0, skipped: 0, malformed: 0 };
3789
3856
  }
@@ -3795,7 +3862,7 @@ function walkMeta(dir, inSubagents, out) {
3795
3862
  return;
3796
3863
  }
3797
3864
  for (const e of entries) {
3798
- const p = join6(dir, e.name);
3865
+ const p = join7(dir, e.name);
3799
3866
  if (e.isDirectory())
3800
3867
  walkMeta(p, inSubagents || e.name === "subagents", out);
3801
3868
  else if (e.isFile() && inSubagents && /^agent-.+\.meta\.json$/.test(e.name))
@@ -3804,11 +3871,11 @@ function walkMeta(dir, inSubagents, out) {
3804
3871
  }
3805
3872
  function discoverMeta(sourceArchiveDir) {
3806
3873
  const out = [];
3807
- walkMeta(join6(sourceArchiveDir, "projects"), false, out);
3874
+ walkMeta(join7(sourceArchiveDir, "projects"), false, out);
3808
3875
  try {
3809
- for (const ts of readdirSync4(join6(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3876
+ for (const ts of readdirSync4(join7(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3810
3877
  if (ts.isDirectory())
3811
- walkMeta(join6(sourceArchiveDir, ts.name, "projects"), false, out);
3878
+ walkMeta(join7(sourceArchiveDir, ts.name, "projects"), false, out);
3812
3879
  } catch {
3813
3880
  }
3814
3881
  return out;
@@ -3882,7 +3949,7 @@ function ingestSubagentMeta(db, sourceArchiveDir, sourceId, excludedDirs, now2,
3882
3949
 
3883
3950
  // ../ingest/dist/toolResults.js
3884
3951
  import { readdirSync as readdirSync5, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
3885
- import { basename as basename5, join as join7 } from "path";
3952
+ import { basename as basename5, join as join8 } from "path";
3886
3953
  function newToolResultStats() {
3887
3954
  return { files: 0, upserted: 0, skipped: 0, malformed: 0 };
3888
3955
  }
@@ -3894,7 +3961,7 @@ function walkResults(dir, inResults, out) {
3894
3961
  return;
3895
3962
  }
3896
3963
  for (const e of entries) {
3897
- const p = join7(dir, e.name);
3964
+ const p = join8(dir, e.name);
3898
3965
  if (e.isDirectory())
3899
3966
  walkResults(p, inResults || e.name === "tool-results", out);
3900
3967
  else if (e.isFile() && inResults && e.name.endsWith(".txt"))
@@ -3903,11 +3970,11 @@ function walkResults(dir, inResults, out) {
3903
3970
  }
3904
3971
  function discoverResults(sourceArchiveDir) {
3905
3972
  const out = [];
3906
- walkResults(join7(sourceArchiveDir, "projects"), false, out);
3973
+ walkResults(join8(sourceArchiveDir, "projects"), false, out);
3907
3974
  try {
3908
- for (const ts of readdirSync5(join7(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3975
+ for (const ts of readdirSync5(join8(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3909
3976
  if (ts.isDirectory())
3910
- walkResults(join7(sourceArchiveDir, ts.name, "projects"), false, out);
3977
+ walkResults(join8(sourceArchiveDir, ts.name, "projects"), false, out);
3911
3978
  } catch {
3912
3979
  }
3913
3980
  return out;
@@ -3984,8 +4051,8 @@ function parseIngestArgs(argv) {
3984
4051
  function runIngest(argv = process.argv.slice(2)) {
3985
4052
  const args = parseIngestArgs(argv);
3986
4053
  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");
4054
+ const archiveRoot = args.archive || process.env.AGENT_LENS_ARCHIVE || join9(dataDir, "archive");
4055
+ const { path: dbPath } = resolveDbPath(args.db);
3989
4056
  if (!existsSync4(archiveRoot)) {
3990
4057
  console.error(`agent-lens-ingest: archive not found: ${archiveRoot} (run 'agent-lens collect' first)`);
3991
4058
  process.exit(1);
@@ -4023,7 +4090,7 @@ function runIngest(argv = process.argv.slice(2)) {
4023
4090
  }
4024
4091
  stmts.insAgent.run(adapter.agentId, adapter.agentName);
4025
4092
  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);
4093
+ let files = adapter.discover(join9(archiveRoot, source.label), source.label);
4027
4094
  if (excludedDirs.length)
4028
4095
  files = files.filter((f) => !isExcludedArchivePath(f.path, excludedDirs));
4029
4096
  files.sort((a, b) => Number(a.isVersion) - Number(b.isVersion));
@@ -4055,9 +4122,9 @@ function runIngest(argv = process.argv.slice(2)) {
4055
4122
  ingestFile(db, stmts, adapter, file, lines, { size: st.size, mtimeMs, hash: hash2 }, now2, stats);
4056
4123
  dirty.add(file.sessionId);
4057
4124
  }
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);
4125
+ ingestWorkflowResults(db, join9(archiveRoot, source.label), source.label, excludedDirs, now2, wfStats, args.full);
4126
+ ingestSubagentMeta(db, join9(archiveRoot, source.label), source.label, excludedDirs, now2, metaStats, args.full);
4127
+ ingestToolResults(db, join9(archiveRoot, source.label), excludedDirs, now2, trStats, args.full);
4061
4128
  }
4062
4129
  const expanded = rebuildDerived(db, args.full ? null : dirty);
4063
4130
  const classified = classify(db, args.full ? null : expanded);
@@ -4095,8 +4162,7 @@ function runMetrics(argv = process.argv.slice(2)) {
4095
4162
  for (let i = 0; i < argv.length; i++)
4096
4163
  if (argv[i] === "--db")
4097
4164
  dbArg = argv[++i] ?? "";
4098
- const dataDir = resolveDataDir(findRepoRoot());
4099
- const dbPath = dbArg || process.env.AGENT_LENS_DB || join8(dataDir, "agent-lens.db");
4165
+ const { path: dbPath } = resolveDbPath(dbArg);
4100
4166
  if (!existsSync4(dbPath)) {
4101
4167
  console.error(`agent-lens-metrics: db not found: ${dbPath} (run ingest first)`);
4102
4168
  process.exit(1);
@@ -4111,7 +4177,7 @@ function runMetrics(argv = process.argv.slice(2)) {
4111
4177
 
4112
4178
  // ../server/dist/run.js
4113
4179
  import { existsSync as existsSync7 } from "fs";
4114
- import { dirname as dirname5, join as join10 } from "path";
4180
+ import { dirname as dirname5, join as join11 } from "path";
4115
4181
  import { fileURLToPath as fileURLToPath2 } from "url";
4116
4182
 
4117
4183
  // ../server/dist/db.js
@@ -4967,7 +5033,7 @@ function dashboardBreakdowns(db, f) {
4967
5033
 
4968
5034
  // ../server/dist/refresh.js
4969
5035
  import { existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
4970
- import { join as join9 } from "path";
5036
+ import { join as join10 } from "path";
4971
5037
  function originAllowed(origin) {
4972
5038
  if (!origin)
4973
5039
  return true;
@@ -4988,12 +5054,12 @@ function writeBlocked(headers) {
4988
5054
  }
4989
5055
  function runRefresh() {
4990
5056
  const dataDir = resolveDataDir(findRepoRoot());
4991
- const lock = acquireLock(join9(dataDir, ".agent-lens.lock"));
5057
+ const lock = acquireLock(join10(dataDir, ".agent-lens.lock"));
4992
5058
  if (!lock)
4993
5059
  return null;
4994
5060
  try {
4995
5061
  const collected = collectAll();
4996
- const archiveRoot = process.env.AGENT_LENS_ARCHIVE || join9(dataDir, "archive");
5062
+ const archiveRoot = process.env.AGENT_LENS_ARCHIVE || join10(dataDir, "archive");
4997
5063
  if (!existsSync5(archiveRoot))
4998
5064
  mkdirSync5(archiveRoot, { recursive: true });
4999
5065
  runIngest([]);
@@ -5309,12 +5375,11 @@ async function createApp(db, opts = {}) {
5309
5375
  }
5310
5376
 
5311
5377
  // ../server/dist/run.js
5312
- async function startServer() {
5378
+ async function startServer(overrides = {}) {
5313
5379
  const here = dirname5(fileURLToPath2(import.meta.url));
5314
5380
  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);
5381
+ const { path: dbPath } = resolveDbPath(overrides.db);
5382
+ const { host, port } = resolveServerConfig({ port: overrides.port, host: overrides.host });
5318
5383
  const webDist = resolveWebDist(here, repoRoot);
5319
5384
  if (host !== "127.0.0.1" && host !== "localhost" && !process.env.AGENT_LENS_ALLOW_NONLOCAL) {
5320
5385
  console.error(`agent-lens-server: refusing to bind non-loopback host '${host}' (privacy). Set AGENT_LENS_ALLOW_NONLOCAL=1 to override.`);
@@ -5324,7 +5389,7 @@ async function startServer() {
5324
5389
  console.error(`agent-lens-server: db not found: ${dbPath} (run ingest first)`);
5325
5390
  process.exit(1);
5326
5391
  }
5327
- const triageDbPath = process.env.AGENT_LENS_TRIAGE_DB || join10(dirname5(dbPath), "triage.db");
5392
+ const triageDbPath = process.env.AGENT_LENS_TRIAGE_DB || join11(dirname5(dbPath), "triage.db");
5328
5393
  const db = openReadonly(dbPath);
5329
5394
  const enforceLoopbackHost = host === "127.0.0.1" || host === "localhost";
5330
5395
  const app = await createApp(db, { webDist, triageDbPath, enforceLoopbackHost });
@@ -5341,11 +5406,11 @@ async function startServer() {
5341
5406
 
5342
5407
  // src/watch.ts
5343
5408
  import { existsSync as existsSync8 } from "fs";
5344
- import { join as join11 } from "path";
5409
+ import { join as join12 } from "path";
5345
5410
  import chokidar from "chokidar";
5346
5411
  async function runWatch(opts = {}) {
5347
5412
  const sources = loadSources();
5348
- const lockPath = join11(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5413
+ const lockPath = join12(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5349
5414
  let running = false;
5350
5415
  let pending = false;
5351
5416
  const cycle = () => {
@@ -5378,7 +5443,7 @@ async function runWatch(opts = {}) {
5378
5443
  }
5379
5444
  };
5380
5445
  cycle();
5381
- const watchPaths = sources.map((s) => join11(s.configDir, "projects")).filter((p) => existsSync8(p));
5446
+ const watchPaths = sources.map((s) => join12(s.configDir, "projects")).filter((p) => existsSync8(p));
5382
5447
  if (watchPaths.length === 0) {
5383
5448
  console.error("agent-lens watch: no source 'projects' directories found \u2014 nothing to watch");
5384
5449
  }
@@ -5406,14 +5471,14 @@ async function runWatch(opts = {}) {
5406
5471
  // src/index.ts
5407
5472
  function version() {
5408
5473
  try {
5409
- const pkg = JSON.parse(readFileSync8(join12(dirname6(fileURLToPath3(import.meta.url)), "../package.json"), "utf8"));
5474
+ const pkg = JSON.parse(readFileSync8(join13(dirname6(fileURLToPath3(import.meta.url)), "../package.json"), "utf8"));
5410
5475
  return pkg.version ?? "0.0.0";
5411
5476
  } catch {
5412
5477
  return "0.0.0";
5413
5478
  }
5414
5479
  }
5415
5480
  function withLock(fn) {
5416
- const lockPath = join12(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5481
+ const lockPath = join13(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5417
5482
  const lock = acquireLock(lockPath);
5418
5483
  if (!lock) {
5419
5484
  console.error("agent-lens: another collect/ingest run is in progress \u2014 skipping");
@@ -5435,24 +5500,29 @@ cli.command("collect", "Copy new session traces into the local archive").option(
5435
5500
  if (opts.thenIngest) runIngest([]);
5436
5501
  });
5437
5502
  });
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) => {
5503
+ 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
5504
  const argv = [];
5440
5505
  if (opts.full) argv.push("--full");
5441
5506
  if (opts.db) argv.push("--db", opts.db);
5442
5507
  if (opts.archive) argv.push("--archive", opts.archive);
5443
5508
  withLock(() => runIngest(argv));
5444
5509
  });
5445
- cli.command("serve", "Serve the web UI + read-only API on 127.0.0.1").action(async () => {
5446
- await startServer();
5510
+ 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) => {
5511
+ try {
5512
+ await startServer({ port: opts.port, host: opts.host, db: opts.db });
5513
+ } catch (e) {
5514
+ console.error(e instanceof Error ? e.message : String(e));
5515
+ process.exit(1);
5516
+ }
5447
5517
  });
5448
5518
  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
5519
  await runWatch({ intervalSec: opts.interval ? Number(opts.interval) : void 0, poll: !!opts.poll });
5450
5520
  });
5451
- cli.command("metrics", "Re-run classification over an already-ingested DB").option("--db <path>", "SQLite DB path").action((opts) => {
5521
+ 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
5522
  runMetrics(opts.db ? ["--db", opts.db] : []);
5453
5523
  });
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");
5524
+ 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) => {
5525
+ const { path: dbPath } = resolveDbPath(opts.db);
5456
5526
  if (!existsSync9(dbPath)) {
5457
5527
  console.error(`agent-lens: db not found: ${dbPath} (run ingest first)`);
5458
5528
  process.exit(1);
@@ -5478,6 +5548,39 @@ cli.command(
5478
5548
  const hours = action === "install" ? parseHours(opts.times) : void 0;
5479
5549
  runService(action, { cliEntry: fileURLToPath3(import.meta.url), hours, targets });
5480
5550
  });
5551
+ cli.command("config", "Show the effective configuration (sources, paths, server) and where each value came from").action(() => {
5552
+ const repoRoot = findRepoRoot();
5553
+ const dataDir = resolveDataDir(repoRoot);
5554
+ const configFile = resolveConfigFile(repoRoot, dataDir);
5555
+ let server;
5556
+ try {
5557
+ server = resolveServerConfig();
5558
+ } catch (e) {
5559
+ console.error(e instanceof Error ? e.message : String(e));
5560
+ process.exit(1);
5561
+ }
5562
+ const { port, host, portOrigin, hostOrigin } = server;
5563
+ const { path: dbPath, origin: dbOrigin } = resolveDbPath();
5564
+ const archive = process.env.AGENT_LENS_ARCHIVE || join13(dataDir, "archive");
5565
+ const triageDb = process.env.AGENT_LENS_TRIAGE_DB || join13(dirname6(dbPath), "triage.db");
5566
+ const keepDays = process.env.AGENT_LENS_VERSIONS_KEEP_DAYS || "90";
5567
+ const keepOrigin = process.env.AGENT_LENS_VERSIONS_KEEP_DAYS ? "env" : "default";
5568
+ console.log("agent-lens config (precedence: flag > env > config file > default)\n");
5569
+ console.log("Paths");
5570
+ console.log(` config file ${configFile ?? "(none \u2014 using built-in default source)"}`);
5571
+ console.log(` data dir ${dataDir}${process.env.AGENT_LENS_DATA ? " [env]" : ""}`);
5572
+ console.log(` archive ${archive}${process.env.AGENT_LENS_ARCHIVE ? " [env]" : ""}`);
5573
+ console.log(` db ${dbPath} [${dbOrigin}]`);
5574
+ console.log(` triage db ${triageDb}${process.env.AGENT_LENS_TRIAGE_DB ? " [env]" : ""}`);
5575
+ console.log("\nServer");
5576
+ console.log(` host ${host} [${hostOrigin}]`);
5577
+ console.log(` port ${port} [${portOrigin}]`);
5578
+ console.log(` non-local bind ${process.env.AGENT_LENS_ALLOW_NONLOCAL ? "allowed (AGENT_LENS_ALLOW_NONLOCAL)" : "blocked (loopback only)"}`);
5579
+ console.log("\nRetention");
5580
+ console.log(` .versions keep ${keepDays} day(s) [${keepOrigin}]`);
5581
+ console.log("\nSources");
5582
+ for (const s of loadSources()) console.log(` ${s.label.padEnd(14)} ${s.configDir} (${s.agent})`);
5583
+ });
5481
5584
  cli.help();
5482
5585
  cli.version(version());
5483
5586
  cli.parse();