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.
- package/README.md +2 -2
- package/dist/{chunk-A4U2SMJJ.js → chunk-DHNP33L4.js} +20 -12
- package/dist/chunk-DHNP33L4.js.map +1 -0
- package/dist/{chunk-7APNDHQ6.js → chunk-HIYT2ZJL.js} +659 -353
- package/dist/chunk-HIYT2ZJL.js.map +1 -0
- package/dist/{dist-KEPJFHOC.js → dist-DMGLFBW6.js} +14 -4
- package/dist/index.js +224 -81
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +2 -2
- package/dist/search-index-worker.js +73 -39
- package/dist/search-index-worker.js.map +1 -1
- package/dist/smart-tag-worker.js +2 -2
- package/dist/web/assets/AgentIcon-DcUJM198.js +1 -0
- package/dist/web/assets/BookmarkButton-CJjTjyae.js +1 -0
- package/dist/web/assets/Dashboard-wjTLT17d.js +39 -0
- package/dist/web/assets/DialogTitle-Bz0eXTGu.js +1 -0
- package/dist/web/assets/InteractiveReceipt-xnRqJvfd.js +1 -0
- package/dist/web/assets/PrismHighlighter-C7OfXHq2.js +6 -0
- package/dist/web/assets/Projects-DBxubQuM.js +1 -0
- package/dist/web/assets/SearchFilterBar-BG9ah_Po.js +1 -0
- package/dist/web/assets/SearchResultsPanel-Clmf2xKw.js +1 -0
- package/dist/web/assets/SessionDetail-CgR7ipmi.js +54 -0
- package/dist/web/assets/index-DR07vs5V.js +1890 -0
- package/dist/web/assets/index-DuB6tGoe.css +2 -0
- package/dist/web/assets/index.dom-CCLJ9_FR.js +1 -0
- package/dist/web/assets/jsx-runtime-n5LQ9ujS.js +1 -0
- package/dist/web/assets/preload-helper-CmKXJxR3.js +1 -0
- package/dist/web/assets/session-indexes-EHJdYE4j.js +4 -0
- package/dist/web/assets/session-title-CNhapdPJ.js +1 -0
- package/dist/web/assets/useTheme-CwDcVdGz.js +1 -0
- package/dist/web/assets/utils-B6KiDbIe.js +1 -0
- package/dist/web/index.html +11 -7
- package/package.json +3 -3
- package/dist/chunk-7APNDHQ6.js.map +0 -1
- package/dist/chunk-A4U2SMJJ.js.map +0 -1
- package/dist/web/assets/index-B076CRkD.js +0 -109
- package/dist/web/assets/index-CWNbVhet.css +0 -2
- package/dist/web/assets/markdown-dR4kAewZ.js +0 -14
- package/dist/web/assets/react-D9s532MZ.js +0 -1821
- package/dist/web/assets/rolldown-runtime-QTnfLwEv.js +0 -1
- package/dist/web/assets/syntax-mZXQuVcd.js +0 -6
- package/dist/web/assets/vendor-i9U7_fNF.js +0 -41
- /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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
51
|
+
startOfCalendarDay,
|
|
47
52
|
upsertBookmark,
|
|
48
53
|
upsertSessionAlias
|
|
49
|
-
} from "./chunk-
|
|
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
|
|
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 *
|
|
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 ??=
|
|
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 ??=
|
|
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:
|
|
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 *
|
|
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
|
|
416
|
-
|
|
417
|
-
|
|
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 ??
|
|
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({
|
|
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
|
|
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", {
|
|
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)
|
|
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 ?
|
|
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(
|
|
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 =
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
3190
|
-
|
|
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:
|
|
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
|
};
|