agenttop 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,6 +8,8 @@ import {
8
8
  discoverSessions,
9
9
  getArchived,
10
10
  getNicknames,
11
+ getProjectsDirs,
12
+ getTaskDirs,
11
13
  isFirstRun,
12
14
  loadConfig,
13
15
  purgeExpiredArchives,
@@ -17,11 +19,11 @@ import {
17
19
  setNickname,
18
20
  startMcpServer,
19
21
  unarchiveSession
20
- } from "./chunk-6D2UXDV4.js";
22
+ } from "./chunk-3LEBAMTZ.js";
21
23
 
22
24
  // src/index.tsx
23
- import { readFileSync as readFileSync4 } from "fs";
24
- import { join as join4, dirname as dirname4 } from "path";
25
+ import { readFileSync as readFileSync3 } from "fs";
26
+ import { join as join5, dirname as dirname4 } from "path";
25
27
  import { fileURLToPath as fileURLToPath3 } from "url";
26
28
  import React16 from "react";
27
29
  import { render } from "ink";
@@ -348,33 +350,36 @@ var deriveSeverityColors = (c) => ({
348
350
  });
349
351
 
350
352
  // src/updates.ts
351
- import { execSync, exec } from "child_process";
352
- import { readFileSync } from "fs";
353
+ import { execFile, exec } from "child_process";
354
+ import { readFile } from "fs/promises";
353
355
  import { join, dirname } from "path";
354
356
  import { fileURLToPath } from "url";
355
- var getPackageVersion = () => {
357
+ var getPackageVersion = async () => {
356
358
  try {
357
359
  const thisFile = fileURLToPath(import.meta.url);
358
360
  const pkgPath = join(dirname(thisFile), "..", "package.json");
359
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
361
+ const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
360
362
  return pkg.version || "0.0.0";
361
363
  } catch {
362
364
  return "0.0.0";
363
365
  }
364
366
  };
365
- var checkForUpdate = () => {
366
- const current = getPackageVersion();
367
- try {
368
- const latest = execSync("npm view agenttop version", { encoding: "utf-8", timeout: 5e3 }).trim();
369
- return {
370
- current,
371
- latest,
372
- available: latest !== current && compareVersions(latest, current) > 0
373
- };
374
- } catch {
375
- return { current, latest: current, available: false };
376
- }
377
- };
367
+ var checkForUpdate = () => new Promise((resolve) => {
368
+ getPackageVersion().then((current) => {
369
+ execFile("npm", ["view", "agenttop", "version"], { encoding: "utf-8", timeout: 5e3 }, (err, stdout) => {
370
+ if (err || !stdout) {
371
+ resolve({ current, latest: current, available: false });
372
+ return;
373
+ }
374
+ const latest = stdout.trim();
375
+ resolve({
376
+ current,
377
+ latest,
378
+ available: latest !== current && compareVersions(latest, current) > 0
379
+ });
380
+ });
381
+ });
382
+ });
378
383
  var installUpdate = () => {
379
384
  return new Promise((resolve, reject) => {
380
385
  exec("npm install -g agenttop@latest", { timeout: 6e4 }, (err, stdout) => {
@@ -490,11 +495,24 @@ var formatTokens = (n) => {
490
495
  return String(n);
491
496
  };
492
497
  var truncate = (s, max) => s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
493
- var SIDEBAR_WIDTH = 30;
494
- var INNER_WIDTH = SIDEBAR_WIDTH - 4;
498
+ var getDisplayName = (s) => {
499
+ if (s.nickname) return s.nickname;
500
+ if (s.cwd) {
501
+ const parts = s.cwd.replace(/\/+$/, "").split("/");
502
+ return parts[parts.length - 1] || s.slug;
503
+ }
504
+ if (s.project) {
505
+ const parts = s.project.replace(/\/+$/, "").split("/");
506
+ return parts[parts.length - 1] || s.slug;
507
+ }
508
+ return s.slug;
509
+ };
510
+ var DEFAULT_SIDEBAR_WIDTH = 30;
495
511
  var LINES_PER_ITEM = 3;
496
512
  var SessionList = React2.memo(
497
- ({ visibleItems, selectedIndex, focused, height, filter, viewingArchive, totalSessions }) => {
513
+ ({ visibleItems, selectedIndex, focused, height, filter, viewingArchive, totalSessions, sidebarWidth }) => {
514
+ const SIDEBAR_WIDTH = sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH;
515
+ const INNER_WIDTH = SIDEBAR_WIDTH - 4;
498
516
  const availableRows = height - 2;
499
517
  const maxVisible = Math.max(1, Math.floor((availableRows + 1) / LINES_PER_ITEM));
500
518
  const halfView = Math.floor(maxVisible / 2);
@@ -546,14 +564,14 @@ var SessionList = React2.memo(
546
564
  paddingX: 1,
547
565
  backgroundColor: isSelected ? colors.selected : void 0,
548
566
  children: [
549
- /* @__PURE__ */ jsxs2(Text2, { color: nameColor2, bold: isSelected, wrap: "truncate", children: [
567
+ /* @__PURE__ */ jsxs2(Text2, { color: nameColor2, bold: isSelected, underline: isSelected, wrap: "truncate", children: [
550
568
  arrow,
551
569
  " ",
552
570
  /* @__PURE__ */ jsx2(Text2, { color: dotColor2, children: statusDot2 }),
553
571
  " ",
554
572
  label2
555
573
  ] }),
556
- /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
574
+ /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
557
575
  " ",
558
576
  model2,
559
577
  " ",
@@ -576,7 +594,7 @@ var SessionList = React2.memo(
576
594
  const model = formatModel(session.model);
577
595
  if (item.type === "session") {
578
596
  const nameColor2 = isSelected ? colors.bright : isActive ? colors.secondary : colors.muted;
579
- const displayName2 = truncate(session.slug, INNER_WIDTH - 6);
597
+ const displayName2 = truncate(session.nickname || session.slug, INNER_WIDTH - 6);
580
598
  return /* @__PURE__ */ jsxs2(
581
599
  Box2,
582
600
  {
@@ -584,14 +602,14 @@ var SessionList = React2.memo(
584
602
  paddingX: 1,
585
603
  backgroundColor: isSelected ? colors.selected : void 0,
586
604
  children: [
587
- /* @__PURE__ */ jsxs2(Text2, { color: nameColor2, bold: isSelected, wrap: "truncate", children: [
605
+ /* @__PURE__ */ jsxs2(Text2, { color: nameColor2, bold: isSelected, underline: isSelected, wrap: "truncate", children: [
588
606
  " ",
589
607
  " ",
590
608
  /* @__PURE__ */ jsx2(Text2, { color: dotColor, children: statusDot }),
591
609
  " ",
592
610
  displayName2
593
611
  ] }),
594
- /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
612
+ /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
595
613
  " ",
596
614
  model,
597
615
  " ",
@@ -608,19 +626,7 @@ var SessionList = React2.memo(
608
626
  }
609
627
  const indicator = isSelected ? "\u25B8" : " ";
610
628
  const nameColor = isSelected ? colors.bright : isActive ? colors.secondary : colors.text;
611
- const getDisplayName2 = (s) => {
612
- if (s.nickname) return s.nickname;
613
- if (s.cwd) {
614
- const parts = s.cwd.replace(/\/+$/, "").split("/");
615
- return parts[parts.length - 1] || s.slug;
616
- }
617
- if (s.project) {
618
- const parts = s.project.replace(/\/+$/, "").split("/");
619
- return parts[parts.length - 1] || s.slug;
620
- }
621
- return s.slug;
622
- };
623
- const displayName = truncate(getDisplayName2(session), INNER_WIDTH - 4);
629
+ const displayName = truncate(getDisplayName(session), INNER_WIDTH - 4);
624
630
  return /* @__PURE__ */ jsxs2(
625
631
  Box2,
626
632
  {
@@ -628,14 +634,14 @@ var SessionList = React2.memo(
628
634
  paddingX: 1,
629
635
  backgroundColor: isSelected ? colors.selected : void 0,
630
636
  children: [
631
- /* @__PURE__ */ jsxs2(Text2, { color: nameColor, bold: isSelected, wrap: "truncate", children: [
637
+ /* @__PURE__ */ jsxs2(Text2, { color: nameColor, bold: isSelected, underline: isSelected, wrap: "truncate", children: [
632
638
  indicator,
633
639
  " ",
634
640
  /* @__PURE__ */ jsx2(Text2, { color: dotColor, children: statusDot }),
635
641
  " ",
636
642
  displayName
637
643
  ] }),
638
- /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
644
+ /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
639
645
  " ",
640
646
  model,
641
647
  " ",
@@ -1099,7 +1105,9 @@ var KEYBIND_LABELS = {
1099
1105
  pinLeft: "Pin to left panel",
1100
1106
  pinRight: "Pin to right panel",
1101
1107
  swapPanels: "Swap panels",
1102
- closePanel: "Close panel"
1108
+ closePanel: "Close panel",
1109
+ sidebarNarrower: "Sidebar narrower",
1110
+ sidebarWider: "Sidebar wider"
1103
1111
  };
1104
1112
  var RULE_LABELS = {
1105
1113
  network: "Network detection",
@@ -1129,7 +1137,9 @@ var DEFAULT_KEYBINDINGS = {
1129
1137
  pinLeft: "1",
1130
1138
  pinRight: "2",
1131
1139
  swapPanels: "S",
1132
- closePanel: "X"
1140
+ closePanel: "X",
1141
+ sidebarNarrower: "<",
1142
+ sidebarWider: ">"
1133
1143
  };
1134
1144
  var SEVERITY_OPTIONS = ["info", "warn", "high", "critical"];
1135
1145
  var ARCHIVE_EXPIRY_OPTIONS = [0, 7, 14, 30, 60, 90];
@@ -1930,20 +1940,402 @@ var SplitPanel = React14.memo(
1930
1940
 
1931
1941
  // src/ui/hooks/useSessions.ts
1932
1942
  import { useState as useState8, useEffect as useEffect5, useCallback as useCallback2, useRef as useRef3, useMemo as useMemo2 } from "react";
1933
- var ACTIVE_POLL_MS = 1e4;
1934
- var IDLE_POLL_MS = 3e4;
1935
- var getDisplayName = (session) => {
1936
- if (session.nickname) return session.nickname;
1937
- if (session.cwd) {
1938
- const parts = session.cwd.replace(/\/+$/, "").split("/");
1939
- return parts[parts.length - 1] || session.slug;
1943
+
1944
+ // src/discovery/sessionsAsync.ts
1945
+ import { readdir, stat as stat2 } from "fs/promises";
1946
+ import { join as join2, basename } from "path";
1947
+
1948
+ // src/discovery/cache.ts
1949
+ var sessions = [];
1950
+ var refreshInFlight = false;
1951
+ var listeners = /* @__PURE__ */ new Set();
1952
+ var getCachedSessions = () => sessions;
1953
+ var setCachedSessions = (next) => {
1954
+ sessions = next;
1955
+ for (const fn of listeners) fn(next);
1956
+ };
1957
+ var subscribe = (fn) => {
1958
+ listeners.add(fn);
1959
+ return () => {
1960
+ listeners.delete(fn);
1961
+ };
1962
+ };
1963
+ var isRefreshInFlight = () => refreshInFlight;
1964
+ var setRefreshInFlight = (v) => {
1965
+ refreshInFlight = v;
1966
+ };
1967
+ var MAX_CACHE_ENTRIES = 500;
1968
+ var fileMetaCache = /* @__PURE__ */ new Map();
1969
+ var pruneFileMetaCache = (validPaths) => {
1970
+ for (const key of fileMetaCache.keys()) {
1971
+ if (!validPaths.has(key)) fileMetaCache.delete(key);
1940
1972
  }
1941
- if (session.project) {
1942
- const parts = session.project.replace(/\/+$/, "").split("/");
1943
- return parts[parts.length - 1] || session.slug;
1973
+ if (fileMetaCache.size > MAX_CACHE_ENTRIES) {
1974
+ const excess = fileMetaCache.size - MAX_CACHE_ENTRIES;
1975
+ const iter = fileMetaCache.keys();
1976
+ for (let i = 0; i < excess; i++) {
1977
+ const next = iter.next();
1978
+ if (!next.done) fileMetaCache.delete(next.value);
1979
+ }
1944
1980
  }
1945
- return session.slug;
1946
1981
  };
1982
+
1983
+ // src/discovery/asyncHelpers.ts
1984
+ import { execFile as execFile2 } from "child_process";
1985
+ import { open, stat, readlink } from "fs/promises";
1986
+ var normalisePath = (p) => p.replace(/\/+$/, "");
1987
+ var getClaudeProcessesAsync = async () => {
1988
+ const stdout = await new Promise((resolve) => {
1989
+ execFile2("ps", ["aux"], { encoding: "utf-8", timeout: 5e3, maxBuffer: 4 * 1024 * 1024 }, (err, out) => {
1990
+ resolve(err || !out ? "" : out);
1991
+ });
1992
+ });
1993
+ if (!stdout) return [];
1994
+ const procs = [];
1995
+ for (const line of stdout.split("\n")) {
1996
+ if (!line.includes("/claude") || line.includes("grep") || line.includes("agenttop")) continue;
1997
+ const parts = line.trim().split(/\s+/);
1998
+ const pid = parseInt(parts[1], 10);
1999
+ if (isNaN(pid)) continue;
2000
+ const command = parts.slice(10).join(" ");
2001
+ if (command.startsWith("sudo")) continue;
2002
+ procs.push({
2003
+ pid,
2004
+ cpu: parseFloat(parts[2]) || 0,
2005
+ mem: parseFloat(parts[3]) || 0,
2006
+ memKB: parseInt(parts[5], 10) || 0,
2007
+ startTime: parts[8] || "",
2008
+ command,
2009
+ cwd: ""
2010
+ });
2011
+ }
2012
+ await Promise.all(
2013
+ procs.map(async (p) => {
2014
+ try {
2015
+ p.cwd = await readlink(`/proc/${p.pid}/cwd`);
2016
+ } catch {
2017
+ }
2018
+ })
2019
+ );
2020
+ return procs;
2021
+ };
2022
+ var readFirstLinesAsync = async (filePath, bytes) => {
2023
+ let fh;
2024
+ try {
2025
+ fh = await open(filePath, "r");
2026
+ const buf = Buffer.alloc(bytes);
2027
+ const { bytesRead } = await fh.read(buf, 0, bytes, 0);
2028
+ return buf.subarray(0, bytesRead).toString("utf-8").split("\n").filter(Boolean);
2029
+ } catch {
2030
+ return [];
2031
+ } finally {
2032
+ await fh?.close();
2033
+ }
2034
+ };
2035
+ var extractSessionMetaCached = async (filePath) => {
2036
+ let fstat;
2037
+ try {
2038
+ fstat = await stat(filePath);
2039
+ if (!fstat.isFile() || fstat.size === 0) return null;
2040
+ } catch {
2041
+ return null;
2042
+ }
2043
+ const cached = fileMetaCache.get(filePath);
2044
+ if (cached && cached.mtimeMs === fstat.mtimeMs && cached.size === fstat.size) {
2045
+ return {
2046
+ sessionId: cached.sessionId,
2047
+ cwd: cached.cwd,
2048
+ version: cached.version,
2049
+ gitBranch: cached.gitBranch,
2050
+ model: cached.model,
2051
+ usage: cached.usage
2052
+ };
2053
+ }
2054
+ const lines = await readFirstLinesAsync(filePath, 65536);
2055
+ let sessionId = "";
2056
+ let cwd = "";
2057
+ let version = "";
2058
+ let gitBranch = "";
2059
+ let model = "";
2060
+ const usage = { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 };
2061
+ for (const line of lines) {
2062
+ try {
2063
+ const evt = JSON.parse(line);
2064
+ if (!sessionId && evt.sessionId) sessionId = String(evt.sessionId);
2065
+ if (!cwd && evt.cwd) cwd = String(evt.cwd);
2066
+ if (!version && evt.version) version = String(evt.version);
2067
+ if (!gitBranch && evt.gitBranch) gitBranch = String(evt.gitBranch);
2068
+ if (evt.type === "assistant") {
2069
+ if (!model && evt.message?.model) model = String(evt.message.model);
2070
+ const u = evt.message?.usage;
2071
+ if (u) {
2072
+ usage.inputTokens += u.input_tokens ?? 0;
2073
+ usage.cacheCreationTokens += u.cache_creation_input_tokens ?? 0;
2074
+ usage.cacheReadTokens += u.cache_read_input_tokens ?? 0;
2075
+ usage.outputTokens += u.output_tokens ?? 0;
2076
+ }
2077
+ }
2078
+ } catch {
2079
+ continue;
2080
+ }
2081
+ }
2082
+ if (!sessionId) return null;
2083
+ fileMetaCache.set(filePath, {
2084
+ mtimeMs: fstat.mtimeMs,
2085
+ size: fstat.size,
2086
+ sessionId,
2087
+ cwd,
2088
+ version,
2089
+ gitBranch,
2090
+ model,
2091
+ usage
2092
+ });
2093
+ return { sessionId, cwd, version, gitBranch, model, usage };
2094
+ };
2095
+ var readFirstEventAsync = async (filePath) => {
2096
+ const lines = await readFirstLinesAsync(filePath, 16384);
2097
+ if (lines.length === 0) return null;
2098
+ try {
2099
+ return JSON.parse(lines[0]);
2100
+ } catch {
2101
+ return null;
2102
+ }
2103
+ };
2104
+ var findModelAndUsageAsync = async (filePath) => {
2105
+ let fstat;
2106
+ try {
2107
+ fstat = await stat(filePath);
2108
+ } catch {
2109
+ return { model: "", usage: { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 } };
2110
+ }
2111
+ const cached = fileMetaCache.get(filePath);
2112
+ if (cached && cached.mtimeMs === fstat.mtimeMs && cached.size === fstat.size) {
2113
+ return { model: cached.model, usage: cached.usage };
2114
+ }
2115
+ const usage = { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 };
2116
+ let model = "";
2117
+ const lines = await readFirstLinesAsync(filePath, 65536);
2118
+ for (const line of lines) {
2119
+ try {
2120
+ const evt = JSON.parse(line);
2121
+ if (evt.type === "assistant") {
2122
+ if (!model && evt.message?.model) model = String(evt.message.model);
2123
+ const u = evt.message?.usage;
2124
+ if (u) {
2125
+ usage.inputTokens += u.input_tokens ?? 0;
2126
+ usage.cacheCreationTokens += u.cache_creation_input_tokens ?? 0;
2127
+ usage.cacheReadTokens += u.cache_read_input_tokens ?? 0;
2128
+ usage.outputTokens += u.output_tokens ?? 0;
2129
+ }
2130
+ }
2131
+ } catch {
2132
+ continue;
2133
+ }
2134
+ }
2135
+ return { model, usage };
2136
+ };
2137
+
2138
+ // src/discovery/sessionsAsync.ts
2139
+ var discoverFromProjectsAsync = async (allUsers, processes, sessionMap, seenFiles) => {
2140
+ const projectsDirs = getProjectsDirs(allUsers);
2141
+ for (const projectsDir of projectsDirs) {
2142
+ let projectNames;
2143
+ try {
2144
+ projectNames = await readdir(projectsDir);
2145
+ } catch {
2146
+ continue;
2147
+ }
2148
+ for (const projectName of projectNames) {
2149
+ const projectPath = join2(projectsDir, projectName);
2150
+ try {
2151
+ const s = await stat2(projectPath);
2152
+ if (!s.isDirectory()) continue;
2153
+ } catch {
2154
+ continue;
2155
+ }
2156
+ let files;
2157
+ try {
2158
+ files = (await readdir(projectPath)).filter((f) => f.endsWith(".jsonl"));
2159
+ } catch {
2160
+ continue;
2161
+ }
2162
+ for (const file of files) {
2163
+ const filePath = join2(projectPath, file);
2164
+ seenFiles.add(filePath);
2165
+ let fstat;
2166
+ try {
2167
+ fstat = await stat2(filePath);
2168
+ if (!fstat.isFile() || fstat.size === 0) continue;
2169
+ } catch {
2170
+ continue;
2171
+ }
2172
+ const meta = await extractSessionMetaCached(filePath);
2173
+ if (!meta) continue;
2174
+ if (sessionMap.has(meta.sessionId)) continue;
2175
+ const normCwd = normalisePath(meta.cwd);
2176
+ const matchingProcess = processes.find((p) => p.cwd && normalisePath(p.cwd) === normCwd);
2177
+ const session = {
2178
+ sessionId: meta.sessionId,
2179
+ slug: meta.sessionId.slice(0, 12),
2180
+ project: projectName.replace(/-/g, "/"),
2181
+ cwd: meta.cwd,
2182
+ model: meta.model || "unknown",
2183
+ version: meta.version,
2184
+ gitBranch: meta.gitBranch,
2185
+ pid: matchingProcess?.pid ?? null,
2186
+ cpu: matchingProcess?.cpu ?? 0,
2187
+ mem: matchingProcess?.mem ?? 0,
2188
+ memMB: matchingProcess ? Math.round(matchingProcess.memKB / 1024) : 0,
2189
+ agentCount: 1,
2190
+ agentIds: [basename(file, ".jsonl")],
2191
+ outputFiles: [filePath],
2192
+ startTime: fstat.birthtimeMs || fstat.ctimeMs,
2193
+ lastActivity: fstat.mtimeMs,
2194
+ usage: meta.usage
2195
+ };
2196
+ sessionMap.set(meta.sessionId, session);
2197
+ }
2198
+ }
2199
+ }
2200
+ };
2201
+ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) => {
2202
+ const taskDirs = getTaskDirs(allUsers);
2203
+ for (const taskDir of taskDirs) {
2204
+ let projectDirs;
2205
+ try {
2206
+ projectDirs = await readdir(taskDir);
2207
+ } catch {
2208
+ continue;
2209
+ }
2210
+ for (const projectName of projectDirs) {
2211
+ const projectPath = join2(taskDir, projectName);
2212
+ try {
2213
+ const s = await stat2(projectPath);
2214
+ if (!s.isDirectory()) continue;
2215
+ } catch {
2216
+ continue;
2217
+ }
2218
+ const tasksPaths = [];
2219
+ const tasksDir = join2(projectPath, "tasks");
2220
+ try {
2221
+ await readdir(tasksDir);
2222
+ tasksPaths.push(tasksDir);
2223
+ } catch {
2224
+ try {
2225
+ for (const sub of await readdir(projectPath)) {
2226
+ const subTasks = join2(projectPath, sub, "tasks");
2227
+ try {
2228
+ await readdir(subTasks);
2229
+ tasksPaths.push(subTasks);
2230
+ } catch {
2231
+ continue;
2232
+ }
2233
+ }
2234
+ } catch {
2235
+ continue;
2236
+ }
2237
+ }
2238
+ for (const tDir of tasksPaths) {
2239
+ let outputFiles;
2240
+ try {
2241
+ outputFiles = (await readdir(tDir)).filter((f) => f.endsWith(".output")).map((f) => join2(tDir, f));
2242
+ } catch {
2243
+ continue;
2244
+ }
2245
+ if (outputFiles.length === 0) continue;
2246
+ const agentIds = [];
2247
+ let sessionId = "";
2248
+ let slug = "";
2249
+ let cwd = "";
2250
+ let model = "";
2251
+ let version = "";
2252
+ let gitBranch = "";
2253
+ let startTime = Infinity;
2254
+ let lastActivity = 0;
2255
+ const totalUsage = {
2256
+ inputTokens: 0,
2257
+ cacheCreationTokens: 0,
2258
+ cacheReadTokens: 0,
2259
+ outputTokens: 0
2260
+ };
2261
+ for (const outputFile of outputFiles) {
2262
+ seenFiles.add(outputFile);
2263
+ const agentId = basename(outputFile, ".output");
2264
+ agentIds.push(agentId);
2265
+ const firstEvent = await readFirstEventAsync(outputFile);
2266
+ if (firstEvent) {
2267
+ if (!sessionId) sessionId = String(firstEvent.sessionId || "");
2268
+ if (!slug) slug = String(firstEvent.slug || "");
2269
+ if (!cwd) cwd = String(firstEvent.cwd || "");
2270
+ if (!version) version = String(firstEvent.version || "");
2271
+ if (!gitBranch) gitBranch = String(firstEvent.gitBranch || "");
2272
+ }
2273
+ try {
2274
+ const fstat = await stat2(outputFile);
2275
+ const created = fstat.birthtimeMs || fstat.ctimeMs;
2276
+ if (created < startTime) startTime = created;
2277
+ if (fstat.mtimeMs > lastActivity) lastActivity = fstat.mtimeMs;
2278
+ } catch {
2279
+ }
2280
+ const result = await findModelAndUsageAsync(outputFile);
2281
+ if (!model && result.model) model = result.model;
2282
+ totalUsage.inputTokens += result.usage.inputTokens;
2283
+ totalUsage.cacheCreationTokens += result.usage.cacheCreationTokens;
2284
+ totalUsage.cacheReadTokens += result.usage.cacheReadTokens;
2285
+ totalUsage.outputTokens += result.usage.outputTokens;
2286
+ }
2287
+ if (!sessionId && !slug) continue;
2288
+ if (sessionMap.has(sessionId || projectName)) continue;
2289
+ const normCwd = normalisePath(cwd);
2290
+ const matchingProcess = processes.find((p) => p.cwd && normalisePath(p.cwd) === normCwd);
2291
+ const session = {
2292
+ sessionId,
2293
+ slug: slug || sessionId.slice(0, 12),
2294
+ project: projectName.replace(/-/g, "/"),
2295
+ cwd,
2296
+ model: model || "unknown",
2297
+ version,
2298
+ gitBranch,
2299
+ pid: matchingProcess?.pid ?? null,
2300
+ cpu: matchingProcess?.cpu ?? 0,
2301
+ mem: matchingProcess?.mem ?? 0,
2302
+ memMB: matchingProcess ? Math.round(matchingProcess.memKB / 1024) : 0,
2303
+ agentCount: agentIds.length,
2304
+ agentIds,
2305
+ outputFiles,
2306
+ startTime: startTime === Infinity ? Date.now() : startTime,
2307
+ lastActivity,
2308
+ usage: totalUsage
2309
+ };
2310
+ sessionMap.set(sessionId || projectName, session);
2311
+ }
2312
+ }
2313
+ }
2314
+ };
2315
+ var discoverSessionsAsync = async (allUsers) => {
2316
+ const processes = await getClaudeProcessesAsync();
2317
+ const sessionMap = /* @__PURE__ */ new Map();
2318
+ const seenFiles = /* @__PURE__ */ new Set();
2319
+ await discoverFromProjectsAsync(allUsers, processes, sessionMap, seenFiles);
2320
+ await discoverFromTmpAsync(allUsers, processes, sessionMap, seenFiles);
2321
+ pruneFileMetaCache(seenFiles);
2322
+ return Array.from(sessionMap.values()).sort((a, b) => {
2323
+ const aActive = a.pid !== null ? 1 : 0;
2324
+ const bActive = b.pid !== null ? 1 : 0;
2325
+ if (aActive !== bActive) return bActive - aActive;
2326
+ return b.lastActivity - a.lastActivity;
2327
+ });
2328
+ };
2329
+ var triggerRefresh = (allUsers) => {
2330
+ if (isRefreshInFlight()) return;
2331
+ setRefreshInFlight(true);
2332
+ discoverSessionsAsync(allUsers).then((result) => setCachedSessions(result)).catch(() => {
2333
+ }).finally(() => setRefreshInFlight(false));
2334
+ };
2335
+
2336
+ // src/ui/hooks/useSessions.ts
2337
+ var ACTIVE_POLL_MS = 1e4;
2338
+ var IDLE_POLL_MS = 3e4;
1947
2339
  var getGroupKey = (session) => {
1948
2340
  if (session.cwd) {
1949
2341
  const parts = session.cwd.replace(/\/+$/, "").split("/");
@@ -1955,19 +2347,16 @@ var getGroupKey = (session) => {
1955
2347
  }
1956
2348
  return session.slug;
1957
2349
  };
1958
- var buildGroups = (sessions, expandedKeys) => {
2350
+ var buildGroups = (sessions2, expandedKeys) => {
1959
2351
  const byKey = /* @__PURE__ */ new Map();
1960
- for (const s of sessions) {
2352
+ for (const s of sessions2) {
1961
2353
  const key = getGroupKey(s);
1962
2354
  const existing = byKey.get(key);
1963
- if (existing) {
1964
- existing.sessions.push(s);
1965
- } else {
1966
- byKey.set(key, { displayName: getDisplayName(s), sessions: [s] });
1967
- }
2355
+ if (existing) existing.push(s);
2356
+ else byKey.set(key, [s]);
1968
2357
  }
1969
2358
  const groups = [];
1970
- for (const [key, { displayName, sessions: list }] of byKey) {
2359
+ for (const [key, list] of byKey) {
1971
2360
  list.sort((a, b) => b.lastActivity - a.lastActivity);
1972
2361
  const totalIn = list.reduce((sum, s) => sum + s.usage.inputTokens + s.usage.cacheReadTokens, 0);
1973
2362
  const totalOut = list.reduce((sum, s) => sum + s.usage.outputTokens, 0);
@@ -2002,52 +2391,64 @@ var buildVisibleItems = (groups) => {
2002
2391
  }
2003
2392
  return items;
2004
2393
  };
2394
+ var enrichAndFilter = (found, usageOverrides, filter, archivedIds, viewingArchive) => {
2395
+ const nicknames = getNicknames();
2396
+ let enriched = found.map((s) => {
2397
+ const override = usageOverrides.get(s.sessionId);
2398
+ return {
2399
+ ...s,
2400
+ nickname: nicknames[s.sessionId],
2401
+ usage: override ? {
2402
+ inputTokens: s.usage.inputTokens + override.inputTokens,
2403
+ cacheCreationTokens: s.usage.cacheCreationTokens + override.cacheCreationTokens,
2404
+ cacheReadTokens: s.usage.cacheReadTokens + override.cacheReadTokens,
2405
+ outputTokens: s.usage.outputTokens + override.outputTokens
2406
+ } : s.usage
2407
+ };
2408
+ });
2409
+ if (archivedIds && archivedIds.size > 0) {
2410
+ if (viewingArchive) {
2411
+ enriched = enriched.filter((s) => archivedIds.has(s.sessionId));
2412
+ } else {
2413
+ enriched = enriched.filter((s) => !archivedIds.has(s.sessionId));
2414
+ }
2415
+ } else if (viewingArchive) {
2416
+ enriched = [];
2417
+ }
2418
+ if (filter) {
2419
+ const lower = filter.toLowerCase();
2420
+ enriched = enriched.filter(
2421
+ (s) => s.slug.toLowerCase().includes(lower) || s.nickname?.toLowerCase().includes(lower) || s.project.toLowerCase().includes(lower) || s.model.toLowerCase().includes(lower)
2422
+ );
2423
+ }
2424
+ return enriched;
2425
+ };
2005
2426
  var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2006
- const [sessions, setSessions] = useState8([]);
2427
+ const [sessions2, setSessions] = useState8([]);
2007
2428
  const [selectedIndex, setSelectedIndex] = useState8(0);
2008
2429
  const [expandedKeys, setExpandedKeys] = useState8(/* @__PURE__ */ new Set());
2009
2430
  const usageOverrides = useRef3(/* @__PURE__ */ new Map());
2010
2431
  const refresh = useCallback2(() => {
2011
- const found = discoverSessions(allUsers);
2012
- const nicknames = getNicknames();
2013
- const enriched = found.map((s) => {
2014
- const override = usageOverrides.current.get(s.sessionId);
2015
- return {
2016
- ...s,
2017
- nickname: nicknames[s.sessionId],
2018
- usage: override ? {
2019
- inputTokens: s.usage.inputTokens + override.inputTokens,
2020
- cacheCreationTokens: s.usage.cacheCreationTokens + override.cacheCreationTokens,
2021
- cacheReadTokens: s.usage.cacheReadTokens + override.cacheReadTokens,
2022
- outputTokens: s.usage.outputTokens + override.outputTokens
2023
- } : s.usage
2024
- };
2025
- });
2026
- let filtered = enriched;
2027
- if (archivedIds && archivedIds.size > 0) {
2028
- if (viewingArchive) {
2029
- filtered = filtered.filter((s) => archivedIds.has(s.sessionId));
2030
- } else {
2031
- filtered = filtered.filter((s) => !archivedIds.has(s.sessionId));
2032
- }
2033
- } else if (viewingArchive) {
2034
- filtered = [];
2035
- }
2036
- if (filter) {
2037
- const lower = filter.toLowerCase();
2038
- filtered = filtered.filter(
2039
- (s) => s.slug.toLowerCase().includes(lower) || s.nickname?.toLowerCase().includes(lower) || s.project.toLowerCase().includes(lower) || s.model.toLowerCase().includes(lower)
2040
- );
2041
- }
2432
+ const found = getCachedSessions();
2433
+ const filtered = enrichAndFilter(found, usageOverrides.current, filter, archivedIds, viewingArchive);
2042
2434
  setSessions(filtered);
2435
+ triggerRefresh(allUsers);
2043
2436
  }, [allUsers, filter, archivedIds, viewingArchive]);
2437
+ useEffect5(() => {
2438
+ const unsubscribe = subscribe(() => {
2439
+ const found = getCachedSessions();
2440
+ const filtered = enrichAndFilter(found, usageOverrides.current, filter, archivedIds, viewingArchive);
2441
+ setSessions(filtered);
2442
+ });
2443
+ return unsubscribe;
2444
+ }, [filter, archivedIds, viewingArchive]);
2044
2445
  useEffect5(() => {
2045
2446
  refresh();
2046
- const pollMs = sessions.length > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS;
2047
- const interval = setInterval(refresh, pollMs);
2447
+ const pollMs = sessions2.length > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS;
2448
+ const interval = setInterval(() => triggerRefresh(allUsers), pollMs);
2048
2449
  return () => clearInterval(interval);
2049
- }, [refresh, sessions.length > 0]);
2050
- const groups = useMemo2(() => buildGroups(sessions, expandedKeys), [sessions, expandedKeys]);
2450
+ }, [refresh, sessions2.length > 0]);
2451
+ const groups = useMemo2(() => buildGroups(sessions2, expandedKeys), [sessions2, expandedKeys]);
2051
2452
  const visibleItems = useMemo2(() => buildVisibleItems(groups), [groups]);
2052
2453
  const itemCountRef = useRef3(visibleItems.length);
2053
2454
  itemCountRef.current = visibleItems.length;
@@ -2085,7 +2486,7 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2085
2486
  }
2086
2487
  }, []);
2087
2488
  return {
2088
- sessions,
2489
+ sessions: sessions2,
2089
2490
  groups,
2090
2491
  visibleItems,
2091
2492
  selectedSession,
@@ -2106,24 +2507,32 @@ var MAX_EVENTS = 200;
2106
2507
  var useActivityStream = (session, allUsers) => {
2107
2508
  const [events, setEvents] = useState9([]);
2108
2509
  const watcherRef = useRef4(null);
2109
- const sessions = session === null ? [] : Array.isArray(session) ? session : [session];
2110
- const sessionKey = sessions.map((s) => s.sessionId).sort().join(",");
2111
- const sessionIdSet = new Set(sessions.map((s) => s.sessionId));
2510
+ const sessions2 = session === null ? [] : Array.isArray(session) ? session : [session];
2511
+ const sessionKey = sessions2.map((s) => s.sessionId).sort().join(",");
2512
+ const sessionIdSet = new Set(sessions2.map((s) => s.sessionId));
2112
2513
  useEffect6(() => {
2113
2514
  setEvents([]);
2114
- if (sessions.length === 0) return;
2115
- const existingCalls = [];
2116
- const tempWatcher = new Watcher(() => {
2117
- }, allUsers);
2118
- const allFiles = sessions.flatMap((s) => s.outputFiles);
2119
- const seen = /* @__PURE__ */ new Set();
2120
- for (const file of allFiles) {
2121
- if (seen.has(file)) continue;
2122
- seen.add(file);
2123
- existingCalls.push(...tempWatcher.readExisting(file));
2124
- }
2125
- existingCalls.sort((a, b) => a.timestamp - b.timestamp);
2126
- setEvents(existingCalls.slice(-MAX_EVENTS));
2515
+ if (sessions2.length === 0) return;
2516
+ let cancelled = false;
2517
+ const loadExisting = async () => {
2518
+ const tempWatcher = new Watcher(() => {
2519
+ }, allUsers);
2520
+ const allFiles = sessions2.flatMap((s) => s.outputFiles);
2521
+ const seen = /* @__PURE__ */ new Set();
2522
+ const existingCalls = [];
2523
+ for (const file of allFiles) {
2524
+ if (cancelled) return;
2525
+ if (seen.has(file)) continue;
2526
+ seen.add(file);
2527
+ if (cancelled) return;
2528
+ const calls = await tempWatcher.readExistingAsync(file);
2529
+ existingCalls.push(...calls);
2530
+ }
2531
+ if (cancelled) return;
2532
+ existingCalls.sort((a, b) => a.timestamp - b.timestamp);
2533
+ setEvents(existingCalls.slice(-MAX_EVENTS));
2534
+ };
2535
+ loadExisting();
2127
2536
  const handler = (calls) => {
2128
2537
  const matched = calls.filter((c) => sessionIdSet.has(c.sessionId));
2129
2538
  if (matched.length === 0) return;
@@ -2133,6 +2542,7 @@ var useActivityStream = (session, allUsers) => {
2133
2542
  watcherRef.current = watcher;
2134
2543
  watcher.start();
2135
2544
  return () => {
2545
+ cancelled = true;
2136
2546
  watcher.stop();
2137
2547
  watcherRef.current = null;
2138
2548
  };
@@ -2507,6 +2917,14 @@ var useKeyHandler = (deps) => {
2507
2917
  return;
2508
2918
  }
2509
2919
  if (d.activePanel === "sessions") {
2920
+ if (matchKey(d.kb.sidebarWider, input, key)) {
2921
+ d.setSidebarWidth((w) => Math.min(w + 5, 60));
2922
+ return;
2923
+ }
2924
+ if (matchKey(d.kb.sidebarNarrower, input, key)) {
2925
+ d.setSidebarWidth((w) => Math.max(w - 5, 20));
2926
+ return;
2927
+ }
2510
2928
  if (matchKey(d.kb.navDown, input, key) || key.downArrow) d.selectNext();
2511
2929
  if (matchKey(d.kb.navUp, input, key) || key.upArrow) d.selectPrev();
2512
2930
  }
@@ -2537,19 +2955,19 @@ var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
2537
2955
  const [updateInfo, setUpdateInfo] = useState12(null);
2538
2956
  useEffect8(() => {
2539
2957
  if (disabled || !checkOnLaunch) return;
2540
- try {
2541
- const i = checkForUpdate();
2542
- if (i.available) setUpdateInfo(i);
2543
- } catch {
2544
- }
2545
- const iv = setInterval(() => {
2546
- try {
2547
- const i = checkForUpdate();
2548
- if (i.available) setUpdateInfo(i);
2549
- } catch {
2550
- }
2551
- }, checkInterval);
2552
- return () => clearInterval(iv);
2958
+ let cancelled = false;
2959
+ const check = () => {
2960
+ checkForUpdate().then((i) => {
2961
+ if (!cancelled && i.available) setUpdateInfo(i);
2962
+ }).catch(() => {
2963
+ });
2964
+ };
2965
+ check();
2966
+ const iv = setInterval(check, checkInterval);
2967
+ return () => {
2968
+ cancelled = true;
2969
+ clearInterval(iv);
2970
+ };
2553
2971
  }, []);
2554
2972
  return updateInfo;
2555
2973
  };
@@ -2558,35 +2976,35 @@ var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
2558
2976
  import { useState as useState13, useCallback as useCallback4 } from "react";
2559
2977
 
2560
2978
  // src/hooks/installer.ts
2561
- import { existsSync, readFileSync as readFileSync2, writeFileSync, copyFileSync, mkdirSync as mkdirSync2, chmodSync } from "fs";
2562
- import { join as join2, dirname as dirname3 } from "path";
2979
+ import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync as mkdirSync2, chmodSync } from "fs";
2980
+ import { join as join3, dirname as dirname3 } from "path";
2563
2981
  import { homedir } from "os";
2564
2982
  import { fileURLToPath as fileURLToPath2 } from "url";
2565
2983
  var HOOK_FILENAME = "agenttop-guard.py";
2566
- var SETTINGS_PATH = join2(homedir(), ".claude", "settings.json");
2984
+ var SETTINGS_PATH = join3(homedir(), ".claude", "settings.json");
2567
2985
  var getHookSource = () => {
2568
2986
  const thisFile = fileURLToPath2(import.meta.url);
2569
- const srcHooksDir = join2(dirname3(thisFile), "..", "src", "hooks");
2570
- const distHooksDir = join2(dirname3(thisFile), "hooks");
2987
+ const srcHooksDir = join3(dirname3(thisFile), "..", "src", "hooks");
2988
+ const distHooksDir = join3(dirname3(thisFile), "hooks");
2571
2989
  for (const dir of [distHooksDir, srcHooksDir]) {
2572
- const path = join2(dir, HOOK_FILENAME);
2990
+ const path = join3(dir, HOOK_FILENAME);
2573
2991
  if (existsSync(path)) return path;
2574
2992
  }
2575
- const npmGlobalPath = join2(dirname3(thisFile), "..", "hooks", HOOK_FILENAME);
2993
+ const npmGlobalPath = join3(dirname3(thisFile), "..", "hooks", HOOK_FILENAME);
2576
2994
  if (existsSync(npmGlobalPath)) return npmGlobalPath;
2577
2995
  throw new Error(`cannot find ${HOOK_FILENAME} \u2014 is agenttop installed correctly?`);
2578
2996
  };
2579
2997
  var getHookTarget = () => {
2580
- const claudeHooksDir = join2(homedir(), ".claude", "hooks");
2998
+ const claudeHooksDir = join3(homedir(), ".claude", "hooks");
2581
2999
  mkdirSync2(claudeHooksDir, { recursive: true });
2582
- return join2(claudeHooksDir, HOOK_FILENAME);
3000
+ return join3(claudeHooksDir, HOOK_FILENAME);
2583
3001
  };
2584
3002
  var readSettings = () => {
2585
3003
  if (!existsSync(SETTINGS_PATH)) {
2586
3004
  return {};
2587
3005
  }
2588
3006
  try {
2589
- return JSON.parse(readFileSync2(SETTINGS_PATH, "utf-8"));
3007
+ return JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
2590
3008
  } catch {
2591
3009
  return {};
2592
3010
  }
@@ -2653,20 +3071,20 @@ var uninstallHooks = () => {
2653
3071
  };
2654
3072
 
2655
3073
  // src/install-mcp.ts
2656
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
2657
- import { join as join3 } from "path";
3074
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
3075
+ import { join as join4 } from "path";
2658
3076
  import { homedir as homedir2 } from "os";
2659
3077
  var installMcpConfig = () => {
2660
- const settingsPath = join3(homedir2(), ".claude", "settings.json");
3078
+ const settingsPath = join4(homedir2(), ".claude", "settings.json");
2661
3079
  let settings = {};
2662
3080
  try {
2663
- settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
3081
+ settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
2664
3082
  } catch {
2665
3083
  }
2666
3084
  const mcpServers = settings.mcpServers ?? {};
2667
3085
  mcpServers.agenttop = { command: "agenttop", args: ["--mcp"] };
2668
3086
  settings.mcpServers = mcpServers;
2669
- mkdirSync3(join3(homedir2(), ".claude"), { recursive: true });
3087
+ mkdirSync3(join4(homedir2(), ".claude"), { recursive: true });
2670
3088
  writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
2671
3089
  process.stdout.write("agenttop MCP server registered in Claude Code settings\n");
2672
3090
  process.stdout.write(` settings: ${settingsPath}
@@ -2867,7 +3285,19 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
2867
3285
  null
2868
3286
  );
2869
3287
  const [archivedIds, setArchivedIds] = useState15(() => new Set(Object.keys(getArchived())));
3288
+ const [sidebarWidth, setSidebarWidth] = useState15(() => initialConfig.sidebarWidth ?? 30);
2870
3289
  const refreshArchived = useCallback6(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
3290
+ const persistSidebarWidth = useCallback6((v) => {
3291
+ setSidebarWidth((prev) => {
3292
+ const next = typeof v === "function" ? v(prev) : v;
3293
+ if (next !== prev) {
3294
+ const cfg = loadConfig();
3295
+ cfg.sidebarWidth = next;
3296
+ saveConfig(cfg);
3297
+ }
3298
+ return next;
3299
+ });
3300
+ }, []);
2871
3301
  const updateInfo = useUpdateChecker(
2872
3302
  options.noUpdates,
2873
3303
  setup.liveConfig.updates.checkOnLaunch,
@@ -2877,7 +3307,7 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
2877
3307
  applyTheme(resolveTheme(setup.liveConfig.theme, setup.liveConfig.customThemes));
2878
3308
  }, [setup.liveConfig.theme, setup.liveConfig.customThemes]);
2879
3309
  const {
2880
- sessions,
3310
+ sessions: sessions2,
2881
3311
  visibleItems,
2882
3312
  selectedSession,
2883
3313
  selectedGroup,
@@ -3000,6 +3430,7 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3000
3430
  setActivityScroll,
3001
3431
  setConfirmAction,
3002
3432
  setUpdateStatus,
3433
+ setSidebarWidth: persistSidebarWidth,
3003
3434
  nicknameInput,
3004
3435
  filterInput,
3005
3436
  onNickname: (id) => {
@@ -3125,7 +3556,7 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3125
3556
  }
3126
3557
  );
3127
3558
  return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", height: termHeight, children: [
3128
- /* @__PURE__ */ jsx15(StatusBar, { sessionCount: sessions.length, alertCount: alerts.length, version, updateInfo }),
3559
+ /* @__PURE__ */ jsx15(StatusBar, { sessionCount: sessions2.length, alertCount: alerts.length, version, updateInfo }),
3129
3560
  /* @__PURE__ */ jsxs15(Box15, { flexGrow: 1, height: mainHeight, children: [
3130
3561
  /* @__PURE__ */ jsx15(
3131
3562
  SessionList,
@@ -3136,7 +3567,8 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3136
3567
  height: mainHeight,
3137
3568
  filter: filter || void 0,
3138
3569
  viewingArchive,
3139
- totalSessions: sessions.length
3570
+ totalSessions: sessions2.length,
3571
+ sidebarWidth
3140
3572
  }
3141
3573
  ),
3142
3574
  rightPanel
@@ -3176,11 +3608,11 @@ var formatTokens3 = (n) => {
3176
3608
  };
3177
3609
  var runStreamMode = (options, isJson) => {
3178
3610
  const engine = options.noSecurity ? null : new SecurityEngine(options.alertLevel);
3179
- const sessions = discoverSessions(options.allUsers);
3611
+ const sessions2 = discoverSessions(options.allUsers);
3180
3612
  if (isJson) {
3181
- write(JSON.stringify({ type: "sessions", data: sessions }));
3613
+ write(JSON.stringify({ type: "sessions", data: sessions2 }));
3182
3614
  } else {
3183
- for (const s of sessions) {
3615
+ for (const s of sessions2) {
3184
3616
  write(
3185
3617
  `SESSION ${s.slug} | ${s.model} | ${s.cwd} | CPU ${s.cpu}% | ${s.memMB}MB | ${formatTokens3(s.usage.inputTokens)} in / ${formatTokens3(s.usage.outputTokens)} out`
3186
3618
  );
@@ -3236,8 +3668,8 @@ process.title = "agenttop";
3236
3668
  var getVersion = () => {
3237
3669
  try {
3238
3670
  const thisFile = fileURLToPath3(import.meta.url);
3239
- const pkgPath = join4(dirname4(thisFile), "..", "package.json");
3240
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
3671
+ const pkgPath = join5(dirname4(thisFile), "..", "package.json");
3672
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
3241
3673
  return pkg.version || "0.0.0";
3242
3674
  } catch {
3243
3675
  return "0.0.0";