codesesh 0.5.0 → 0.6.0

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
@@ -5,6 +5,7 @@ import {
5
5
  computeIdentity,
6
6
  createRegisteredAgents,
7
7
  deleteBookmark,
8
+ extractSessionFileActivity,
8
9
  filterSessions,
9
10
  getAgentInfoMap,
10
11
  getCursorDataPath,
@@ -12,16 +13,19 @@ import {
12
13
  importBookmarks,
13
14
  listBookmarks,
14
15
  listCachedProjectGroups,
16
+ listFileActivity,
17
+ parseSearchQuery,
15
18
  perf,
16
19
  realFs,
17
20
  refreshPricingCache,
18
21
  resolveProviderRoots,
19
22
  saveCachedSessions,
20
23
  scanSessions,
24
+ searchFileActivitySessions,
21
25
  searchSessions,
22
26
  syncSessionSearchIndex,
23
27
  upsertBookmark
24
- } from "./chunk-FZNZAMTZ.js";
28
+ } from "./chunk-SQYHWMQV.js";
25
29
 
26
30
  // src/index.ts
27
31
  import { defineCommand, runMain } from "citty";
@@ -167,6 +171,24 @@ var AppLogger = class {
167
171
  }
168
172
  };
169
173
  var appLogger = new AppLogger();
174
+ function logSearchIndexSync(context, result, data = {}) {
175
+ if (!result || result.mode !== "bulk" || result.rebuildDurationMs == null) {
176
+ return;
177
+ }
178
+ appLogger.info("search_index.sync", {
179
+ context,
180
+ agent: result.agentName,
181
+ mode: result.mode,
182
+ sessions: result.sessions,
183
+ changed: result.changed,
184
+ deleted: result.deleted,
185
+ indexed: result.indexed,
186
+ skipped: result.skipped,
187
+ duration_ms: Math.round(result.durationMs),
188
+ rebuild_duration_ms: Math.round(result.rebuildDurationMs),
189
+ ...data
190
+ });
191
+ }
170
192
 
171
193
  // src/api/handlers.ts
172
194
  function isRecord(value) {
@@ -195,6 +217,9 @@ function parseBookmarkPayload(value) {
195
217
  function getTotalTokens(stats) {
196
218
  return stats.total_tokens ?? stats.total_input_tokens + stats.total_output_tokens;
197
219
  }
220
+ function getSessionAgentName(session) {
221
+ return session.slug.split("/")[0]?.toLowerCase() || "unknown";
222
+ }
198
223
  function getSessionActivityTime(session) {
199
224
  return session.time_updated ?? session.time_created;
200
225
  }
@@ -203,6 +228,56 @@ function parseDateParam(value, fallback) {
203
228
  const ts = new Date(value).getTime();
204
229
  return Number.isNaN(ts) ? fallback : ts;
205
230
  }
231
+ function parseNumberParam(value) {
232
+ if (value == null || !value.trim()) return void 0;
233
+ const number = Number(value);
234
+ return Number.isFinite(number) ? number : void 0;
235
+ }
236
+ function searchParams(c) {
237
+ return new URL(c.req.url ?? "http://localhost/", "http://localhost/").searchParams;
238
+ }
239
+ function queryValues(params, ...names) {
240
+ return names.flatMap(
241
+ (name) => params.getAll(name).flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean)
242
+ );
243
+ }
244
+ function parseSmartTags(values) {
245
+ const tags = values.map((value) => value.toLowerCase()).filter(
246
+ (value) => [
247
+ "bugfix",
248
+ "refactoring",
249
+ "feature-dev",
250
+ "testing",
251
+ "docs",
252
+ "git-ops",
253
+ "build-deploy",
254
+ "exploration",
255
+ "planning"
256
+ ].includes(value)
257
+ );
258
+ return tags.length > 0 ? [...new Set(tags)] : void 0;
259
+ }
260
+ function parseSearchOptions(c, defaults) {
261
+ const params = searchParams(c);
262
+ const limitValue = parseNumberParam(params.get("limit") ?? void 0);
263
+ return {
264
+ agent: optionalQueryValue(params.get("agent") ?? void 0),
265
+ project: optionalQueryValue(params.get("project") ?? void 0),
266
+ projectKey: optionalQueryValue(params.get("projectKey") ?? void 0),
267
+ cwd: optionalQueryValue(params.get("cwd") ?? void 0),
268
+ tags: parseSmartTags(queryValues(params, "tag", "tags", "signal")),
269
+ tools: queryValues(params, "tool", "tools").map((tool) => tool.toLowerCase()),
270
+ file: optionalQueryValue(params.get("file") ?? params.get("path") ?? void 0),
271
+ fileKind: parseFileActivityKind(
272
+ optionalQueryValue(params.get("fileKind") ?? params.get("fileActivity") ?? void 0)
273
+ ),
274
+ costMin: parseNumberParam(params.get("costMin") ?? void 0),
275
+ costMax: parseNumberParam(params.get("costMax") ?? void 0),
276
+ from: parseDateParam(params.get("from") ?? void 0, defaults.from),
277
+ to: parseDateParam(params.get("to") ?? void 0, defaults.to),
278
+ limit: limitValue && limitValue > 0 ? Math.min(limitValue, 100) : 50
279
+ };
280
+ }
206
281
  function filterSessionsByWindow(sessions, from, to) {
207
282
  return filterSessionsByActivityWindow(sessions, from, to);
208
283
  }
@@ -233,6 +308,148 @@ function sanitizeClientLogData(value) {
233
308
  })
234
309
  );
235
310
  }
311
+ function sessionMatchesCostFilter(session, options) {
312
+ const cost = session.stats.total_cost;
313
+ if (options.costMin != null) {
314
+ if (options.costMinExclusive ? cost <= options.costMin : cost < options.costMin) return false;
315
+ }
316
+ if (options.costMax != null) {
317
+ if (options.costMaxExclusive ? cost >= options.costMax : cost > options.costMax) return false;
318
+ }
319
+ return true;
320
+ }
321
+ function mergeSearchLists(left, right) {
322
+ const values = [...left ?? [], ...right ?? []];
323
+ return values.length > 0 ? [...new Set(values)] : void 0;
324
+ }
325
+ function mergeSearchOptions(options, filters) {
326
+ return {
327
+ ...options,
328
+ agent: options.agent ?? filters.agent,
329
+ project: options.project ?? filters.project,
330
+ projectKey: options.projectKey ?? filters.projectKey,
331
+ cwd: options.cwd ?? filters.cwd,
332
+ tags: mergeSearchLists(options.tags, filters.tags),
333
+ tools: mergeSearchLists(options.tools, filters.tools),
334
+ file: options.file ?? filters.file,
335
+ fileKind: options.fileKind ?? filters.fileKind,
336
+ costMin: options.costMin ?? filters.costMin,
337
+ costMax: options.costMax ?? filters.costMax,
338
+ costMinExclusive: options.costMinExclusive ?? filters.costMinExclusive,
339
+ costMaxExclusive: options.costMaxExclusive ?? filters.costMaxExclusive
340
+ };
341
+ }
342
+ function mergeSearchResults(results, limit) {
343
+ const seen = /* @__PURE__ */ new Set();
344
+ const merged = [];
345
+ for (const result of results) {
346
+ const key = `${result.agentName}/${result.session.id}`;
347
+ if (seen.has(key)) continue;
348
+ seen.add(key);
349
+ merged.push(result);
350
+ if (merged.length >= limit) break;
351
+ }
352
+ return merged;
353
+ }
354
+ function getProjectGroupKey(identityKind, identityKey) {
355
+ return `${identityKind}:${identityKey}`;
356
+ }
357
+ function attachProjectMetrics(projects, sessions) {
358
+ const metrics = /* @__PURE__ */ new Map();
359
+ for (const session of sessions) {
360
+ const identity = session.project_identity;
361
+ if (!identity) continue;
362
+ const key = getProjectGroupKey(identity.kind, identity.key);
363
+ let current = metrics.get(key);
364
+ if (!current) {
365
+ current = {
366
+ messages: 0,
367
+ tokens: 0,
368
+ cost: 0,
369
+ hasEstimatedCost: false,
370
+ agentStats: /* @__PURE__ */ new Map()
371
+ };
372
+ metrics.set(key, current);
373
+ }
374
+ const tokens = getTotalTokens(session.stats);
375
+ const cost = session.stats.total_cost ?? 0;
376
+ current.messages += session.stats.message_count;
377
+ current.tokens += tokens;
378
+ current.cost += cost;
379
+ if (session.stats.cost_source === "estimated") current.hasEstimatedCost = true;
380
+ const agentName = getSessionAgentName(session);
381
+ const agent = current.agentStats.get(agentName);
382
+ if (agent) {
383
+ agent.sessions += 1;
384
+ agent.messages += session.stats.message_count;
385
+ agent.tokens += tokens;
386
+ agent.cost += cost;
387
+ } else {
388
+ current.agentStats.set(agentName, {
389
+ name: agentName,
390
+ sessions: 1,
391
+ messages: session.stats.message_count,
392
+ tokens,
393
+ cost
394
+ });
395
+ }
396
+ }
397
+ return projects.map((project) => {
398
+ const metric = metrics.get(getProjectGroupKey(project.identityKind, project.identityKey));
399
+ return {
400
+ ...project,
401
+ messages: metric?.messages ?? 0,
402
+ tokens: metric?.tokens ?? 0,
403
+ cost: metric?.cost ?? 0,
404
+ cost_source: metric && metric.cost > 0 ? metric.hasEstimatedCost ? "estimated" : "recorded" : void 0,
405
+ agentStats: [...metric?.agentStats.values() ?? []].sort((a, b) => b.sessions - a.sessions)
406
+ };
407
+ });
408
+ }
409
+ function matchesDashboardScope(session, scope) {
410
+ if (scope.agent && getSessionAgentName(session) !== scope.agent) return false;
411
+ if (scope.projectKey) {
412
+ const identity = session.project_identity;
413
+ if (!identity || identity.key !== scope.projectKey) return false;
414
+ if (scope.projectKind && identity.kind !== scope.projectKind) return false;
415
+ }
416
+ return true;
417
+ }
418
+ function filterSessionsByDashboardScope(sessions, scope) {
419
+ if (!scope.agent && !scope.projectKey) return sessions;
420
+ return sessions.filter((session) => matchesDashboardScope(session, scope));
421
+ }
422
+ function matchesRecentSearchFilters(session, options) {
423
+ if (options.projectKey && session.project_identity?.key !== options.projectKey) return false;
424
+ if (options.cwd && !matchesProjectScope(session, options.cwd)) return false;
425
+ if (options.project) {
426
+ const projectNeedle = options.project.toLowerCase();
427
+ const projectText = [
428
+ session.project_identity?.key,
429
+ session.project_identity?.displayName,
430
+ session.directory
431
+ ].filter(Boolean).join("\n").toLowerCase();
432
+ if (!projectText.includes(projectNeedle)) return false;
433
+ }
434
+ if (options.tags?.length && !options.tags.every((tag) => session.smart_tags?.includes(tag))) {
435
+ return false;
436
+ }
437
+ if (!sessionMatchesCostFilter(session, options)) return false;
438
+ return true;
439
+ }
440
+ function recentSearchSessions(scanResult, options) {
441
+ const entries = options.agent ? [[options.agent, scanResult.byAgent[options.agent] ?? []]] : Object.entries(scanResult.byAgent);
442
+ return entries.flatMap(
443
+ ([agentName, sessions]) => filterSessionsByActivityWindow(sessions, options.from, options.to).filter((session) => matchesRecentSearchFilters(session, options)).map((session) => ({ agentName, session }))
444
+ ).toSorted(
445
+ (a, b) => (b.session.time_updated ?? b.session.time_created) - (a.session.time_updated ?? a.session.time_created)
446
+ ).slice(0, options.limit).map(({ agentName, session }) => ({
447
+ agentName,
448
+ session,
449
+ snippet: `Recent session \xB7 ${session.directory}`,
450
+ matchType: "recent"
451
+ }));
452
+ }
236
453
  function handleGetConfig(c, defaults) {
237
454
  return c.json({
238
455
  window: {
@@ -257,10 +474,9 @@ function handleGetAgents(c, scanSource, defaults = {}) {
257
474
  function handleGetProjects(c, scanSource, defaults = {}) {
258
475
  const scanResult = scanSource.getSnapshot();
259
476
  const { from, to } = defaults;
477
+ const sessions = filterSessionsByActivityWindow(scanResult.sessions, from, to);
260
478
  return c.json({
261
- projects: listCachedProjectGroups(
262
- filterSessionsByActivityWindow(scanResult.sessions, from, to)
263
- )
479
+ projects: attachProjectMetrics(listCachedProjectGroups(sessions), sessions)
264
480
  });
265
481
  }
266
482
  function handleGetSessions(c, scanSource, defaults = {}) {
@@ -294,31 +510,60 @@ function handleGetSessions(c, scanSource, defaults = {}) {
294
510
  }
295
511
  function handleSearchSessions(c, scanSource, defaults = {}) {
296
512
  const query = c.req.query("q")?.trim() ?? "";
297
- if (!query) {
298
- return c.json({ results: [] });
299
- }
300
513
  const scanResult = scanSource.getSnapshot();
301
- const agent = c.req.query("agent");
302
- const cwd = c.req.query("cwd");
303
- const from = parseDateParam(c.req.query("from"), defaults.from);
304
- const to = parseDateParam(c.req.query("to"), defaults.to);
305
- for (const indexedAgent of scanResult.agents) {
306
- const sessions = scanResult.byAgent[indexedAgent.name] ?? [];
307
- syncSessionSearchIndex(
308
- indexedAgent.name,
309
- sessions,
310
- (sessionId) => indexedAgent.getSessionData(sessionId)
311
- );
514
+ const searchOptions = parseSearchOptions(c, defaults);
515
+ const parsedQuery = parseSearchQuery(query);
516
+ const mergedSearchOptions = mergeSearchOptions(searchOptions, parsedQuery.filters);
517
+ const textQuery = parsedQuery.text || (parsedQuery.hasQualifiers ? "" : query);
518
+ const needsIndexedSearch = Boolean(
519
+ textQuery || mergedSearchOptions.file || mergedSearchOptions.fileKind || mergedSearchOptions.tools?.length
520
+ );
521
+ if (!needsIndexedSearch) {
522
+ return c.json({
523
+ results: recentSearchSessions(
524
+ scanResult,
525
+ mergedSearchOptions
526
+ )
527
+ });
312
528
  }
313
- const results = searchSessions(query, {
314
- agent,
315
- cwd,
316
- from,
317
- to,
318
- limit: 50
319
- });
529
+ const fileQuery = mergedSearchOptions.file ?? (!parsedQuery.text ? parsedQuery.filters.file : void 0) ?? (!parsedQuery.hasQualifiers && query ? parsedQuery.text || query : "");
530
+ const results = mergeSearchResults(
531
+ [
532
+ ...fileQuery ? searchFileActivitySessions(fileQuery, mergedSearchOptions) : [],
533
+ ...searchSessions(query, mergedSearchOptions)
534
+ ],
535
+ mergedSearchOptions.limit ?? 50
536
+ );
320
537
  return c.json({ results });
321
538
  }
539
+ function parseFileActivityKind(value) {
540
+ if (value === "read" || value === "edit" || value === "write" || value === "delete") {
541
+ return value;
542
+ }
543
+ return void 0;
544
+ }
545
+ function optionalQueryValue(value) {
546
+ const normalized = value?.trim();
547
+ return normalized ? normalized : void 0;
548
+ }
549
+ function handleGetFileActivity(c, defaults = {}) {
550
+ const limitValue = Number(c.req.query("limit"));
551
+ const limit = Number.isFinite(limitValue) && limitValue > 0 ? Math.min(limitValue, 200) : 50;
552
+ return c.json({
553
+ activity: listFileActivity({
554
+ agent: optionalQueryValue(c.req.query("agent")),
555
+ sessionId: optionalQueryValue(c.req.query("sessionId")),
556
+ projectKey: optionalQueryValue(c.req.query("projectKey")),
557
+ project: optionalQueryValue(c.req.query("project")),
558
+ cwd: optionalQueryValue(c.req.query("cwd")),
559
+ path: optionalQueryValue(c.req.query("path")),
560
+ kind: parseFileActivityKind(optionalQueryValue(c.req.query("kind"))),
561
+ from: parseDateParam(c.req.query("from"), defaults.from),
562
+ to: parseDateParam(c.req.query("to"), defaults.to),
563
+ limit
564
+ })
565
+ });
566
+ }
322
567
  async function handleGetSessionData(c, scanSource) {
323
568
  const startedAt = performance.now();
324
569
  const scanResult = scanSource.getSnapshot();
@@ -339,6 +584,7 @@ async function handleGetSessionData(c, scanSource) {
339
584
  const smartTags = classifySessionTags(data);
340
585
  const tagDuration = performance.now() - tagStartedAt;
341
586
  const head = scanResult.byAgent[agentName]?.find((item) => item.id === sessionId);
587
+ const projectIdentity = data.project_identity ?? head?.project_identity ?? computeIdentity(data.directory, realFs);
342
588
  appLogger.info("api.session_data", {
343
589
  agent: agentName,
344
590
  session_id: sessionId,
@@ -349,9 +595,15 @@ async function handleGetSessionData(c, scanSource) {
349
595
  });
350
596
  return c.json({
351
597
  ...data,
352
- project_identity: data.project_identity ?? head?.project_identity,
598
+ project_identity: projectIdentity,
353
599
  smart_tags: smartTags,
354
- smart_tags_source_updated_at: getSmartTagSourceTimestamp(data)
600
+ smart_tags_source_updated_at: getSmartTagSourceTimestamp(data),
601
+ file_activity: extractSessionFileActivity(
602
+ agentName,
603
+ sessionId,
604
+ projectIdentity.key,
605
+ data.messages
606
+ )
355
607
  });
356
608
  } catch (err) {
357
609
  const message = err instanceof Error ? err.message : "Failed to load session";
@@ -473,10 +725,19 @@ function handleGetDashboard(c, scanSource, defaults = {}) {
473
725
  c.req.query("from"),
474
726
  c.req.query("to")
475
727
  );
476
- const windowed = filterSessionsByActivityWindow(scanResult.sessions, from, to);
728
+ const scope = {
729
+ agent: optionalQueryValue(c.req.query("agent"))?.toLowerCase(),
730
+ projectKind: optionalQueryValue(c.req.query("projectKind")),
731
+ projectKey: optionalQueryValue(c.req.query("projectKey"))
732
+ };
733
+ const scopedSessions = filterSessionsByDashboardScope(scanResult.sessions, scope);
734
+ const windowed = filterSessionsByActivityWindow(scopedSessions, from, to);
735
+ const scopedByAgent = Object.fromEntries(
736
+ Object.entries(scanResult.byAgent).filter(([name]) => !scope.agent || name.toLowerCase() === scope.agent).map(([name, sessions]) => [name, filterSessionsByDashboardScope(sessions, scope)])
737
+ );
477
738
  const agentInfo = getAgentInfoMap(
478
739
  Object.fromEntries(
479
- Object.entries(scanResult.byAgent).map(([name, sessions]) => [
740
+ Object.entries(scopedByAgent).map(([name, sessions]) => [
480
741
  name,
481
742
  filterSessionsByActivityWindow(sessions, from, to).length
482
743
  ])
@@ -496,7 +757,7 @@ function handleGetDashboard(c, scanSource, defaults = {}) {
496
757
  const activity = getSessionActivityTime(session);
497
758
  if (activity > latestActivity) latestActivity = activity;
498
759
  }
499
- const perAgent = Object.entries(scanResult.byAgent).map(([name, sessions]) => {
760
+ const perAgent = Object.entries(scopedByAgent).map(([name, sessions]) => {
500
761
  const info = agentInfoMap.get(name);
501
762
  const agentWindowed = filterSessionsByActivityWindow(sessions, from, to);
502
763
  let messages = 0;
@@ -558,7 +819,7 @@ function handleGetDashboard(c, scanSource, defaults = {}) {
558
819
  const dailyTokenActivity = [...dailyTokenMap.values()];
559
820
  const modelDistribution = [...modelAgg.entries()].map(([model, { tokens, sessions: count }]) => ({ model, tokens, sessions: count })).sort((a, b) => b.tokens - a.tokens);
560
821
  const recentSessions = [...windowed].sort((a, b) => getSessionActivityTime(b) - getSessionActivityTime(a)).slice(0, 10).map((session) => {
561
- const agentKey = session.slug.split("/")[0] ?? "unknown";
822
+ const agentKey = getSessionAgentName(session);
562
823
  return { ...session, agentName: agentKey };
563
824
  });
564
825
  const data = {
@@ -575,6 +836,13 @@ function handleGetDashboard(c, scanSource, defaults = {}) {
575
836
  dailyTokenActivity,
576
837
  modelDistribution,
577
838
  recentSessions,
839
+ recentFileActivities: listFileActivity({
840
+ agent: scope.agent,
841
+ projectKey: scope.projectKey,
842
+ from,
843
+ to,
844
+ limit: 12
845
+ }),
578
846
  window: { from, to, days }
579
847
  };
580
848
  return c.json(data);
@@ -632,6 +900,7 @@ function createApiRoutes(scanSource, store, options = {}) {
632
900
  api.get("/projects", (c) => handleGetProjects(c, scanSource, listDefaults));
633
901
  api.get("/sessions", (c) => handleGetSessions(c, scanSource, listDefaults));
634
902
  api.get("/search", (c) => handleSearchSessions(c, scanSource, listDefaults));
903
+ api.get("/file-activity", (c) => handleGetFileActivity(c, listDefaults));
635
904
  api.get("/sessions/:agent/:id", (c) => handleGetSessionData(c, scanSource));
636
905
  api.get("/dashboard", (c) => handleGetDashboard(c, scanSource, listDefaults));
637
906
  api.get("/bookmarks", (c) => handleGetBookmarks(c));
@@ -659,10 +928,10 @@ function findWebDistPath() {
659
928
  return null;
660
929
  }
661
930
  function waitForListening(server) {
662
- return new Promise((resolve3, reject) => {
931
+ return new Promise((resolve4, reject) => {
663
932
  const handleListening = () => {
664
933
  server.off("error", handleError);
665
- resolve3();
934
+ resolve4();
666
935
  };
667
936
  const handleError = (error) => {
668
937
  server.off("listening", handleListening);
@@ -744,9 +1013,17 @@ async function createServer(port, store, options = {}) {
744
1013
  }
745
1014
 
746
1015
  // src/live-scan.ts
747
- import { existsSync as existsSync3 } from "fs";
748
- import { dirname as dirname2, isAbsolute, join as join2 } from "path";
749
- import chokidar from "chokidar";
1016
+ import { existsSync as existsSync3, readdirSync as readdirSync2, statSync as statSync2, watch } from "fs";
1017
+ import { dirname as dirname2, isAbsolute, join as join2, relative, resolve as resolve2 } from "path";
1018
+ import { fileURLToPath as fileURLToPath2 } from "url";
1019
+ import { Worker } from "worker_threads";
1020
+ var REFRESH_DEBOUNCE_MS = 200;
1021
+ var EMPTY_AGENT_REFRESH_DEBOUNCE_MS = 3e4;
1022
+ var PENDING_REFRESH_DELAY_MS = 100;
1023
+ var WRITE_STABILITY_THRESHOLD_MS = 250;
1024
+ var WRITE_STABILITY_POLL_MS = 100;
1025
+ var NEW_SESSION_EVENT_WINDOW_MS = 250;
1026
+ var SEARCH_INDEX_BULK_PENDING_PATH_THRESHOLD = 100;
750
1027
  function sortSessions(sessions) {
751
1028
  return [...sessions].sort(
752
1029
  (a, b) => (b.time_updated ?? b.time_created) - (a.time_updated ?? a.time_created)
@@ -808,11 +1085,14 @@ function buildUpdateEvent(agentName, previousSessions, nextSessions) {
808
1085
  timestamp: Date.now()
809
1086
  };
810
1087
  }
1088
+ function toAbsolutePath(path) {
1089
+ return isAbsolute(path) ? path : resolve2(path);
1090
+ }
811
1091
  function closestWatchablePath(targetPath) {
812
1092
  if (!isAbsolute(targetPath) && !existsSync3(targetPath)) {
813
1093
  return null;
814
1094
  }
815
- let current = targetPath;
1095
+ let current = toAbsolutePath(targetPath);
816
1096
  while (!existsSync3(current)) {
817
1097
  const parent = dirname2(current);
818
1098
  if (parent === current) {
@@ -822,31 +1102,85 @@ function closestWatchablePath(targetPath) {
822
1102
  }
823
1103
  return current;
824
1104
  }
1105
+ function getWatchRoot(path) {
1106
+ const stat = statSync2(path);
1107
+ return stat.isDirectory() ? path : dirname2(path);
1108
+ }
1109
+ function isRecursiveWatchSupported(platform = process.platform, nodeVersion = process.versions.node) {
1110
+ if (platform === "darwin" || platform === "win32") {
1111
+ return true;
1112
+ }
1113
+ if (platform !== "linux" && platform !== "aix" && platform !== "ibmi") {
1114
+ return false;
1115
+ }
1116
+ const [major = 0, minor = 0] = nodeVersion.split(".").map((part) => Number(part));
1117
+ return major > 19 || major === 19 && minor >= 1;
1118
+ }
1119
+ function isRecursiveWatchUnavailable(error) {
1120
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
1121
+ }
1122
+ function isSameOrChildPath(parentPath, childPath) {
1123
+ const path = relative(parentPath, childPath);
1124
+ return path === "" || !path.startsWith("..") && !isAbsolute(path);
1125
+ }
1126
+ function isRelatedPath(changedPath, targetPath) {
1127
+ return isSameOrChildPath(targetPath, changedPath) || isSameOrChildPath(changedPath, targetPath);
1128
+ }
1129
+ function mergeEvents(previous, next) {
1130
+ return {
1131
+ type: "sessions-updated",
1132
+ changedAgents: Array.from(/* @__PURE__ */ new Set([...previous.changedAgents, ...next.changedAgents])),
1133
+ newSessions: previous.newSessions + next.newSessions,
1134
+ updatedSessions: previous.updatedSessions + next.updatedSessions,
1135
+ removedSessions: previous.removedSessions + next.removedSessions,
1136
+ totalSessions: next.totalSessions,
1137
+ timestamp: next.timestamp
1138
+ };
1139
+ }
1140
+ function mergeScopes(target, scopes) {
1141
+ for (const scope of scopes) {
1142
+ if (!target.some(
1143
+ (item) => item.agentName === scope.agentName && item.targetPath === scope.targetPath
1144
+ )) {
1145
+ target.push(scope);
1146
+ }
1147
+ }
1148
+ }
1149
+ function resolveWatchEventPath(watchPath, filename) {
1150
+ const filenameText = filename?.toString();
1151
+ if (!filenameText) {
1152
+ return watchPath;
1153
+ }
1154
+ return isAbsolute(filenameText) ? filenameText : join2(watchPath, filenameText);
1155
+ }
825
1156
  function resolveAgentWatchTargets(agentName) {
826
1157
  const roots = resolveProviderRoots();
827
1158
  const cursorDataPath = getCursorDataPath();
828
1159
  switch (agentName) {
829
1160
  case "claudecode":
830
1161
  return [
831
- { path: join2(roots.claudeRoot, "projects"), depth: 2 },
832
- { path: "data/claudecode", depth: 2 }
1162
+ { root: roots.claudeRoot, path: join2(roots.claudeRoot, "projects") },
1163
+ { path: "data/claudecode" }
833
1164
  ];
834
1165
  case "codex":
835
- return [{ path: join2(roots.codexRoot, "sessions"), depth: 4 }];
1166
+ return [{ root: roots.codexRoot, path: join2(roots.codexRoot, "sessions") }];
836
1167
  case "cursor":
837
1168
  return cursorDataPath ? [
838
- { path: join2(cursorDataPath, "globalStorage", "state.vscdb") },
839
- { path: join2(cursorDataPath, "workspaceStorage"), depth: 2 }
1169
+ {
1170
+ root: cursorDataPath,
1171
+ path: join2(cursorDataPath, "globalStorage", "state.vscdb")
1172
+ },
1173
+ { root: cursorDataPath, path: join2(cursorDataPath, "workspaceStorage") }
840
1174
  ] : [];
841
1175
  case "kimi":
842
1176
  return [
843
- { path: join2(roots.kimiRoot, "sessions"), depth: 2 },
844
- { path: "data/kimi", depth: 2 }
1177
+ { root: roots.kimiRoot, path: join2(roots.kimiRoot, "sessions") },
1178
+ { path: "data/kimi" }
845
1179
  ];
846
1180
  case "opencode":
847
1181
  return [
848
- { path: join2(roots.opencodeRoot, "opencode.db") },
849
- { path: "data/opencode/opencode.db" }
1182
+ { root: roots.opencodeRoot, path: join2(roots.opencodeRoot, "opencode.db") },
1183
+ { root: "data/opencode", path: "data/opencode/opencode.db" }
850
1184
  ];
851
1185
  default:
852
1186
  return [];
@@ -869,7 +1203,14 @@ var LiveScanStore = class {
869
1203
  refreshTimestamps = /* @__PURE__ */ new Map();
870
1204
  refreshInFlight = /* @__PURE__ */ new Set();
871
1205
  pendingRefreshes = /* @__PURE__ */ new Set();
1206
+ pendingRefreshPathCounts = /* @__PURE__ */ new Map();
872
1207
  watchers = [];
1208
+ fallbackWatchScopes = /* @__PURE__ */ new Map();
1209
+ stablePaths = /* @__PURE__ */ new Map();
1210
+ pendingEvent = null;
1211
+ pendingEventTimer = null;
1212
+ initialSearchIndexTimer = null;
1213
+ searchIndexWorker = null;
873
1214
  async initialize() {
874
1215
  const startedAt = performance.now();
875
1216
  appLogger.info("scan.initial.start", {
@@ -897,6 +1238,10 @@ var LiveScanStore = class {
897
1238
  });
898
1239
  if (this.watchEnabled) {
899
1240
  this.startWatching();
1241
+ this.initialSearchIndexTimer = setTimeout(() => {
1242
+ this.initialSearchIndexTimer = null;
1243
+ this.startSearchIndexWorker("scan.initial.background");
1244
+ }, 1e3);
900
1245
  }
901
1246
  }
902
1247
  getSnapshot() {
@@ -917,20 +1262,108 @@ var LiveScanStore = class {
917
1262
  clearTimeout(timer);
918
1263
  }
919
1264
  this.refreshTimers.clear();
1265
+ this.pendingRefreshPathCounts.clear();
1266
+ for (const state of this.stablePaths.values()) {
1267
+ if (state.timer) {
1268
+ clearTimeout(state.timer);
1269
+ }
1270
+ }
1271
+ this.stablePaths.clear();
1272
+ if (this.pendingEventTimer) {
1273
+ clearTimeout(this.pendingEventTimer);
1274
+ this.pendingEventTimer = null;
1275
+ }
1276
+ if (this.initialSearchIndexTimer) {
1277
+ clearTimeout(this.initialSearchIndexTimer);
1278
+ this.initialSearchIndexTimer = null;
1279
+ }
1280
+ if (this.searchIndexWorker) {
1281
+ await this.searchIndexWorker.terminate();
1282
+ this.searchIndexWorker = null;
1283
+ }
1284
+ this.pendingEvent = null;
920
1285
  await Promise.all(this.watchers.map((watcher) => watcher.close()));
921
1286
  this.watchers = [];
1287
+ this.fallbackWatchScopes.clear();
922
1288
  }
923
1289
  emit(event) {
1290
+ if (this.pendingEvent || event.newSessions > 0) {
1291
+ this.queueEvent(event);
1292
+ return;
1293
+ }
1294
+ this.emitNow(event);
1295
+ }
1296
+ emitNow(event) {
924
1297
  for (const listener of this.listeners) {
925
1298
  listener(event);
926
1299
  }
927
1300
  }
1301
+ queueEvent(event) {
1302
+ this.pendingEvent = this.pendingEvent ? mergeEvents(this.pendingEvent, event) : event;
1303
+ if (this.pendingEventTimer) {
1304
+ return;
1305
+ }
1306
+ this.pendingEventTimer = setTimeout(() => {
1307
+ const pending = this.pendingEvent;
1308
+ this.pendingEvent = null;
1309
+ this.pendingEventTimer = null;
1310
+ if (pending) {
1311
+ this.emitNow(pending);
1312
+ }
1313
+ }, NEW_SESSION_EVENT_WINDOW_MS);
1314
+ }
928
1315
  rebuildSessions() {
929
1316
  this.sessions = sortSessions(Object.values(this.byAgent).flat());
930
1317
  }
931
1318
  hasStartupWindow() {
932
1319
  return this.startupScanOptions.from != null || this.startupScanOptions.to != null;
933
1320
  }
1321
+ getSearchIndexWorkerUrl() {
1322
+ const workerUrl = new URL("./search-index-worker.js", import.meta.url);
1323
+ if (workerUrl.protocol === "file:" && !existsSync3(fileURLToPath2(workerUrl))) {
1324
+ return null;
1325
+ }
1326
+ return workerUrl;
1327
+ }
1328
+ startSearchIndexWorker(context) {
1329
+ if (this.searchIndexWorker) return;
1330
+ const workerUrl = this.getSearchIndexWorkerUrl();
1331
+ if (!workerUrl) {
1332
+ appLogger.warn("search_index.worker_missing", { context });
1333
+ return;
1334
+ }
1335
+ const worker = new Worker(workerUrl, {
1336
+ workerData: {
1337
+ context,
1338
+ agentNames: this.agents.map((agent) => agent.name),
1339
+ sessionsByAgent: this.byAgent,
1340
+ metaByAgent: Object.fromEntries(
1341
+ this.agents.map((agent) => [agent.name, buildAgentCacheMeta(agent)])
1342
+ )
1343
+ }
1344
+ });
1345
+ worker.unref();
1346
+ this.searchIndexWorker = worker;
1347
+ worker.on("message", (message) => {
1348
+ if (message.type === "sync-result") {
1349
+ logSearchIndexSync(message.context, message.result);
1350
+ } else if (message.type === "done") {
1351
+ appLogger.info(`${message.context}.done`, {
1352
+ duration_ms: Math.round(message.durationMs),
1353
+ sessions: message.sessions
1354
+ });
1355
+ }
1356
+ });
1357
+ worker.on("error", (error) => {
1358
+ appLogger.error("search_index.worker_error", { context, error });
1359
+ });
1360
+ worker.on("exit", (code) => {
1361
+ this.searchIndexWorker = null;
1362
+ if (code !== 0) {
1363
+ appLogger.warn("search_index.worker_exit", { context, code });
1364
+ }
1365
+ });
1366
+ }
934
1367
  applyScanResult(result) {
935
1368
  const knownAgents = createRegisteredAgents();
936
1369
  const agentMap = /* @__PURE__ */ new Map();
@@ -966,50 +1399,195 @@ var LiveScanStore = class {
966
1399
  return filterSessions(sessions, { ...this.scanOptions, ...this.startupScanOptions });
967
1400
  }
968
1401
  startWatching() {
1402
+ const scopesByRoot = /* @__PURE__ */ new Map();
969
1403
  for (const agent of this.agents) {
970
- const rawTargets = resolveAgentWatchTargets(agent.name);
971
- const watchTargets = rawTargets.map((target) => {
972
- const watchPath = closestWatchablePath(target.path);
973
- return watchPath ? { ...target, path: watchPath } : null;
974
- }).filter((target) => target !== null).filter(
975
- (target, index, items) => items.findIndex((item) => item.path === target.path && item.depth === target.depth) === index
976
- );
1404
+ const watchTargets = resolveAgentWatchTargets(agent.name);
977
1405
  if (watchTargets.length === 0) {
978
1406
  appLogger.debug("watch.skip", { agent: agent.name });
979
1407
  continue;
980
1408
  }
1409
+ for (const target of watchTargets) {
1410
+ const watchRootPath = closestWatchablePath(target.root ?? target.path);
1411
+ if (!watchRootPath) continue;
1412
+ let rootPath;
1413
+ try {
1414
+ rootPath = getWatchRoot(watchRootPath);
1415
+ } catch (error) {
1416
+ this.reportWatchError("watch.resolve.error", { path: watchRootPath, error });
1417
+ continue;
1418
+ }
1419
+ const targetPath = toAbsolutePath(target.path);
1420
+ const scopes = scopesByRoot.get(rootPath) ?? [];
1421
+ if (!scopes.some((scope) => scope.agentName === agent.name && scope.targetPath === targetPath)) {
1422
+ scopes.push({ agentName: agent.name, targetPath });
1423
+ }
1424
+ scopesByRoot.set(rootPath, scopes);
1425
+ }
1426
+ }
1427
+ for (const [rootPath, scopes] of scopesByRoot.entries()) {
1428
+ const agents = Array.from(new Set(scopes.map((scope) => scope.agentName)));
981
1429
  appLogger.info("watch.start", {
982
- agent: agent.name,
983
- targets: watchTargets.map((target) => ({
984
- path: target.path,
985
- depth: target.depth ?? 0
1430
+ root: rootPath,
1431
+ agents,
1432
+ targets: scopes.map((scope) => ({
1433
+ agent: scope.agentName,
1434
+ path: scope.targetPath
986
1435
  }))
987
1436
  });
988
- const watcher = chokidar.watch(
989
- watchTargets.map((target) => target.path),
990
- {
991
- ignoreInitial: true,
992
- awaitWriteFinish: {
993
- stabilityThreshold: 250,
994
- pollInterval: 100
995
- },
996
- depth: watchTargets.reduce(
997
- (maxDepth, target) => Math.max(maxDepth, target.depth ?? 0),
998
- 0
999
- )
1437
+ if (isRecursiveWatchSupported()) {
1438
+ const started = this.watchDirectory(rootPath, scopes, true);
1439
+ if (started) {
1440
+ continue;
1000
1441
  }
1001
- );
1002
- watcher.on("all", () => {
1003
- this.scheduleRefresh(agent.name);
1442
+ }
1443
+ this.watchDirectoryTree(rootPath, scopes);
1444
+ }
1445
+ }
1446
+ watchDirectory(path, scopes, recursive) {
1447
+ try {
1448
+ const watcher = watch(path, { recursive }, (eventType, filename) => {
1449
+ queueMicrotask(() => {
1450
+ try {
1451
+ const activeScopes = recursive ? scopes : this.fallbackWatchScopes.get(path) ?? scopes;
1452
+ this.handleWatchEvent(path, activeScopes, eventType, filename);
1453
+ if (!recursive) {
1454
+ this.watchNewDirectories(path, filename, activeScopes);
1455
+ }
1456
+ } catch (error) {
1457
+ this.reportWatchError("watch.event.error", { path, recursive, error });
1458
+ }
1459
+ });
1004
1460
  });
1005
1461
  watcher.on("error", (error) => {
1006
- appLogger.error("watch.error", { agent: agent.name, error });
1007
- console.error(`[${agent.name}] File watcher failed:`, error);
1462
+ this.reportWatchError("watch.error", { path, recursive, error });
1008
1463
  });
1009
1464
  this.watchers.push(watcher);
1465
+ return true;
1466
+ } catch (error) {
1467
+ if (recursive && isRecursiveWatchUnavailable(error)) {
1468
+ appLogger.warn("watch.recursive_unavailable", { path, error });
1469
+ return false;
1470
+ }
1471
+ this.reportWatchError("watch.start.error", { path, recursive, error });
1472
+ return false;
1010
1473
  }
1011
1474
  }
1012
- scheduleRefresh(agentName, delayMs = 200) {
1475
+ watchDirectoryTree(rootPath, scopes) {
1476
+ const pending = [rootPath];
1477
+ while (pending.length > 0) {
1478
+ const dirPath = pending.pop();
1479
+ this.watchFallbackDirectory(dirPath, scopes);
1480
+ try {
1481
+ for (const entry of readdirSync2(dirPath, { withFileTypes: true })) {
1482
+ if (entry.isDirectory()) {
1483
+ pending.push(join2(dirPath, entry.name));
1484
+ }
1485
+ }
1486
+ } catch (error) {
1487
+ this.reportWatchError("watch.scan.error", { path: dirPath, error });
1488
+ }
1489
+ }
1490
+ }
1491
+ watchFallbackDirectory(path, scopes) {
1492
+ const existingScopes = this.fallbackWatchScopes.get(path);
1493
+ if (existingScopes) {
1494
+ mergeScopes(existingScopes, scopes);
1495
+ return;
1496
+ }
1497
+ const storedScopes = [...scopes];
1498
+ this.fallbackWatchScopes.set(path, storedScopes);
1499
+ if (!this.watchDirectory(path, storedScopes, false)) {
1500
+ this.fallbackWatchScopes.delete(path);
1501
+ }
1502
+ }
1503
+ watchNewDirectories(watchPath, filename, scopes) {
1504
+ const path = resolveWatchEventPath(watchPath, filename);
1505
+ try {
1506
+ if (statSync2(path).isDirectory()) {
1507
+ this.watchDirectoryTree(path, scopes);
1508
+ }
1509
+ } catch {
1510
+ }
1511
+ }
1512
+ handleWatchEvent(watchPath, scopes, eventType, filename) {
1513
+ const changedPath = resolveWatchEventPath(watchPath, filename);
1514
+ const agentNames = new Set(
1515
+ scopes.filter((scope) => isRelatedPath(changedPath, scope.targetPath)).map((scope) => scope.agentName)
1516
+ );
1517
+ if (agentNames.size === 0) {
1518
+ return;
1519
+ }
1520
+ appLogger.debug("watch.event", {
1521
+ event: eventType,
1522
+ path: changedPath,
1523
+ agents: Array.from(agentNames)
1524
+ });
1525
+ this.waitForStablePath(changedPath, agentNames);
1526
+ }
1527
+ waitForStablePath(path, agentNames) {
1528
+ const existing = this.stablePaths.get(path);
1529
+ if (existing) {
1530
+ for (const agentName of agentNames) {
1531
+ existing.agentNames.add(agentName);
1532
+ }
1533
+ return;
1534
+ }
1535
+ const state = {
1536
+ path,
1537
+ agentNames: new Set(agentNames),
1538
+ lastMtimeMs: null,
1539
+ lastSize: null,
1540
+ stableSince: Date.now(),
1541
+ timer: null
1542
+ };
1543
+ this.stablePaths.set(path, state);
1544
+ this.pollStablePath(path);
1545
+ }
1546
+ pollStablePath(path) {
1547
+ const state = this.stablePaths.get(path);
1548
+ if (!state) {
1549
+ return;
1550
+ }
1551
+ let size;
1552
+ let mtimeMs;
1553
+ try {
1554
+ const stat = statSync2(path);
1555
+ size = stat.size;
1556
+ mtimeMs = stat.mtimeMs;
1557
+ } catch {
1558
+ this.stablePaths.delete(path);
1559
+ this.scheduleRefreshForAgents(state.agentNames);
1560
+ return;
1561
+ }
1562
+ const now = Date.now();
1563
+ const unchanged = state.lastSize === size && state.lastMtimeMs === mtimeMs;
1564
+ if (!unchanged) {
1565
+ state.lastSize = size;
1566
+ state.lastMtimeMs = mtimeMs;
1567
+ state.stableSince = now;
1568
+ }
1569
+ if (unchanged && now - state.stableSince >= WRITE_STABILITY_THRESHOLD_MS) {
1570
+ this.stablePaths.delete(path);
1571
+ this.scheduleRefreshForAgents(state.agentNames);
1572
+ return;
1573
+ }
1574
+ state.timer = setTimeout(() => this.pollStablePath(path), WRITE_STABILITY_POLL_MS);
1575
+ }
1576
+ scheduleRefreshForAgents(agentNames) {
1577
+ for (const agentName of agentNames) {
1578
+ this.pendingRefreshPathCounts.set(
1579
+ agentName,
1580
+ (this.pendingRefreshPathCounts.get(agentName) ?? 0) + 1
1581
+ );
1582
+ const delayMs = (this.byAgent[agentName]?.length ?? 0) === 0 ? EMPTY_AGENT_REFRESH_DEBOUNCE_MS : REFRESH_DEBOUNCE_MS;
1583
+ this.scheduleRefresh(agentName, delayMs);
1584
+ }
1585
+ }
1586
+ reportWatchError(event, data) {
1587
+ appLogger.error(event, data);
1588
+ console.error("[watch] File watcher failed:", data.error);
1589
+ }
1590
+ scheduleRefresh(agentName, delayMs = REFRESH_DEBOUNCE_MS) {
1013
1591
  appLogger.debug("scan.refresh.schedule", { agent: agentName, delay_ms: delayMs });
1014
1592
  const existing = this.refreshTimers.get(agentName);
1015
1593
  if (existing) {
@@ -1030,15 +1608,20 @@ var LiveScanStore = class {
1030
1608
  this.refreshInFlight.add(agentName);
1031
1609
  try {
1032
1610
  await this.runRefresh(agentName);
1611
+ } catch (error) {
1612
+ appLogger.error("scan.refresh.error", { agent: agentName, error });
1613
+ console.error(`[${agentName}] Session refresh failed:`, error);
1033
1614
  } finally {
1034
1615
  this.refreshInFlight.delete(agentName);
1035
1616
  if (this.pendingRefreshes.delete(agentName)) {
1036
- this.scheduleRefresh(agentName, 100);
1617
+ this.scheduleRefresh(agentName, PENDING_REFRESH_DELAY_MS);
1037
1618
  }
1038
1619
  }
1039
1620
  }
1040
1621
  async runRefresh(agentName) {
1041
1622
  const startedAt = performance.now();
1623
+ const pendingPathCount = this.pendingRefreshPathCounts.get(agentName) ?? 0;
1624
+ this.pendingRefreshPathCounts.delete(agentName);
1042
1625
  const agent = this.agents.find((item) => item.name === agentName);
1043
1626
  if (!agent) {
1044
1627
  appLogger.warn("scan.refresh.missing_agent", { agent: agentName });
@@ -1072,7 +1655,18 @@ var LiveScanStore = class {
1072
1655
  if (!this.hasStartupWindow()) {
1073
1656
  saveCachedSessions(agentName, nextSessions, buildAgentCacheMeta(agent));
1074
1657
  }
1075
- syncSessionSearchIndex(agentName, nextSessions, (sessionId) => agent.getSessionData(sessionId));
1658
+ const searchIndexOptions = pendingPathCount >= SEARCH_INDEX_BULK_PENDING_PATH_THRESHOLD ? { isBulk: true } : void 0;
1659
+ const syncResult = searchIndexOptions ? syncSessionSearchIndex(
1660
+ agentName,
1661
+ nextSessions,
1662
+ (sessionId) => agent.getSessionData(sessionId),
1663
+ searchIndexOptions
1664
+ ) : syncSessionSearchIndex(
1665
+ agentName,
1666
+ nextSessions,
1667
+ (sessionId) => agent.getSessionData(sessionId)
1668
+ );
1669
+ logSearchIndexSync("scan.refresh", syncResult, { pending_paths: pendingPathCount });
1076
1670
  const event = buildUpdateEvent(agentName, previousSessions, nextSessions);
1077
1671
  this.byAgent[agentName] = sortSessions(nextSessions);
1078
1672
  this.rebuildSessions();
@@ -1086,7 +1680,10 @@ var LiveScanStore = class {
1086
1680
  sessions: nextSessions.length,
1087
1681
  new_sessions: event?.newSessions ?? 0,
1088
1682
  updated_sessions: event?.updatedSessions ?? 0,
1089
- removed_sessions: event?.removedSessions ?? 0
1683
+ removed_sessions: event?.removedSessions ?? 0,
1684
+ pending_paths: pendingPathCount,
1685
+ search_index_mode: syncResult?.mode,
1686
+ search_index_rebuild_duration_ms: syncResult?.rebuildDurationMs == null ? void 0 : Math.round(syncResult.rebuildDurationMs)
1090
1687
  });
1091
1688
  }
1092
1689
  };
@@ -1096,10 +1693,10 @@ import { consola } from "consola";
1096
1693
 
1097
1694
  // src/version.ts
1098
1695
  import { readFileSync } from "fs";
1099
- import { resolve as resolve2, dirname as dirname3 } from "path";
1100
- import { fileURLToPath as fileURLToPath2 } from "url";
1101
- var __dirname = dirname3(fileURLToPath2(import.meta.url));
1102
- var pkg = JSON.parse(readFileSync(resolve2(__dirname, "../package.json"), "utf-8"));
1696
+ import { resolve as resolve3, dirname as dirname3 } from "path";
1697
+ import { fileURLToPath as fileURLToPath3 } from "url";
1698
+ var __dirname = dirname3(fileURLToPath3(import.meta.url));
1699
+ var pkg = JSON.parse(readFileSync(resolve3(__dirname, "../package.json"), "utf-8"));
1103
1700
  var VERSION = pkg.version;
1104
1701
 
1105
1702
  // src/output.ts
@@ -1243,7 +1840,7 @@ var main = defineCommand({
1243
1840
  log_path: appLogger.getLogPath()
1244
1841
  });
1245
1842
  if (clearCache) {
1246
- const { clearCache: clear } = await import("./dist-DMEDEJ2D.js");
1843
+ const { clearCache: clear } = await import("./dist-NT4CH6KD.js");
1247
1844
  clear();
1248
1845
  appLogger.info("cache.clear");
1249
1846
  console.log("Cache cleared.");