codesesh 0.17.0 → 0.19.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.
Files changed (43) hide show
  1. package/README.md +6 -4
  2. package/dist/{chunk-7APNDHQ6.js → chunk-G2BTNW3C.js} +2262 -495
  3. package/dist/chunk-G2BTNW3C.js.map +1 -0
  4. package/dist/{chunk-A4U2SMJJ.js → chunk-KD3DGWZY.js} +20 -12
  5. package/dist/chunk-KD3DGWZY.js.map +1 -0
  6. package/dist/{dist-KEPJFHOC.js → dist-ALXOBKV2.js} +26 -4
  7. package/dist/index.js +382 -114
  8. package/dist/index.js.map +1 -1
  9. package/dist/scan-refresh-worker.js +243 -24
  10. package/dist/scan-refresh-worker.js.map +1 -1
  11. package/dist/search-index-worker.js +73 -39
  12. package/dist/search-index-worker.js.map +1 -1
  13. package/dist/smart-tag-worker.js +2 -2
  14. package/dist/web/assets/AgentIcon-CEj7HO1m.js +1 -0
  15. package/dist/web/assets/BookmarkButton-DLEnIyeE.js +1 -0
  16. package/dist/web/assets/Dashboard-DUG0IFcy.js +39 -0
  17. package/dist/web/assets/DialogTitle-Dv6DQPIb.js +1 -0
  18. package/dist/web/assets/InteractiveReceipt-xnRqJvfd.js +1 -0
  19. package/dist/web/assets/PrismHighlighter-C7OfXHq2.js +6 -0
  20. package/dist/web/assets/Projects-BMaZc6J5.js +1 -0
  21. package/dist/web/assets/SearchFilterBar-iP6pzlZo.js +1 -0
  22. package/dist/web/assets/SearchResultsPanel-RuiLPkbP.js +1 -0
  23. package/dist/web/assets/SessionDetail-De1Wl3mu.js +54 -0
  24. package/dist/web/assets/index-B-CV7pFu.css +2 -0
  25. package/dist/web/assets/index-CQI8F_8X.js +1930 -0
  26. package/dist/web/assets/index.dom-CCLJ9_FR.js +1 -0
  27. package/dist/web/assets/jsx-runtime-n5LQ9ujS.js +1 -0
  28. package/dist/web/assets/session-indexes-BuxBafvh.js +4 -0
  29. package/dist/web/assets/session-title-CNhapdPJ.js +1 -0
  30. package/dist/web/assets/useTheme-CwDcVdGz.js +1 -0
  31. package/dist/web/assets/utils-B6KiDbIe.js +1 -0
  32. package/dist/web/index.html +10 -7
  33. package/package.json +3 -3
  34. package/dist/chunk-7APNDHQ6.js.map +0 -1
  35. package/dist/chunk-A4U2SMJJ.js.map +0 -1
  36. package/dist/web/assets/index-B076CRkD.js +0 -109
  37. package/dist/web/assets/index-CWNbVhet.css +0 -2
  38. package/dist/web/assets/markdown-dR4kAewZ.js +0 -14
  39. package/dist/web/assets/react-D9s532MZ.js +0 -1821
  40. package/dist/web/assets/rolldown-runtime-QTnfLwEv.js +0 -1
  41. package/dist/web/assets/syntax-mZXQuVcd.js +0 -6
  42. package/dist/web/assets/vendor-i9U7_fNF.js +0 -41
  43. /package/dist/{dist-KEPJFHOC.js.map → dist-ALXOBKV2.js.map} +0 -0
package/dist/index.js CHANGED
@@ -2,23 +2,27 @@
2
2
  import {
3
3
  appLogger,
4
4
  logSearchIndexSync
5
- } from "./chunk-A4U2SMJJ.js";
5
+ } from "./chunk-KD3DGWZY.js";
6
6
  import {
7
7
  FileSystemSessionSource,
8
8
  SessionAliasValidationError,
9
9
  StateStorageUnavailableError,
10
+ addCalendarDays,
10
11
  attachMissingProjectIdentities,
11
12
  attachProjectMetrics,
12
13
  buildAgentCacheMeta,
13
14
  buildDashboard,
14
15
  computeSessionDiff,
16
+ countCalendarDays,
15
17
  createProjectScopeMatcher,
16
18
  createRegisteredAgents,
17
19
  deleteBookmark,
18
20
  deleteSessionAlias,
19
21
  executeSessionSearch,
20
22
  filterSessionSearchCandidates,
23
+ filterSessionTreeByActivityWindow,
21
24
  formatSessionReference,
25
+ getAgentFullSyncCursor,
22
26
  getAgentInfoMap,
23
27
  getAgentLastFullSyncAt,
24
28
  getSessionActivityTime,
@@ -31,22 +35,30 @@ import {
31
35
  listFileActivity,
32
36
  listSessionAliases,
33
37
  loadCachedSessions,
38
+ markAgentCacheInitialized,
34
39
  markAgentFullSyncCompleted,
40
+ markAgentFullSyncProgress,
41
+ markAgentFullSyncStarted,
35
42
  matchesProjectIdentity,
36
43
  matchesProjectScope,
37
44
  materializeSessionDetailResponse,
38
45
  mergeSearchQueryOptions,
46
+ mergeSessionsUpdatedEvents,
39
47
  mergeSortedSessions,
40
48
  normalizeSessionReference,
41
49
  perf,
50
+ publishPendingPricing,
42
51
  refreshPricingCache,
52
+ saveCachedSessionChanges,
53
+ saveCachedSessions,
43
54
  scanSessions,
55
+ sessionRoutePath,
44
56
  sessionSignature,
45
57
  sortSessions,
46
- startOfLocalDay,
58
+ startOfCalendarDay,
47
59
  upsertBookmark,
48
60
  upsertSessionAlias
49
- } from "./chunk-7APNDHQ6.js";
61
+ } from "./chunk-G2BTNW3C.js";
50
62
 
51
63
  // src/index.ts
52
64
  import { defineCommand, runMain } from "citty";
@@ -58,6 +70,7 @@ import { compress } from "hono/compress";
58
70
  import { serve } from "@hono/node-server";
59
71
  import { serveStatic } from "@hono/node-server/serve-static";
60
72
  import { existsSync } from "fs";
73
+ import { createServer as createHttpsServer } from "https";
61
74
  import { resolve, dirname } from "path";
62
75
  import { fileURLToPath } from "url";
63
76
 
@@ -65,7 +78,7 @@ import { fileURLToPath } from "url";
65
78
  import { Hono } from "hono";
66
79
 
67
80
  // src/time-window-resolution.ts
68
- var DAY_MS = 24 * 60 * 60 * 1e3;
81
+ var ROLLING_DAY_MS = 24 * 60 * 60 * 1e3;
69
82
  var DEFAULT_DASHBOARD_DAYS = 30;
70
83
  function resolveTimeWindow(request) {
71
84
  return request.mode === "cli" ? resolveCliWindow(request) : resolveDashboardWindow(request);
@@ -78,7 +91,7 @@ function resolveCliWindow(request) {
78
91
  const days = parseDays(request.days);
79
92
  if (days === 0) return { to, days };
80
93
  if (days == null || days < 0) return { to };
81
- const rollingFrom = now - days * DAY_MS;
94
+ const rollingFrom = now - days * ROLLING_DAY_MS;
82
95
  return Number.isFinite(rollingFrom) ? { from: rollingFrom, to, days } : { to };
83
96
  }
84
97
  function resolveDashboardWindow(request) {
@@ -90,19 +103,19 @@ function resolveDashboardWindow(request) {
90
103
  let days = parsedDays != null && parsedDays > 0 ? parsedDays : defaults.days;
91
104
  const queryFrom = parseOptionalDate(request.query.from);
92
105
  if (queryFrom != null) {
93
- days ??= elapsedDays(queryFrom, to);
106
+ days ??= countCalendarDays(queryFrom, to);
94
107
  return { from: queryFrom, to, days };
95
108
  }
96
109
  if (parsedDays === 0 || !hasQueryDays && defaults.days === 0) {
97
110
  return { to, days: 0 };
98
111
  }
99
112
  if (defaults.from != null) {
100
- days ??= elapsedDays(defaults.from, to);
113
+ days ??= countCalendarDays(defaults.from, to);
101
114
  return { from: defaults.from, to, days };
102
115
  }
103
116
  const resolvedDays = days != null && days > 0 ? days : DEFAULT_DASHBOARD_DAYS;
104
117
  return {
105
- from: startOfLocalDay(to) - (resolvedDays - 1) * DAY_MS,
118
+ from: addCalendarDays(startOfCalendarDay(to), -(resolvedDays - 1)),
106
119
  to,
107
120
  days: resolvedDays
108
121
  };
@@ -121,10 +134,7 @@ function parseOptionalDate(value) {
121
134
  function parseDays(value) {
122
135
  if (value == null) return void 0;
123
136
  const days = Number.parseInt(value, 10);
124
- return Number.isSafeInteger(days) && Number.isSafeInteger(days * DAY_MS) ? days : void 0;
125
- }
126
- function elapsedDays(from, to) {
127
- return Math.max(1, Math.ceil((to - from) / DAY_MS));
137
+ return Number.isSafeInteger(days) && Number.isSafeInteger(days * ROLLING_DAY_MS) ? days : void 0;
128
138
  }
129
139
 
130
140
  // src/api/query-params.ts
@@ -203,13 +213,7 @@ function parseSearchOptions(c, defaults, projectIdentity) {
203
213
  };
204
214
  }
205
215
  function filterSessionsByActivityWindow(sessions, from, to) {
206
- if (from == null && to == null) return sessions;
207
- return sessions.filter((session) => {
208
- const activity = getSessionActivityTime(session);
209
- if (from != null && activity < from) return false;
210
- if (to != null && activity > to) return false;
211
- return true;
212
- });
216
+ return filterSessionTreeByActivityWindow(sessions, from, to);
213
217
  }
214
218
 
215
219
  // src/api/session-aliases-view.ts
@@ -398,6 +402,7 @@ function getSessionHeadReference(session) {
398
402
  sessionId: session.id
399
403
  };
400
404
  }
405
+ var SESSION_DETAIL_STREAM_BATCH_CHARS = 64 * 1024;
401
406
  function createSessionDetailJsonResponse(data, messages) {
402
407
  const encoder = new TextEncoder();
403
408
  const headerJson = JSON.stringify(data);
@@ -412,14 +417,24 @@ function createSessionDetailJsonResponse(data, messages) {
412
417
  wroteHeader = true;
413
418
  return;
414
419
  }
415
- const next = iterator.next();
416
- if (!next.done) {
417
- controller.enqueue(encoder.encode(`${wroteMessage ? "," : ""}${next.value}`));
420
+ const batch = [];
421
+ let batchLength = 0;
422
+ let next = iterator.next();
423
+ while (!next.done) {
424
+ const prefix = wroteMessage ? "," : "";
425
+ batch.push(prefix, next.value);
426
+ batchLength += prefix.length + next.value.length;
418
427
  wroteMessage = true;
428
+ if (batchLength >= SESSION_DETAIL_STREAM_BATCH_CHARS) break;
429
+ next = iterator.next();
430
+ }
431
+ if (next.done) {
432
+ batch.push("]}");
433
+ controller.enqueue(encoder.encode(batch.join("")));
434
+ controller.close();
419
435
  return;
420
436
  }
421
- controller.enqueue(encoder.encode("]}"));
422
- controller.close();
437
+ controller.enqueue(encoder.encode(batch.join("")));
423
438
  },
424
439
  cancel() {
425
440
  iterator.return?.();
@@ -757,7 +772,7 @@ function handleGetDashboard(c, scanSource, defaults = {}) {
757
772
  scope.projectKind,
758
773
  scope.projectKey,
759
774
  from,
760
- fixedTo ?? startOfLocalDay(to)
775
+ fixedTo ?? startOfCalendarDay(to)
761
776
  ],
762
777
  () => {
763
778
  const agentInfo = getAgentInfoMap({});
@@ -888,8 +903,48 @@ function createApiRoutes(scanSource, eventSource, options = {}) {
888
903
 
889
904
  // src/remote-access.ts
890
905
  import { randomBytes, timingSafeEqual } from "crypto";
906
+ import { readFileSync } from "fs";
891
907
  import { isIP } from "net";
892
908
  var REMOTE_ACCESS_QUERY_PARAM = "access_token";
909
+ var RemoteTransportError = class extends Error {
910
+ };
911
+ function resolveRemoteTransport(request) {
912
+ const hasCert = Boolean(request.tlsCertPath);
913
+ const hasKey = Boolean(request.tlsKeyPath);
914
+ if (hasCert !== hasKey) {
915
+ throw new RemoteTransportError("TLS requires both --tls-cert and --tls-key.");
916
+ }
917
+ if (hasCert && request.trustProxy) {
918
+ throw new RemoteTransportError(
919
+ "Use either --tls-cert/--tls-key or --trust-proxy, not both: only one of them terminates TLS."
920
+ );
921
+ }
922
+ if (hasCert) {
923
+ try {
924
+ return {
925
+ kind: "tls",
926
+ cert: readFileSync(request.tlsCertPath),
927
+ key: readFileSync(request.tlsKeyPath)
928
+ };
929
+ } catch (error) {
930
+ throw new RemoteTransportError(
931
+ `Unable to read the TLS certificate or key: ${error instanceof Error ? error.message : String(error)}`
932
+ );
933
+ }
934
+ }
935
+ if (request.trustProxy) return { kind: "trusted-proxy" };
936
+ return isLoopbackHostname(request.hostname) ? { kind: "loopback" } : { kind: "plaintext" };
937
+ }
938
+ var FORWARDED_PROTO_HEADER = "X-Forwarded-Proto";
939
+ function requireProxyTls() {
940
+ return async (c, next) => {
941
+ const proto = c.req.header(FORWARDED_PROTO_HEADER)?.split(",")[0]?.trim().toLowerCase();
942
+ if (proto !== "https") {
943
+ return c.json({ error: "Requests must arrive over TLS through the trusted proxy" }, 403);
944
+ }
945
+ await next();
946
+ };
947
+ }
893
948
  function createRemoteAccessToken() {
894
949
  return randomBytes(32).toString("base64url");
895
950
  }
@@ -969,6 +1024,7 @@ async function createServer(port, store, options = {}) {
969
1024
  const app = new Hono2();
970
1025
  const hostname = options.hostname ?? "127.0.0.1";
971
1026
  const isLoopback = isLoopbackHostname(hostname);
1027
+ const transport = options.transport ?? (isLoopback ? { kind: "loopback" } : { kind: "plaintext" });
972
1028
  const remoteAccessToken = !isLoopback ? options.remoteAccessToken ?? (options.remoteAccess ? createRemoteAccessToken() : null) : null;
973
1029
  if (!isLoopback && !remoteAccessToken) {
974
1030
  throw new Error(
@@ -995,6 +1051,9 @@ async function createServer(port, store, options = {}) {
995
1051
  });
996
1052
  }
997
1053
  });
1054
+ if (transport.kind === "trusted-proxy") {
1055
+ app.use("/api/*", requireProxyTls());
1056
+ }
998
1057
  if (remoteAccessToken) {
999
1058
  app.use("/api/*", remoteAccessAuth(remoteAccessToken));
1000
1059
  }
@@ -1012,7 +1071,7 @@ async function createServer(port, store, options = {}) {
1012
1071
  defaultSessionDays: options.defaultSessionDays
1013
1072
  };
1014
1073
  app.route("/api", createApiRoutes(store, store, routeOptions));
1015
- const webDistPath = findWebDistPath();
1074
+ const webDistPath = options.webDistPath ?? findWebDistPath();
1016
1075
  if (webDistPath) {
1017
1076
  app.use("/*", serveStatic({ root: webDistPath }));
1018
1077
  app.get("/*", serveStatic({ root: webDistPath, path: "index.html" }));
@@ -1022,7 +1081,15 @@ async function createServer(port, store, options = {}) {
1022
1081
  let actualPort = port;
1023
1082
  for (let offset = 0; offset < attempts; offset += 1) {
1024
1083
  const candidatePort = port + offset;
1025
- server = serve({ fetch: app.fetch, port: candidatePort, hostname });
1084
+ server = serve({
1085
+ fetch: app.fetch,
1086
+ port: candidatePort,
1087
+ hostname,
1088
+ ...transport.kind === "tls" ? {
1089
+ createServer: createHttpsServer,
1090
+ serverOptions: { cert: transport.cert, key: transport.key }
1091
+ } : {}
1092
+ });
1026
1093
  try {
1027
1094
  await waitForListening(server);
1028
1095
  actualPort = getListeningPort(server, candidatePort);
@@ -1042,19 +1109,33 @@ async function createServer(port, store, options = {}) {
1042
1109
  throw new Error(getServerStartupErrorMessage(error, candidatePort));
1043
1110
  }
1044
1111
  }
1045
- const baseUrl = isLoopback ? `http://localhost:${actualPort}` : `http://${hostname}:${actualPort}`;
1112
+ const scheme = transport.kind === "tls" ? "https" : "http";
1113
+ const baseUrl = isLoopback ? `${scheme}://localhost:${actualPort}` : `${scheme}://${hostname}:${actualPort}`;
1046
1114
  const url = remoteAccessToken ? `${baseUrl}/?${REMOTE_ACCESS_QUERY_PARAM}=${encodeURIComponent(remoteAccessToken)}` : baseUrl;
1047
1115
  appLogger.info("server.listen", {
1048
1116
  port: actualPort,
1049
1117
  requested_port: port,
1050
1118
  hostname,
1051
- remote_access: Boolean(remoteAccessToken)
1119
+ remote_access: Boolean(remoteAccessToken),
1120
+ transport: transport.kind
1052
1121
  });
1053
1122
  if (!isLoopback) {
1054
- appLogger.warn("server.listen.remote_access", { hostname, port: actualPort });
1123
+ appLogger.warn("server.listen.remote_access", {
1124
+ hostname,
1125
+ port: actualPort,
1126
+ transport: transport.kind
1127
+ });
1055
1128
  console.warn(`
1056
1129
  \u26A0 \u8FDC\u7A0B\u8BBF\u95EE\u5DF2\u542F\u7528\u3002\u4EFB\u4F55\u6301\u6709\u542F\u52A8 URL \u7684\u4EBA\u90FD\u53EF\u4EE5\u8BFB\u53D6\u4F60\u7684 AI \u4F1A\u8BDD\u8BB0\u5F55\u3002
1057
1130
  `);
1131
+ if (transport.kind === "plaintext") {
1132
+ appLogger.warn("server.listen.plaintext", { hostname, port: actualPort });
1133
+ console.warn(
1134
+ `\u26A0 \u4F20\u8F93\u672A\u52A0\u5BC6\u3002\u8BBF\u95EE\u4EE4\u724C\u4E0E\u5B8C\u6574\u4F1A\u8BDD\u5185\u5BB9\u4EE5\u660E\u6587\u7ECF\u8FC7\u7F51\u7EDC\uFF0CURL \u4E2D\u7684\u4EE4\u724C\u8FD8\u53EF\u80FD\u5199\u5165\u4EE3\u7406\u8BBF\u95EE\u65E5\u5FD7\u3002
1135
+ \u4F7F\u7528 --tls-cert/--tls-key \u76F4\u63A5\u542F\u7528 TLS\uFF0C\u6216\u5728\u53D7\u4FE1\u53CD\u5411\u4EE3\u7406\u540E\u4F7F\u7528 --trust-proxy\u3002
1136
+ `
1137
+ );
1138
+ }
1058
1139
  }
1059
1140
  return {
1060
1141
  url,
@@ -1532,6 +1613,22 @@ var SearchIndexJobRunner = class {
1532
1613
  logSearchIndexSync(message.context, message.result);
1533
1614
  return;
1534
1615
  }
1616
+ if (message.type === "persist-failed") {
1617
+ appLogger.error("search_index.persist_failed", {
1618
+ batch_id: batch.id,
1619
+ context: message.context,
1620
+ stage: message.stage,
1621
+ agent: message.agentName,
1622
+ sessions: message.sessions
1623
+ });
1624
+ this.settle(
1625
+ batch,
1626
+ new Error(
1627
+ `Search index worker failed to persist ${message.stage} for ${message.agentName}`
1628
+ )
1629
+ );
1630
+ return;
1631
+ }
1535
1632
  if (message.type !== "done") return;
1536
1633
  appLogger.info(`${message.context}.done`, {
1537
1634
  duration_ms: Math.round(message.durationMs),
@@ -1588,6 +1685,17 @@ var ScanStatusModel = class {
1588
1685
  backfill: { active: false, pendingAgents: [], completedAgents: [], failedAgents: [] }
1589
1686
  };
1590
1687
  snapshot() {
1688
+ const backfill = {
1689
+ ...this.status.backfill,
1690
+ pendingAgents: [...this.status.backfill.pendingAgents],
1691
+ completedAgents: [...this.status.backfill.completedAgents],
1692
+ failedAgents: [...this.status.backfill.failedAgents]
1693
+ };
1694
+ if (this.status.backfill.progress) {
1695
+ backfill.progress = { ...this.status.backfill.progress };
1696
+ } else {
1697
+ delete backfill.progress;
1698
+ }
1591
1699
  return {
1592
1700
  type: "scan-status",
1593
1701
  ...this.status,
@@ -1600,12 +1708,7 @@ var ScanStatusModel = class {
1600
1708
  { ...status }
1601
1709
  ])
1602
1710
  ),
1603
- backfill: {
1604
- ...this.status.backfill,
1605
- pendingAgents: [...this.status.backfill.pendingAgents],
1606
- completedAgents: [...this.status.backfill.completedAgents],
1607
- failedAgents: [...this.status.backfill.failedAgents]
1608
- }
1711
+ backfill
1609
1712
  };
1610
1713
  }
1611
1714
  startBatch(agentNames, phase, sessionCounts) {
@@ -1675,26 +1778,34 @@ var ScanStatusModel = class {
1675
1778
  }
1676
1779
  updateAgent(agentName, progress) {
1677
1780
  const status = this.status.agentStatuses[agentName];
1678
- if (!status || status.status !== "scanning") return null;
1781
+ if (!status || status.status === "pending" || status.status === "complete" || status.status === "indexing" && progress.phase !== "finalizing") {
1782
+ return null;
1783
+ }
1679
1784
  const now = Date.now();
1785
+ const nextStatus = progress.phase === "finalizing" ? "finalizing" : "scanning";
1786
+ const agentStatuses = {
1787
+ ...this.status.agentStatuses,
1788
+ [agentName]: {
1789
+ ...status,
1790
+ status: nextStatus,
1791
+ total: progress.total ?? status.total,
1792
+ processed: progress.processed ?? status.processed,
1793
+ sessions: progress.sessions ?? status.sessions,
1794
+ updatedAt: now
1795
+ }
1796
+ };
1680
1797
  return this.set({
1681
1798
  ...this.status,
1682
- agentStatuses: {
1683
- ...this.status.agentStatuses,
1684
- [agentName]: {
1685
- ...status,
1686
- total: progress.total ?? status.total,
1687
- processed: progress.processed ?? status.processed,
1688
- sessions: progress.sessions ?? status.sessions,
1689
- updatedAt: now
1690
- }
1691
- },
1799
+ phase: this.activePhase(this.status.pendingAgents, this.status.scanningAgents, agentStatuses),
1800
+ agentStatuses,
1692
1801
  updatedAt: now
1693
1802
  });
1694
1803
  }
1695
1804
  indexAgent(agentName) {
1696
1805
  const status = this.status.agentStatuses[agentName];
1697
- if (!this.status.active || !status || status.status !== "scanning") return null;
1806
+ if (!this.status.active || !status || status.status !== "scanning" && status.status !== "finalizing") {
1807
+ return null;
1808
+ }
1698
1809
  const now = Date.now();
1699
1810
  const agentStatuses = {
1700
1811
  ...this.status.agentStatuses,
@@ -1766,7 +1877,10 @@ var ScanStatusModel = class {
1766
1877
  });
1767
1878
  }
1768
1879
  activePhase(pendingAgents, scanningAgents, agentStatuses) {
1769
- if (pendingAgents.length === 0 && scanningAgents.length > 0 && scanningAgents.every((agentName) => agentStatuses[agentName]?.status === "indexing")) {
1880
+ if (pendingAgents.length === 0 && scanningAgents.length > 0 && scanningAgents.every((agentName) => {
1881
+ const status = agentStatuses[agentName]?.status;
1882
+ return status === "finalizing" || status === "indexing";
1883
+ })) {
1770
1884
  return "indexing";
1771
1885
  }
1772
1886
  return this.status.phase === "initializing" ? "initializing" : "scanning";
@@ -1782,6 +1896,7 @@ var REFRESH_DEBOUNCE_MS = 200;
1782
1896
  var EMPTY_AGENT_REFRESH_DEBOUNCE_MS = 3e4;
1783
1897
  var SEARCH_INDEX_BULK_PENDING_PATH_THRESHOLD = 100;
1784
1898
  var BACKFILL_INTERVAL_MS = 24 * 60 * 60 * 1e3;
1899
+ var CACHE_TRUNCATION_COVERAGE = 0.5;
1785
1900
  function buildPersistenceDiff(previousSessions, nextSessions, candidateChangedIds = []) {
1786
1901
  const { changes, removedSessionIds } = computeSessionDiff(
1787
1902
  previousSessions,
@@ -1805,6 +1920,8 @@ var AgentSyncEngine = class {
1805
1920
  sessionIndex = new LiveSessionIndex();
1806
1921
  backfillQueue = [];
1807
1922
  currentBackfillAgent;
1923
+ currentBackfillProgress;
1924
+ cacheIntegrityCheckedAgents = /* @__PURE__ */ new Set();
1808
1925
  completedBackfillAgents = [];
1809
1926
  failedBackfillAgents = [];
1810
1927
  sessionsChangedListeners = /* @__PURE__ */ new Set();
@@ -1884,6 +2001,8 @@ var AgentSyncEngine = class {
1884
2001
  }
1885
2002
  this.backfillQueue.length = 0;
1886
2003
  this.currentBackfillAgent = void 0;
2004
+ this.currentBackfillProgress = void 0;
2005
+ this.cacheIntegrityCheckedAgents.clear();
1887
2006
  const searchIndexSnapshot = this.searchIndexJobs.snapshot();
1888
2007
  appLogger.info("search_index.shutdown.started", {
1889
2008
  active_batch_id: searchIndexSnapshot.activeBatchId,
@@ -1916,6 +2035,16 @@ var AgentSyncEngine = class {
1916
2035
  );
1917
2036
  }
1918
2037
  updateAgentScanProgress(agentName, progress) {
2038
+ if (this.currentBackfillAgent === agentName) {
2039
+ this.currentBackfillProgress = {
2040
+ phase: progress.phase,
2041
+ total: progress.total,
2042
+ processed: progress.processed,
2043
+ sessions: progress.sessions
2044
+ };
2045
+ this.publishBackfillStatus();
2046
+ return;
2047
+ }
1919
2048
  this.publishStatus(this.scanStatus.updateAgent(agentName, progress));
1920
2049
  }
1921
2050
  beginAgentIndexing(agentName) {
@@ -2040,14 +2169,30 @@ var AgentSyncEngine = class {
2040
2169
  });
2041
2170
  return "committed";
2042
2171
  }
2172
+ /**
2173
+ * An agent that cannot be reached was never scanned, so there is nothing to
2174
+ * compare against. Publishing an empty result here would diff every known
2175
+ * session into a removal — the same mistake as reading a failed query as an
2176
+ * empty database.
2177
+ */
2043
2178
  refreshUnavailableAgent(agentName) {
2044
2179
  this.lastRefreshAtByAgent.set(agentName, Date.now());
2180
+ const previousSessions = this.sessionIndex.snapshot().byAgent[agentName] ?? [];
2181
+ if (previousSessions.length > 0) {
2182
+ appLogger.warn("scan.refresh.agent_unavailable", {
2183
+ agent: agentName,
2184
+ retained_sessions: previousSessions.length
2185
+ });
2186
+ return this.refreshStrategyResult(previousSessions, { status: "unchanged" });
2187
+ }
2045
2188
  return this.refreshStrategyResult([]);
2046
2189
  }
2047
2190
  async initializeAgent(agent, previousSessions) {
2048
2191
  this.setScanPhase("initializing");
2049
2192
  const scanStartedAt = performance.now();
2050
- const result = await this.runWorker(agent, previousSessions, null, this.startupScanOptions());
2193
+ const result = await this.runWorker(agent, previousSessions, null, this.startupScanOptions(), {
2194
+ checkpoint: true
2195
+ });
2051
2196
  agent.setSessionMetaMap(new Map(Object.entries(result.meta)));
2052
2197
  const sessions = attachMissingProjectIdentities(result.sessions);
2053
2198
  this.lastRefreshAtByAgent.set(agent.name, Date.now());
@@ -2092,7 +2237,7 @@ var AgentSyncEngine = class {
2092
2237
  const preciseChangedIds = checkResult.changedIds ?? null;
2093
2238
  const scanStartedAt = performance.now();
2094
2239
  if (preciseChangedIds === null) {
2095
- const result = await this.runWorker(agent, baseline, null, {});
2240
+ const result = await this.runWorker(agent, baseline, null, {}, { checkpoint: true });
2096
2241
  agent.setSessionMetaMap(new Map(Object.entries(result.meta)));
2097
2242
  const sessions2 = attachMissingProjectIdentities(result.sessions);
2098
2243
  return this.refreshStrategyResult(sessions2, {
@@ -2113,7 +2258,7 @@ var AgentSyncEngine = class {
2113
2258
  }
2114
2259
  async scanAgentFully(agent, previousSessions) {
2115
2260
  const scanStartedAt = performance.now();
2116
- const result = await this.runWorker(agent, previousSessions, null, {});
2261
+ const result = await this.runWorker(agent, previousSessions, null, {}, { checkpoint: true });
2117
2262
  agent.setSessionMetaMap(new Map(Object.entries(result.meta)));
2118
2263
  const sessions = attachMissingProjectIdentities(result.sessions);
2119
2264
  this.lastRefreshAtByAgent.set(agent.name, Date.now());
@@ -2140,8 +2285,63 @@ var AgentSyncEngine = class {
2140
2285
  changedIds,
2141
2286
  scanOptions,
2142
2287
  sourceSync: workerOptions.sourceSync,
2288
+ backfill: workerOptions.backfill,
2289
+ backfillCursor: workerOptions.backfillCursor,
2290
+ checkpoint: workerOptions.checkpoint,
2143
2291
  meta: workerOptions.meta ?? buildAgentCacheMeta(agent),
2144
- onProgress: (progress) => this.updateAgentScanProgress(agent.name, progress)
2292
+ onProgress: (progress) => this.updateAgentScanProgress(agent.name, progress),
2293
+ onCheckpoint: (checkpoint) => this.handleWorkerCheckpoint(agent, checkpoint)
2294
+ });
2295
+ }
2296
+ handleWorkerCheckpoint(agent, checkpoint) {
2297
+ if (this.isShuttingDown) return;
2298
+ if (checkpoint.stage === "scanned") {
2299
+ let persisted2 = false;
2300
+ try {
2301
+ persisted2 = saveCachedSessions(agent.name, checkpoint.sessions, checkpoint.meta);
2302
+ if (persisted2) markAgentCacheInitialized(agent.name);
2303
+ } catch (error) {
2304
+ appLogger.error("scan.checkpoint.failed", {
2305
+ agent: agent.name,
2306
+ stage: checkpoint.stage,
2307
+ error
2308
+ });
2309
+ }
2310
+ agent.setSessionMetaMap(new Map(Object.entries(checkpoint.meta)));
2311
+ const event = this.sessionIndex.commitAgentSessions(agent.name, checkpoint.sessions);
2312
+ this.emitSessionsChanged({
2313
+ agentName: agent.name,
2314
+ sessions: this.sessionIndex.snapshot().byAgent[agent.name] ?? [],
2315
+ event
2316
+ });
2317
+ appLogger.info("scan.checkpoint.persisted", {
2318
+ agent: agent.name,
2319
+ stage: checkpoint.stage,
2320
+ sessions: checkpoint.sessions.length,
2321
+ persisted: persisted2
2322
+ });
2323
+ return;
2324
+ }
2325
+ let persisted = false;
2326
+ try {
2327
+ persisted = saveCachedSessionChanges(agent.name, checkpoint.changes, [], checkpoint.meta);
2328
+ } catch (error) {
2329
+ appLogger.error("scan.checkpoint.failed", {
2330
+ agent: agent.name,
2331
+ stage: checkpoint.stage,
2332
+ sessions: checkpoint.changes.length,
2333
+ error
2334
+ });
2335
+ }
2336
+ if (persisted && checkpoint.backfillCursor) {
2337
+ markAgentFullSyncProgress(agent.name, checkpoint.backfillCursor);
2338
+ }
2339
+ appLogger.debug("scan.checkpoint.persisted", {
2340
+ agent: agent.name,
2341
+ stage: checkpoint.stage,
2342
+ sessions: checkpoint.changes.length,
2343
+ backfill_cursor: checkpoint.backfillCursor,
2344
+ persisted
2145
2345
  });
2146
2346
  }
2147
2347
  needsBackfill(agent) {
@@ -2149,7 +2349,23 @@ var AgentSyncEngine = class {
2149
2349
  if (startupScanOptions.from == null && startupScanOptions.to == null) return false;
2150
2350
  if (!agent.isAvailable()) return false;
2151
2351
  const lastSyncAt = getAgentLastFullSyncAt(agent.name);
2152
- return lastSyncAt == null || Date.now() - lastSyncAt > BACKFILL_INTERVAL_MS;
2352
+ if (lastSyncAt == null || Date.now() - lastSyncAt > BACKFILL_INTERVAL_MS) return true;
2353
+ if (!(agent instanceof FileSystemSessionSource)) return false;
2354
+ if (this.cacheIntegrityCheckedAgents.has(agent.name)) return false;
2355
+ const cached = loadCachedSessions(agent.name);
2356
+ const sourceCount = agent.listSessionSources().length;
2357
+ const cachedCount = cached?.sessions.length ?? 0;
2358
+ this.cacheIntegrityCheckedAgents.add(agent.name);
2359
+ if (sourceCount > 0 && cachedCount / sourceCount < CACHE_TRUNCATION_COVERAGE) {
2360
+ appLogger.warn("scan.backfill.cache_truncated", {
2361
+ agent: agent.name,
2362
+ cached_sessions: cachedCount,
2363
+ source_files: sourceCount,
2364
+ last_sync_at: lastSyncAt
2365
+ });
2366
+ return true;
2367
+ }
2368
+ return false;
2153
2369
  }
2154
2370
  enqueueBackfill(agentName) {
2155
2371
  if (this.isShuttingDown || this.currentBackfillAgent === agentName || this.backfillQueue.includes(agentName)) {
@@ -2164,10 +2380,12 @@ var AgentSyncEngine = class {
2164
2380
  const agentName = this.backfillQueue.shift();
2165
2381
  if (!agentName) return;
2166
2382
  this.currentBackfillAgent = agentName;
2383
+ this.currentBackfillProgress = void 0;
2167
2384
  this.publishBackfillStatus();
2168
2385
  void this.runBackfill(agentName).then((result) => {
2169
2386
  if (this.isShuttingDown) return;
2170
2387
  this.currentBackfillAgent = void 0;
2388
+ this.currentBackfillProgress = void 0;
2171
2389
  if (result === "committed") {
2172
2390
  if (!this.completedBackfillAgents.includes(agentName)) {
2173
2391
  this.completedBackfillAgents.push(agentName);
@@ -2177,6 +2395,7 @@ var AgentSyncEngine = class {
2177
2395
  );
2178
2396
  } else if (!this.failedBackfillAgents.includes(agentName)) {
2179
2397
  this.failedBackfillAgents.push(agentName);
2398
+ this.cacheIntegrityCheckedAgents.delete(agentName);
2180
2399
  }
2181
2400
  this.publishBackfillStatus();
2182
2401
  this.pumpBackfillQueue();
@@ -2193,8 +2412,10 @@ var AgentSyncEngine = class {
2193
2412
  const cached = loadCachedSessions(agentName);
2194
2413
  const baseline = cached?.sessions ?? snapshot.byAgent[agentName] ?? [];
2195
2414
  const meta = cached?.meta ?? buildAgentCacheMeta(agent);
2415
+ const backfillCursor = getAgentFullSyncCursor(agentName);
2196
2416
  if (cached) restoreAgentCacheMeta(agent, cached);
2197
2417
  try {
2418
+ markAgentFullSyncStarted(agentName);
2198
2419
  const result = await this.runWorker(
2199
2420
  agent,
2200
2421
  baseline,
@@ -2202,11 +2423,16 @@ var AgentSyncEngine = class {
2202
2423
  {},
2203
2424
  {
2204
2425
  sourceSync: agent instanceof FileSystemSessionSource,
2205
- meta
2426
+ backfill: true,
2427
+ backfillCursor,
2428
+ meta,
2429
+ checkpoint: true
2206
2430
  }
2207
2431
  );
2208
2432
  agent.setSessionMetaMap(new Map(Object.entries(result.meta)));
2209
2433
  const fullSessions = attachMissingProjectIdentities(result.sessions);
2434
+ this.currentBackfillProgress = { phase: "indexing", sessions: fullSessions.length };
2435
+ this.publishBackfillStatus();
2210
2436
  await this.commitSessionPublication({
2211
2437
  context: "scan.backfill",
2212
2438
  agentName,
@@ -2240,6 +2466,7 @@ var AgentSyncEngine = class {
2240
2466
  active: this.currentBackfillAgent != null || this.backfillQueue.length > 0,
2241
2467
  pendingAgents: [...this.backfillQueue],
2242
2468
  currentAgent: this.currentBackfillAgent,
2469
+ progress: this.currentBackfillProgress,
2243
2470
  completedAgents: [...this.completedBackfillAgents],
2244
2471
  failedAgents: [...this.failedBackfillAgents]
2245
2472
  };
@@ -2377,8 +2604,12 @@ function isSameOrChildPath(parentPath, childPath) {
2377
2604
  const path = relative(parentPath, childPath);
2378
2605
  return path === "" || !path.startsWith("..") && !isAbsolute(path);
2379
2606
  }
2607
+ var SQLITE_SIDECAR_SUFFIXES = ["-wal", "-journal"];
2608
+ function isSqliteSidecarOf(changedPath, targetPath) {
2609
+ return SQLITE_SIDECAR_SUFFIXES.some((suffix) => changedPath === `${targetPath}${suffix}`);
2610
+ }
2380
2611
  function isRelatedPath(changedPath, targetPath) {
2381
- return isSameOrChildPath(targetPath, changedPath) || isSameOrChildPath(changedPath, targetPath);
2612
+ return isSameOrChildPath(targetPath, changedPath) || isSameOrChildPath(changedPath, targetPath) || isSqliteSidecarOf(changedPath, targetPath);
2382
2613
  }
2383
2614
  function mergeScopes(target, scopes) {
2384
2615
  for (const scope of scopes) {
@@ -2669,6 +2900,9 @@ var ThreadWorkerRunner = class {
2669
2900
  previousSessions: payload.previousSessions,
2670
2901
  changedIds: payload.changedIds,
2671
2902
  sourceSync: payload.sourceSync,
2903
+ backfill: payload.backfill,
2904
+ backfillCursor: payload.backfillCursor,
2905
+ checkpoint: payload.checkpoint,
2672
2906
  scanOptions: payload.scanOptions,
2673
2907
  meta: payload.meta
2674
2908
  };
@@ -2687,7 +2921,8 @@ var ThreadWorkerRunner = class {
2687
2921
  resolve: resolve4,
2688
2922
  reject,
2689
2923
  payload,
2690
- onProgress: payload.onProgress
2924
+ onProgress: payload.onProgress,
2925
+ onCheckpoint: payload.onCheckpoint
2691
2926
  });
2692
2927
  if (!isNewWorker) {
2693
2928
  try {
@@ -2739,6 +2974,10 @@ var ThreadWorkerRunner = class {
2739
2974
  pending.onProgress?.(message.progress);
2740
2975
  return;
2741
2976
  }
2977
+ if (message.type === "checkpoint") {
2978
+ pending.onCheckpoint?.(message.checkpoint);
2979
+ return;
2980
+ }
2742
2981
  slot.pending.delete(message.requestId);
2743
2982
  if (message.type === "error") {
2744
2983
  pending.reject(new Error(message.error));
@@ -2772,36 +3011,6 @@ var ThreadWorkerRunner = class {
2772
3011
 
2773
3012
  // src/live-scan.ts
2774
3013
  var NEW_SESSION_EVENT_WINDOW_MS = 250;
2775
- function mergeEvents(previous, next) {
2776
- const changedSessionHeads = /* @__PURE__ */ new Map();
2777
- const removedSessionRefs = /* @__PURE__ */ new Map();
2778
- const sessionKey = (agentName, sessionId) => `${agentName}\0${sessionId}`;
2779
- const addChanged = (item) => {
2780
- const key = sessionKey(item.reference.agentName, item.reference.sessionId);
2781
- removedSessionRefs.delete(key);
2782
- changedSessionHeads.set(key, item);
2783
- };
2784
- const addRemoved = (item) => {
2785
- const key = sessionKey(item.agentName, item.sessionId);
2786
- changedSessionHeads.delete(key);
2787
- removedSessionRefs.set(key, item);
2788
- };
2789
- for (const item of previous.changedSessionHeads) addChanged(item);
2790
- for (const item of previous.removedSessionRefs) addRemoved(item);
2791
- for (const item of next.changedSessionHeads) addChanged(item);
2792
- for (const item of next.removedSessionRefs) addRemoved(item);
2793
- return {
2794
- type: "sessions-updated",
2795
- changedAgents: Array.from(/* @__PURE__ */ new Set([...previous.changedAgents, ...next.changedAgents])),
2796
- newSessions: previous.newSessions + next.newSessions,
2797
- updatedSessions: previous.updatedSessions + next.updatedSessions,
2798
- removedSessions: previous.removedSessions + next.removedSessions,
2799
- totalSessions: next.totalSessions,
2800
- timestamp: next.timestamp,
2801
- changedSessionHeads: [...changedSessionHeads.values()],
2802
- removedSessionRefs: [...removedSessionRefs.values()]
2803
- };
2804
- }
2805
3014
  var LiveScanStore = class {
2806
3015
  watchEnabled;
2807
3016
  scanOptions;
@@ -2854,7 +3063,11 @@ var LiveScanStore = class {
2854
3063
  });
2855
3064
  const snapshot = this.getSnapshot();
2856
3065
  const indexStartedAt = performance.now();
2857
- if (!this.deferInitialRefresh) await this.syncEngine.syncInitialIndex();
3066
+ if (!this.deferInitialRefresh) {
3067
+ await this.syncEngine.syncInitialIndex().catch((error) => {
3068
+ appLogger.error("scan.initial.index_failed", { error });
3069
+ });
3070
+ }
2858
3071
  const indexDuration = performance.now() - indexStartedAt;
2859
3072
  appLogger.info("scan.initial.done", {
2860
3073
  duration_ms: Math.round(performance.now() - startedAt),
@@ -2928,7 +3141,7 @@ var LiveScanStore = class {
2928
3141
  for (const listener of this.listeners) listener(event);
2929
3142
  }
2930
3143
  queueEvent(event) {
2931
- this.pendingEvent = this.pendingEvent ? mergeEvents(this.pendingEvent, event) : event;
3144
+ this.pendingEvent = this.pendingEvent ? mergeSessionsUpdatedEvents(this.pendingEvent, event) : event;
2932
3145
  if (this.pendingEventTimer) return;
2933
3146
  this.pendingEventTimer = setTimeout(() => {
2934
3147
  const pending = this.pendingEvent;
@@ -2952,11 +3165,11 @@ var LiveScanStore = class {
2952
3165
  import { consola } from "consola";
2953
3166
 
2954
3167
  // src/version.ts
2955
- import { readFileSync } from "fs";
3168
+ import { readFileSync as readFileSync2 } from "fs";
2956
3169
  import { resolve as resolve3, dirname as dirname3 } from "path";
2957
3170
  import { fileURLToPath as fileURLToPath4 } from "url";
2958
3171
  var __dirname = dirname3(fileURLToPath4(import.meta.url));
2959
- var pkg = JSON.parse(readFileSync(resolve3(__dirname, "../package.json"), "utf-8"));
3172
+ var pkg = JSON.parse(readFileSync2(resolve3(__dirname, "../package.json"), "utf-8"));
2960
3173
  var VERSION = pkg.version;
2961
3174
 
2962
3175
  // src/output.ts
@@ -2978,6 +3191,25 @@ function printScanResults(agents) {
2978
3191
  consola.log("");
2979
3192
  }
2980
3193
 
3194
+ // src/session-index-output.ts
3195
+ function buildSessionIndexOutput(snapshot, window = {}) {
3196
+ const sessions = filterSessionTreeByActivityWindow(snapshot.sessions, window.from, window.to);
3197
+ const agents = getAgentInfoMap(
3198
+ Object.fromEntries(
3199
+ Object.entries(snapshot.byAgent).map(([name, list]) => [
3200
+ name,
3201
+ filterSessionTreeByActivityWindow(list, window.from, window.to).length
3202
+ ])
3203
+ )
3204
+ ).map(({ name, displayName, count }) => ({
3205
+ name,
3206
+ displayName,
3207
+ count,
3208
+ available: count > 0
3209
+ }));
3210
+ return { agents, sessions };
3211
+ }
3212
+
2981
3213
  // src/runtime-plan.ts
2982
3214
  function parseSessionUri(uri) {
2983
3215
  const match = uri.match(/^([a-z]+):\/\/(.+)$/i);
@@ -3003,7 +3235,10 @@ function buildCliRuntimePlan(input, environment) {
3003
3235
  function resolveStartupUrl(startupUrl, targetSession) {
3004
3236
  if (!targetSession) return startupUrl;
3005
3237
  const url = new URL(startupUrl);
3006
- url.pathname = `/${targetSession.agent.toLowerCase()}/${targetSession.sessionId}`;
3238
+ url.pathname = sessionRoutePath({
3239
+ agentName: targetSession.agent,
3240
+ sessionId: targetSession.sessionId
3241
+ });
3007
3242
  return url.toString();
3008
3243
  }
3009
3244
  function redactStartupUrl(startupUrl) {
@@ -3028,6 +3263,27 @@ function hasExplicitPortArg(argv) {
3028
3263
  });
3029
3264
  }
3030
3265
 
3266
+ // src/pricing-refresh.ts
3267
+ var REFRESH_TIMEOUT_MS = 1e4;
3268
+ function startPricingRefresh(timeoutMs = REFRESH_TIMEOUT_MS) {
3269
+ const controller = new AbortController();
3270
+ const completion = refreshPricingCache({ signal: controller.signal, timeoutMs }).catch(
3271
+ (error) => {
3272
+ appLogger.warn("pricing.refresh.error", { error });
3273
+ return false;
3274
+ }
3275
+ );
3276
+ return {
3277
+ publish() {
3278
+ if (publishPendingPricing()) appLogger.info("pricing.refresh.published", {});
3279
+ },
3280
+ async cancel() {
3281
+ controller.abort();
3282
+ await completion;
3283
+ }
3284
+ };
3285
+ }
3286
+
3031
3287
  // src/index.ts
3032
3288
  var main = defineCommand({
3033
3289
  meta: {
@@ -3052,6 +3308,19 @@ var main = defineCommand({
3052
3308
  description: "Allow authenticated access when binding to a non-loopback address",
3053
3309
  default: false
3054
3310
  },
3311
+ "tls-cert": {
3312
+ type: "string",
3313
+ description: "Path to a TLS certificate; serve remote access over HTTPS"
3314
+ },
3315
+ "tls-key": {
3316
+ type: "string",
3317
+ description: "Path to the TLS private key matching --tls-cert"
3318
+ },
3319
+ "trust-proxy": {
3320
+ type: "boolean",
3321
+ description: "A reverse proxy in front of CodeSesh terminates TLS",
3322
+ default: false
3323
+ },
3055
3324
  agent: {
3056
3325
  type: "string",
3057
3326
  alias: "a",
@@ -3124,6 +3393,18 @@ var main = defineCommand({
3124
3393
  );
3125
3394
  process.exit(1);
3126
3395
  }
3396
+ let transport;
3397
+ try {
3398
+ transport = resolveRemoteTransport({
3399
+ hostname,
3400
+ tlsCertPath: args["tls-cert"],
3401
+ tlsKeyPath: args["tls-key"],
3402
+ trustProxy: args["trust-proxy"]
3403
+ });
3404
+ } catch (error) {
3405
+ console.error(error instanceof Error ? error.message : String(error));
3406
+ process.exit(1);
3407
+ }
3127
3408
  if (trace) {
3128
3409
  perf.enable();
3129
3410
  }
@@ -3137,12 +3418,12 @@ var main = defineCommand({
3137
3418
  log_path: appLogger.getLogPath()
3138
3419
  });
3139
3420
  if (clearCache) {
3140
- const { clearCache: clear } = await import("./dist-KEPJFHOC.js");
3421
+ const { clearCache: clear } = await import("./dist-ALXOBKV2.js");
3141
3422
  clear();
3142
3423
  appLogger.info("cache.clear");
3143
3424
  console.log("Cache cleared.");
3144
3425
  }
3145
- void refreshPricingCache();
3426
+ const pricingRefresh = startPricingRefresh();
3146
3427
  let targetSession = null;
3147
3428
  if (args.session) {
3148
3429
  targetSession = parseSessionUri(args.session);
@@ -3186,26 +3467,10 @@ var main = defineCommand({
3186
3467
  console.log(perf.getReport());
3187
3468
  }
3188
3469
  if (jsonOnly) {
3189
- const windowed = result.sessions.filter((s) => {
3190
- const activity = s.time_updated ?? s.time_created;
3191
- if (listDefaultFrom != null && activity < listDefaultFrom) return false;
3192
- if (listDefaultTo != null && activity > listDefaultTo) return false;
3193
- return true;
3194
- });
3195
- const info = getAgentInfoMap(
3196
- Object.fromEntries(Object.entries(result.byAgent).map(([k, v]) => [k, v.length]))
3197
- );
3198
- const output = {
3199
- agents: info.map(({ name, displayName, count }) => ({
3200
- name,
3201
- displayName,
3202
- count,
3203
- available: count > 0
3204
- })),
3205
- sessions: windowed
3206
- };
3470
+ await pricingRefresh.cancel();
3471
+ const output = buildSessionIndexOutput(result, { from: listDefaultFrom, to: listDefaultTo });
3207
3472
  appLogger.info("cli.json_output", {
3208
- sessions: windowed.length,
3473
+ sessions: output.sessions.length,
3209
3474
  duration_ms: Math.round(performance.now() - startedAt)
3210
3475
  });
3211
3476
  console.log(JSON.stringify(output, null, 2));
@@ -3221,7 +3486,8 @@ var main = defineCommand({
3221
3486
  defaultSessionDays: listDefaultDays,
3222
3487
  portFallbackAttempts: explicitPort ? 1 : DEFAULT_PORT_FALLBACK_ATTEMPTS,
3223
3488
  hostname,
3224
- remoteAccess
3489
+ remoteAccess,
3490
+ transport
3225
3491
  });
3226
3492
  } catch (error) {
3227
3493
  console.error(getServerStartupErrorMessage(error, port));
@@ -3229,6 +3495,7 @@ var main = defineCommand({
3229
3495
  }
3230
3496
  const { url } = app;
3231
3497
  if (!jsonOnly) {
3498
+ pricingRefresh.publish();
3232
3499
  store.startBackgroundRefresh();
3233
3500
  }
3234
3501
  let shuttingDown = false;
@@ -3236,6 +3503,7 @@ var main = defineCommand({
3236
3503
  if (shuttingDown) return;
3237
3504
  shuttingDown = true;
3238
3505
  appLogger.info("cli.shutdown", { signal });
3506
+ await pricingRefresh.cancel();
3239
3507
  await app.shutdown();
3240
3508
  process.exit(0);
3241
3509
  };