codesesh 0.10.0 → 0.12.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/README.md +2 -1
- package/dist/{chunk-ZQMIB7QO.js → chunk-GCOAE7KI.js} +131 -34
- package/dist/chunk-GCOAE7KI.js.map +1 -0
- package/dist/{dist-GZSUB4NY.js → dist-TZTL6VP4.js} +14 -2
- package/dist/index.js +226 -47
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +35 -9
- package/dist/scan-refresh-worker.js.map +1 -1
- package/dist/search-index-worker.js +6 -1
- package/dist/search-index-worker.js.map +1 -1
- package/dist/smart-tag-worker.js +1 -1
- package/dist/web/assets/index-D05hsCzb.css +2 -0
- package/dist/web/assets/index-DOy8q3la.js +113 -0
- package/dist/web/assets/markdown-CGVJZBE7.js +14 -0
- package/dist/web/assets/{react-CGUTVnhO.js → react-DkEeEGji.js} +1 -1
- package/dist/web/assets/{syntax-Cjw-OADI.js → syntax-CFQ-7fm6.js} +2 -2
- package/dist/web/assets/vendor-B2nLfkMs.js +80 -0
- package/dist/web/icon/agent/zcode.svg +1 -0
- package/dist/web/index.html +6 -6
- package/package.json +4 -3
- package/dist/chunk-ZQMIB7QO.js.map +0 -1
- package/dist/web/assets/index-BHmQBGNx.css +0 -2
- package/dist/web/assets/index-CxJTOv8L.js +0 -111
- package/dist/web/assets/markdown-Bm__FT1K.js +0 -14
- package/dist/web/assets/vendor-CQhWyNUf.js +0 -40
- /package/dist/{dist-GZSUB4NY.js.map → dist-TZTL6VP4.js.map} +0 -0
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
createProjectScopeMatcher,
|
|
26
26
|
createRegisteredAgents,
|
|
27
27
|
deleteBookmark,
|
|
28
|
+
ensureSessionTagsSync,
|
|
28
29
|
estimateCostForTokens,
|
|
29
30
|
estimateTokenCost,
|
|
30
31
|
extractFileActivityOccurrences,
|
|
@@ -39,7 +40,9 @@ import {
|
|
|
39
40
|
firstVisibleLine,
|
|
40
41
|
getAgentByName,
|
|
41
42
|
getAgentInfoMap,
|
|
43
|
+
getAgentLastFullSyncAt,
|
|
42
44
|
getCacheInfo,
|
|
45
|
+
getCachePath2,
|
|
43
46
|
getCursorDataPath,
|
|
44
47
|
getParsedSession,
|
|
45
48
|
getPricingRegistry,
|
|
@@ -59,7 +62,9 @@ import {
|
|
|
59
62
|
loadCachedSessionData,
|
|
60
63
|
loadCachedSessions,
|
|
61
64
|
markAgentCacheInitialized,
|
|
65
|
+
markAgentFullSyncCompleted,
|
|
62
66
|
matchesProjectScope,
|
|
67
|
+
matchesScanWindow,
|
|
63
68
|
normalizeGitRemote,
|
|
64
69
|
normalizeTitleText,
|
|
65
70
|
openDb,
|
|
@@ -83,6 +88,7 @@ import {
|
|
|
83
88
|
searchFileActivitySessions,
|
|
84
89
|
searchSessions,
|
|
85
90
|
sessionSignature,
|
|
91
|
+
setFtsIntegrityCheckedPath,
|
|
86
92
|
skipped,
|
|
87
93
|
skippedSession,
|
|
88
94
|
sortSessions,
|
|
@@ -93,7 +99,7 @@ import {
|
|
|
93
99
|
toLocalDateKey,
|
|
94
100
|
upsertBookmark,
|
|
95
101
|
withEstimatedSessionCost
|
|
96
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-GCOAE7KI.js";
|
|
97
103
|
export {
|
|
98
104
|
BaseAgent,
|
|
99
105
|
BookmarkStorageUnavailableError,
|
|
@@ -120,6 +126,7 @@ export {
|
|
|
120
126
|
createProjectScopeMatcher,
|
|
121
127
|
createRegisteredAgents,
|
|
122
128
|
deleteBookmark,
|
|
129
|
+
ensureSessionTagsSync,
|
|
123
130
|
estimateCostForTokens,
|
|
124
131
|
estimateTokenCost,
|
|
125
132
|
extractFileActivityOccurrences,
|
|
@@ -134,7 +141,9 @@ export {
|
|
|
134
141
|
firstVisibleLine,
|
|
135
142
|
getAgentByName,
|
|
136
143
|
getAgentInfoMap,
|
|
144
|
+
getAgentLastFullSyncAt,
|
|
137
145
|
getCacheInfo,
|
|
146
|
+
getCachePath2 as getCachePath,
|
|
138
147
|
getCursorDataPath,
|
|
139
148
|
getParsedSession,
|
|
140
149
|
getPricingRegistry,
|
|
@@ -154,7 +163,9 @@ export {
|
|
|
154
163
|
loadCachedSessionData,
|
|
155
164
|
loadCachedSessions,
|
|
156
165
|
markAgentCacheInitialized,
|
|
166
|
+
markAgentFullSyncCompleted,
|
|
157
167
|
matchesProjectScope,
|
|
168
|
+
matchesScanWindow,
|
|
158
169
|
normalizeGitRemote,
|
|
159
170
|
normalizeTitleText,
|
|
160
171
|
openDb,
|
|
@@ -178,6 +189,7 @@ export {
|
|
|
178
189
|
searchFileActivitySessions,
|
|
179
190
|
searchSessions,
|
|
180
191
|
sessionSignature,
|
|
192
|
+
setFtsIntegrityCheckedPath,
|
|
181
193
|
skipped,
|
|
182
194
|
skippedSession,
|
|
183
195
|
sortSessions,
|
|
@@ -189,4 +201,4 @@ export {
|
|
|
189
201
|
upsertBookmark,
|
|
190
202
|
withEstimatedSessionCost
|
|
191
203
|
};
|
|
192
|
-
//# sourceMappingURL=dist-
|
|
204
|
+
//# sourceMappingURL=dist-TZTL6VP4.js.map
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
extractSessionFileActivity,
|
|
15
15
|
filterSessions,
|
|
16
16
|
getAgentInfoMap,
|
|
17
|
+
getAgentLastFullSyncAt,
|
|
17
18
|
getCursorDataPath,
|
|
18
19
|
getSessionActivityTime,
|
|
19
20
|
getSessionAgentName,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
listSessionFileActivity,
|
|
28
29
|
loadCachedSessionData,
|
|
29
30
|
loadCachedSessions,
|
|
31
|
+
markAgentFullSyncCompleted,
|
|
30
32
|
matchesProjectScope,
|
|
31
33
|
parseSearchQuery,
|
|
32
34
|
perf,
|
|
@@ -40,7 +42,7 @@ import {
|
|
|
40
42
|
sortSessions,
|
|
41
43
|
startOfLocalDay,
|
|
42
44
|
upsertBookmark
|
|
43
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-GCOAE7KI.js";
|
|
44
46
|
|
|
45
47
|
// src/index.ts
|
|
46
48
|
import { defineCommand, runMain } from "citty";
|
|
@@ -899,11 +901,12 @@ async function createServer(port, store, options = {}) {
|
|
|
899
901
|
app.get("/*", serveStatic({ root: webDistPath, path: "index.html" }));
|
|
900
902
|
}
|
|
901
903
|
const attempts = Math.max(1, options.portFallbackAttempts ?? 1);
|
|
904
|
+
const hostname = options.hostname ?? "127.0.0.1";
|
|
902
905
|
let server = null;
|
|
903
906
|
let actualPort = port;
|
|
904
907
|
for (let offset = 0; offset < attempts; offset += 1) {
|
|
905
908
|
const candidatePort = port + offset;
|
|
906
|
-
server = serve({ fetch: app.fetch, port: candidatePort });
|
|
909
|
+
server = serve({ fetch: app.fetch, port: candidatePort, hostname });
|
|
907
910
|
try {
|
|
908
911
|
await waitForListening(server);
|
|
909
912
|
actualPort = getListeningPort(server, candidatePort);
|
|
@@ -925,8 +928,17 @@ async function createServer(port, store, options = {}) {
|
|
|
925
928
|
throw new Error(getServerStartupErrorMessage(error, candidatePort));
|
|
926
929
|
}
|
|
927
930
|
}
|
|
928
|
-
const
|
|
929
|
-
|
|
931
|
+
const isLoopback = hostname === "127.0.0.1" || hostname === "localhost";
|
|
932
|
+
const url = isLoopback ? `http://localhost:${actualPort}` : `http://${hostname}:${actualPort}`;
|
|
933
|
+
appLogger.info("server.listen", { port: actualPort, requested_port: port, hostname, url });
|
|
934
|
+
if (!isLoopback) {
|
|
935
|
+
appLogger.warn("server.listen.exposed", { hostname, port: actualPort });
|
|
936
|
+
console.warn(
|
|
937
|
+
`
|
|
938
|
+
\u26A0 \u670D\u52A1\u76D1\u542C ${hostname}\uFF0C\u5C40\u57DF\u7F51\u5185\u5176\u4ED6\u8BBE\u5907\u53EF\u8BFB\u53D6\u4F60\u7684\u5168\u90E8 AI \u4F1A\u8BDD\u8BB0\u5F55\uFF08\u65E0\u9274\u6743\uFF09\u3002
|
|
939
|
+
`
|
|
940
|
+
);
|
|
941
|
+
}
|
|
930
942
|
return {
|
|
931
943
|
url,
|
|
932
944
|
shutdown: async () => {
|
|
@@ -1049,6 +1061,8 @@ function resolveAgentWatchTargets(agentName) {
|
|
|
1049
1061
|
{ root: roots.opencodeRoot, path: join2(roots.opencodeRoot, "opencode.db") },
|
|
1050
1062
|
{ root: "data/opencode", path: "data/opencode/opencode.db" }
|
|
1051
1063
|
];
|
|
1064
|
+
case "zcode":
|
|
1065
|
+
return roots.zcodeRoot ? [{ root: roots.zcodeRoot, path: join2(roots.zcodeRoot, "cli", "db", "db.sqlite") }] : [];
|
|
1052
1066
|
default:
|
|
1053
1067
|
return [];
|
|
1054
1068
|
}
|
|
@@ -1270,8 +1284,11 @@ var SessionWatcher = class {
|
|
|
1270
1284
|
var REFRESH_DEBOUNCE_MS = 200;
|
|
1271
1285
|
var EMPTY_AGENT_REFRESH_DEBOUNCE_MS = 3e4;
|
|
1272
1286
|
var PENDING_REFRESH_DELAY_MS = 100;
|
|
1287
|
+
var MAX_ADAPTIVE_REFRESH_DELAY_MS = 3e4;
|
|
1288
|
+
var ADAPTIVE_REFRESH_DELAY_MULTIPLIER = 4;
|
|
1273
1289
|
var NEW_SESSION_EVENT_WINDOW_MS = 250;
|
|
1274
1290
|
var SEARCH_INDEX_BULK_PENDING_PATH_THRESHOLD = 100;
|
|
1291
|
+
var BACKFILL_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1275
1292
|
function buildRefreshDiff(agentName, previousSessions, nextSessions, candidateChangedIds = []) {
|
|
1276
1293
|
const { changes, removedSessionIds, counts } = computeSessionDiff(
|
|
1277
1294
|
previousSessions,
|
|
@@ -1355,13 +1372,12 @@ var LiveScanStore = class {
|
|
|
1355
1372
|
completedAgents: [],
|
|
1356
1373
|
agentStatuses: {},
|
|
1357
1374
|
totalAgents: 0,
|
|
1358
|
-
updatedAt: Date.now()
|
|
1375
|
+
updatedAt: Date.now(),
|
|
1376
|
+
backfill: { active: false, pendingAgents: [], completedAgents: [] }
|
|
1359
1377
|
};
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
pendingRefreshes = /* @__PURE__ */ new Set();
|
|
1364
|
-
pendingRefreshPathCounts = /* @__PURE__ */ new Map();
|
|
1378
|
+
backfillQueue = [];
|
|
1379
|
+
backfillRunning = false;
|
|
1380
|
+
refreshStates = /* @__PURE__ */ new Map();
|
|
1365
1381
|
watcher = null;
|
|
1366
1382
|
pendingEvent = null;
|
|
1367
1383
|
pendingEventTimer = null;
|
|
@@ -1369,6 +1385,24 @@ var LiveScanStore = class {
|
|
|
1369
1385
|
searchIndexWorker = null;
|
|
1370
1386
|
pendingSearchIndexJobs = [];
|
|
1371
1387
|
shuttingDown = false;
|
|
1388
|
+
/** Set once a search-index worker in this process has completed the FTS integrity check. */
|
|
1389
|
+
ftsIntegrityChecked = false;
|
|
1390
|
+
getRefreshState(agentName) {
|
|
1391
|
+
let state = this.refreshStates.get(agentName);
|
|
1392
|
+
if (!state) {
|
|
1393
|
+
state = {
|
|
1394
|
+
timer: null,
|
|
1395
|
+
timerDeadline: 0,
|
|
1396
|
+
inFlight: false,
|
|
1397
|
+
pendingRerun: false,
|
|
1398
|
+
lastRefreshAt: 0,
|
|
1399
|
+
lastRefreshDurationMs: 0,
|
|
1400
|
+
pendingPathCount: 0
|
|
1401
|
+
};
|
|
1402
|
+
this.refreshStates.set(agentName, state);
|
|
1403
|
+
}
|
|
1404
|
+
return state;
|
|
1405
|
+
}
|
|
1372
1406
|
async initialize() {
|
|
1373
1407
|
const startedAt = performance.now();
|
|
1374
1408
|
const deferInitialRefresh = this.storeOptions.deferInitialRefresh === true;
|
|
@@ -1425,10 +1459,7 @@ var LiveScanStore = class {
|
|
|
1425
1459
|
this.watcher = new SessionWatcher();
|
|
1426
1460
|
this.watcher.onAgentsChanged((agentNames) => {
|
|
1427
1461
|
for (const agentName of agentNames) {
|
|
1428
|
-
this.
|
|
1429
|
-
agentName,
|
|
1430
|
-
(this.pendingRefreshPathCounts.get(agentName) ?? 0) + 1
|
|
1431
|
-
);
|
|
1462
|
+
this.getRefreshState(agentName).pendingPathCount += 1;
|
|
1432
1463
|
const delayMs = (this.byAgent[agentName]?.length ?? 0) === 0 ? EMPTY_AGENT_REFRESH_DEBOUNCE_MS : REFRESH_DEBOUNCE_MS;
|
|
1433
1464
|
this.scheduleRefresh(agentName, delayMs);
|
|
1434
1465
|
}
|
|
@@ -1450,6 +1481,11 @@ var LiveScanStore = class {
|
|
|
1450
1481
|
if (agentNames.length === 0) {
|
|
1451
1482
|
this.finishScanBatch();
|
|
1452
1483
|
}
|
|
1484
|
+
for (const agent of this.agents) {
|
|
1485
|
+
if (this.needsBackfill(agent)) {
|
|
1486
|
+
this.enqueueBackfill(agent.name);
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1453
1489
|
}, 0);
|
|
1454
1490
|
}
|
|
1455
1491
|
getSnapshot() {
|
|
@@ -1471,7 +1507,12 @@ var LiveScanStore = class {
|
|
|
1471
1507
|
agentName,
|
|
1472
1508
|
{ ...status }
|
|
1473
1509
|
])
|
|
1474
|
-
)
|
|
1510
|
+
),
|
|
1511
|
+
backfill: {
|
|
1512
|
+
...this.scanStatus.backfill,
|
|
1513
|
+
pendingAgents: [...this.scanStatus.backfill.pendingAgents],
|
|
1514
|
+
completedAgents: [...this.scanStatus.backfill.completedAgents]
|
|
1515
|
+
}
|
|
1475
1516
|
};
|
|
1476
1517
|
}
|
|
1477
1518
|
subscribe(listener) {
|
|
@@ -1488,11 +1529,13 @@ var LiveScanStore = class {
|
|
|
1488
1529
|
}
|
|
1489
1530
|
async shutdown() {
|
|
1490
1531
|
this.shuttingDown = true;
|
|
1491
|
-
for (const
|
|
1492
|
-
|
|
1532
|
+
for (const state of this.refreshStates.values()) {
|
|
1533
|
+
if (state.timer) {
|
|
1534
|
+
clearTimeout(state.timer);
|
|
1535
|
+
state.timer = null;
|
|
1536
|
+
state.timerDeadline = 0;
|
|
1537
|
+
}
|
|
1493
1538
|
}
|
|
1494
|
-
this.refreshTimers.clear();
|
|
1495
|
-
this.pendingRefreshPathCounts.clear();
|
|
1496
1539
|
if (this.pendingEventTimer) {
|
|
1497
1540
|
clearTimeout(this.pendingEventTimer);
|
|
1498
1541
|
this.pendingEventTimer = null;
|
|
@@ -1553,6 +1596,7 @@ var LiveScanStore = class {
|
|
|
1553
1596
|
])
|
|
1554
1597
|
);
|
|
1555
1598
|
this.updateScanStatus({
|
|
1599
|
+
...this.scanStatus,
|
|
1556
1600
|
active: uniqueAgentNames.length > 0,
|
|
1557
1601
|
phase: uniqueAgentNames.length > 0 ? phase : "idle",
|
|
1558
1602
|
pendingAgents: uniqueAgentNames,
|
|
@@ -1678,6 +1722,108 @@ var LiveScanStore = class {
|
|
|
1678
1722
|
completedAt: now
|
|
1679
1723
|
});
|
|
1680
1724
|
}
|
|
1725
|
+
updateBackfillStatus(patch) {
|
|
1726
|
+
this.updateScanStatus({
|
|
1727
|
+
...this.scanStatus,
|
|
1728
|
+
backfill: { ...this.scanStatus.backfill, ...patch },
|
|
1729
|
+
updatedAt: Date.now()
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Only FileSystemSessionSource agents pay the O(history) enumeration cost this
|
|
1734
|
+
* guards against: database agents already do a cheap single-file mtime check.
|
|
1735
|
+
* With no startup window configured, the regular refresh path already walks
|
|
1736
|
+
* full history, so backfill would be redundant.
|
|
1737
|
+
*/
|
|
1738
|
+
needsBackfill(agent) {
|
|
1739
|
+
if (this.startupScanOptions.from == null && this.startupScanOptions.to == null) return false;
|
|
1740
|
+
if (!(agent instanceof FileSystemSessionSource)) return false;
|
|
1741
|
+
if (!agent.isAvailable()) return false;
|
|
1742
|
+
const lastSyncAt = getAgentLastFullSyncAt(agent.name);
|
|
1743
|
+
return lastSyncAt == null || Date.now() - lastSyncAt > BACKFILL_INTERVAL_MS;
|
|
1744
|
+
}
|
|
1745
|
+
enqueueBackfill(agentName) {
|
|
1746
|
+
if (this.backfillQueue.includes(agentName) || this.scanStatus.backfill.currentAgent === agentName) {
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
this.backfillQueue.push(agentName);
|
|
1750
|
+
this.updateBackfillStatus({ active: true, pendingAgents: [...this.backfillQueue] });
|
|
1751
|
+
this.pumpBackfillQueue();
|
|
1752
|
+
}
|
|
1753
|
+
pumpBackfillQueue() {
|
|
1754
|
+
if (this.backfillRunning) return;
|
|
1755
|
+
const agentName = this.backfillQueue.shift();
|
|
1756
|
+
if (!agentName) return;
|
|
1757
|
+
this.backfillRunning = true;
|
|
1758
|
+
this.updateBackfillStatus({ currentAgent: agentName, pendingAgents: [...this.backfillQueue] });
|
|
1759
|
+
void this.runBackfill(agentName).finally(() => {
|
|
1760
|
+
this.backfillRunning = false;
|
|
1761
|
+
this.updateBackfillStatus({
|
|
1762
|
+
currentAgent: void 0,
|
|
1763
|
+
completedAgents: [.../* @__PURE__ */ new Set([...this.scanStatus.backfill.completedAgents, agentName])],
|
|
1764
|
+
active: this.backfillQueue.length > 0
|
|
1765
|
+
});
|
|
1766
|
+
this.pumpBackfillQueue();
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1769
|
+
/** Unbounded per-source sync to reconcile the full session history, not just the display window. */
|
|
1770
|
+
async runBackfill(agentName) {
|
|
1771
|
+
const startedAt = performance.now();
|
|
1772
|
+
const agent = this.agents.find((item) => item.name === agentName);
|
|
1773
|
+
if (!agent || !(agent instanceof FileSystemSessionSource) || !agent.isAvailable()) {
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
const cached = loadCachedSessions(agentName);
|
|
1777
|
+
const baseline = cached?.sessions ?? this.byAgent[agentName] ?? [];
|
|
1778
|
+
const meta = cached?.meta ?? buildAgentCacheMeta(agent);
|
|
1779
|
+
if (cached) {
|
|
1780
|
+
restoreAgentCacheMeta(agent, cached.meta);
|
|
1781
|
+
}
|
|
1782
|
+
try {
|
|
1783
|
+
const result = await this.scanAgentInWorker(
|
|
1784
|
+
agent,
|
|
1785
|
+
baseline,
|
|
1786
|
+
null,
|
|
1787
|
+
{},
|
|
1788
|
+
{ sourceSync: true, meta }
|
|
1789
|
+
);
|
|
1790
|
+
agent.setSessionMetaMap(new Map(Object.entries(result.meta)));
|
|
1791
|
+
const fullSessions = attachMissingProjectIdentities(result.sessions);
|
|
1792
|
+
const filtered = this.applyFilters(fullSessions);
|
|
1793
|
+
const diff = buildRefreshDiff(
|
|
1794
|
+
agentName,
|
|
1795
|
+
this.byAgent[agentName] ?? [],
|
|
1796
|
+
filtered,
|
|
1797
|
+
result.changedIds ?? []
|
|
1798
|
+
);
|
|
1799
|
+
this.byAgent[agentName] = sortSessions(filtered);
|
|
1800
|
+
this.rebuildSessions();
|
|
1801
|
+
await this.enqueueSearchIndexJobs("scan.backfill", [
|
|
1802
|
+
{
|
|
1803
|
+
kind: "full",
|
|
1804
|
+
context: "scan.backfill",
|
|
1805
|
+
agentName,
|
|
1806
|
+
sessions: fullSessions,
|
|
1807
|
+
meta: buildAgentCacheMeta(agent),
|
|
1808
|
+
saveCache: true
|
|
1809
|
+
}
|
|
1810
|
+
]);
|
|
1811
|
+
markAgentFullSyncCompleted(agentName);
|
|
1812
|
+
if (diff.event) {
|
|
1813
|
+
diff.event.totalSessions = this.sessions.length;
|
|
1814
|
+
this.emit(diff.event);
|
|
1815
|
+
}
|
|
1816
|
+
appLogger.info("scan.backfill.done", {
|
|
1817
|
+
agent: agentName,
|
|
1818
|
+
duration_ms: Math.round(performance.now() - startedAt),
|
|
1819
|
+
sessions: fullSessions.length,
|
|
1820
|
+
changed: result.changedIds?.length ?? 0
|
|
1821
|
+
});
|
|
1822
|
+
} catch (error) {
|
|
1823
|
+
appLogger.error("scan.backfill.error", { agent: agentName, error });
|
|
1824
|
+
console.error(`[${agentName}] Backfill failed:`, error);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1681
1827
|
queueEvent(event) {
|
|
1682
1828
|
this.pendingEvent = this.pendingEvent ? mergeEvents(this.pendingEvent, event) : event;
|
|
1683
1829
|
if (this.pendingEventTimer) {
|
|
@@ -1811,7 +1957,8 @@ var LiveScanStore = class {
|
|
|
1811
1957
|
jobs: batch.jobs,
|
|
1812
1958
|
agentNames: [],
|
|
1813
1959
|
sessionsByAgent: {},
|
|
1814
|
-
metaByAgent: {}
|
|
1960
|
+
metaByAgent: {},
|
|
1961
|
+
skipFtsIntegrityCheck: this.ftsIntegrityChecked
|
|
1815
1962
|
}
|
|
1816
1963
|
});
|
|
1817
1964
|
worker.unref();
|
|
@@ -1825,6 +1972,7 @@ var LiveScanStore = class {
|
|
|
1825
1972
|
sessions: message.sessions
|
|
1826
1973
|
});
|
|
1827
1974
|
settled = true;
|
|
1975
|
+
this.ftsIntegrityChecked = true;
|
|
1828
1976
|
batch.resolve();
|
|
1829
1977
|
}
|
|
1830
1978
|
});
|
|
@@ -1871,7 +2019,7 @@ var LiveScanStore = class {
|
|
|
1871
2019
|
this.byAgent = {};
|
|
1872
2020
|
for (const agent of this.agents) {
|
|
1873
2021
|
this.byAgent[agent.name] = sortSessions(result.byAgent[agent.name] ?? []);
|
|
1874
|
-
this.
|
|
2022
|
+
this.getRefreshState(agent.name).lastRefreshAt = result.cacheTimestamps?.[agent.name] ?? Date.now();
|
|
1875
2023
|
}
|
|
1876
2024
|
this.rebuildSessions();
|
|
1877
2025
|
}
|
|
@@ -1901,25 +2049,41 @@ var LiveScanStore = class {
|
|
|
1901
2049
|
console.error("[search] Background index sync failed:", error);
|
|
1902
2050
|
}
|
|
1903
2051
|
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Throttles rather than debounces: a pending timer only gets replaced by a
|
|
2054
|
+
* request with an earlier deadline. Plain debounce (reset on every call)
|
|
2055
|
+
* would let a steady stream of events — each arriving before the adaptive
|
|
2056
|
+
* backoff elapses — push the deadline out forever and starve the refresh.
|
|
2057
|
+
*/
|
|
1904
2058
|
scheduleRefresh(agentName, delayMs = REFRESH_DEBOUNCE_MS) {
|
|
1905
|
-
|
|
1906
|
-
const
|
|
1907
|
-
|
|
1908
|
-
|
|
2059
|
+
const state = this.getRefreshState(agentName);
|
|
2060
|
+
const adaptiveDelayMs = Math.min(
|
|
2061
|
+
state.lastRefreshDurationMs * ADAPTIVE_REFRESH_DELAY_MULTIPLIER,
|
|
2062
|
+
MAX_ADAPTIVE_REFRESH_DELAY_MS
|
|
2063
|
+
);
|
|
2064
|
+
const effectiveDelayMs = Math.max(delayMs, adaptiveDelayMs);
|
|
2065
|
+
const deadline = Date.now() + effectiveDelayMs;
|
|
2066
|
+
if (state.timer !== null) {
|
|
2067
|
+
if (deadline >= state.timerDeadline) {
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
2070
|
+
clearTimeout(state.timer);
|
|
1909
2071
|
}
|
|
1910
|
-
|
|
1911
|
-
|
|
2072
|
+
appLogger.debug("scan.refresh.schedule", { agent: agentName, delay_ms: effectiveDelayMs });
|
|
2073
|
+
state.timerDeadline = deadline;
|
|
2074
|
+
state.timer = setTimeout(() => {
|
|
2075
|
+
state.timer = null;
|
|
1912
2076
|
void this.refreshAgent(agentName);
|
|
1913
|
-
},
|
|
1914
|
-
this.refreshTimers.set(agentName, timer);
|
|
2077
|
+
}, effectiveDelayMs);
|
|
1915
2078
|
}
|
|
1916
2079
|
async refreshAgent(agentName) {
|
|
1917
|
-
|
|
2080
|
+
const state = this.getRefreshState(agentName);
|
|
2081
|
+
if (state.inFlight) {
|
|
1918
2082
|
appLogger.debug("scan.refresh.pending", { agent: agentName });
|
|
1919
|
-
|
|
2083
|
+
state.pendingRerun = true;
|
|
1920
2084
|
return;
|
|
1921
2085
|
}
|
|
1922
|
-
|
|
2086
|
+
state.inFlight = true;
|
|
1923
2087
|
this.beginAgentScan(agentName);
|
|
1924
2088
|
try {
|
|
1925
2089
|
await this.runRefresh(agentName);
|
|
@@ -1927,17 +2091,19 @@ var LiveScanStore = class {
|
|
|
1927
2091
|
appLogger.error("scan.refresh.error", { agent: agentName, error });
|
|
1928
2092
|
console.error(`[${agentName}] Session refresh failed:`, error);
|
|
1929
2093
|
} finally {
|
|
1930
|
-
|
|
2094
|
+
state.inFlight = false;
|
|
1931
2095
|
this.finishAgentScan(agentName);
|
|
1932
|
-
if (
|
|
2096
|
+
if (state.pendingRerun) {
|
|
2097
|
+
state.pendingRerun = false;
|
|
1933
2098
|
this.scheduleRefresh(agentName, PENDING_REFRESH_DELAY_MS);
|
|
1934
2099
|
}
|
|
1935
2100
|
}
|
|
1936
2101
|
}
|
|
1937
2102
|
async runRefresh(agentName) {
|
|
1938
2103
|
const startedAt = performance.now();
|
|
1939
|
-
const
|
|
1940
|
-
|
|
2104
|
+
const state = this.getRefreshState(agentName);
|
|
2105
|
+
const pendingPathCount = state.pendingPathCount;
|
|
2106
|
+
state.pendingPathCount = 0;
|
|
1941
2107
|
const agent = this.agents.find((item) => item.name === agentName);
|
|
1942
2108
|
if (!agent) {
|
|
1943
2109
|
appLogger.warn("scan.refresh.missing_agent", { agent: agentName });
|
|
@@ -1946,7 +2112,7 @@ var LiveScanStore = class {
|
|
|
1946
2112
|
const previousSessions = this.byAgent[agentName] ?? [];
|
|
1947
2113
|
const cached = loadCachedSessions(agentName);
|
|
1948
2114
|
const refreshBaseline = cached?.sessions ?? previousSessions;
|
|
1949
|
-
const cacheTimestamp = cached?.timestamp ??
|
|
2115
|
+
const cacheTimestamp = cached?.timestamp ?? state.lastRefreshAt;
|
|
1950
2116
|
if (cached) {
|
|
1951
2117
|
restoreAgentCacheMeta(agent, cached.meta);
|
|
1952
2118
|
}
|
|
@@ -1969,24 +2135,29 @@ var LiveScanStore = class {
|
|
|
1969
2135
|
availabilityDuration = performance.now() - availabilityStartedAt;
|
|
1970
2136
|
if (!isAvailable) {
|
|
1971
2137
|
nextSessions = [];
|
|
1972
|
-
|
|
2138
|
+
state.lastRefreshAt = Date.now();
|
|
1973
2139
|
} else if (!isInitialized) {
|
|
1974
2140
|
this.setScanPhase("initializing");
|
|
1975
2141
|
const scanStartedAt = performance.now();
|
|
1976
|
-
const result = await this.scanAgentInWorker(
|
|
2142
|
+
const result = await this.scanAgentInWorker(
|
|
2143
|
+
agent,
|
|
2144
|
+
previousSessions,
|
|
2145
|
+
null,
|
|
2146
|
+
this.startupScanOptions
|
|
2147
|
+
);
|
|
1977
2148
|
nextSessions = result.sessions;
|
|
1978
2149
|
agent.setSessionMetaMap?.(new Map(Object.entries(result.meta)));
|
|
1979
2150
|
fullScanSessions = attachMissingProjectIdentities(nextSessions);
|
|
1980
2151
|
nextSessions = fullScanSessions;
|
|
1981
2152
|
scanDuration = performance.now() - scanStartedAt;
|
|
1982
|
-
|
|
2153
|
+
state.lastRefreshAt = Date.now();
|
|
1983
2154
|
} else if (cached && agent instanceof FileSystemSessionSource) {
|
|
1984
2155
|
const scanStartedAt = performance.now();
|
|
1985
2156
|
const result = await this.scanAgentInWorker(
|
|
1986
2157
|
agent,
|
|
1987
2158
|
cached.sessions,
|
|
1988
2159
|
null,
|
|
1989
|
-
|
|
2160
|
+
this.startupScanOptions,
|
|
1990
2161
|
{
|
|
1991
2162
|
sourceSync: true,
|
|
1992
2163
|
meta: cached.meta
|
|
@@ -2003,7 +2174,7 @@ var LiveScanStore = class {
|
|
|
2003
2174
|
preciseChangedIds
|
|
2004
2175
|
);
|
|
2005
2176
|
scanDuration = performance.now() - scanStartedAt;
|
|
2006
|
-
|
|
2177
|
+
state.lastRefreshAt = Date.now();
|
|
2007
2178
|
if (preciseChangedIds.length === 0) {
|
|
2008
2179
|
appLogger.debug("scan.refresh.unchanged", {
|
|
2009
2180
|
agent: agentName,
|
|
@@ -2016,7 +2187,7 @@ var LiveScanStore = class {
|
|
|
2016
2187
|
agent.checkForChanges(cacheTimestamp, refreshBaseline)
|
|
2017
2188
|
);
|
|
2018
2189
|
checkDuration = performance.now() - checkStartedAt;
|
|
2019
|
-
|
|
2190
|
+
state.lastRefreshAt = checkResult.timestamp;
|
|
2020
2191
|
if (!checkResult.hasChanges) {
|
|
2021
2192
|
appLogger.debug("scan.refresh.unchanged", {
|
|
2022
2193
|
agent: agentName,
|
|
@@ -2047,7 +2218,7 @@ var LiveScanStore = class {
|
|
|
2047
2218
|
fullScanSessions = attachMissingProjectIdentities(nextSessions);
|
|
2048
2219
|
nextSessions = fullScanSessions;
|
|
2049
2220
|
scanDuration = performance.now() - scanStartedAt;
|
|
2050
|
-
|
|
2221
|
+
state.lastRefreshAt = Date.now();
|
|
2051
2222
|
}
|
|
2052
2223
|
nextSessions = attachMissingProjectIdentities(nextSessions);
|
|
2053
2224
|
const filterStartedAt = performance.now();
|
|
@@ -2108,9 +2279,11 @@ var LiveScanStore = class {
|
|
|
2108
2279
|
event.totalSessions = this.sessions.length;
|
|
2109
2280
|
this.emit(event);
|
|
2110
2281
|
}
|
|
2282
|
+
const totalDurationMs = performance.now() - startedAt;
|
|
2283
|
+
state.lastRefreshDurationMs = totalDurationMs;
|
|
2111
2284
|
appLogger.info("scan.refresh.done", {
|
|
2112
2285
|
agent: agentName,
|
|
2113
|
-
duration_ms: Math.round(
|
|
2286
|
+
duration_ms: Math.round(totalDurationMs),
|
|
2114
2287
|
sessions: nextSessions.length,
|
|
2115
2288
|
new_sessions: event?.newSessions ?? 0,
|
|
2116
2289
|
updated_sessions: event?.updatedSessions ?? 0,
|
|
@@ -2199,6 +2372,11 @@ var main = defineCommand({
|
|
|
2199
2372
|
description: "HTTP server port",
|
|
2200
2373
|
default: String(DEFAULT_PORT)
|
|
2201
2374
|
},
|
|
2375
|
+
host: {
|
|
2376
|
+
type: "string",
|
|
2377
|
+
description: "HTTP server bind address (default 127.0.0.1, local access only)",
|
|
2378
|
+
default: "127.0.0.1"
|
|
2379
|
+
},
|
|
2202
2380
|
agent: {
|
|
2203
2381
|
type: "string",
|
|
2204
2382
|
alias: "a",
|
|
@@ -2276,7 +2454,7 @@ var main = defineCommand({
|
|
|
2276
2454
|
log_path: appLogger.getLogPath()
|
|
2277
2455
|
});
|
|
2278
2456
|
if (clearCache) {
|
|
2279
|
-
const { clearCache: clear } = await import("./dist-
|
|
2457
|
+
const { clearCache: clear } = await import("./dist-TZTL6VP4.js");
|
|
2280
2458
|
clear();
|
|
2281
2459
|
appLogger.info("cache.clear");
|
|
2282
2460
|
console.log("Cache cleared.");
|
|
@@ -2365,7 +2543,8 @@ var main = defineCommand({
|
|
|
2365
2543
|
defaultSessionFrom: listDefaultFrom,
|
|
2366
2544
|
defaultSessionTo: listDefaultTo,
|
|
2367
2545
|
defaultSessionDays: listDefaultDays,
|
|
2368
|
-
portFallbackAttempts: explicitPort ? 1 : DEFAULT_PORT_FALLBACK_ATTEMPTS
|
|
2546
|
+
portFallbackAttempts: explicitPort ? 1 : DEFAULT_PORT_FALLBACK_ATTEMPTS,
|
|
2547
|
+
hostname: args.host
|
|
2369
2548
|
});
|
|
2370
2549
|
} catch (error) {
|
|
2371
2550
|
console.error(getServerStartupErrorMessage(error, port));
|