codesesh 0.17.0 → 0.18.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 +2 -2
  2. package/dist/{chunk-A4U2SMJJ.js → chunk-DHNP33L4.js} +20 -12
  3. package/dist/chunk-DHNP33L4.js.map +1 -0
  4. package/dist/{chunk-7APNDHQ6.js → chunk-HIYT2ZJL.js} +659 -353
  5. package/dist/chunk-HIYT2ZJL.js.map +1 -0
  6. package/dist/{dist-KEPJFHOC.js → dist-DMGLFBW6.js} +14 -4
  7. package/dist/index.js +224 -81
  8. package/dist/index.js.map +1 -1
  9. package/dist/scan-refresh-worker.js +2 -2
  10. package/dist/search-index-worker.js +73 -39
  11. package/dist/search-index-worker.js.map +1 -1
  12. package/dist/smart-tag-worker.js +2 -2
  13. package/dist/web/assets/AgentIcon-DcUJM198.js +1 -0
  14. package/dist/web/assets/BookmarkButton-CJjTjyae.js +1 -0
  15. package/dist/web/assets/Dashboard-wjTLT17d.js +39 -0
  16. package/dist/web/assets/DialogTitle-Bz0eXTGu.js +1 -0
  17. package/dist/web/assets/InteractiveReceipt-xnRqJvfd.js +1 -0
  18. package/dist/web/assets/PrismHighlighter-C7OfXHq2.js +6 -0
  19. package/dist/web/assets/Projects-DBxubQuM.js +1 -0
  20. package/dist/web/assets/SearchFilterBar-BG9ah_Po.js +1 -0
  21. package/dist/web/assets/SearchResultsPanel-Clmf2xKw.js +1 -0
  22. package/dist/web/assets/SessionDetail-CgR7ipmi.js +54 -0
  23. package/dist/web/assets/index-DR07vs5V.js +1890 -0
  24. package/dist/web/assets/index-DuB6tGoe.css +2 -0
  25. package/dist/web/assets/index.dom-CCLJ9_FR.js +1 -0
  26. package/dist/web/assets/jsx-runtime-n5LQ9ujS.js +1 -0
  27. package/dist/web/assets/preload-helper-CmKXJxR3.js +1 -0
  28. package/dist/web/assets/session-indexes-EHJdYE4j.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 +11 -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-DMGLFBW6.js.map} +0 -0
@@ -16,15 +16,18 @@ import {
16
16
  deleteBookmark,
17
17
  deleteSessionAlias,
18
18
  diffSessionSources,
19
+ ensurePrivateDirectory,
19
20
  ensureSessionTagsSync,
20
21
  executeSessionSearch,
21
22
  filterSessionSearchCandidates,
22
23
  getAgentInfoMap,
23
24
  getAgentLastFullSyncAt,
24
25
  getCachePath2,
26
+ getPricingGeneration,
25
27
  getRegisteredAgents,
26
28
  getSessionActivityTime,
27
29
  getSmartTagSourceTimestamp,
30
+ hasPendingPricing,
28
31
  importBookmarks,
29
32
  isAgentCacheInitialized,
30
33
  isProjectIdentityKind,
@@ -41,20 +44,22 @@ import {
41
44
  mergeSearchQueryOptions,
42
45
  mergeSortedSessions,
43
46
  perf,
47
+ publishPendingPricing,
44
48
  refreshPricingCache,
45
49
  registerAgent,
50
+ restrictExistingPrivateFiles,
51
+ restrictPrivateFile,
46
52
  saveCachedSessionChanges,
47
53
  saveCachedSessions,
48
54
  scanSessions,
49
55
  sessionSignature,
50
56
  setCoreDiagnostics,
51
57
  sortSessions,
52
- startOfLocalDay,
53
58
  syncSessionSearchIndex,
54
59
  syncSessionSearchIndexChanges,
55
60
  upsertBookmark,
56
61
  upsertSessionAlias
57
- } from "./chunk-7APNDHQ6.js";
62
+ } from "./chunk-HIYT2ZJL.js";
58
63
  export {
59
64
  BaseAgent,
60
65
  StateStorageUnavailableError as BookmarkStorageUnavailableError,
@@ -73,15 +78,18 @@ export {
73
78
  deleteBookmark,
74
79
  deleteSessionAlias,
75
80
  diffSessionSources,
81
+ ensurePrivateDirectory,
76
82
  ensureSessionTagsSync,
77
83
  executeSessionSearch,
78
84
  filterSessionSearchCandidates,
79
85
  getAgentInfoMap,
80
86
  getAgentLastFullSyncAt,
81
87
  getCachePath2 as getCachePath,
88
+ getPricingGeneration,
82
89
  getRegisteredAgents,
83
90
  getSessionActivityTime,
84
91
  getSmartTagSourceTimestamp,
92
+ hasPendingPricing,
85
93
  importBookmarks,
86
94
  isAgentCacheInitialized,
87
95
  isProjectIdentityKind,
@@ -98,18 +106,20 @@ export {
98
106
  mergeSearchQueryOptions,
99
107
  mergeSortedSessions,
100
108
  perf,
109
+ publishPendingPricing,
101
110
  refreshPricingCache,
102
111
  registerAgent,
112
+ restrictExistingPrivateFiles,
113
+ restrictPrivateFile,
103
114
  saveCachedSessionChanges,
104
115
  saveCachedSessions,
105
116
  scanSessions,
106
117
  sessionSignature,
107
118
  setCoreDiagnostics,
108
119
  sortSessions,
109
- startOfLocalDay,
110
120
  syncSessionSearchIndex,
111
121
  syncSessionSearchIndexChanges,
112
122
  upsertBookmark,
113
123
  upsertSessionAlias
114
124
  };
115
- //# sourceMappingURL=dist-KEPJFHOC.js.map
125
+ //# sourceMappingURL=dist-DMGLFBW6.js.map
package/dist/index.js CHANGED
@@ -2,16 +2,18 @@
2
2
  import {
3
3
  appLogger,
4
4
  logSearchIndexSync
5
- } from "./chunk-A4U2SMJJ.js";
5
+ } from "./chunk-DHNP33L4.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,
@@ -36,17 +38,20 @@ import {
36
38
  matchesProjectScope,
37
39
  materializeSessionDetailResponse,
38
40
  mergeSearchQueryOptions,
41
+ mergeSessionsUpdatedEvents,
39
42
  mergeSortedSessions,
40
43
  normalizeSessionReference,
41
44
  perf,
45
+ publishPendingPricing,
42
46
  refreshPricingCache,
43
47
  scanSessions,
48
+ sessionRoutePath,
44
49
  sessionSignature,
45
50
  sortSessions,
46
- startOfLocalDay,
51
+ startOfCalendarDay,
47
52
  upsertBookmark,
48
53
  upsertSessionAlias
49
- } from "./chunk-7APNDHQ6.js";
54
+ } from "./chunk-HIYT2ZJL.js";
50
55
 
51
56
  // src/index.ts
52
57
  import { defineCommand, runMain } from "citty";
@@ -58,6 +63,7 @@ import { compress } from "hono/compress";
58
63
  import { serve } from "@hono/node-server";
59
64
  import { serveStatic } from "@hono/node-server/serve-static";
60
65
  import { existsSync } from "fs";
66
+ import { createServer as createHttpsServer } from "https";
61
67
  import { resolve, dirname } from "path";
62
68
  import { fileURLToPath } from "url";
63
69
 
@@ -65,7 +71,7 @@ import { fileURLToPath } from "url";
65
71
  import { Hono } from "hono";
66
72
 
67
73
  // src/time-window-resolution.ts
68
- var DAY_MS = 24 * 60 * 60 * 1e3;
74
+ var ROLLING_DAY_MS = 24 * 60 * 60 * 1e3;
69
75
  var DEFAULT_DASHBOARD_DAYS = 30;
70
76
  function resolveTimeWindow(request) {
71
77
  return request.mode === "cli" ? resolveCliWindow(request) : resolveDashboardWindow(request);
@@ -78,7 +84,7 @@ function resolveCliWindow(request) {
78
84
  const days = parseDays(request.days);
79
85
  if (days === 0) return { to, days };
80
86
  if (days == null || days < 0) return { to };
81
- const rollingFrom = now - days * DAY_MS;
87
+ const rollingFrom = now - days * ROLLING_DAY_MS;
82
88
  return Number.isFinite(rollingFrom) ? { from: rollingFrom, to, days } : { to };
83
89
  }
84
90
  function resolveDashboardWindow(request) {
@@ -90,19 +96,19 @@ function resolveDashboardWindow(request) {
90
96
  let days = parsedDays != null && parsedDays > 0 ? parsedDays : defaults.days;
91
97
  const queryFrom = parseOptionalDate(request.query.from);
92
98
  if (queryFrom != null) {
93
- days ??= elapsedDays(queryFrom, to);
99
+ days ??= countCalendarDays(queryFrom, to);
94
100
  return { from: queryFrom, to, days };
95
101
  }
96
102
  if (parsedDays === 0 || !hasQueryDays && defaults.days === 0) {
97
103
  return { to, days: 0 };
98
104
  }
99
105
  if (defaults.from != null) {
100
- days ??= elapsedDays(defaults.from, to);
106
+ days ??= countCalendarDays(defaults.from, to);
101
107
  return { from: defaults.from, to, days };
102
108
  }
103
109
  const resolvedDays = days != null && days > 0 ? days : DEFAULT_DASHBOARD_DAYS;
104
110
  return {
105
- from: startOfLocalDay(to) - (resolvedDays - 1) * DAY_MS,
111
+ from: addCalendarDays(startOfCalendarDay(to), -(resolvedDays - 1)),
106
112
  to,
107
113
  days: resolvedDays
108
114
  };
@@ -121,10 +127,7 @@ function parseOptionalDate(value) {
121
127
  function parseDays(value) {
122
128
  if (value == null) return void 0;
123
129
  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));
130
+ return Number.isSafeInteger(days) && Number.isSafeInteger(days * ROLLING_DAY_MS) ? days : void 0;
128
131
  }
129
132
 
130
133
  // src/api/query-params.ts
@@ -398,6 +401,7 @@ function getSessionHeadReference(session) {
398
401
  sessionId: session.id
399
402
  };
400
403
  }
404
+ var SESSION_DETAIL_STREAM_BATCH_CHARS = 64 * 1024;
401
405
  function createSessionDetailJsonResponse(data, messages) {
402
406
  const encoder = new TextEncoder();
403
407
  const headerJson = JSON.stringify(data);
@@ -412,14 +416,24 @@ function createSessionDetailJsonResponse(data, messages) {
412
416
  wroteHeader = true;
413
417
  return;
414
418
  }
415
- const next = iterator.next();
416
- if (!next.done) {
417
- controller.enqueue(encoder.encode(`${wroteMessage ? "," : ""}${next.value}`));
419
+ const batch = [];
420
+ let batchLength = 0;
421
+ let next = iterator.next();
422
+ while (!next.done) {
423
+ const prefix = wroteMessage ? "," : "";
424
+ batch.push(prefix, next.value);
425
+ batchLength += prefix.length + next.value.length;
418
426
  wroteMessage = true;
427
+ if (batchLength >= SESSION_DETAIL_STREAM_BATCH_CHARS) break;
428
+ next = iterator.next();
429
+ }
430
+ if (next.done) {
431
+ batch.push("]}");
432
+ controller.enqueue(encoder.encode(batch.join("")));
433
+ controller.close();
419
434
  return;
420
435
  }
421
- controller.enqueue(encoder.encode("]}"));
422
- controller.close();
436
+ controller.enqueue(encoder.encode(batch.join("")));
423
437
  },
424
438
  cancel() {
425
439
  iterator.return?.();
@@ -757,7 +771,7 @@ function handleGetDashboard(c, scanSource, defaults = {}) {
757
771
  scope.projectKind,
758
772
  scope.projectKey,
759
773
  from,
760
- fixedTo ?? startOfLocalDay(to)
774
+ fixedTo ?? startOfCalendarDay(to)
761
775
  ],
762
776
  () => {
763
777
  const agentInfo = getAgentInfoMap({});
@@ -888,8 +902,48 @@ function createApiRoutes(scanSource, eventSource, options = {}) {
888
902
 
889
903
  // src/remote-access.ts
890
904
  import { randomBytes, timingSafeEqual } from "crypto";
905
+ import { readFileSync } from "fs";
891
906
  import { isIP } from "net";
892
907
  var REMOTE_ACCESS_QUERY_PARAM = "access_token";
908
+ var RemoteTransportError = class extends Error {
909
+ };
910
+ function resolveRemoteTransport(request) {
911
+ const hasCert = Boolean(request.tlsCertPath);
912
+ const hasKey = Boolean(request.tlsKeyPath);
913
+ if (hasCert !== hasKey) {
914
+ throw new RemoteTransportError("TLS requires both --tls-cert and --tls-key.");
915
+ }
916
+ if (hasCert && request.trustProxy) {
917
+ throw new RemoteTransportError(
918
+ "Use either --tls-cert/--tls-key or --trust-proxy, not both: only one of them terminates TLS."
919
+ );
920
+ }
921
+ if (hasCert) {
922
+ try {
923
+ return {
924
+ kind: "tls",
925
+ cert: readFileSync(request.tlsCertPath),
926
+ key: readFileSync(request.tlsKeyPath)
927
+ };
928
+ } catch (error) {
929
+ throw new RemoteTransportError(
930
+ `Unable to read the TLS certificate or key: ${error instanceof Error ? error.message : String(error)}`
931
+ );
932
+ }
933
+ }
934
+ if (request.trustProxy) return { kind: "trusted-proxy" };
935
+ return isLoopbackHostname(request.hostname) ? { kind: "loopback" } : { kind: "plaintext" };
936
+ }
937
+ var FORWARDED_PROTO_HEADER = "X-Forwarded-Proto";
938
+ function requireProxyTls() {
939
+ return async (c, next) => {
940
+ const proto = c.req.header(FORWARDED_PROTO_HEADER)?.split(",")[0]?.trim().toLowerCase();
941
+ if (proto !== "https") {
942
+ return c.json({ error: "Requests must arrive over TLS through the trusted proxy" }, 403);
943
+ }
944
+ await next();
945
+ };
946
+ }
893
947
  function createRemoteAccessToken() {
894
948
  return randomBytes(32).toString("base64url");
895
949
  }
@@ -969,6 +1023,7 @@ async function createServer(port, store, options = {}) {
969
1023
  const app = new Hono2();
970
1024
  const hostname = options.hostname ?? "127.0.0.1";
971
1025
  const isLoopback = isLoopbackHostname(hostname);
1026
+ const transport = options.transport ?? (isLoopback ? { kind: "loopback" } : { kind: "plaintext" });
972
1027
  const remoteAccessToken = !isLoopback ? options.remoteAccessToken ?? (options.remoteAccess ? createRemoteAccessToken() : null) : null;
973
1028
  if (!isLoopback && !remoteAccessToken) {
974
1029
  throw new Error(
@@ -995,6 +1050,9 @@ async function createServer(port, store, options = {}) {
995
1050
  });
996
1051
  }
997
1052
  });
1053
+ if (transport.kind === "trusted-proxy") {
1054
+ app.use("/api/*", requireProxyTls());
1055
+ }
998
1056
  if (remoteAccessToken) {
999
1057
  app.use("/api/*", remoteAccessAuth(remoteAccessToken));
1000
1058
  }
@@ -1012,7 +1070,7 @@ async function createServer(port, store, options = {}) {
1012
1070
  defaultSessionDays: options.defaultSessionDays
1013
1071
  };
1014
1072
  app.route("/api", createApiRoutes(store, store, routeOptions));
1015
- const webDistPath = findWebDistPath();
1073
+ const webDistPath = options.webDistPath ?? findWebDistPath();
1016
1074
  if (webDistPath) {
1017
1075
  app.use("/*", serveStatic({ root: webDistPath }));
1018
1076
  app.get("/*", serveStatic({ root: webDistPath, path: "index.html" }));
@@ -1022,7 +1080,15 @@ async function createServer(port, store, options = {}) {
1022
1080
  let actualPort = port;
1023
1081
  for (let offset = 0; offset < attempts; offset += 1) {
1024
1082
  const candidatePort = port + offset;
1025
- server = serve({ fetch: app.fetch, port: candidatePort, hostname });
1083
+ server = serve({
1084
+ fetch: app.fetch,
1085
+ port: candidatePort,
1086
+ hostname,
1087
+ ...transport.kind === "tls" ? {
1088
+ createServer: createHttpsServer,
1089
+ serverOptions: { cert: transport.cert, key: transport.key }
1090
+ } : {}
1091
+ });
1026
1092
  try {
1027
1093
  await waitForListening(server);
1028
1094
  actualPort = getListeningPort(server, candidatePort);
@@ -1042,19 +1108,33 @@ async function createServer(port, store, options = {}) {
1042
1108
  throw new Error(getServerStartupErrorMessage(error, candidatePort));
1043
1109
  }
1044
1110
  }
1045
- const baseUrl = isLoopback ? `http://localhost:${actualPort}` : `http://${hostname}:${actualPort}`;
1111
+ const scheme = transport.kind === "tls" ? "https" : "http";
1112
+ const baseUrl = isLoopback ? `${scheme}://localhost:${actualPort}` : `${scheme}://${hostname}:${actualPort}`;
1046
1113
  const url = remoteAccessToken ? `${baseUrl}/?${REMOTE_ACCESS_QUERY_PARAM}=${encodeURIComponent(remoteAccessToken)}` : baseUrl;
1047
1114
  appLogger.info("server.listen", {
1048
1115
  port: actualPort,
1049
1116
  requested_port: port,
1050
1117
  hostname,
1051
- remote_access: Boolean(remoteAccessToken)
1118
+ remote_access: Boolean(remoteAccessToken),
1119
+ transport: transport.kind
1052
1120
  });
1053
1121
  if (!isLoopback) {
1054
- appLogger.warn("server.listen.remote_access", { hostname, port: actualPort });
1122
+ appLogger.warn("server.listen.remote_access", {
1123
+ hostname,
1124
+ port: actualPort,
1125
+ transport: transport.kind
1126
+ });
1055
1127
  console.warn(`
1056
1128
  \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
1129
  `);
1130
+ if (transport.kind === "plaintext") {
1131
+ appLogger.warn("server.listen.plaintext", { hostname, port: actualPort });
1132
+ console.warn(
1133
+ `\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
1134
+ \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
1135
+ `
1136
+ );
1137
+ }
1058
1138
  }
1059
1139
  return {
1060
1140
  url,
@@ -1532,6 +1612,22 @@ var SearchIndexJobRunner = class {
1532
1612
  logSearchIndexSync(message.context, message.result);
1533
1613
  return;
1534
1614
  }
1615
+ if (message.type === "persist-failed") {
1616
+ appLogger.error("search_index.persist_failed", {
1617
+ batch_id: batch.id,
1618
+ context: message.context,
1619
+ stage: message.stage,
1620
+ agent: message.agentName,
1621
+ sessions: message.sessions
1622
+ });
1623
+ this.settle(
1624
+ batch,
1625
+ new Error(
1626
+ `Search index worker failed to persist ${message.stage} for ${message.agentName}`
1627
+ )
1628
+ );
1629
+ return;
1630
+ }
1535
1631
  if (message.type !== "done") return;
1536
1632
  appLogger.info(`${message.context}.done`, {
1537
1633
  duration_ms: Math.round(message.durationMs),
@@ -2040,8 +2136,22 @@ var AgentSyncEngine = class {
2040
2136
  });
2041
2137
  return "committed";
2042
2138
  }
2139
+ /**
2140
+ * An agent that cannot be reached was never scanned, so there is nothing to
2141
+ * compare against. Publishing an empty result here would diff every known
2142
+ * session into a removal — the same mistake as reading a failed query as an
2143
+ * empty database.
2144
+ */
2043
2145
  refreshUnavailableAgent(agentName) {
2044
2146
  this.lastRefreshAtByAgent.set(agentName, Date.now());
2147
+ const previousSessions = this.sessionIndex.snapshot().byAgent[agentName] ?? [];
2148
+ if (previousSessions.length > 0) {
2149
+ appLogger.warn("scan.refresh.agent_unavailable", {
2150
+ agent: agentName,
2151
+ retained_sessions: previousSessions.length
2152
+ });
2153
+ return this.refreshStrategyResult(previousSessions, { status: "unchanged" });
2154
+ }
2045
2155
  return this.refreshStrategyResult([]);
2046
2156
  }
2047
2157
  async initializeAgent(agent, previousSessions) {
@@ -2377,8 +2487,12 @@ function isSameOrChildPath(parentPath, childPath) {
2377
2487
  const path = relative(parentPath, childPath);
2378
2488
  return path === "" || !path.startsWith("..") && !isAbsolute(path);
2379
2489
  }
2490
+ var SQLITE_SIDECAR_SUFFIXES = ["-wal", "-journal"];
2491
+ function isSqliteSidecarOf(changedPath, targetPath) {
2492
+ return SQLITE_SIDECAR_SUFFIXES.some((suffix) => changedPath === `${targetPath}${suffix}`);
2493
+ }
2380
2494
  function isRelatedPath(changedPath, targetPath) {
2381
- return isSameOrChildPath(targetPath, changedPath) || isSameOrChildPath(changedPath, targetPath);
2495
+ return isSameOrChildPath(targetPath, changedPath) || isSameOrChildPath(changedPath, targetPath) || isSqliteSidecarOf(changedPath, targetPath);
2382
2496
  }
2383
2497
  function mergeScopes(target, scopes) {
2384
2498
  for (const scope of scopes) {
@@ -2772,36 +2886,6 @@ var ThreadWorkerRunner = class {
2772
2886
 
2773
2887
  // src/live-scan.ts
2774
2888
  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
2889
  var LiveScanStore = class {
2806
2890
  watchEnabled;
2807
2891
  scanOptions;
@@ -2854,7 +2938,11 @@ var LiveScanStore = class {
2854
2938
  });
2855
2939
  const snapshot = this.getSnapshot();
2856
2940
  const indexStartedAt = performance.now();
2857
- if (!this.deferInitialRefresh) await this.syncEngine.syncInitialIndex();
2941
+ if (!this.deferInitialRefresh) {
2942
+ await this.syncEngine.syncInitialIndex().catch((error) => {
2943
+ appLogger.error("scan.initial.index_failed", { error });
2944
+ });
2945
+ }
2858
2946
  const indexDuration = performance.now() - indexStartedAt;
2859
2947
  appLogger.info("scan.initial.done", {
2860
2948
  duration_ms: Math.round(performance.now() - startedAt),
@@ -2928,7 +3016,7 @@ var LiveScanStore = class {
2928
3016
  for (const listener of this.listeners) listener(event);
2929
3017
  }
2930
3018
  queueEvent(event) {
2931
- this.pendingEvent = this.pendingEvent ? mergeEvents(this.pendingEvent, event) : event;
3019
+ this.pendingEvent = this.pendingEvent ? mergeSessionsUpdatedEvents(this.pendingEvent, event) : event;
2932
3020
  if (this.pendingEventTimer) return;
2933
3021
  this.pendingEventTimer = setTimeout(() => {
2934
3022
  const pending = this.pendingEvent;
@@ -2952,11 +3040,11 @@ var LiveScanStore = class {
2952
3040
  import { consola } from "consola";
2953
3041
 
2954
3042
  // src/version.ts
2955
- import { readFileSync } from "fs";
3043
+ import { readFileSync as readFileSync2 } from "fs";
2956
3044
  import { resolve as resolve3, dirname as dirname3 } from "path";
2957
3045
  import { fileURLToPath as fileURLToPath4 } from "url";
2958
3046
  var __dirname = dirname3(fileURLToPath4(import.meta.url));
2959
- var pkg = JSON.parse(readFileSync(resolve3(__dirname, "../package.json"), "utf-8"));
3047
+ var pkg = JSON.parse(readFileSync2(resolve3(__dirname, "../package.json"), "utf-8"));
2960
3048
  var VERSION = pkg.version;
2961
3049
 
2962
3050
  // src/output.ts
@@ -2978,6 +3066,25 @@ function printScanResults(agents) {
2978
3066
  consola.log("");
2979
3067
  }
2980
3068
 
3069
+ // src/session-index-output.ts
3070
+ function buildSessionIndexOutput(snapshot, window = {}) {
3071
+ const sessions = snapshot.sessions.filter((session) => {
3072
+ const activity = session.time_updated ?? session.time_created;
3073
+ if (window.from != null && activity < window.from) return false;
3074
+ if (window.to != null && activity > window.to) return false;
3075
+ return true;
3076
+ });
3077
+ const agents = getAgentInfoMap(
3078
+ Object.fromEntries(Object.entries(snapshot.byAgent).map(([name, list]) => [name, list.length]))
3079
+ ).map(({ name, displayName, count }) => ({
3080
+ name,
3081
+ displayName,
3082
+ count,
3083
+ available: count > 0
3084
+ }));
3085
+ return { agents, sessions };
3086
+ }
3087
+
2981
3088
  // src/runtime-plan.ts
2982
3089
  function parseSessionUri(uri) {
2983
3090
  const match = uri.match(/^([a-z]+):\/\/(.+)$/i);
@@ -3003,7 +3110,10 @@ function buildCliRuntimePlan(input, environment) {
3003
3110
  function resolveStartupUrl(startupUrl, targetSession) {
3004
3111
  if (!targetSession) return startupUrl;
3005
3112
  const url = new URL(startupUrl);
3006
- url.pathname = `/${targetSession.agent.toLowerCase()}/${targetSession.sessionId}`;
3113
+ url.pathname = sessionRoutePath({
3114
+ agentName: targetSession.agent,
3115
+ sessionId: targetSession.sessionId
3116
+ });
3007
3117
  return url.toString();
3008
3118
  }
3009
3119
  function redactStartupUrl(startupUrl) {
@@ -3028,6 +3138,27 @@ function hasExplicitPortArg(argv) {
3028
3138
  });
3029
3139
  }
3030
3140
 
3141
+ // src/pricing-refresh.ts
3142
+ var REFRESH_TIMEOUT_MS = 1e4;
3143
+ function startPricingRefresh(timeoutMs = REFRESH_TIMEOUT_MS) {
3144
+ const controller = new AbortController();
3145
+ const completion = refreshPricingCache({ signal: controller.signal, timeoutMs }).catch(
3146
+ (error) => {
3147
+ appLogger.warn("pricing.refresh.error", { error });
3148
+ return false;
3149
+ }
3150
+ );
3151
+ return {
3152
+ publish() {
3153
+ if (publishPendingPricing()) appLogger.info("pricing.refresh.published", {});
3154
+ },
3155
+ async cancel() {
3156
+ controller.abort();
3157
+ await completion;
3158
+ }
3159
+ };
3160
+ }
3161
+
3031
3162
  // src/index.ts
3032
3163
  var main = defineCommand({
3033
3164
  meta: {
@@ -3052,6 +3183,19 @@ var main = defineCommand({
3052
3183
  description: "Allow authenticated access when binding to a non-loopback address",
3053
3184
  default: false
3054
3185
  },
3186
+ "tls-cert": {
3187
+ type: "string",
3188
+ description: "Path to a TLS certificate; serve remote access over HTTPS"
3189
+ },
3190
+ "tls-key": {
3191
+ type: "string",
3192
+ description: "Path to the TLS private key matching --tls-cert"
3193
+ },
3194
+ "trust-proxy": {
3195
+ type: "boolean",
3196
+ description: "A reverse proxy in front of CodeSesh terminates TLS",
3197
+ default: false
3198
+ },
3055
3199
  agent: {
3056
3200
  type: "string",
3057
3201
  alias: "a",
@@ -3124,6 +3268,18 @@ var main = defineCommand({
3124
3268
  );
3125
3269
  process.exit(1);
3126
3270
  }
3271
+ let transport;
3272
+ try {
3273
+ transport = resolveRemoteTransport({
3274
+ hostname,
3275
+ tlsCertPath: args["tls-cert"],
3276
+ tlsKeyPath: args["tls-key"],
3277
+ trustProxy: args["trust-proxy"]
3278
+ });
3279
+ } catch (error) {
3280
+ console.error(error instanceof Error ? error.message : String(error));
3281
+ process.exit(1);
3282
+ }
3127
3283
  if (trace) {
3128
3284
  perf.enable();
3129
3285
  }
@@ -3137,12 +3293,12 @@ var main = defineCommand({
3137
3293
  log_path: appLogger.getLogPath()
3138
3294
  });
3139
3295
  if (clearCache) {
3140
- const { clearCache: clear } = await import("./dist-KEPJFHOC.js");
3296
+ const { clearCache: clear } = await import("./dist-DMGLFBW6.js");
3141
3297
  clear();
3142
3298
  appLogger.info("cache.clear");
3143
3299
  console.log("Cache cleared.");
3144
3300
  }
3145
- void refreshPricingCache();
3301
+ const pricingRefresh = startPricingRefresh();
3146
3302
  let targetSession = null;
3147
3303
  if (args.session) {
3148
3304
  targetSession = parseSessionUri(args.session);
@@ -3186,26 +3342,10 @@ var main = defineCommand({
3186
3342
  console.log(perf.getReport());
3187
3343
  }
3188
3344
  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
- };
3345
+ await pricingRefresh.cancel();
3346
+ const output = buildSessionIndexOutput(result, { from: listDefaultFrom, to: listDefaultTo });
3207
3347
  appLogger.info("cli.json_output", {
3208
- sessions: windowed.length,
3348
+ sessions: output.sessions.length,
3209
3349
  duration_ms: Math.round(performance.now() - startedAt)
3210
3350
  });
3211
3351
  console.log(JSON.stringify(output, null, 2));
@@ -3221,7 +3361,8 @@ var main = defineCommand({
3221
3361
  defaultSessionDays: listDefaultDays,
3222
3362
  portFallbackAttempts: explicitPort ? 1 : DEFAULT_PORT_FALLBACK_ATTEMPTS,
3223
3363
  hostname,
3224
- remoteAccess
3364
+ remoteAccess,
3365
+ transport
3225
3366
  });
3226
3367
  } catch (error) {
3227
3368
  console.error(getServerStartupErrorMessage(error, port));
@@ -3229,6 +3370,7 @@ var main = defineCommand({
3229
3370
  }
3230
3371
  const { url } = app;
3231
3372
  if (!jsonOnly) {
3373
+ pricingRefresh.publish();
3232
3374
  store.startBackgroundRefresh();
3233
3375
  }
3234
3376
  let shuttingDown = false;
@@ -3236,6 +3378,7 @@ var main = defineCommand({
3236
3378
  if (shuttingDown) return;
3237
3379
  shuttingDown = true;
3238
3380
  appLogger.info("cli.shutdown", { signal });
3381
+ await pricingRefresh.cancel();
3239
3382
  await app.shutdown();
3240
3383
  process.exit(0);
3241
3384
  };