fleetlens 0.12.1 → 0.12.3

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.
Files changed (98) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/build-manifest.json +3 -3
  3. package/app/apps/web/.next/prerender-manifest.json +3 -3
  4. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  5. package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
  6. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/app/apps/web/.next/server/app/_not-found/page.js +1 -1
  12. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  13. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/app/apps/web/.next/server/app/changelog/page.js +1 -1
  15. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  16. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  17. package/app/apps/web/.next/server/app/day/[date]/page.js +1 -1
  18. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  19. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  20. package/app/apps/web/.next/server/app/day/page.js +1 -1
  21. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  22. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  23. package/app/apps/web/.next/server/app/digest/[date]/page.js +1 -1
  24. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  25. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  26. package/app/apps/web/.next/server/app/digest/page.js +1 -1
  27. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  28. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  29. package/app/apps/web/.next/server/app/insights/[key]/page.js +1 -1
  30. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  31. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  32. package/app/apps/web/.next/server/app/insights/page.js +1 -1
  33. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  34. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  35. package/app/apps/web/.next/server/app/page.js +1 -1
  36. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  37. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  38. package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
  39. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  40. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  41. package/app/apps/web/.next/server/app/print/[key]/page.js +1 -1
  42. package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
  43. package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
  44. package/app/apps/web/.next/server/app/projects/[slug]/page.js +1 -1
  45. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  46. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  47. package/app/apps/web/.next/server/app/projects/page.js +1 -1
  48. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  49. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  50. package/app/apps/web/.next/server/app/runs/page.js +1 -1
  51. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  52. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  53. package/app/apps/web/.next/server/app/sessions/[id]/page.js +1 -1
  54. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  55. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  56. package/app/apps/web/.next/server/app/sessions/page.js +1 -1
  57. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  58. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  59. package/app/apps/web/.next/server/app/settings/page.js +1 -1
  60. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  61. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  62. package/app/apps/web/.next/server/app/team/page.js +1 -1
  63. package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
  64. package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
  65. package/app/apps/web/.next/server/app/usage/page.js +1 -1
  66. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  67. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  68. package/app/apps/web/.next/server/chunks/packages_0c-sz9n._.js +1 -1
  69. package/app/apps/web/.next/server/chunks/packages_0wywuu.._.js +1 -1
  70. package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
  71. package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
  72. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0exckd4._.js +1 -1
  73. package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
  74. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0txtsv5._.js +1 -1
  75. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
  76. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
  77. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +3 -0
  78. package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
  79. package/app/apps/web/.next/server/middleware-manifest.json +5 -5
  80. package/app/apps/web/.next/server/pages/500.html +1 -1
  81. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  82. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  83. package/app/apps/web/.next/static/chunks/0htgc_hr1bw67.js +1 -0
  84. package/app/apps/web/.next/static/chunks/{0~e7pnd7ot84w.js → 0lziv8y_6yp5w.js} +1 -1
  85. package/app/apps/web/app/layout.tsx +14 -6
  86. package/app/apps/web/app/projects/[slug]/page.tsx +3 -4
  87. package/app/apps/web/components/live-sessions-widget.tsx +1 -1
  88. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  89. package/app/apps/web/package.json +1 -1
  90. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  91. package/dist/daemon-worker.js +29 -17
  92. package/dist/index.js +150 -82
  93. package/package.json +1 -1
  94. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_12.endt._.js +0 -3
  95. package/app/apps/web/.next/static/chunks/03_7wmpodz36t.js +0 -1
  96. /package/app/apps/web/.next/static/{rrY-ephAArv8tjJOXD622 → 7kQ9mNICnVHhf4-LmSGcy}/_buildManifest.js +0 -0
  97. /package/app/apps/web/.next/static/{rrY-ephAArv8tjJOXD622 → 7kQ9mNICnVHhf4-LmSGcy}/_clientMiddlewareManifest.js +0 -0
  98. /package/app/apps/web/.next/static/{rrY-ephAArv8tjJOXD622 → 7kQ9mNICnVHhf4-LmSGcy}/_ssgManifest.js +0 -0
@@ -1824,6 +1824,14 @@ function foldWorkflowAgentTime(activeSegments, spans) {
1824
1824
  const airTimeMs = merged.reduce((n, s) => n + (s.endMs - s.startMs), 0);
1825
1825
  return { activeSegments: merged, airTimeMs };
1826
1826
  }
1827
+ async function applySubagentUsage(meta, rawLines, f) {
1828
+ const sessionId = sessionIdFromFileName(f.fileName);
1829
+ const projectDirPath = path.dirname(f.fullPath);
1830
+ const fallbackDay = meta.firstTimestamp ? toLocalDay(Date.parse(meta.firstTimestamp)) : void 0;
1831
+ const { usage, perDay: perDayTokens } = await computeSessionUsageWithSubagents(rawLines, projectDirPath, sessionId, fallbackDay);
1832
+ meta.totalUsage = usage;
1833
+ meta.dailyBreakdown = mergeDailyTokens(meta.dailyBreakdown, perDayTokens, usage);
1834
+ }
1827
1835
  async function getCachedMeta(f) {
1828
1836
  const cached = metaCache.get(f.fullPath);
1829
1837
  if (cached && cached.mtimeMs === f.mtimeMs && cached.sizeBytes === f.sizeBytes) {
@@ -1832,12 +1840,9 @@ async function getCachedMeta(f) {
1832
1840
  try {
1833
1841
  const rawLines = await readJsonlFile(f.fullPath);
1834
1842
  const { meta } = parseTranscript(rawLines);
1843
+ await applySubagentUsage(meta, rawLines, f);
1835
1844
  const sessionId = sessionIdFromFileName(f.fileName);
1836
1845
  const projectDirPath = path.dirname(f.fullPath);
1837
- const fallbackDay = meta.firstTimestamp ? toLocalDay(Date.parse(meta.firstTimestamp)) : void 0;
1838
- const { usage, perDay: perDayTokens } = await computeSessionUsageWithSubagents(rawLines, projectDirPath, sessionId, fallbackDay);
1839
- meta.totalUsage = usage;
1840
- meta.dailyBreakdown = mergeDailyTokens(meta.dailyBreakdown, perDayTokens, usage);
1841
1846
  const wf = await scanWorkflowAggregate(projectDirPath, sessionId);
1842
1847
  const full = {
1843
1848
  ...meta,
@@ -1863,6 +1868,7 @@ async function getCachedDetail(f) {
1863
1868
  const rawLines = await readJsonlFile(f.fullPath);
1864
1869
  const { meta, events } = parseTranscript(rawLines);
1865
1870
  const sessionId = sessionIdFromFileName(f.fileName);
1871
+ await applySubagentUsage(meta, rawLines, f);
1866
1872
  const wf = await scanWorkflowAggregate(path.dirname(f.fullPath), sessionId);
1867
1873
  const detail = {
1868
1874
  ...meta,
@@ -5361,6 +5367,20 @@ init_fs();
5361
5367
  import { readFileSync as readFileSync4, readdirSync, writeFileSync as writeFileSync2, renameSync, chmodSync, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "node:fs";
5362
5368
  import { dirname as dirname2, join as join3 } from "node:path";
5363
5369
  var digestsDirCached = null;
5370
+ function parseDigestFile(path7, expectedVersion) {
5371
+ try {
5372
+ const parsed = JSON.parse(readFileSync4(path7, "utf8"));
5373
+ if (!parsed || typeof parsed !== "object")
5374
+ return null;
5375
+ if (parsed.version !== expectedVersion)
5376
+ return null;
5377
+ if (!Array.isArray(parsed.shipped))
5378
+ return null;
5379
+ return parsed;
5380
+ } catch {
5381
+ return null;
5382
+ }
5383
+ }
5364
5384
  function digestsDir() {
5365
5385
  if (digestsDirCached)
5366
5386
  return digestsDirCached;
@@ -5391,11 +5411,7 @@ function readDayDigest(date) {
5391
5411
  const p = dayDigestPath(date);
5392
5412
  if (!existsSync2(p))
5393
5413
  return null;
5394
- try {
5395
- return JSON.parse(readFileSync4(p, "utf8"));
5396
- } catch {
5397
- return null;
5398
- }
5414
+ return parseDigestFile(p, CURRENT_DAY_DIGEST_SCHEMA_VERSION);
5399
5415
  }
5400
5416
  var TODAY_TTL_MS = 10 * 60 * 1e3;
5401
5417
  var todayCache = null;
@@ -5428,11 +5444,7 @@ function readWeekDigest(monday) {
5428
5444
  const p = weekDigestPath(monday);
5429
5445
  if (!existsSync2(p))
5430
5446
  return null;
5431
- try {
5432
- return JSON.parse(readFileSync4(p, "utf8"));
5433
- } catch {
5434
- return null;
5435
- }
5447
+ return parseDigestFile(p, CURRENT_WEEK_DIGEST_SCHEMA_VERSION);
5436
5448
  }
5437
5449
  var CURRENT_WEEK_TTL_MS = 10 * 60 * 1e3;
5438
5450
  var currentWeekCache = null;
@@ -15146,14 +15158,14 @@ async function getUpdateAvailability() {
15146
15158
  if (isDevMode()) return null;
15147
15159
  const latest = await fetchLatestVersion();
15148
15160
  if (latest === null) return {
15149
- current: "0.12.1",
15161
+ current: "0.12.3",
15150
15162
  latest: null,
15151
15163
  updateAvailable: false
15152
15164
  };
15153
15165
  return {
15154
- current: "0.12.1",
15166
+ current: "0.12.3",
15155
15167
  latest,
15156
- updateAvailable: shouldUpdate("0.12.1", latest)
15168
+ updateAvailable: shouldUpdate("0.12.3", latest)
15157
15169
  };
15158
15170
  }
15159
15171
 
package/dist/index.js CHANGED
@@ -12,19 +12,27 @@ var __export = (target, all) => {
12
12
  // src/pid.ts
13
13
  import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
14
14
  import { dirname } from "node:path";
15
- function writePid(filePath, pid, port) {
15
+ function writePid(filePath, pid, port, version) {
16
16
  mkdirSync(dirname(filePath), { recursive: true });
17
- const content = port != null ? `${pid}:${port}` : String(pid);
17
+ let content = String(pid);
18
+ if (port != null) {
19
+ content += `:${port}`;
20
+ if (version) content += `:${version}`;
21
+ }
18
22
  writeFileSync(filePath, content, "utf8");
19
23
  }
20
24
  function readPid(filePath) {
21
25
  try {
22
26
  const content = readFileSync(filePath, "utf8").trim();
23
- const [pidStr, portStr] = content.split(":");
27
+ const [pidStr, portStr, versionStr] = content.split(":");
24
28
  const pid = parseInt(pidStr, 10);
25
29
  if (Number.isNaN(pid)) return null;
26
30
  const port = portStr ? parseInt(portStr, 10) : void 0;
27
- return { pid, port: Number.isNaN(port) ? void 0 : port };
31
+ return {
32
+ pid,
33
+ port: Number.isNaN(port) ? void 0 : port,
34
+ version: versionStr || void 0
35
+ };
28
36
  } catch {
29
37
  return null;
30
38
  }
@@ -1841,6 +1849,14 @@ function foldWorkflowAgentTime(activeSegments, spans) {
1841
1849
  const airTimeMs = merged.reduce((n, s) => n + (s.endMs - s.startMs), 0);
1842
1850
  return { activeSegments: merged, airTimeMs };
1843
1851
  }
1852
+ async function applySubagentUsage(meta, rawLines, f) {
1853
+ const sessionId = sessionIdFromFileName(f.fileName);
1854
+ const projectDirPath = path.dirname(f.fullPath);
1855
+ const fallbackDay = meta.firstTimestamp ? toLocalDay(Date.parse(meta.firstTimestamp)) : void 0;
1856
+ const { usage: usage2, perDay: perDayTokens } = await computeSessionUsageWithSubagents(rawLines, projectDirPath, sessionId, fallbackDay);
1857
+ meta.totalUsage = usage2;
1858
+ meta.dailyBreakdown = mergeDailyTokens(meta.dailyBreakdown, perDayTokens, usage2);
1859
+ }
1844
1860
  async function getCachedMeta(f) {
1845
1861
  const cached = metaCache.get(f.fullPath);
1846
1862
  if (cached && cached.mtimeMs === f.mtimeMs && cached.sizeBytes === f.sizeBytes) {
@@ -1849,12 +1865,9 @@ async function getCachedMeta(f) {
1849
1865
  try {
1850
1866
  const rawLines = await readJsonlFile(f.fullPath);
1851
1867
  const { meta } = parseTranscript(rawLines);
1868
+ await applySubagentUsage(meta, rawLines, f);
1852
1869
  const sessionId = sessionIdFromFileName(f.fileName);
1853
1870
  const projectDirPath = path.dirname(f.fullPath);
1854
- const fallbackDay = meta.firstTimestamp ? toLocalDay(Date.parse(meta.firstTimestamp)) : void 0;
1855
- const { usage: usage2, perDay: perDayTokens } = await computeSessionUsageWithSubagents(rawLines, projectDirPath, sessionId, fallbackDay);
1856
- meta.totalUsage = usage2;
1857
- meta.dailyBreakdown = mergeDailyTokens(meta.dailyBreakdown, perDayTokens, usage2);
1858
1871
  const wf = await scanWorkflowAggregate(projectDirPath, sessionId);
1859
1872
  const full = {
1860
1873
  ...meta,
@@ -1880,6 +1893,7 @@ async function getCachedDetail(f) {
1880
1893
  const rawLines = await readJsonlFile(f.fullPath);
1881
1894
  const { meta, events } = parseTranscript(rawLines);
1882
1895
  const sessionId = sessionIdFromFileName(f.fileName);
1896
+ await applySubagentUsage(meta, rawLines, f);
1883
1897
  const wf = await scanWorkflowAggregate(path.dirname(f.fullPath), sessionId);
1884
1898
  const detail = {
1885
1899
  ...meta,
@@ -4468,7 +4482,9 @@ var init_fs = __esm({
4468
4482
  // src/server.ts
4469
4483
  var server_exports = {};
4470
4484
  __export(server_exports, {
4485
+ ensureCurrentServer: () => ensureCurrentServer,
4471
4486
  getServerStatus: () => getServerStatus,
4487
+ isServerStale: () => isServerStale,
4472
4488
  openBrowser: () => openBrowser,
4473
4489
  startServer: () => startServer,
4474
4490
  stopServer: () => stopServer
@@ -4486,10 +4502,42 @@ function getServerStatus() {
4486
4502
  cleanStalePid(PID_FILE);
4487
4503
  const entry = readPid(PID_FILE);
4488
4504
  if (entry !== null && isProcessAlive(entry.pid)) {
4489
- return { running: true, pid: entry.pid, port: entry.port ?? DEFAULT_PORT };
4505
+ return { running: true, pid: entry.pid, port: entry.port ?? DEFAULT_PORT, version: entry.version };
4490
4506
  }
4491
4507
  return { running: false };
4492
4508
  }
4509
+ function isServerStale(status2) {
4510
+ return status2.running === true && status2.version !== "0.12.3";
4511
+ }
4512
+ async function ensureCurrentServer(opts = {}) {
4513
+ const status2 = getServerStatus();
4514
+ if (status2.running && !isServerStale(status2)) {
4515
+ return { pid: status2.pid, port: status2.port, restarted: false };
4516
+ }
4517
+ if (!status2.running) {
4518
+ const result = await startServer({ port: opts.port });
4519
+ return { ...result, restarted: false };
4520
+ }
4521
+ const previousVersion = status2.version;
4522
+ const oldPid = status2.pid;
4523
+ const port = opts.port ?? status2.port;
4524
+ stopServer();
4525
+ await waitForPortFree(port, 5e3);
4526
+ if (await checkPort(port)) {
4527
+ try {
4528
+ process.kill(oldPid, "SIGKILL");
4529
+ } catch {
4530
+ }
4531
+ await waitForPortFree(port, 3e3);
4532
+ }
4533
+ try {
4534
+ const result = await startServer({ port });
4535
+ return { ...result, restarted: true, previousVersion };
4536
+ } catch (err) {
4537
+ if (isProcessAlive(oldPid)) writePid(PID_FILE, oldPid, port, previousVersion);
4538
+ throw err;
4539
+ }
4540
+ }
4493
4541
  async function startServer(opts = {}) {
4494
4542
  const port = opts.port ?? (parseInt(process.env.CCLENS_PORT ?? "", 10) || DEFAULT_PORT);
4495
4543
  const serverJs = join2(appDir(), "server.js");
@@ -4515,7 +4563,7 @@ async function startServer(opts = {}) {
4515
4563
  });
4516
4564
  child.unref();
4517
4565
  const pid = child.pid;
4518
- writePid(PID_FILE, pid, port);
4566
+ writePid(PID_FILE, pid, port, "0.12.3");
4519
4567
  await waitForHealth(`http://localhost:${port}`, 1e4);
4520
4568
  return { pid, port };
4521
4569
  }
@@ -4543,6 +4591,13 @@ async function checkPort(port) {
4543
4591
  server.listen(port, "localhost");
4544
4592
  });
4545
4593
  }
4594
+ async function waitForPortFree(port, timeoutMs) {
4595
+ const start2 = Date.now();
4596
+ while (Date.now() - start2 < timeoutMs) {
4597
+ if (!await checkPort(port)) return;
4598
+ await new Promise((r) => setTimeout(r, 200));
4599
+ }
4600
+ }
4546
4601
  async function waitForHealth(url, timeoutMs) {
4547
4602
  const start2 = Date.now();
4548
4603
  while (Date.now() - start2 < timeoutMs) {
@@ -4868,9 +4923,9 @@ Installed: ${PACKAGE_NAME}@${verify.installedVersion} at ${verify.installedPath
4868
4923
  console.warn(
4869
4924
  ` \u2022 reinstall in the correct Node env: 'npm install -g ${PACKAGE_NAME}@latest'`
4870
4925
  );
4871
- } else if (verify.installedVersion !== "0.12.1") {
4926
+ } else if (verify.installedVersion !== "0.12.3") {
4872
4927
  console.log(
4873
- ` \u2192 This process is still running ${"0.12.1"}. Next invocation will use ${verify.installedVersion}.`
4928
+ ` \u2192 This process is still running ${"0.12.3"}. Next invocation will use ${verify.installedVersion}.`
4874
4929
  );
4875
4930
  }
4876
4931
  }
@@ -4905,14 +4960,14 @@ async function getUpdateAvailability() {
4905
4960
  if (isDevMode()) return null;
4906
4961
  const latest = await fetchLatestVersion();
4907
4962
  if (latest === null) return {
4908
- current: "0.12.1",
4963
+ current: "0.12.3",
4909
4964
  latest: null,
4910
4965
  updateAvailable: false
4911
4966
  };
4912
4967
  return {
4913
- current: "0.12.1",
4968
+ current: "0.12.3",
4914
4969
  latest,
4915
- updateAvailable: shouldUpdate("0.12.1", latest)
4970
+ updateAvailable: shouldUpdate("0.12.3", latest)
4916
4971
  };
4917
4972
  }
4918
4973
  async function checkForUpdate() {
@@ -5002,7 +5057,7 @@ async function restartServices(state) {
5002
5057
  }
5003
5058
  async function forceUpdate() {
5004
5059
  const latest = await fetchLatestVersion();
5005
- const current = "0.12.1";
5060
+ const current = "0.12.3";
5006
5061
  if (latest === null) {
5007
5062
  console.error("Could not reach npm registry. Check your network.");
5008
5063
  process.exit(1);
@@ -5053,24 +5108,23 @@ async function start(args2) {
5053
5108
  await checkForUpdate();
5054
5109
  } catch {
5055
5110
  }
5056
- const status2 = getServerStatus();
5057
5111
  let serverUrl;
5058
5112
  let serverPid;
5059
- if (status2.running) {
5060
- serverUrl = `http://localhost:${status2.port}`;
5061
- serverPid = status2.pid;
5062
- console.log(`Server: already running on ${serverUrl} (PID ${serverPid})`);
5063
- } else {
5113
+ try {
5064
5114
  console.log("Starting Fleetlens...");
5065
- try {
5066
- const result = await startServer({ port });
5067
- serverUrl = `http://localhost:${result.port}`;
5068
- serverPid = result.pid;
5069
- console.log(`Server: started on ${serverUrl} (PID ${serverPid})`);
5070
- } catch (err) {
5071
- console.error(`Failed to start server: ${err.message}`);
5072
- process.exit(1);
5115
+ const result = await ensureCurrentServer({ port });
5116
+ serverUrl = `http://localhost:${result.port}`;
5117
+ serverPid = result.pid;
5118
+ if (result.restarted) {
5119
+ console.log(
5120
+ `Server: restarted on ${serverUrl} (PID ${serverPid}) \u2014 replaced stale ${result.previousVersion ?? "unknown"} server`
5121
+ );
5122
+ } else {
5123
+ console.log(`Server: running on ${serverUrl} (PID ${serverPid})`);
5073
5124
  }
5125
+ } catch (err) {
5126
+ console.error(`Failed to start server: ${err.message}`);
5127
+ process.exit(1);
5074
5128
  }
5075
5129
  if (!noDaemon) {
5076
5130
  const daemon2 = startDaemonSilent();
@@ -5143,7 +5197,13 @@ async function status() {
5143
5197
  const server = getServerStatus();
5144
5198
  if (server.running) {
5145
5199
  const url = `http://localhost:${server.port}`;
5146
- console.log(`Server: running on ${url} (PID ${server.pid})`);
5200
+ const ver = server.version ?? "unknown";
5201
+ console.log(`Server: running on ${url} (PID ${server.pid}, v${ver})`);
5202
+ if (isServerStale(server)) {
5203
+ console.log(
5204
+ ` \u26A0 serving stale ${ver} (CLI is ${"0.12.3"}) \u2014 run 'fleetlens start' to restart on the current version`
5205
+ );
5206
+ }
5147
5207
  } else {
5148
5208
  console.log("Server: not running");
5149
5209
  }
@@ -5181,31 +5241,23 @@ async function web(args2) {
5181
5241
  const positional = args2.filter((a) => !a.startsWith("--"));
5182
5242
  const rawPath = positional[0] ?? "";
5183
5243
  const path7 = rawPath.startsWith("/") ? rawPath : rawPath ? `/${rawPath}` : "";
5184
- const status2 = getServerStatus();
5185
- if (status2.running) {
5186
- const url = `http://localhost:${status2.port}${path7}`;
5187
- if (open) {
5188
- console.log(`Opening ${url}`);
5189
- openBrowser(url);
5190
- } else {
5191
- console.log(`Dashboard ready at ${url} (re-run with --open to launch it automatically).`);
5192
- }
5193
- return;
5194
- }
5195
- console.log("fleetlens is not running. Starting server...");
5244
+ let result;
5196
5245
  try {
5197
- const result = await startServer({});
5198
- const url = `http://localhost:${result.port}${path7}`;
5199
- console.log(`fleetlens running on ${url} (PID ${result.pid})`);
5200
- if (open) {
5201
- openBrowser(url);
5202
- } else {
5203
- console.log(`Open ${url} in your browser, or re-run with --open to launch it automatically.`);
5204
- }
5246
+ result = await ensureCurrentServer({});
5205
5247
  } catch (err) {
5206
5248
  console.error(`Failed to start: ${err.message}`);
5207
5249
  process.exit(1);
5208
5250
  }
5251
+ const url = `http://localhost:${result.port}${path7}`;
5252
+ if (result.restarted) {
5253
+ console.log(`fleetlens restarted on http://localhost:${result.port} (PID ${result.pid}) \u2014 replaced stale ${result.previousVersion ?? "unknown"} server`);
5254
+ }
5255
+ if (open) {
5256
+ console.log(`Opening ${url}`);
5257
+ openBrowser(url);
5258
+ } else {
5259
+ console.log(`Dashboard ready at ${url} (re-run with --open to launch it automatically).`);
5260
+ }
5209
5261
  }
5210
5262
  var init_web = __esm({
5211
5263
  "src/commands/web.ts"() {
@@ -5498,9 +5550,9 @@ var init_pricing = __esm({
5498
5550
  function fmtNum(n) {
5499
5551
  return n.toLocaleString("en-US");
5500
5552
  }
5501
- function fmtCost(cost) {
5553
+ function fmtCost(cost, partial = false) {
5502
5554
  if (cost === null) return "\u2014";
5503
- return `$${cost.toFixed(2)}`;
5555
+ return `${partial ? "\u2265" : ""}$${cost.toFixed(2)}`;
5504
5556
  }
5505
5557
  function pad(s, width, align = "right") {
5506
5558
  if (align === "left") return s.padEnd(width);
@@ -5523,9 +5575,9 @@ function renderTable(rows, title) {
5523
5575
  ].join(" ");
5524
5576
  lines.push(`${DIM2}${header}${RESET2}`);
5525
5577
  lines.push(`${DIM2}${"\u2500".repeat(header.length)}${RESET2}`);
5526
- let hasUnknownCost = false;
5578
+ let costIncomplete = false;
5527
5579
  for (const r of rows) {
5528
- if (r.cost === null) hasUnknownCost = true;
5580
+ if (r.cost === null || r.costPartial) costIncomplete = true;
5529
5581
  lines.push([
5530
5582
  pad(r.date, 12, "left"),
5531
5583
  pad(r.models, 20, "left"),
@@ -5534,7 +5586,7 @@ function renderTable(rows, title) {
5534
5586
  pad(fmtNum(r.cacheCreate), 14),
5535
5587
  pad(fmtNum(r.cacheRead), 14),
5536
5588
  pad(fmtNum(r.totalTokens), 14),
5537
- pad(fmtCost(r.cost), 12)
5589
+ pad(fmtCost(r.cost, r.costPartial), 12)
5538
5590
  ].join(" "));
5539
5591
  }
5540
5592
  const totals = rows.reduce(
@@ -5544,9 +5596,10 @@ function renderTable(rows, title) {
5544
5596
  cacheCreate: acc.cacheCreate + r.cacheCreate,
5545
5597
  cacheRead: acc.cacheRead + r.cacheRead,
5546
5598
  totalTokens: acc.totalTokens + r.totalTokens,
5547
- cost: r.cost !== null && acc.cost !== null ? acc.cost + r.cost : acc.cost
5599
+ cost: acc.cost + (r.cost ?? 0),
5600
+ anyCost: acc.anyCost || r.cost !== null
5548
5601
  }),
5549
- { input: 0, output: 0, cacheCreate: 0, cacheRead: 0, totalTokens: 0, cost: 0 }
5602
+ { input: 0, output: 0, cacheCreate: 0, cacheRead: 0, totalTokens: 0, cost: 0, anyCost: false }
5550
5603
  );
5551
5604
  lines.push(`${DIM2}${"\u2500".repeat(header.length)}${RESET2}`);
5552
5605
  lines.push(
@@ -5558,12 +5611,14 @@ function renderTable(rows, title) {
5558
5611
  pad(fmtNum(totals.cacheCreate), 14),
5559
5612
  pad(fmtNum(totals.cacheRead), 14),
5560
5613
  pad(fmtNum(totals.totalTokens), 14),
5561
- pad(fmtCost(totals.cost), 12)
5614
+ pad(totals.anyCost ? fmtCost(totals.cost, costIncomplete) : fmtCost(null), 12)
5562
5615
  ].join(" ")}${RESET2}`
5563
5616
  );
5564
- if (hasUnknownCost) {
5617
+ if (costIncomplete) {
5565
5618
  lines.push("");
5566
- lines.push(`${DIM2} Note: some models had unknown pricing (shown as \u2014)${RESET2}`);
5619
+ lines.push(
5620
+ `${DIM2} Cost covers priced usage only \u2014 "\u2014" = no priced sessions that day, "\u2265" = lower bound (some sessions had unknown pricing).${RESET2}`
5621
+ );
5567
5622
  }
5568
5623
  lines.push("");
5569
5624
  return lines.join("\n");
@@ -5616,6 +5671,9 @@ async function printStats(sinceDate) {
5616
5671
  console.log(` ${todaySessions} sessions today \xB7 ${serverLine}`);
5617
5672
  console.log("");
5618
5673
  }
5674
+ function displayModel(raw) {
5675
+ return raw.replace(/-\d{8}$/, "").replace(/^claude-/, "");
5676
+ }
5619
5677
  function aggregateByDay(sessions) {
5620
5678
  const byDay = /* @__PURE__ */ new Map();
5621
5679
  for (const s of sessions) {
@@ -5623,12 +5681,14 @@ function aggregateByDay(sessions) {
5623
5681
  if (!byDay.has(day2)) byDay.set(day2, { sessions: [], models: /* @__PURE__ */ new Set() });
5624
5682
  const bucket = byDay.get(day2);
5625
5683
  bucket.sessions.push(s);
5626
- if (s.model) bucket.models.add(s.model.replace(/-\d{8}$/, ""));
5684
+ if (s.model) bucket.models.add(displayModel(s.model));
5627
5685
  }
5628
5686
  const sorted = [...byDay.entries()].sort((a, b) => b[0].localeCompare(a[0]));
5629
5687
  return sorted.map(([date, { sessions: sessions2, models }]) => {
5630
5688
  const usage2 = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
5631
5689
  let cost = 0;
5690
+ let anyPriced = false;
5691
+ let anyUnpriced = false;
5632
5692
  for (const s of sessions2) {
5633
5693
  usage2.input += s.totalUsage.input;
5634
5694
  usage2.output += s.totalUsage.output;
@@ -5636,11 +5696,15 @@ function aggregateByDay(sessions) {
5636
5696
  usage2.cacheWrite += s.totalUsage.cacheWrite;
5637
5697
  if (s.model) {
5638
5698
  const c = estimateCost(s.model, s.totalUsage);
5639
- if (c !== null && cost !== null) cost += c;
5640
- else cost = null;
5699
+ if (c !== null) {
5700
+ cost += c;
5701
+ anyPriced = true;
5702
+ } else {
5703
+ anyUnpriced = true;
5704
+ }
5641
5705
  }
5642
5706
  }
5643
- const modelNames = [...models].map((m) => m.replace("claude-", "").replace(/-\d+$/, "")).join(", ");
5707
+ const modelNames = [...models].join(", ");
5644
5708
  return {
5645
5709
  date,
5646
5710
  models: modelNames || "\u2014",
@@ -5649,7 +5713,8 @@ function aggregateByDay(sessions) {
5649
5713
  cacheCreate: usage2.cacheWrite,
5650
5714
  cacheRead: usage2.cacheRead,
5651
5715
  totalTokens: usage2.input + usage2.output + usage2.cacheRead + usage2.cacheWrite,
5652
- cost
5716
+ cost: anyPriced ? cost : null,
5717
+ costPartial: anyPriced && anyUnpriced
5653
5718
  };
5654
5719
  });
5655
5720
  }
@@ -6474,6 +6539,20 @@ var init_dist2 = __esm({
6474
6539
  // ../entries/dist/digest-fs.js
6475
6540
  import { readFileSync as readFileSync7, readdirSync, writeFileSync as writeFileSync3, renameSync, chmodSync, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "node:fs";
6476
6541
  import { dirname as dirname6, join as join6 } from "node:path";
6542
+ function parseDigestFile(path7, expectedVersion) {
6543
+ try {
6544
+ const parsed = JSON.parse(readFileSync7(path7, "utf8"));
6545
+ if (!parsed || typeof parsed !== "object")
6546
+ return null;
6547
+ if (parsed.version !== expectedVersion)
6548
+ return null;
6549
+ if (!Array.isArray(parsed.shipped))
6550
+ return null;
6551
+ return parsed;
6552
+ } catch {
6553
+ return null;
6554
+ }
6555
+ }
6477
6556
  function digestsDir() {
6478
6557
  if (digestsDirCached)
6479
6558
  return digestsDirCached;
@@ -6513,11 +6592,7 @@ function readDayDigest(date) {
6513
6592
  const p = dayDigestPath(date);
6514
6593
  if (!existsSync5(p))
6515
6594
  return null;
6516
- try {
6517
- return JSON.parse(readFileSync7(p, "utf8"));
6518
- } catch {
6519
- return null;
6520
- }
6595
+ return parseDigestFile(p, CURRENT_DAY_DIGEST_SCHEMA_VERSION);
6521
6596
  }
6522
6597
  function getTodayDigestFromCache(date, nowMs) {
6523
6598
  if (!todayCache)
@@ -6553,11 +6628,7 @@ function readWeekDigest(monday) {
6553
6628
  const p = weekDigestPath(monday);
6554
6629
  if (!existsSync5(p))
6555
6630
  return null;
6556
- try {
6557
- return JSON.parse(readFileSync7(p, "utf8"));
6558
- } catch {
6559
- return null;
6560
- }
6631
+ return parseDigestFile(p, CURRENT_WEEK_DIGEST_SCHEMA_VERSION);
6561
6632
  }
6562
6633
  function listWeekDigestKeys() {
6563
6634
  const dir = join6(digestsDir(), "week");
@@ -6594,11 +6665,7 @@ function readMonthDigest(yearMonth) {
6594
6665
  const p = monthDigestPath(yearMonth);
6595
6666
  if (!existsSync5(p))
6596
6667
  return null;
6597
- try {
6598
- return JSON.parse(readFileSync7(p, "utf8"));
6599
- } catch {
6600
- return null;
6601
- }
6668
+ return parseDigestFile(p, CURRENT_MONTH_DIGEST_SCHEMA_VERSION);
6602
6669
  }
6603
6670
  function listMonthDigestKeys() {
6604
6671
  const dir = join6(digestsDir(), "month");
@@ -6623,6 +6690,7 @@ var init_digest_fs = __esm({
6623
6690
  "../entries/dist/digest-fs.js"() {
6624
6691
  "use strict";
6625
6692
  init_fs();
6693
+ init_types();
6626
6694
  digestsDirCached = null;
6627
6695
  TODAY_TTL_MS = 10 * 60 * 1e3;
6628
6696
  todayCache = null;
@@ -17626,7 +17694,7 @@ async function main() {
17626
17694
  case "version":
17627
17695
  case "--version":
17628
17696
  case "-v":
17629
- console.log(`fleetlens ${"0.12.1"}`);
17697
+ console.log(`fleetlens ${"0.12.3"}`);
17630
17698
  break;
17631
17699
  case "help":
17632
17700
  case "--help":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetlens",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "description": "fleetlens — local-only dashboard and usage tracker for Claude Code sessions and agent fleets",
5
5
  "license": "MIT",
6
6
  "repository": {