codesesh 0.11.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/dist/{chunk-BIXOP5QX.js → chunk-GCOAE7KI.js} +62 -14
- package/dist/chunk-GCOAE7KI.js.map +1 -0
- package/dist/{dist-T737I76S.js → dist-TZTL6VP4.js} +14 -2
- package/dist/index.js +224 -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/index.html +6 -6
- package/package.json +1 -1
- package/dist/chunk-BIXOP5QX.js.map +0 -1
- package/dist/web/assets/index-DlZEB-Op.css +0 -2
- package/dist/web/assets/index-O6YSX0s1.js +0 -113
- package/dist/web/assets/markdown-Bm__FT1K.js +0 -14
- package/dist/web/assets/vendor-CR3FdUHs.js +0 -40
- /package/dist/{dist-T737I76S.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 () => {
|
|
@@ -1272,8 +1284,11 @@ var SessionWatcher = class {
|
|
|
1272
1284
|
var REFRESH_DEBOUNCE_MS = 200;
|
|
1273
1285
|
var EMPTY_AGENT_REFRESH_DEBOUNCE_MS = 3e4;
|
|
1274
1286
|
var PENDING_REFRESH_DELAY_MS = 100;
|
|
1287
|
+
var MAX_ADAPTIVE_REFRESH_DELAY_MS = 3e4;
|
|
1288
|
+
var ADAPTIVE_REFRESH_DELAY_MULTIPLIER = 4;
|
|
1275
1289
|
var NEW_SESSION_EVENT_WINDOW_MS = 250;
|
|
1276
1290
|
var SEARCH_INDEX_BULK_PENDING_PATH_THRESHOLD = 100;
|
|
1291
|
+
var BACKFILL_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1277
1292
|
function buildRefreshDiff(agentName, previousSessions, nextSessions, candidateChangedIds = []) {
|
|
1278
1293
|
const { changes, removedSessionIds, counts } = computeSessionDiff(
|
|
1279
1294
|
previousSessions,
|
|
@@ -1357,13 +1372,12 @@ var LiveScanStore = class {
|
|
|
1357
1372
|
completedAgents: [],
|
|
1358
1373
|
agentStatuses: {},
|
|
1359
1374
|
totalAgents: 0,
|
|
1360
|
-
updatedAt: Date.now()
|
|
1375
|
+
updatedAt: Date.now(),
|
|
1376
|
+
backfill: { active: false, pendingAgents: [], completedAgents: [] }
|
|
1361
1377
|
};
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
pendingRefreshes = /* @__PURE__ */ new Set();
|
|
1366
|
-
pendingRefreshPathCounts = /* @__PURE__ */ new Map();
|
|
1378
|
+
backfillQueue = [];
|
|
1379
|
+
backfillRunning = false;
|
|
1380
|
+
refreshStates = /* @__PURE__ */ new Map();
|
|
1367
1381
|
watcher = null;
|
|
1368
1382
|
pendingEvent = null;
|
|
1369
1383
|
pendingEventTimer = null;
|
|
@@ -1371,6 +1385,24 @@ var LiveScanStore = class {
|
|
|
1371
1385
|
searchIndexWorker = null;
|
|
1372
1386
|
pendingSearchIndexJobs = [];
|
|
1373
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
|
+
}
|
|
1374
1406
|
async initialize() {
|
|
1375
1407
|
const startedAt = performance.now();
|
|
1376
1408
|
const deferInitialRefresh = this.storeOptions.deferInitialRefresh === true;
|
|
@@ -1427,10 +1459,7 @@ var LiveScanStore = class {
|
|
|
1427
1459
|
this.watcher = new SessionWatcher();
|
|
1428
1460
|
this.watcher.onAgentsChanged((agentNames) => {
|
|
1429
1461
|
for (const agentName of agentNames) {
|
|
1430
|
-
this.
|
|
1431
|
-
agentName,
|
|
1432
|
-
(this.pendingRefreshPathCounts.get(agentName) ?? 0) + 1
|
|
1433
|
-
);
|
|
1462
|
+
this.getRefreshState(agentName).pendingPathCount += 1;
|
|
1434
1463
|
const delayMs = (this.byAgent[agentName]?.length ?? 0) === 0 ? EMPTY_AGENT_REFRESH_DEBOUNCE_MS : REFRESH_DEBOUNCE_MS;
|
|
1435
1464
|
this.scheduleRefresh(agentName, delayMs);
|
|
1436
1465
|
}
|
|
@@ -1452,6 +1481,11 @@ var LiveScanStore = class {
|
|
|
1452
1481
|
if (agentNames.length === 0) {
|
|
1453
1482
|
this.finishScanBatch();
|
|
1454
1483
|
}
|
|
1484
|
+
for (const agent of this.agents) {
|
|
1485
|
+
if (this.needsBackfill(agent)) {
|
|
1486
|
+
this.enqueueBackfill(agent.name);
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1455
1489
|
}, 0);
|
|
1456
1490
|
}
|
|
1457
1491
|
getSnapshot() {
|
|
@@ -1473,7 +1507,12 @@ var LiveScanStore = class {
|
|
|
1473
1507
|
agentName,
|
|
1474
1508
|
{ ...status }
|
|
1475
1509
|
])
|
|
1476
|
-
)
|
|
1510
|
+
),
|
|
1511
|
+
backfill: {
|
|
1512
|
+
...this.scanStatus.backfill,
|
|
1513
|
+
pendingAgents: [...this.scanStatus.backfill.pendingAgents],
|
|
1514
|
+
completedAgents: [...this.scanStatus.backfill.completedAgents]
|
|
1515
|
+
}
|
|
1477
1516
|
};
|
|
1478
1517
|
}
|
|
1479
1518
|
subscribe(listener) {
|
|
@@ -1490,11 +1529,13 @@ var LiveScanStore = class {
|
|
|
1490
1529
|
}
|
|
1491
1530
|
async shutdown() {
|
|
1492
1531
|
this.shuttingDown = true;
|
|
1493
|
-
for (const
|
|
1494
|
-
|
|
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
|
+
}
|
|
1495
1538
|
}
|
|
1496
|
-
this.refreshTimers.clear();
|
|
1497
|
-
this.pendingRefreshPathCounts.clear();
|
|
1498
1539
|
if (this.pendingEventTimer) {
|
|
1499
1540
|
clearTimeout(this.pendingEventTimer);
|
|
1500
1541
|
this.pendingEventTimer = null;
|
|
@@ -1555,6 +1596,7 @@ var LiveScanStore = class {
|
|
|
1555
1596
|
])
|
|
1556
1597
|
);
|
|
1557
1598
|
this.updateScanStatus({
|
|
1599
|
+
...this.scanStatus,
|
|
1558
1600
|
active: uniqueAgentNames.length > 0,
|
|
1559
1601
|
phase: uniqueAgentNames.length > 0 ? phase : "idle",
|
|
1560
1602
|
pendingAgents: uniqueAgentNames,
|
|
@@ -1680,6 +1722,108 @@ var LiveScanStore = class {
|
|
|
1680
1722
|
completedAt: now
|
|
1681
1723
|
});
|
|
1682
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
|
+
}
|
|
1683
1827
|
queueEvent(event) {
|
|
1684
1828
|
this.pendingEvent = this.pendingEvent ? mergeEvents(this.pendingEvent, event) : event;
|
|
1685
1829
|
if (this.pendingEventTimer) {
|
|
@@ -1813,7 +1957,8 @@ var LiveScanStore = class {
|
|
|
1813
1957
|
jobs: batch.jobs,
|
|
1814
1958
|
agentNames: [],
|
|
1815
1959
|
sessionsByAgent: {},
|
|
1816
|
-
metaByAgent: {}
|
|
1960
|
+
metaByAgent: {},
|
|
1961
|
+
skipFtsIntegrityCheck: this.ftsIntegrityChecked
|
|
1817
1962
|
}
|
|
1818
1963
|
});
|
|
1819
1964
|
worker.unref();
|
|
@@ -1827,6 +1972,7 @@ var LiveScanStore = class {
|
|
|
1827
1972
|
sessions: message.sessions
|
|
1828
1973
|
});
|
|
1829
1974
|
settled = true;
|
|
1975
|
+
this.ftsIntegrityChecked = true;
|
|
1830
1976
|
batch.resolve();
|
|
1831
1977
|
}
|
|
1832
1978
|
});
|
|
@@ -1873,7 +2019,7 @@ var LiveScanStore = class {
|
|
|
1873
2019
|
this.byAgent = {};
|
|
1874
2020
|
for (const agent of this.agents) {
|
|
1875
2021
|
this.byAgent[agent.name] = sortSessions(result.byAgent[agent.name] ?? []);
|
|
1876
|
-
this.
|
|
2022
|
+
this.getRefreshState(agent.name).lastRefreshAt = result.cacheTimestamps?.[agent.name] ?? Date.now();
|
|
1877
2023
|
}
|
|
1878
2024
|
this.rebuildSessions();
|
|
1879
2025
|
}
|
|
@@ -1903,25 +2049,41 @@ var LiveScanStore = class {
|
|
|
1903
2049
|
console.error("[search] Background index sync failed:", error);
|
|
1904
2050
|
}
|
|
1905
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
|
+
*/
|
|
1906
2058
|
scheduleRefresh(agentName, delayMs = REFRESH_DEBOUNCE_MS) {
|
|
1907
|
-
|
|
1908
|
-
const
|
|
1909
|
-
|
|
1910
|
-
|
|
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);
|
|
1911
2071
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
2072
|
+
appLogger.debug("scan.refresh.schedule", { agent: agentName, delay_ms: effectiveDelayMs });
|
|
2073
|
+
state.timerDeadline = deadline;
|
|
2074
|
+
state.timer = setTimeout(() => {
|
|
2075
|
+
state.timer = null;
|
|
1914
2076
|
void this.refreshAgent(agentName);
|
|
1915
|
-
},
|
|
1916
|
-
this.refreshTimers.set(agentName, timer);
|
|
2077
|
+
}, effectiveDelayMs);
|
|
1917
2078
|
}
|
|
1918
2079
|
async refreshAgent(agentName) {
|
|
1919
|
-
|
|
2080
|
+
const state = this.getRefreshState(agentName);
|
|
2081
|
+
if (state.inFlight) {
|
|
1920
2082
|
appLogger.debug("scan.refresh.pending", { agent: agentName });
|
|
1921
|
-
|
|
2083
|
+
state.pendingRerun = true;
|
|
1922
2084
|
return;
|
|
1923
2085
|
}
|
|
1924
|
-
|
|
2086
|
+
state.inFlight = true;
|
|
1925
2087
|
this.beginAgentScan(agentName);
|
|
1926
2088
|
try {
|
|
1927
2089
|
await this.runRefresh(agentName);
|
|
@@ -1929,17 +2091,19 @@ var LiveScanStore = class {
|
|
|
1929
2091
|
appLogger.error("scan.refresh.error", { agent: agentName, error });
|
|
1930
2092
|
console.error(`[${agentName}] Session refresh failed:`, error);
|
|
1931
2093
|
} finally {
|
|
1932
|
-
|
|
2094
|
+
state.inFlight = false;
|
|
1933
2095
|
this.finishAgentScan(agentName);
|
|
1934
|
-
if (
|
|
2096
|
+
if (state.pendingRerun) {
|
|
2097
|
+
state.pendingRerun = false;
|
|
1935
2098
|
this.scheduleRefresh(agentName, PENDING_REFRESH_DELAY_MS);
|
|
1936
2099
|
}
|
|
1937
2100
|
}
|
|
1938
2101
|
}
|
|
1939
2102
|
async runRefresh(agentName) {
|
|
1940
2103
|
const startedAt = performance.now();
|
|
1941
|
-
const
|
|
1942
|
-
|
|
2104
|
+
const state = this.getRefreshState(agentName);
|
|
2105
|
+
const pendingPathCount = state.pendingPathCount;
|
|
2106
|
+
state.pendingPathCount = 0;
|
|
1943
2107
|
const agent = this.agents.find((item) => item.name === agentName);
|
|
1944
2108
|
if (!agent) {
|
|
1945
2109
|
appLogger.warn("scan.refresh.missing_agent", { agent: agentName });
|
|
@@ -1948,7 +2112,7 @@ var LiveScanStore = class {
|
|
|
1948
2112
|
const previousSessions = this.byAgent[agentName] ?? [];
|
|
1949
2113
|
const cached = loadCachedSessions(agentName);
|
|
1950
2114
|
const refreshBaseline = cached?.sessions ?? previousSessions;
|
|
1951
|
-
const cacheTimestamp = cached?.timestamp ??
|
|
2115
|
+
const cacheTimestamp = cached?.timestamp ?? state.lastRefreshAt;
|
|
1952
2116
|
if (cached) {
|
|
1953
2117
|
restoreAgentCacheMeta(agent, cached.meta);
|
|
1954
2118
|
}
|
|
@@ -1971,24 +2135,29 @@ var LiveScanStore = class {
|
|
|
1971
2135
|
availabilityDuration = performance.now() - availabilityStartedAt;
|
|
1972
2136
|
if (!isAvailable) {
|
|
1973
2137
|
nextSessions = [];
|
|
1974
|
-
|
|
2138
|
+
state.lastRefreshAt = Date.now();
|
|
1975
2139
|
} else if (!isInitialized) {
|
|
1976
2140
|
this.setScanPhase("initializing");
|
|
1977
2141
|
const scanStartedAt = performance.now();
|
|
1978
|
-
const result = await this.scanAgentInWorker(
|
|
2142
|
+
const result = await this.scanAgentInWorker(
|
|
2143
|
+
agent,
|
|
2144
|
+
previousSessions,
|
|
2145
|
+
null,
|
|
2146
|
+
this.startupScanOptions
|
|
2147
|
+
);
|
|
1979
2148
|
nextSessions = result.sessions;
|
|
1980
2149
|
agent.setSessionMetaMap?.(new Map(Object.entries(result.meta)));
|
|
1981
2150
|
fullScanSessions = attachMissingProjectIdentities(nextSessions);
|
|
1982
2151
|
nextSessions = fullScanSessions;
|
|
1983
2152
|
scanDuration = performance.now() - scanStartedAt;
|
|
1984
|
-
|
|
2153
|
+
state.lastRefreshAt = Date.now();
|
|
1985
2154
|
} else if (cached && agent instanceof FileSystemSessionSource) {
|
|
1986
2155
|
const scanStartedAt = performance.now();
|
|
1987
2156
|
const result = await this.scanAgentInWorker(
|
|
1988
2157
|
agent,
|
|
1989
2158
|
cached.sessions,
|
|
1990
2159
|
null,
|
|
1991
|
-
|
|
2160
|
+
this.startupScanOptions,
|
|
1992
2161
|
{
|
|
1993
2162
|
sourceSync: true,
|
|
1994
2163
|
meta: cached.meta
|
|
@@ -2005,7 +2174,7 @@ var LiveScanStore = class {
|
|
|
2005
2174
|
preciseChangedIds
|
|
2006
2175
|
);
|
|
2007
2176
|
scanDuration = performance.now() - scanStartedAt;
|
|
2008
|
-
|
|
2177
|
+
state.lastRefreshAt = Date.now();
|
|
2009
2178
|
if (preciseChangedIds.length === 0) {
|
|
2010
2179
|
appLogger.debug("scan.refresh.unchanged", {
|
|
2011
2180
|
agent: agentName,
|
|
@@ -2018,7 +2187,7 @@ var LiveScanStore = class {
|
|
|
2018
2187
|
agent.checkForChanges(cacheTimestamp, refreshBaseline)
|
|
2019
2188
|
);
|
|
2020
2189
|
checkDuration = performance.now() - checkStartedAt;
|
|
2021
|
-
|
|
2190
|
+
state.lastRefreshAt = checkResult.timestamp;
|
|
2022
2191
|
if (!checkResult.hasChanges) {
|
|
2023
2192
|
appLogger.debug("scan.refresh.unchanged", {
|
|
2024
2193
|
agent: agentName,
|
|
@@ -2049,7 +2218,7 @@ var LiveScanStore = class {
|
|
|
2049
2218
|
fullScanSessions = attachMissingProjectIdentities(nextSessions);
|
|
2050
2219
|
nextSessions = fullScanSessions;
|
|
2051
2220
|
scanDuration = performance.now() - scanStartedAt;
|
|
2052
|
-
|
|
2221
|
+
state.lastRefreshAt = Date.now();
|
|
2053
2222
|
}
|
|
2054
2223
|
nextSessions = attachMissingProjectIdentities(nextSessions);
|
|
2055
2224
|
const filterStartedAt = performance.now();
|
|
@@ -2110,9 +2279,11 @@ var LiveScanStore = class {
|
|
|
2110
2279
|
event.totalSessions = this.sessions.length;
|
|
2111
2280
|
this.emit(event);
|
|
2112
2281
|
}
|
|
2282
|
+
const totalDurationMs = performance.now() - startedAt;
|
|
2283
|
+
state.lastRefreshDurationMs = totalDurationMs;
|
|
2113
2284
|
appLogger.info("scan.refresh.done", {
|
|
2114
2285
|
agent: agentName,
|
|
2115
|
-
duration_ms: Math.round(
|
|
2286
|
+
duration_ms: Math.round(totalDurationMs),
|
|
2116
2287
|
sessions: nextSessions.length,
|
|
2117
2288
|
new_sessions: event?.newSessions ?? 0,
|
|
2118
2289
|
updated_sessions: event?.updatedSessions ?? 0,
|
|
@@ -2201,6 +2372,11 @@ var main = defineCommand({
|
|
|
2201
2372
|
description: "HTTP server port",
|
|
2202
2373
|
default: String(DEFAULT_PORT)
|
|
2203
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
|
+
},
|
|
2204
2380
|
agent: {
|
|
2205
2381
|
type: "string",
|
|
2206
2382
|
alias: "a",
|
|
@@ -2278,7 +2454,7 @@ var main = defineCommand({
|
|
|
2278
2454
|
log_path: appLogger.getLogPath()
|
|
2279
2455
|
});
|
|
2280
2456
|
if (clearCache) {
|
|
2281
|
-
const { clearCache: clear } = await import("./dist-
|
|
2457
|
+
const { clearCache: clear } = await import("./dist-TZTL6VP4.js");
|
|
2282
2458
|
clear();
|
|
2283
2459
|
appLogger.info("cache.clear");
|
|
2284
2460
|
console.log("Cache cleared.");
|
|
@@ -2367,7 +2543,8 @@ var main = defineCommand({
|
|
|
2367
2543
|
defaultSessionFrom: listDefaultFrom,
|
|
2368
2544
|
defaultSessionTo: listDefaultTo,
|
|
2369
2545
|
defaultSessionDays: listDefaultDays,
|
|
2370
|
-
portFallbackAttempts: explicitPort ? 1 : DEFAULT_PORT_FALLBACK_ATTEMPTS
|
|
2546
|
+
portFallbackAttempts: explicitPort ? 1 : DEFAULT_PORT_FALLBACK_ATTEMPTS,
|
|
2547
|
+
hostname: args.host
|
|
2371
2548
|
});
|
|
2372
2549
|
} catch (error) {
|
|
2373
2550
|
console.error(getServerStartupErrorMessage(error, port));
|