ahp-inspector 1.4.1 → 1.4.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.
package/dist/index.js CHANGED
@@ -2,48 +2,156 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { existsSync as existsSync2, readFileSync, statSync as statSync3 } from "fs";
5
- import { dirname as dirname3, join as join7, resolve as resolvePath } from "path";
5
+ import { dirname as dirname5, join as join7, resolve as resolvePath } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
 
8
8
  // ../host-node/src/discovery.ts
9
9
  import { createHash } from "crypto";
10
- import { readdir, stat } from "fs/promises";
11
10
  import { homedir } from "os";
12
- import { basename, join, sep } from "path";
11
+ import { basename as basename2, dirname as dirname2, isAbsolute, join as join2, relative, sep } from "path";
12
+
13
+ // ../host-node/src/bounded-log-discovery.ts
14
+ import { opendir, stat } from "fs/promises";
15
+ import { basename, dirname, join } from "path";
16
+ async function scanConfiguredRoots(options) {
17
+ const now = options.now ?? Date.now;
18
+ const maxDepthBelowLaunch = options.maxDepthBelowLaunch ?? 3;
19
+ const rootResults = [];
20
+ for (const [rootIndex, root] of options.roots.entries()) {
21
+ const state = {
22
+ stats: 0,
23
+ immediateEntries: 0,
24
+ truncated: false,
25
+ startedAt: now(),
26
+ files: []
27
+ };
28
+ const launchEntries = [];
29
+ let rootDirectory;
30
+ try {
31
+ rootDirectory = await opendir(root.dir);
32
+ while (true) {
33
+ if (rootScanOverBudget(state, options, now, true)) break;
34
+ const entry = await rootDirectory.read();
35
+ if (!entry) break;
36
+ state.immediateEntries++;
37
+ const absPath = join(root.dir, entry.name);
38
+ const entryStat = await boundedStat(absPath, state, options, now);
39
+ if (!entryStat) continue;
40
+ if (entryStat.isDirectory()) {
41
+ launchEntries.push({ absPath, mtimeMs: entryStat.mtimeMs });
42
+ } else if (entryStat.isFile() && options.matchesFilename(entry.name)) {
43
+ state.files.push(toScannedFile(absPath, root.dir, entryStat.mtimeMs, entryStat.size));
44
+ }
45
+ }
46
+ } catch {
47
+ } finally {
48
+ await rootDirectory?.close().catch(() => {
49
+ });
50
+ }
51
+ launchEntries.sort((left, right) => right.mtimeMs - left.mtimeMs);
52
+ if (launchEntries.length > options.topLaunchDirs) state.truncated = true;
53
+ for (const launch of launchEntries.slice(0, options.topLaunchDirs)) {
54
+ if (rootScanOverBudget(state, options, now, false)) break;
55
+ await walkLaunchDirectory(launch.absPath, launch.absPath, maxDepthBelowLaunch, state, options, now);
56
+ }
57
+ rootResults.push({
58
+ rootIndex,
59
+ files: state.files,
60
+ truncated: state.truncated,
61
+ statsAttempted: state.stats,
62
+ immediateEntriesExamined: state.immediateEntries
63
+ });
64
+ }
65
+ return { roots: rootResults, truncated: rootResults.some((root) => root.truncated) };
66
+ }
67
+ async function walkLaunchDirectory(absDir, launchDir, depthLeft, state, options, now) {
68
+ if (depthLeft < 0 || rootScanOverBudget(state, options, now, false)) return;
69
+ let directory;
70
+ try {
71
+ directory = await opendir(absDir);
72
+ while (true) {
73
+ if (rootScanOverBudget(state, options, now, false)) return;
74
+ const entry = await directory.read();
75
+ if (!entry) return;
76
+ const absPath = join(absDir, entry.name);
77
+ const entryStat = await boundedStat(absPath, state, options, now);
78
+ if (!entryStat) continue;
79
+ if (entryStat.isDirectory()) {
80
+ if (depthLeft === 0) {
81
+ state.truncated = true;
82
+ } else {
83
+ await walkLaunchDirectory(absPath, launchDir, depthLeft - 1, state, options, now);
84
+ }
85
+ } else if (entryStat.isFile() && options.matchesFilename(entry.name)) {
86
+ state.files.push(toScannedFile(absPath, launchDir, entryStat.mtimeMs, entryStat.size));
87
+ }
88
+ }
89
+ } catch {
90
+ } finally {
91
+ await directory?.close().catch(() => {
92
+ });
93
+ }
94
+ }
95
+ async function boundedStat(absPath, state, options, now) {
96
+ if (rootScanOverBudget(state, options, now, false)) return void 0;
97
+ state.stats++;
98
+ try {
99
+ return await stat(absPath);
100
+ } catch {
101
+ return void 0;
102
+ }
103
+ }
104
+ function rootScanOverBudget(state, options, now, includeImmediateEntries) {
105
+ const overBudget = state.stats >= options.maxStats || now() - state.startedAt >= options.timeBudgetMs || includeImmediateEntries && state.immediateEntries >= options.maxImmediateEntries;
106
+ if (overBudget) state.truncated = true;
107
+ return overBudget;
108
+ }
109
+ function toScannedFile(absPath, launchDir, mtimeMs, sizeBytes) {
110
+ return {
111
+ absPath,
112
+ name: basename(absPath),
113
+ parentDir: dirname(absPath),
114
+ launchDir,
115
+ mtimeMs,
116
+ sizeBytes
117
+ };
118
+ }
119
+
120
+ // ../host-node/src/discovery.ts
13
121
  var DEFAULT_TIME_BUDGET_MS = 1500;
14
122
  var DEFAULT_MAX_STATS = 5e3;
123
+ var DEFAULT_MAX_IMMEDIATE_ENTRIES = 5e3;
15
124
  var DEFAULT_TOP_LAUNCH_DIRS = 10;
16
- var MAX_LAUNCH_LIST = 50;
17
125
  var MAX_RESULTS = 200;
18
126
  var MAX_DEPTH_BELOW_LAUNCH = 3;
19
127
  function defaultRoots() {
20
128
  const home = homedir();
21
129
  const platform = process.platform;
22
130
  const ossDevRoots = [
23
- { origin: "vscode-oss-dev", dir: join(home, ".vscode-oss-dev", "logs") },
24
- { origin: "vscode-oss-dev", dir: join(home, ".vscode-oss-agents-dev", "logs") }
131
+ { origin: "vscode-oss-dev", dir: join2(home, ".vscode-oss-dev", "logs") },
132
+ { origin: "vscode-oss-dev", dir: join2(home, ".vscode-oss-agents-dev", "logs") }
25
133
  ];
26
134
  if (platform === "darwin") {
27
135
  return [
28
- { origin: "vscode", dir: join(home, "Library", "Application Support", "Code", "logs") },
136
+ { origin: "vscode", dir: join2(home, "Library", "Application Support", "Code", "logs") },
29
137
  {
30
138
  origin: "vscode-insiders",
31
- dir: join(home, "Library", "Application Support", "Code - Insiders", "logs")
139
+ dir: join2(home, "Library", "Application Support", "Code - Insiders", "logs")
32
140
  },
33
141
  ...ossDevRoots
34
142
  ];
35
143
  }
36
144
  if (platform === "win32") {
37
- const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
145
+ const appData = process.env.APPDATA ?? join2(home, "AppData", "Roaming");
38
146
  return [
39
- { origin: "vscode", dir: join(appData, "Code", "logs") },
40
- { origin: "vscode-insiders", dir: join(appData, "Code - Insiders", "logs") },
147
+ { origin: "vscode", dir: join2(appData, "Code", "logs") },
148
+ { origin: "vscode-insiders", dir: join2(appData, "Code - Insiders", "logs") },
41
149
  ...ossDevRoots
42
150
  ];
43
151
  }
44
152
  return [
45
- { origin: "vscode", dir: join(home, ".config", "Code", "logs") },
46
- { origin: "vscode-insiders", dir: join(home, ".config", "Code - Insiders", "logs") },
153
+ { origin: "vscode", dir: join2(home, ".config", "Code", "logs") },
154
+ { origin: "vscode-insiders", dir: join2(home, ".config", "Code - Insiders", "logs") },
47
155
  ...ossDevRoots
48
156
  ];
49
157
  }
@@ -70,132 +178,97 @@ function makeId(absPath) {
70
178
  return createHash("sha256").update(absPath).digest("hex").slice(0, 32);
71
179
  }
72
180
  function makeContextLabel(absPath, launchDir) {
73
- const idx = absPath.indexOf(launchDir);
74
- if (idx < 0) return "";
75
- const tail = absPath.slice(idx).split(sep).filter(Boolean);
76
- tail.pop();
77
- return tail.join(" / ");
181
+ const relativeParent = relative(launchDir, dirname2(absPath));
182
+ if (relativeParent === "" || relativeParent === ".") return "";
183
+ if (isAbsolute(relativeParent) || relativeParent === ".." || relativeParent.startsWith(`..${sep}`)) return "";
184
+ return [basename2(launchDir), ...relativeParent.split(sep).filter(Boolean)].join(" / ");
78
185
  }
79
186
  var idToPath = /* @__PURE__ */ new Map();
187
+ var discoveryGeneration = 0;
80
188
  function resolveCandidateId(id) {
81
189
  return idToPath.get(id) ?? null;
82
190
  }
83
191
  async function discoverVsCodeLogs(opts = {}) {
84
192
  const roots = opts.roots ?? defaultRoots();
85
- const timeBudget = opts.timeBudgetMs ?? DEFAULT_TIME_BUDGET_MS;
86
- const maxStats = opts.maxStats ?? DEFAULT_MAX_STATS;
87
- const topLaunch = opts.topLaunchDirs ?? DEFAULT_TOP_LAUNCH_DIRS;
88
- const now = opts.now ?? Date.now;
89
- const startedAt = now();
90
- let stats = 0;
91
- let truncated = false;
92
- const collected = [];
193
+ const generation = ++discoveryGeneration;
93
194
  idToPath.clear();
94
- const overBudget = () => stats >= maxStats || now() - startedAt >= timeBudget;
95
- for (const root of roots) {
96
- if (overBudget()) {
97
- truncated = true;
98
- break;
99
- }
100
- const launchEntries = [];
101
- try {
102
- const names = await readdir(root.dir);
103
- const limited = names.slice(0, MAX_LAUNCH_LIST);
104
- for (const name of limited) {
105
- if (overBudget()) {
106
- truncated = true;
107
- break;
108
- }
109
- try {
110
- const st = await stat(join(root.dir, name));
111
- stats++;
112
- if (st.isDirectory()) launchEntries.push({ name, mtimeMs: st.mtimeMs });
113
- } catch {
114
- }
115
- }
116
- } catch {
117
- continue;
118
- }
119
- launchEntries.sort((a, b) => b.mtimeMs - a.mtimeMs);
120
- const topEntries = launchEntries.slice(0, topLaunch);
121
- for (const launch of topEntries) {
122
- if (overBudget()) {
123
- truncated = true;
124
- break;
125
- }
126
- const launchDir = join(root.dir, launch.name);
127
- await walkBounded(
128
- launchDir,
129
- launchDir,
130
- MAX_DEPTH_BELOW_LAUNCH,
131
- root.origin,
132
- collected,
133
- () => {
134
- stats++;
135
- return overBudget();
136
- }
137
- );
138
- }
139
- }
140
- const out = collected.map((c) => c).sort((a, b) => {
141
- const order = { high: 0, medium: 1, low: 2 };
142
- const av = order[a.confidence];
143
- const bv = order[b.confidence];
144
- if (av !== bv) return av - bv;
145
- return b.mtimeMs - a.mtimeMs;
146
- }).slice(0, MAX_RESULTS);
147
- return { candidates: out, truncated };
148
- async function walkBounded(absDir, launchDir, depthLeft, origin, sink, tickAndCheck) {
149
- if (depthLeft < 0) return;
150
- let names;
151
- try {
152
- names = await readdir(absDir);
153
- } catch {
154
- return;
155
- }
156
- for (const name of names) {
157
- if (tickAndCheck()) {
158
- truncated = true;
159
- return;
160
- }
161
- const abs = join(absDir, name);
162
- let st;
163
- try {
164
- st = await stat(abs);
165
- } catch {
166
- continue;
167
- }
168
- if (st.isDirectory()) {
169
- await walkBounded(abs, launchDir, depthLeft - 1, origin, sink, tickAndCheck);
170
- continue;
171
- }
172
- if (!st.isFile()) continue;
173
- const ok = FILENAME_RE_AHP_JSONL.test(name) || FILENAME_RE_AHP_NAMED_JSONL.test(name);
174
- if (!ok) continue;
175
- const sc = score(name, st.mtimeMs, st.size, absDir);
176
- const id = makeId(abs);
177
- idToPath.set(id, abs);
178
- const confidence = tier(sc);
179
- sink.push({
180
- id,
181
- label: basename(abs),
182
- mtimeMs: st.mtimeMs,
183
- sizeBytes: st.size,
184
- origin,
185
- confidence,
186
- contextLabel: makeContextLabel(abs, launchDir)
187
- });
188
- if (sink.length >= MAX_RESULTS * 4) {
189
- truncated = true;
190
- return;
191
- }
195
+ const scan = await scanConfiguredRoots({
196
+ roots,
197
+ matchesFilename: matchesAhpFilename,
198
+ timeBudgetMs: opts.timeBudgetMs ?? DEFAULT_TIME_BUDGET_MS,
199
+ maxStats: opts.maxStats ?? DEFAULT_MAX_STATS,
200
+ maxImmediateEntries: opts.maxImmediateEntries ?? DEFAULT_MAX_IMMEDIATE_ENTRIES,
201
+ topLaunchDirs: opts.topLaunchDirs ?? DEFAULT_TOP_LAUNCH_DIRS,
202
+ maxDepthBelowLaunch: MAX_DEPTH_BELOW_LAUNCH,
203
+ now: opts.now ?? Date.now
204
+ });
205
+ const populatedRoots = scan.roots.map((scannedRoot) => {
206
+ const root = roots[scannedRoot.rootIndex];
207
+ if (!root) return [];
208
+ return scannedRoot.files.map((file) => toPickerCandidate(file, root.origin)).sort(comparePickerCandidates);
209
+ }).filter((candidates) => candidates.length > 0);
210
+ const effectiveResultCap = Math.max(MAX_RESULTS, populatedRoots.length);
211
+ const retained = [];
212
+ const retainedPaths = /* @__PURE__ */ new Set();
213
+ for (const candidates of populatedRoots) {
214
+ const candidate = candidates[0];
215
+ if (!candidate || retainedPaths.has(candidate.absPath)) continue;
216
+ retained.push(candidate);
217
+ retainedPaths.add(candidate.absPath);
218
+ }
219
+ const remainingCapacity = effectiveResultCap - retained.length;
220
+ const extraQuota = populatedRoots.length > 0 ? Math.floor(remainingCapacity / populatedRoots.length) : 0;
221
+ for (const candidates of populatedRoots) {
222
+ for (const candidate of candidates.slice(1, 1 + extraQuota)) {
223
+ if (retainedPaths.has(candidate.absPath)) continue;
224
+ retained.push(candidate);
225
+ retainedPaths.add(candidate.absPath);
226
+ }
227
+ }
228
+ const fillCandidates = populatedRoots.flat().filter((candidate) => !retainedPaths.has(candidate.absPath)).sort(comparePickerCandidates);
229
+ for (const candidate of fillCandidates) {
230
+ if (retained.length >= effectiveResultCap) break;
231
+ if (retainedPaths.has(candidate.absPath)) continue;
232
+ retained.push(candidate);
233
+ retainedPaths.add(candidate.absPath);
234
+ }
235
+ retained.sort(comparePickerCandidates);
236
+ if (generation === discoveryGeneration) {
237
+ idToPath.clear();
238
+ for (const candidate of retained) idToPath.set(candidate.safe.id, candidate.absPath);
239
+ }
240
+ const totalCandidateCount = new Set(populatedRoots.flat().map((candidate) => candidate.absPath)).size;
241
+ return {
242
+ candidates: retained.map((candidate) => candidate.safe),
243
+ truncated: scan.truncated || retained.length < totalCandidateCount
244
+ };
245
+ }
246
+ function toPickerCandidate(file, origin) {
247
+ const confidence = tier(score(file.name, file.mtimeMs, file.sizeBytes, file.parentDir));
248
+ return {
249
+ absPath: file.absPath,
250
+ safe: {
251
+ id: makeId(file.absPath),
252
+ label: file.name,
253
+ mtimeMs: file.mtimeMs,
254
+ sizeBytes: file.sizeBytes,
255
+ origin,
256
+ confidence,
257
+ contextLabel: makeContextLabel(file.absPath, file.launchDir)
192
258
  }
193
- }
259
+ };
260
+ }
261
+ function comparePickerCandidates(left, right) {
262
+ const order = { high: 0, medium: 1, low: 2 };
263
+ const confidenceDifference = order[left.safe.confidence] - order[right.safe.confidence];
264
+ return confidenceDifference || right.safe.mtimeMs - left.safe.mtimeMs;
265
+ }
266
+ function matchesAhpFilename(name) {
267
+ return FILENAME_RE_AHP_JSONL.test(name) || FILENAME_RE_AHP_NAMED_JSONL.test(name);
194
268
  }
195
269
 
196
270
  // ../host-node/src/find-latest-ahp-log.ts
197
- import { open as fsOpen, readdir as readdir2, stat as stat2 } from "fs/promises";
198
- import { join as join2 } from "path";
271
+ import { open as fsOpen } from "fs/promises";
199
272
 
200
273
  // ../parser/src/jsonl.ts
201
274
  var MAX_BUF_BYTES = 16 * 1024 * 1024;
@@ -391,6 +464,13 @@ function objectChild(parent, key) {
391
464
  return typeof child === "object" && child !== null ? child : null;
392
465
  }
393
466
  function candidateObjects(p) {
467
+ if (Array.isArray(p)) {
468
+ const items = [];
469
+ for (const item of p) {
470
+ if (typeof item === "object" && item !== null) items.push(item);
471
+ }
472
+ return items;
473
+ }
394
474
  const out = [p];
395
475
  const action = objectChild(p, "action");
396
476
  if (action) out.push(action);
@@ -405,7 +485,25 @@ function sessionFromObject(p) {
405
485
  const uri = session.uri;
406
486
  if (typeof uri === "string") return uri;
407
487
  }
408
- return asString(p.sessionId);
488
+ const sessionId = asString(p.sessionId);
489
+ if (sessionId) return sessionId;
490
+ const channel = asString(p.channel);
491
+ if (channel) return channel;
492
+ const terminal = asString(p.terminal);
493
+ if (terminal) return terminal;
494
+ const resource = asString(p.resource);
495
+ if (resource) return resource;
496
+ const external = asString(p.external);
497
+ if (external) return external;
498
+ const scheme = asString(p.scheme);
499
+ const path = asString(p.path);
500
+ if (scheme && path) return `${scheme}:${path}`;
501
+ const summary = p.summary;
502
+ if (typeof summary === "object" && summary !== null) {
503
+ const summaryResource = asString(summary.resource);
504
+ if (summaryResource) return summaryResource;
505
+ }
506
+ return null;
409
507
  }
410
508
  function turnFromObject(p) {
411
509
  const fromTop = asString(p.turnId);
@@ -550,55 +648,29 @@ function extractWireMeta(raw2) {
550
648
  // ../host-node/src/find-latest-ahp-log.ts
551
649
  var DEFAULT_TIME_BUDGET_MS2 = 1500;
552
650
  var DEFAULT_MAX_STATS2 = 5e3;
553
- var MAX_DEPTH_BELOW_ROOT = 5;
554
- var MAX_PROBE_CANDIDATES = 10;
651
+ var DEFAULT_MAX_IMMEDIATE_ENTRIES2 = 5e3;
652
+ var DEFAULT_TOP_LAUNCH_DIRS2 = 10;
653
+ var MAX_DEPTH_BELOW_LAUNCH2 = 4;
555
654
  var PROBE_READ_BYTES = 64 * 1024;
556
655
  async function findLatestAhpLog(opts = {}) {
557
656
  const roots = opts.rootsOverride ?? defaultRoots();
558
- const startedAt = Date.now();
559
- let stats = 0;
560
- const overBudget = () => stats >= DEFAULT_MAX_STATS2 || Date.now() - startedAt >= DEFAULT_TIME_BUDGET_MS2;
561
- const collected = [];
562
- for (const root of roots) {
563
- if (overBudget()) break;
564
- await walk(root.dir, MAX_DEPTH_BELOW_ROOT, collected, () => {
565
- stats++;
566
- return overBudget();
567
- });
568
- }
569
- const ranked = collected.filter((c) => c.sizeBytes > 0).sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, MAX_PROBE_CANDIDATES);
570
- for (const c of ranked) {
571
- if (await probeAhpShape(c.absPath)) return c.absPath;
657
+ const scan = await scanConfiguredRoots({
658
+ roots,
659
+ matchesFilename: matchesAhpFilename2,
660
+ timeBudgetMs: DEFAULT_TIME_BUDGET_MS2,
661
+ maxStats: DEFAULT_MAX_STATS2,
662
+ maxImmediateEntries: DEFAULT_MAX_IMMEDIATE_ENTRIES2,
663
+ topLaunchDirs: DEFAULT_TOP_LAUNCH_DIRS2,
664
+ maxDepthBelowLaunch: MAX_DEPTH_BELOW_LAUNCH2
665
+ });
666
+ const ranked = scan.roots.flatMap((root) => root.files).filter((candidate) => candidate.sizeBytes > 0).sort((left, right) => right.mtimeMs - left.mtimeMs);
667
+ for (const candidate of ranked) {
668
+ if (await probeAhpShape(candidate.absPath)) return candidate.absPath;
572
669
  }
573
670
  return null;
574
671
  }
575
- async function walk(absDir, depthLeft, sink, tickAndCheck) {
576
- if (depthLeft < 0) return;
577
- let names;
578
- try {
579
- names = await readdir2(absDir);
580
- } catch {
581
- return;
582
- }
583
- for (const name of names) {
584
- if (tickAndCheck()) return;
585
- const abs = join2(absDir, name);
586
- let st;
587
- try {
588
- st = await stat2(abs);
589
- } catch {
590
- continue;
591
- }
592
- if (st.isDirectory()) {
593
- await walk(abs, depthLeft - 1, sink, tickAndCheck);
594
- continue;
595
- }
596
- if (!st.isFile()) continue;
597
- if (!(FILENAME_RE_AHP_JSONL.test(name) || FILENAME_RE_AHP_NAMED_JSONL.test(name))) {
598
- continue;
599
- }
600
- sink.push({ absPath: abs, mtimeMs: st.mtimeMs, sizeBytes: st.size });
601
- }
672
+ function matchesAhpFilename2(name) {
673
+ return FILENAME_RE_AHP_JSONL.test(name) || FILENAME_RE_AHP_NAMED_JSONL.test(name);
602
674
  }
603
675
  async function probeAhpShape(absPath) {
604
676
  let fh;
@@ -637,7 +709,7 @@ async function probeAhpShape(absPath) {
637
709
 
638
710
  // ../host-node/src/host-adapter.ts
639
711
  import { accessSync, constants, statSync } from "fs";
640
- import { basename as basename4, resolve as pathResolve } from "path";
712
+ import { basename as basename5, resolve as pathResolve } from "path";
641
713
 
642
714
  // ../host-node/src/tail-reader.ts
643
715
  import { createReadStream } from "fs";
@@ -646,11 +718,11 @@ import { stat as fsStat } from "fs/promises";
646
718
  // ../../node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
647
719
  import { EventEmitter } from "events";
648
720
  import { stat as statcb, Stats } from "fs";
649
- import { readdir as readdir4, stat as stat5 } from "fs/promises";
721
+ import { readdir as readdir2, stat as stat4 } from "fs/promises";
650
722
  import * as sp2 from "path";
651
723
 
652
724
  // ../../node_modules/.pnpm/readdirp@5.0.0/node_modules/readdirp/index.js
653
- import { lstat, readdir as readdir3, realpath, stat as stat3 } from "fs/promises";
725
+ import { lstat, readdir, realpath, stat as stat2 } from "fs/promises";
654
726
  import { join as pjoin, relative as prelative, resolve as presolve, sep as psep } from "path";
655
727
  import { Readable } from "stream";
656
728
  var EntryTypes = {
@@ -731,7 +803,7 @@ var ReaddirpStream = class extends Readable {
731
803
  const { root, type } = opts;
732
804
  this._fileFilter = normalizeFilter(opts.fileFilter);
733
805
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
734
- const statMethod = opts.lstat ? lstat : stat3;
806
+ const statMethod = opts.lstat ? lstat : stat2;
735
807
  if (wantBigintFsStats) {
736
808
  this._stat = (path) => statMethod(path, { bigint: true });
737
809
  } else {
@@ -802,7 +874,7 @@ var ReaddirpStream = class extends Readable {
802
874
  async _exploreDir(path, depth) {
803
875
  let files;
804
876
  try {
805
- files = await readdir3(path, this._rdOptions);
877
+ files = await readdir(path, this._rdOptions);
806
878
  } catch (error) {
807
879
  this._onError(error);
808
880
  }
@@ -810,10 +882,10 @@ var ReaddirpStream = class extends Readable {
810
882
  }
811
883
  async _formatEntry(dirent, path) {
812
884
  let entry;
813
- const basename6 = this._isDirent ? dirent.name : dirent;
885
+ const basename7 = this._isDirent ? dirent.name : dirent;
814
886
  try {
815
- const fullPath = presolve(pjoin(path, basename6));
816
- entry = { path: prelative(this._root, fullPath), fullPath, basename: basename6 };
887
+ const fullPath = presolve(pjoin(path, basename7));
888
+ entry = { path: prelative(this._root, fullPath), fullPath, basename: basename7 };
817
889
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
818
890
  } catch (err) {
819
891
  this._onError(err);
@@ -884,7 +956,7 @@ function readdirp(root, options = {}) {
884
956
 
885
957
  // ../../node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/handler.js
886
958
  import { watch as fs_watch, unwatchFile, watchFile } from "fs";
887
- import { realpath as fsrealpath, lstat as lstat2, open, stat as stat4 } from "fs/promises";
959
+ import { realpath as fsrealpath, lstat as lstat2, open, stat as stat3 } from "fs/promises";
888
960
  import { type as osType } from "os";
889
961
  import * as sp from "path";
890
962
  var STR_DATA = "data";
@@ -911,7 +983,7 @@ var EVENTS = {
911
983
  };
912
984
  var EV = EVENTS;
913
985
  var THROTTLE_MODE_WATCH = "watch";
914
- var statMethods = { lstat: lstat2, stat: stat4 };
986
+ var statMethods = { lstat: lstat2, stat: stat3 };
915
987
  var KEY_LISTENERS = "listeners";
916
988
  var KEY_ERR = "errHandlers";
917
989
  var KEY_RAW = "rawEmitters";
@@ -1354,9 +1426,9 @@ var NodeFsHandler = class {
1354
1426
  _watchWithNodeFs(path, listener) {
1355
1427
  const opts = this.fsw.options;
1356
1428
  const directory = sp.dirname(path);
1357
- const basename6 = sp.basename(path);
1429
+ const basename7 = sp.basename(path);
1358
1430
  const parent = this.fsw._getWatchedDir(directory);
1359
- parent.add(basename6);
1431
+ parent.add(basename7);
1360
1432
  const absolutePath = sp.resolve(path);
1361
1433
  const options = {
1362
1434
  persistent: opts.persistent
@@ -1366,7 +1438,7 @@ var NodeFsHandler = class {
1366
1438
  let closer;
1367
1439
  if (opts.usePolling) {
1368
1440
  const enableBin = opts.interval !== opts.binaryInterval;
1369
- options.interval = enableBin && isBinaryPath(basename6) ? opts.binaryInterval : opts.interval;
1441
+ options.interval = enableBin && isBinaryPath(basename7) ? opts.binaryInterval : opts.interval;
1370
1442
  closer = setFsWatchFileListener(path, absolutePath, options, {
1371
1443
  listener,
1372
1444
  rawEmitter: this.fsw._emitRaw
@@ -1388,18 +1460,18 @@ var NodeFsHandler = class {
1388
1460
  if (this.fsw.closed) {
1389
1461
  return;
1390
1462
  }
1391
- const dirname4 = sp.dirname(file);
1392
- const basename6 = sp.basename(file);
1393
- const parent = this.fsw._getWatchedDir(dirname4);
1463
+ const dirname6 = sp.dirname(file);
1464
+ const basename7 = sp.basename(file);
1465
+ const parent = this.fsw._getWatchedDir(dirname6);
1394
1466
  let prevStats = stats;
1395
- if (parent.has(basename6))
1467
+ if (parent.has(basename7))
1396
1468
  return;
1397
1469
  const listener = async (path, newStats) => {
1398
1470
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
1399
1471
  return;
1400
1472
  if (!newStats || newStats.mtimeMs === 0) {
1401
1473
  try {
1402
- const newStats2 = await stat4(file);
1474
+ const newStats2 = await stat3(file);
1403
1475
  if (this.fsw.closed)
1404
1476
  return;
1405
1477
  const at = newStats2.atimeMs;
@@ -1417,9 +1489,9 @@ var NodeFsHandler = class {
1417
1489
  prevStats = newStats2;
1418
1490
  }
1419
1491
  } catch (error) {
1420
- this.fsw._remove(dirname4, basename6);
1492
+ this.fsw._remove(dirname6, basename7);
1421
1493
  }
1422
- } else if (parent.has(basename6)) {
1494
+ } else if (parent.has(basename7)) {
1423
1495
  const at = newStats.atimeMs;
1424
1496
  const mt = newStats.mtimeMs;
1425
1497
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -1667,11 +1739,11 @@ function createPattern(matcher) {
1667
1739
  if (matcher.path === string)
1668
1740
  return true;
1669
1741
  if (matcher.recursive) {
1670
- const relative3 = sp2.relative(matcher.path, string);
1671
- if (!relative3) {
1742
+ const relative4 = sp2.relative(matcher.path, string);
1743
+ if (!relative4) {
1672
1744
  return false;
1673
1745
  }
1674
- return !relative3.startsWith("..") && !sp2.isAbsolute(relative3);
1746
+ return !relative4.startsWith("..") && !sp2.isAbsolute(relative4);
1675
1747
  }
1676
1748
  return false;
1677
1749
  };
@@ -1773,7 +1845,7 @@ var DirEntry = class {
1773
1845
  return;
1774
1846
  const dir = this.path;
1775
1847
  try {
1776
- await readdir4(dir);
1848
+ await readdir2(dir);
1777
1849
  } catch (err) {
1778
1850
  if (this._removeWatcher) {
1779
1851
  this._removeWatcher(sp2.dirname(dir), sp2.basename(dir));
@@ -2123,7 +2195,7 @@ var FSWatcher = class extends EventEmitter {
2123
2195
  const fullPath = opts.cwd ? sp2.join(opts.cwd, path) : path;
2124
2196
  let stats2;
2125
2197
  try {
2126
- stats2 = await stat5(fullPath);
2198
+ stats2 = await stat4(fullPath);
2127
2199
  } catch (err) {
2128
2200
  }
2129
2201
  if (!stats2 || this.closed)
@@ -2250,8 +2322,8 @@ var FSWatcher = class extends EventEmitter {
2250
2322
  }
2251
2323
  return this._userIgnored(path, stats);
2252
2324
  }
2253
- _isntIgnored(path, stat6) {
2254
- return !this._isIgnored(path, stat6);
2325
+ _isntIgnored(path, stat5) {
2326
+ return !this._isIgnored(path, stat5);
2255
2327
  }
2256
2328
  /**
2257
2329
  * Provides a set of common helpers and properties relating to symlink handling.
@@ -2549,21 +2621,21 @@ var NodeHostAdapter = class {
2549
2621
  }
2550
2622
  async openLog(path) {
2551
2623
  const resolved = pathResolve(process.cwd(), path);
2552
- let stat6;
2624
+ let stat5;
2553
2625
  try {
2554
- stat6 = statSync(resolved);
2626
+ stat5 = statSync(resolved);
2555
2627
  } catch {
2556
- throw new Error(`openLog: cannot stat '${basename4(resolved)}'`);
2628
+ throw new Error(`openLog: cannot stat '${basename5(resolved)}'`);
2557
2629
  }
2558
- if (!stat6.isFile()) {
2559
- throw new Error(`openLog: '${basename4(resolved)}' is not a regular file`);
2630
+ if (!stat5.isFile()) {
2631
+ throw new Error(`openLog: '${basename5(resolved)}' is not a regular file`);
2560
2632
  }
2561
2633
  try {
2562
2634
  accessSync(resolved, constants.R_OK);
2563
2635
  } catch {
2564
- throw new Error(`openLog: '${basename4(resolved)}' is not readable`);
2636
+ throw new Error(`openLog: '${basename5(resolved)}' is not readable`);
2565
2637
  }
2566
- return { id: resolved, path: resolved, size: stat6.size };
2638
+ return { id: resolved, path: resolved, size: stat5.size };
2567
2639
  }
2568
2640
  watchLog(handle, sinkOrChunk) {
2569
2641
  const node = handle;
@@ -2589,7 +2661,7 @@ var NodeHostAdapter = class {
2589
2661
 
2590
2662
  // ../server/src/app-state.ts
2591
2663
  import { Buffer as Buffer2 } from "buffer";
2592
- import { basename as basename5 } from "path";
2664
+ import { basename as basename6 } from "path";
2593
2665
 
2594
2666
  // ../core/src/correlator.ts
2595
2667
  var Correlator = class {
@@ -4606,6 +4678,8 @@ function formatTs(ms) {
4606
4678
  )}`;
4607
4679
  }
4608
4680
  function formatSessionShort(sessionId) {
4681
+ const watched = formatResourceWatchChannel(sessionId);
4682
+ if (watched) return watched;
4609
4683
  const parts = sessionId.split(/[/:]+/).filter(Boolean);
4610
4684
  let label = parts.at(-1) ?? sessionId;
4611
4685
  label = label.replace(/^session[-_:]?/i, "");
@@ -4616,6 +4690,24 @@ function formatSessionShort(sessionId) {
4616
4690
  if (label.length <= 18) return label;
4617
4691
  return `${label.slice(0, 17)}\u2026`;
4618
4692
  }
4693
+ function formatResourceWatchChannel(sessionId) {
4694
+ const match2 = sessionId.match(/^ahp-resource-watch:\/\/[^/]*\/(.+)$/u);
4695
+ if (!match2) return null;
4696
+ const encoded = match2[1];
4697
+ if (!encoded) return null;
4698
+ try {
4699
+ const normalized = encoded.replace(/-/g, "+").replace(/_/g, "/");
4700
+ const json = atob(normalized);
4701
+ const parsed = JSON.parse(json);
4702
+ const root = parsed.root ?? parsed.uri ?? parsed.resource;
4703
+ if (typeof root !== "string") return null;
4704
+ const tail = root.split(/[/\\]/u).filter(Boolean).at(-1) ?? root;
4705
+ const name = decodeURIComponent(tail);
4706
+ return `watch:${name.length <= 24 ? name : `${name.slice(0, 23)}\u2026`}`;
4707
+ } catch {
4708
+ return null;
4709
+ }
4710
+ }
4619
4711
  function payloadPreviewOf(raw2) {
4620
4712
  if (raw2 === void 0 || raw2 === null) return "";
4621
4713
  let src = raw2;
@@ -4828,7 +4920,7 @@ async function createAppState(opts) {
4828
4920
  const handlePath = handle.path ?? handle.id;
4829
4921
  const initialMtimeMs = opts.initialMtimeMs ?? Date.now();
4830
4922
  const meta = {
4831
- filename: basename5(handlePath),
4923
+ filename: basename6(handlePath),
4832
4924
  sizeBytes: handle.size ?? 0,
4833
4925
  startedAt: Date.now(),
4834
4926
  logKey: opts.logKey ?? computeLogKey(handlePath, initialMtimeMs)
@@ -8830,7 +8922,7 @@ function classifyDirection(raw2) {
8830
8922
 
8831
8923
  // src/index.ts
8832
8924
  var __filename2 = fileURLToPath(import.meta.url);
8833
- var __dirname2 = dirname3(__filename2);
8925
+ var __dirname2 = dirname5(__filename2);
8834
8926
  function loadVersion() {
8835
8927
  const candidates = [join7(__dirname2, "..", "package.json"), join7(__dirname2, "package.json")];
8836
8928
  for (const p of candidates) {
@@ -8902,9 +8994,9 @@ var program = new Command().name("ahp-inspector").version(VERSION).argument(
8902
8994
  let absPath;
8903
8995
  if (file) {
8904
8996
  absPath = resolvePath(file);
8905
- let stat6;
8997
+ let stat5;
8906
8998
  try {
8907
- stat6 = statSync3(absPath);
8999
+ stat5 = statSync3(absPath);
8908
9000
  } catch (err) {
8909
9001
  const e = err;
8910
9002
  if (e.code === "ENOENT") {
@@ -8914,7 +9006,7 @@ Usage: ahp-inspector [path-to-log.jsonl]`);
8914
9006
  fail(`Error: cannot read ${absPath}: ${e.message}
8915
9007
  Check file permissions.`);
8916
9008
  }
8917
- if (!stat6.isFile()) {
9009
+ if (!stat5.isFile()) {
8918
9010
  fail(`Error: log file not found: ${absPath}
8919
9011
  Usage: ahp-inspector [path-to-log.jsonl]`);
8920
9012
  }