@wrongstack/tools 0.320.1 → 1.0.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/_danger-detect.d.ts +8 -0
- package/dist/_syntax-check.d.ts +1 -2
- package/dist/audit.d.ts +4 -5
- package/dist/audit.js +6 -6
- package/dist/auto-proceed-loop-guard.js +9 -160
- package/dist/bash-kill-guard.d.ts +2 -3
- package/dist/bash.d.ts +2 -3
- package/dist/bash.js +4 -4
- package/dist/batch-tool-use.d.ts +2 -3
- package/dist/batch-tool-use.js +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/tools.d.ts +56 -45
- package/dist/builtin.js +50 -50
- package/dist/{chunk-T77XOX7E.js → chunk-24T5XIJU.js} +1 -1
- package/dist/{chunk-DTQ75VBC.js → chunk-2CTZW6JQ.js} +2 -2
- package/dist/chunk-37QKUQ2Z.js +171 -0
- package/dist/{chunk-KWXIKGE3.js → chunk-3D5DJWOE.js} +3 -3
- package/dist/{chunk-XSSFBS5I.js → chunk-4ERMPDHV.js} +11 -6
- package/dist/{chunk-QSNVDHOZ.js → chunk-4WPBS45A.js} +4 -5
- package/dist/{chunk-4CQWNZ62.js → chunk-5ITQPCLC.js} +2 -2
- package/dist/{chunk-B4HTSV6Z.js → chunk-5KS24LTS.js} +11 -11
- package/dist/{chunk-QPNYDH5B.js → chunk-62OAECEQ.js} +29 -7
- package/dist/{chunk-DFQDJT22.js → chunk-6GOZTS6F.js} +3 -3
- package/dist/{chunk-34VLZ35Z.js → chunk-7HL23T3T.js} +63 -10
- package/dist/{chunk-HFXSNKCV.js → chunk-7JJFC6CT.js} +1 -1
- package/dist/{chunk-NDIT3SBZ.js → chunk-7N5U2RAU.js} +2 -2
- package/dist/{chunk-QBV725YG.js → chunk-7ODZTPCU.js} +2 -2
- package/dist/{chunk-4BABEJC2.js → chunk-AB2CAUOP.js} +64 -53
- package/dist/{chunk-6AAOT4PW.js → chunk-AQ5YBURX.js} +16 -4
- package/dist/{chunk-ZMW6WJFT.js → chunk-ASEXQRA7.js} +3 -3
- package/dist/{chunk-FRN7TIAA.js → chunk-B3MGJBFI.js} +1 -1
- package/dist/{chunk-JLYA4X7K.js → chunk-CGXTR34H.js} +21 -6
- package/dist/{chunk-IU4DV2PA.js → chunk-CHEK3ASV.js} +6 -3
- package/dist/{chunk-GOIZNNOA.js → chunk-DJYLI7EW.js} +1 -1
- package/dist/{chunk-Z6IHMJMC.js → chunk-DK4J2WMH.js} +11 -11
- package/dist/{chunk-OYP27E3I.js → chunk-DRRY6SCM.js} +47 -20
- package/dist/{chunk-BK732J7I.js → chunk-FBUP524S.js} +4 -4
- package/dist/{chunk-NYRLWDT5.js → chunk-FVM3O4JQ.js} +1 -1
- package/dist/chunk-FYDBUVQD.js +244 -0
- package/dist/{chunk-PV6HIZQD.js → chunk-GDDXX27P.js} +50 -50
- package/dist/{chunk-3GIKYHLA.js → chunk-GKSG5CPH.js} +6 -6
- package/dist/{chunk-ESLPBT5A.js → chunk-HDYUBRMF.js} +4 -3
- package/dist/{chunk-ELRRTWNP.js → chunk-IXIS7RSG.js} +2 -2
- package/dist/{chunk-HR67OWEP.js → chunk-IYH4OHBP.js} +5 -5
- package/dist/{chunk-W232EFEM.js → chunk-JDOO4P6Z.js} +7 -4
- package/dist/{chunk-OTPTQBYE.js → chunk-KDEXUQY7.js} +19 -8
- package/dist/{chunk-2OXHS3KZ.js → chunk-KLJZ4WV4.js} +4 -3
- package/dist/{chunk-3M7LLN7W.js → chunk-LQSOU63Z.js} +12 -11
- package/dist/{chunk-JVRLTEO7.js → chunk-M6UV67BH.js} +2 -2
- package/dist/{chunk-C2DQKVLO.js → chunk-MJSJWXBF.js} +1 -1
- package/dist/{chunk-NSPNFOJ7.js → chunk-MK2JK35J.js} +25 -10
- package/dist/{chunk-JSNSMG5R.js → chunk-MSOAX3GC.js} +1 -1
- package/dist/{chunk-J43O3BOX.js → chunk-N3K2SFEI.js} +532 -492
- package/dist/{chunk-J74TMK4M.js → chunk-NJX2RXKJ.js} +50 -7
- package/dist/{chunk-5UFVCIUB.js → chunk-NN2SNP2R.js} +4 -4
- package/dist/{chunk-WAQASTXB.js → chunk-O6BY43ZB.js} +29 -8
- package/dist/{chunk-QGF2C7TF.js → chunk-PVCB7RUZ.js} +2 -2
- package/dist/{chunk-YTKSNTT4.js → chunk-QA6IASYX.js} +8 -8
- package/dist/{chunk-VPALDFYM.js → chunk-RV7BVTDG.js} +1 -1
- package/dist/chunk-S57CDR2H.js +288 -0
- package/dist/{chunk-W2CECGTG.js → chunk-SMARDKAC.js} +1 -1
- package/dist/{chunk-QWKSKFKZ.js → chunk-SOR7OCYP.js} +1 -1
- package/dist/{chunk-I7FBZOEP.js → chunk-T3SMFF5Q.js} +11 -5
- package/dist/{chunk-4BHUSMNL.js → chunk-UI3ZJIG3.js} +1 -1
- package/dist/{chunk-WXJY3BKV.js → chunk-VBF2YXUW.js} +2 -2
- package/dist/{chunk-FQNXYA6W.js → chunk-VU3QBYL4.js} +36 -5
- package/dist/{chunk-VD2LV5WO.js → chunk-VZGPASGD.js} +37 -12
- package/dist/{chunk-UZFY3CKD.js → chunk-WEEVSWQB.js} +17 -6
- package/dist/chunk-XXRMF2BC.js +186 -0
- package/dist/{chunk-LRDIRJJL.js → chunk-YEISRT4S.js} +5 -11
- package/dist/{chunk-Q5JPPCZC.js → chunk-YJTXRH5Y.js} +3 -2
- package/dist/{chunk-23EX5WDO.js → chunk-YULVUTK5.js} +3 -3
- package/dist/{chunk-Z5Q3TXRY.js → chunk-ZTCJCYLN.js} +3 -3
- package/dist/circuit-breaker.d.ts +1 -0
- package/dist/circuit-breaker.js +1 -1
- package/dist/clarify.d.ts +1 -0
- package/dist/codebase-index/codebase-ast-replace-tool.d.ts +2 -3
- package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +5 -4
- package/dist/codebase-index/codebase-incoming-calls-tool.d.ts +4 -3
- package/dist/codebase-index/codebase-index-tool.d.ts +2 -3
- package/dist/codebase-index/codebase-invariant-check-tool.d.ts +2 -3
- package/dist/codebase-index/codebase-outgoing-calls-tool.d.ts +4 -3
- package/dist/codebase-index/codebase-repo-map-tool.d.ts +2 -3
- package/dist/codebase-index/codebase-search-tool.d.ts +2 -3
- package/dist/codebase-index/codebase-skeleton-tool.d.ts +2 -3
- package/dist/codebase-index/codebase-stats-tool.d.ts +9 -3
- package/dist/codebase-index/codebase-targeted-test-tool.d.ts +4 -3
- package/dist/codebase-index/index.d.ts +11 -11
- package/dist/codebase-index/index.js +8 -8
- package/dist/codebase-index/project-server-framing.d.ts +29 -0
- package/dist/codebase-index/project-server-lifecycle.d.ts +20 -0
- package/dist/codebase-index/project-server-operations.d.ts +37 -0
- package/dist/codebase-index/project-server-types.d.ts +30 -0
- package/dist/codebase-index/project-server-watcher.d.ts +25 -0
- package/dist/codebase-index/project-server.js +448 -391
- package/dist/codebase-index/worker.js +2 -2
- package/dist/codebase-index/writer-init.d.ts +13 -0
- package/dist/codebase-index/writer-maintenance.d.ts +14 -0
- package/dist/codebase-index/writer.d.ts +0 -15
- package/dist/design.d.ts +2 -2
- package/dist/design.js +1 -1
- package/dist/diff.d.ts +4 -4
- package/dist/diff.js +1 -1
- package/dist/document.d.ts +3 -4
- package/dist/document.js +1 -1
- package/dist/e2e.d.ts +1 -1
- package/dist/e2e.js +1 -1
- package/dist/edit.d.ts +3 -3
- package/dist/edit.js +6 -6
- package/dist/exec.d.ts +2 -3
- package/dist/exec.js +4 -4
- package/dist/fetch.d.ts +4 -3
- package/dist/fetch.js +1 -1
- package/dist/format.d.ts +17 -45
- package/dist/format.js +12 -8
- package/dist/git.d.ts +3 -3
- package/dist/git.js +1 -1
- package/dist/glob.d.ts +2 -3
- package/dist/glob.js +2 -2
- package/dist/grep.d.ts +7 -5
- package/dist/grep.js +3 -3
- package/dist/index.d.ts +54 -45
- package/dist/index.js +118 -59
- package/dist/install.d.ts +7 -55
- package/dist/install.js +6 -6
- package/dist/json.d.ts +3 -4
- package/dist/json.js +2 -2
- package/dist/kanban-tool-types.d.ts +1 -2
- package/dist/kanban.d.ts +3 -19
- package/dist/kanban.js +1 -1
- package/dist/languages/execute-tool.d.ts +2 -3
- package/dist/languages/index.d.ts +2 -2
- package/dist/languages/index.js +4 -4
- package/dist/languages/tool.d.ts +3 -4
- package/dist/{legacy-bridge-FOV5ODDI.js → legacy-bridge-YXANP5QB.js} +6 -6
- package/dist/lint.d.ts +5 -4
- package/dist/lint.js +6 -6
- package/dist/logs.d.ts +3 -4
- package/dist/logs.js +2 -2
- package/dist/memory.d.ts +7 -8
- package/dist/memory.js +1 -1
- package/dist/mode.d.ts +3 -4
- package/dist/mode.js +1 -1
- package/dist/next-steps-tool.d.ts +2 -3
- package/dist/next-steps.d.ts +4 -0
- package/dist/next-steps.js +9 -175
- package/dist/outdated.d.ts +5 -29
- package/dist/outdated.js +3 -3
- package/dist/pack.js +51 -51
- package/dist/patch.d.ts +2 -3
- package/dist/patch.js +5 -5
- package/dist/plan.d.ts +4 -4
- package/dist/plan.js +3 -3
- package/dist/process-registry.js +2 -2
- package/dist/ps-slash.js +4 -4
- package/dist/pwsh.d.ts +1 -0
- package/dist/read.d.ts +5 -5
- package/dist/read.js +5 -5
- package/dist/replace.d.ts +2 -3
- package/dist/replace.js +6 -6
- package/dist/scaffold.d.ts +7 -3
- package/dist/scaffold.js +3 -1
- package/dist/search.d.ts +17 -3
- package/dist/search.js +5 -1
- package/dist/set-working-dir.d.ts +2 -3
- package/dist/skill.d.ts +4 -5
- package/dist/skill.js +1 -1
- package/dist/task.d.ts +6 -6
- package/dist/task.js +3 -3
- package/dist/test.d.ts +5 -4
- package/dist/test.js +6 -6
- package/dist/todo.d.ts +8 -8
- package/dist/todo.js +2 -2
- package/dist/tool-diff.js +11 -230
- package/dist/tool-help.d.ts +2 -3
- package/dist/tool-help.js +1 -1
- package/dist/tool-search.d.ts +2 -3
- package/dist/tool-search.js +1 -1
- package/dist/tool-summary.js +5 -281
- package/dist/tool-tier.js +52 -52
- package/dist/tool-use.d.ts +2 -3
- package/dist/tool-use.js +1 -1
- package/dist/tree.d.ts +5 -3
- package/dist/tree.js +5 -1
- package/dist/typecheck.d.ts +3 -3
- package/dist/typecheck.js +6 -6
- package/dist/write.d.ts +2 -3
- package/dist/write.js +6 -6
- package/package.json +5 -5
- package/dist/spawn-background.d.ts +0 -80
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
projectIndexServerBuildId,
|
|
11
11
|
projectIndexServerEndpoint,
|
|
12
12
|
projectIndexServerMetadataPath
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-GDDXX27P.js";
|
|
14
14
|
import {
|
|
15
15
|
fileGraphService,
|
|
16
16
|
incomingCallsService,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
searchService,
|
|
23
23
|
statsService,
|
|
24
24
|
symbolGraphService
|
|
25
|
-
} from "../chunk-
|
|
25
|
+
} from "../chunk-N3K2SFEI.js";
|
|
26
26
|
import "../chunk-NU2IZ72J.js";
|
|
27
27
|
import "../chunk-VVZPQPTY.js";
|
|
28
28
|
import "../chunk-6IEBD27U.js";
|
|
@@ -30,22 +30,147 @@ import "../chunk-UCYQTZ7F.js";
|
|
|
30
30
|
import {
|
|
31
31
|
isIndexablePath
|
|
32
32
|
} from "../chunk-YOHO4DVT.js";
|
|
33
|
-
import "../chunk-
|
|
33
|
+
import "../chunk-2CTZW6JQ.js";
|
|
34
34
|
import "../chunk-KE7A76VC.js";
|
|
35
35
|
|
|
36
36
|
// src/codebase-index/project-server.ts
|
|
37
37
|
import { randomBytes } from "node:crypto";
|
|
38
|
-
import * as
|
|
38
|
+
import * as fs2 from "node:fs";
|
|
39
39
|
import * as net from "node:net";
|
|
40
|
+
import * as path3 from "node:path";
|
|
41
|
+
import { startSharedHeapWatchdog, useDaemonPerfDefaults } from "@wrongstack/core/utils";
|
|
42
|
+
import { bindProjectEndpoint } from "@wrongstack/persistence";
|
|
43
|
+
|
|
44
|
+
// src/codebase-index/project-server-framing.ts
|
|
45
|
+
var MAX_CLIENT_WRITE_BUFFER_BYTES = 8 * 1024 * 1024;
|
|
46
|
+
function sendServerMessage(state, message) {
|
|
47
|
+
if (state.socket.destroyed) return;
|
|
48
|
+
if (state.socket.writableLength > MAX_CLIENT_WRITE_BUFFER_BYTES) {
|
|
49
|
+
state.socket.destroy(new Error("Index client fell too far behind on reads"));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
state.socket.write(
|
|
53
|
+
state.binary ? encodeBinaryFrame(message) : encodeProjectServerMessage(message)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
function consumeClientChunk(state, chunk, onMessage) {
|
|
57
|
+
const buffer = state.buffer.length === 0 ? chunk : Buffer.concat([state.buffer, chunk]);
|
|
58
|
+
let scan = 0;
|
|
59
|
+
while (true) {
|
|
60
|
+
if (buffer.length - scan === 0) break;
|
|
61
|
+
if (isBinaryFrame(buffer[scan])) {
|
|
62
|
+
if (buffer.length - scan < 5) break;
|
|
63
|
+
const frameLen = buffer.readUInt32BE(scan + 1);
|
|
64
|
+
if (frameLen > MAX_INBOUND_BINARY_FRAME_BYTES) {
|
|
65
|
+
state.buffer = Buffer.alloc(0);
|
|
66
|
+
state.socket.destroy(new Error("codebase-index client binary frame exceeds the IPC limit"));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (buffer.length - scan < 5 + frameLen) break;
|
|
70
|
+
const payload = buffer.subarray(scan + 5, scan + 5 + frameLen);
|
|
71
|
+
let message2;
|
|
72
|
+
try {
|
|
73
|
+
message2 = decodeBinaryFrame(payload);
|
|
74
|
+
} catch {
|
|
75
|
+
state.buffer = Buffer.alloc(0);
|
|
76
|
+
state.socket.destroy(new Error("invalid codebase-index client binary frame"));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
scan += 5 + frameLen;
|
|
80
|
+
state.binary = true;
|
|
81
|
+
state.lastSeenAt = Date.now();
|
|
82
|
+
onMessage(state, message2);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const newline = buffer.indexOf(10, scan);
|
|
86
|
+
if (newline < 0) {
|
|
87
|
+
if (buffer.length - scan > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
88
|
+
state.buffer = Buffer.alloc(0);
|
|
89
|
+
state.socket.destroy(new Error("codebase-index client message exceeds the IPC limit"));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
if (newline - scan > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
95
|
+
state.buffer = Buffer.alloc(0);
|
|
96
|
+
state.socket.destroy(new Error("codebase-index client message exceeds the IPC limit"));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const line = buffer.subarray(scan, newline).toString("utf8");
|
|
100
|
+
scan = newline + 1;
|
|
101
|
+
if (!line) continue;
|
|
102
|
+
let message;
|
|
103
|
+
try {
|
|
104
|
+
message = JSON.parse(line);
|
|
105
|
+
} catch {
|
|
106
|
+
state.buffer = Buffer.alloc(0);
|
|
107
|
+
state.socket.destroy(new Error("invalid codebase-index client message"));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
state.lastSeenAt = Date.now();
|
|
111
|
+
onMessage(state, message);
|
|
112
|
+
}
|
|
113
|
+
state.buffer = scan === 0 ? buffer : Buffer.from(buffer.subarray(scan));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/codebase-index/project-server-lifecycle.ts
|
|
117
|
+
import * as fs from "node:fs";
|
|
40
118
|
import * as path from "node:path";
|
|
119
|
+
import process2 from "node:process";
|
|
41
120
|
import { restrictFilePermissions } from "@wrongstack/core/security";
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
121
|
+
import { atomicWrite } from "@wrongstack/persistence";
|
|
122
|
+
function removeMetadataIfOwned(metadataPath2, pid) {
|
|
123
|
+
try {
|
|
124
|
+
const current = JSON.parse(fs.readFileSync(metadataPath2, "utf8"));
|
|
125
|
+
if (current.pid === pid) fs.rmSync(metadataPath2, { force: true });
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function writeProjectServerMetadata(metadataPath2, metadata) {
|
|
130
|
+
fs.mkdirSync(path.dirname(metadataPath2), { recursive: true });
|
|
131
|
+
await atomicWrite(metadataPath2, `${JSON.stringify(metadata, null, 2)}
|
|
132
|
+
`, { mode: 384 });
|
|
133
|
+
await restrictFilePermissions(metadataPath2, {
|
|
134
|
+
label: "codebase-index-metadata",
|
|
135
|
+
warn: (message) => process2.stderr.write(`${message}
|
|
136
|
+
`)
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
var SIGNAL_GUARD = /* @__PURE__ */ Symbol.for(
|
|
140
|
+
"wrongstack.codebase-index.project-server.signalGuard"
|
|
141
|
+
);
|
|
142
|
+
function armCodebaseIndexSignalGuard(stop2) {
|
|
143
|
+
const signalGuardStore = globalThis;
|
|
144
|
+
let guard = signalGuardStore[SIGNAL_GUARD];
|
|
145
|
+
if (!guard) {
|
|
146
|
+
let current = async () => void 0;
|
|
147
|
+
let armed = false;
|
|
148
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
149
|
+
const disarm = () => {
|
|
150
|
+
if (!armed) return;
|
|
151
|
+
armed = false;
|
|
152
|
+
for (const [signal, handler] of handlers) process2.removeListener(signal, handler);
|
|
153
|
+
handlers.clear();
|
|
154
|
+
};
|
|
155
|
+
guard = {
|
|
156
|
+
arm(next) {
|
|
157
|
+
current = next;
|
|
158
|
+
if (armed) return;
|
|
159
|
+
armed = true;
|
|
160
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
161
|
+
const handler = () => {
|
|
162
|
+
disarm();
|
|
163
|
+
void current(signal);
|
|
164
|
+
};
|
|
165
|
+
handlers.set(signal, handler);
|
|
166
|
+
process2.on(signal, handler);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
signalGuardStore[SIGNAL_GUARD] = guard;
|
|
171
|
+
}
|
|
172
|
+
guard.arm(stop2);
|
|
173
|
+
}
|
|
49
174
|
|
|
50
175
|
// src/codebase-index/project-server-cache.ts
|
|
51
176
|
var GenerationLruCache = class {
|
|
@@ -155,6 +280,262 @@ var ServerQueryCaches = class {
|
|
|
155
280
|
}
|
|
156
281
|
};
|
|
157
282
|
|
|
283
|
+
// src/codebase-index/project-server-operations.ts
|
|
284
|
+
function preservesQueryCaches(args) {
|
|
285
|
+
return !args.force && args.files !== void 0 && args.files.length > 0;
|
|
286
|
+
}
|
|
287
|
+
function isShareableFullIndex(args) {
|
|
288
|
+
return !args.force && (!args.files || args.files.length === 0) && (!args.langs || args.langs.length === 0) && (!args.ignore || args.ignore.length === 0);
|
|
289
|
+
}
|
|
290
|
+
function fixedArgs(ctx, args) {
|
|
291
|
+
return { ...args, projectRoot: ctx.projectRoot, indexDir: ctx.indexDir };
|
|
292
|
+
}
|
|
293
|
+
async function runFullIndex(ctx, state, id, args) {
|
|
294
|
+
const subscriber = { state, id };
|
|
295
|
+
let active = ctx.getActiveFullIndex();
|
|
296
|
+
if (!active) {
|
|
297
|
+
const controller = new AbortController();
|
|
298
|
+
const subscribers = /* @__PURE__ */ new Set([subscriber]);
|
|
299
|
+
const promise = ctx.withIndexWrite(
|
|
300
|
+
(reportProgress) => indexService(fixedArgs(ctx, args), {
|
|
301
|
+
signal: controller.signal,
|
|
302
|
+
onProgress: (current, total) => {
|
|
303
|
+
reportProgress(current, total);
|
|
304
|
+
for (const item of subscribers) {
|
|
305
|
+
if (!item.state.cancelled.has(item.id)) {
|
|
306
|
+
ctx.send(item.state, { type: "progress", id: item.id, current, total });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}),
|
|
311
|
+
// Shareable full index: whole-index scope, caches drop on completion.
|
|
312
|
+
{ preserveCaches: false }
|
|
313
|
+
);
|
|
314
|
+
active = { promise, controller, subscribers };
|
|
315
|
+
ctx.setActiveFullIndex(active);
|
|
316
|
+
void promise.finally(() => {
|
|
317
|
+
if (ctx.getActiveFullIndex() === active) ctx.setActiveFullIndex(null);
|
|
318
|
+
}).catch(() => {
|
|
319
|
+
});
|
|
320
|
+
} else {
|
|
321
|
+
active.subscribers.add(subscriber);
|
|
322
|
+
}
|
|
323
|
+
const selected = active;
|
|
324
|
+
state.cancel.set(id, () => {
|
|
325
|
+
state.cancelled.add(id);
|
|
326
|
+
selected.subscribers.delete(subscriber);
|
|
327
|
+
if (selected.subscribers.size === 0) {
|
|
328
|
+
selected.controller.abort(new Error("Indexing cancelled"));
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
try {
|
|
332
|
+
return await selected.promise;
|
|
333
|
+
} finally {
|
|
334
|
+
selected.subscribers.delete(subscriber);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async function dispatchOperation(ctx, state, message) {
|
|
338
|
+
const { id, op } = message;
|
|
339
|
+
const indexActivity2 = ctx.getActivity();
|
|
340
|
+
switch (op) {
|
|
341
|
+
case "index": {
|
|
342
|
+
const args = message.args;
|
|
343
|
+
if (isShareableFullIndex(args)) return runFullIndex(ctx, state, id, args);
|
|
344
|
+
const controller = new AbortController();
|
|
345
|
+
state.cancel.set(id, () => {
|
|
346
|
+
state.cancelled.add(id);
|
|
347
|
+
controller.abort(new Error("Indexing cancelled"));
|
|
348
|
+
});
|
|
349
|
+
return ctx.withIndexWrite(
|
|
350
|
+
(reportProgress) => indexService(fixedArgs(ctx, args), {
|
|
351
|
+
signal: controller.signal,
|
|
352
|
+
onProgress: (current, total) => {
|
|
353
|
+
reportProgress(current, total);
|
|
354
|
+
if (!state.cancelled.has(id)) {
|
|
355
|
+
ctx.send(state, { type: "progress", id, current, total });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}),
|
|
359
|
+
{ preserveCaches: preservesQueryCaches(args) }
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
case "search": {
|
|
363
|
+
const { value, stale } = staleAwareRead(
|
|
364
|
+
ctx.queryCaches.searchCache,
|
|
365
|
+
JSON.stringify(message.args),
|
|
366
|
+
indexActivity2,
|
|
367
|
+
() => searchService(fixedArgs(ctx, message.args))
|
|
368
|
+
);
|
|
369
|
+
return stale ? { ...value, stale: true } : value;
|
|
370
|
+
}
|
|
371
|
+
case "stats":
|
|
372
|
+
if (indexActivity2.indexing) {
|
|
373
|
+
throw indexRefreshInProgressError(indexActivity2.currentFile, indexActivity2.totalFiles);
|
|
374
|
+
}
|
|
375
|
+
return staleAwareRead(
|
|
376
|
+
ctx.queryCaches.statsCache,
|
|
377
|
+
"stats",
|
|
378
|
+
indexActivity2,
|
|
379
|
+
() => statsService(fixedArgs(ctx, message.args))
|
|
380
|
+
).value;
|
|
381
|
+
case "packageGraph": {
|
|
382
|
+
const { value, stale } = staleAwareRead(
|
|
383
|
+
ctx.queryCaches.packageGraphCache,
|
|
384
|
+
"package",
|
|
385
|
+
indexActivity2,
|
|
386
|
+
() => packageGraphService(fixedArgs(ctx, message.args))
|
|
387
|
+
);
|
|
388
|
+
return stale ? { ...value, stale: true } : value;
|
|
389
|
+
}
|
|
390
|
+
case "fileGraph": {
|
|
391
|
+
const key = message.args.packageFilter;
|
|
392
|
+
const { value, stale } = staleAwareRead(
|
|
393
|
+
ctx.queryCaches.fileGraphCache,
|
|
394
|
+
key,
|
|
395
|
+
indexActivity2,
|
|
396
|
+
() => fileGraphService(fixedArgs(ctx, message.args))
|
|
397
|
+
);
|
|
398
|
+
return stale ? { ...value, stale: true } : value;
|
|
399
|
+
}
|
|
400
|
+
case "symbolGraph": {
|
|
401
|
+
const key = message.args.fileFilter;
|
|
402
|
+
const { value, stale } = staleAwareRead(
|
|
403
|
+
ctx.queryCaches.symbolGraphCache,
|
|
404
|
+
key,
|
|
405
|
+
indexActivity2,
|
|
406
|
+
() => symbolGraphService(fixedArgs(ctx, message.args))
|
|
407
|
+
);
|
|
408
|
+
return stale ? { ...value, stale: true } : value;
|
|
409
|
+
}
|
|
410
|
+
case "incomingCalls": {
|
|
411
|
+
const callArgs = fixedArgs(ctx, message.args);
|
|
412
|
+
const cacheKey = JSON.stringify([
|
|
413
|
+
callArgs.symbol,
|
|
414
|
+
callArgs.file ?? "",
|
|
415
|
+
callArgs.limit ?? 100,
|
|
416
|
+
callArgs.transitive ?? false
|
|
417
|
+
]);
|
|
418
|
+
const { value, stale } = staleAwareRead(
|
|
419
|
+
ctx.queryCaches.incomingCallsCache,
|
|
420
|
+
cacheKey,
|
|
421
|
+
indexActivity2,
|
|
422
|
+
() => incomingCallsService(callArgs)
|
|
423
|
+
);
|
|
424
|
+
return stale ? { ...value, stale: true } : value;
|
|
425
|
+
}
|
|
426
|
+
case "outgoingCalls": {
|
|
427
|
+
const callArgs = fixedArgs(ctx, message.args);
|
|
428
|
+
const cacheKey = JSON.stringify([
|
|
429
|
+
callArgs.symbol,
|
|
430
|
+
callArgs.file ?? "",
|
|
431
|
+
callArgs.limit ?? 100,
|
|
432
|
+
callArgs.transitive ?? false
|
|
433
|
+
]);
|
|
434
|
+
const { value, stale } = staleAwareRead(
|
|
435
|
+
ctx.queryCaches.outgoingCallsCache,
|
|
436
|
+
cacheKey,
|
|
437
|
+
indexActivity2,
|
|
438
|
+
() => outgoingCallsService(callArgs)
|
|
439
|
+
);
|
|
440
|
+
return stale ? { ...value, stale: true } : value;
|
|
441
|
+
}
|
|
442
|
+
default:
|
|
443
|
+
throw new Error(`unknown index operation: ${String(op)}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/codebase-index/project-server-watcher.ts
|
|
448
|
+
import * as path2 from "node:path";
|
|
449
|
+
import {
|
|
450
|
+
DEFAULT_WALK_IGNORE_SET,
|
|
451
|
+
watchProjectTree
|
|
452
|
+
} from "@wrongstack/core/utils";
|
|
453
|
+
var DEFAULT_EXTERNAL_DEBOUNCE_MS = 400;
|
|
454
|
+
var DEFAULT_EXTERNAL_COALESCE_WINDOW_MS = 50;
|
|
455
|
+
function isIgnoredRelativePath(relativePath) {
|
|
456
|
+
return relativePath.split(/[/\\]/u).some((segment) => DEFAULT_WALK_IGNORE_SET.has(segment));
|
|
457
|
+
}
|
|
458
|
+
var ProjectServerWatcherManager = class {
|
|
459
|
+
constructor(options) {
|
|
460
|
+
this.options = options;
|
|
461
|
+
}
|
|
462
|
+
options;
|
|
463
|
+
externalWatcher;
|
|
464
|
+
externalDebounceMs = DEFAULT_EXTERNAL_DEBOUNCE_MS;
|
|
465
|
+
externalCoalesceWindowMs = DEFAULT_EXTERNAL_COALESCE_WINDOW_MS;
|
|
466
|
+
externalDebounceTimers = /* @__PURE__ */ new Map();
|
|
467
|
+
externalReadyFiles = /* @__PURE__ */ new Set();
|
|
468
|
+
externalReadyFlush;
|
|
469
|
+
get isWatching() {
|
|
470
|
+
return this.externalWatcher !== void 0;
|
|
471
|
+
}
|
|
472
|
+
get pendingFileCount() {
|
|
473
|
+
return this.externalDebounceTimers.size + this.externalReadyFiles.size;
|
|
474
|
+
}
|
|
475
|
+
enqueueExternalFile(file) {
|
|
476
|
+
const previous = this.externalDebounceTimers.get(file);
|
|
477
|
+
if (previous) clearTimeout(previous);
|
|
478
|
+
const timer = setTimeout(() => {
|
|
479
|
+
this.externalDebounceTimers.delete(file);
|
|
480
|
+
this.externalReadyFiles.add(file);
|
|
481
|
+
if (this.externalReadyFlush) clearTimeout(this.externalReadyFlush);
|
|
482
|
+
this.externalReadyFlush = setTimeout(() => {
|
|
483
|
+
this.externalReadyFlush = void 0;
|
|
484
|
+
const files = [...this.externalReadyFiles].sort();
|
|
485
|
+
this.externalReadyFiles.clear();
|
|
486
|
+
this.options.onFilesChanged(files);
|
|
487
|
+
}, this.externalCoalesceWindowMs);
|
|
488
|
+
this.externalReadyFlush.unref?.();
|
|
489
|
+
}, this.externalDebounceMs);
|
|
490
|
+
timer.unref?.();
|
|
491
|
+
this.externalDebounceTimers.set(file, timer);
|
|
492
|
+
}
|
|
493
|
+
ensureExternalWatcher() {
|
|
494
|
+
if (this.externalWatcher) return;
|
|
495
|
+
const projectRoot2 = this.options.projectRoot;
|
|
496
|
+
this.externalWatcher = watchProjectTree(
|
|
497
|
+
projectRoot2,
|
|
498
|
+
({ filename }) => {
|
|
499
|
+
if (!filename || isIgnoredRelativePath(filename)) return;
|
|
500
|
+
const absolute = path2.resolve(projectRoot2, filename);
|
|
501
|
+
const relative2 = path2.relative(projectRoot2, absolute);
|
|
502
|
+
if (relative2 === ".." || relative2.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative2) || !isIndexablePath(absolute)) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
this.enqueueExternalFile(absolute);
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
onError: () => {
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
stop() {
|
|
514
|
+
try {
|
|
515
|
+
this.externalWatcher?.close();
|
|
516
|
+
} catch {
|
|
517
|
+
}
|
|
518
|
+
this.externalWatcher = void 0;
|
|
519
|
+
for (const timer of this.externalDebounceTimers.values()) clearTimeout(timer);
|
|
520
|
+
this.externalDebounceTimers.clear();
|
|
521
|
+
if (this.externalReadyFlush) clearTimeout(this.externalReadyFlush);
|
|
522
|
+
this.externalReadyFlush = void 0;
|
|
523
|
+
this.externalReadyFiles.clear();
|
|
524
|
+
}
|
|
525
|
+
reconcile(clients2) {
|
|
526
|
+
const owners = [...clients2].filter((client) => client.watchExternal);
|
|
527
|
+
if (owners.length === 0) {
|
|
528
|
+
this.externalDebounceMs = DEFAULT_EXTERNAL_DEBOUNCE_MS;
|
|
529
|
+
this.externalCoalesceWindowMs = DEFAULT_EXTERNAL_COALESCE_WINDOW_MS;
|
|
530
|
+
this.stop();
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
this.externalDebounceMs = Math.min(...owners.map((client) => client.debounceMs));
|
|
534
|
+
this.externalCoalesceWindowMs = Math.min(...owners.map((client) => client.coalesceWindowMs));
|
|
535
|
+
this.ensureExternalWatcher();
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
|
|
158
539
|
// src/codebase-index/wal-maintenance.ts
|
|
159
540
|
var DEFAULT_IDLE_MS = 3e4;
|
|
160
541
|
var DEFAULT_OPTIMIZE_EVERY = 8;
|
|
@@ -235,8 +616,8 @@ function parseArgs(argv) {
|
|
|
235
616
|
}
|
|
236
617
|
if (!projectRoot2) throw new Error("codebase-index project server requires --project-root");
|
|
237
618
|
return {
|
|
238
|
-
projectRoot:
|
|
239
|
-
...indexDir2 ? { indexDir:
|
|
619
|
+
projectRoot: path3.resolve(projectRoot2),
|
|
620
|
+
...indexDir2 ? { indexDir: path3.resolve(indexDir2) } : {}
|
|
240
621
|
};
|
|
241
622
|
}
|
|
242
623
|
useDaemonPerfDefaults();
|
|
@@ -245,16 +626,18 @@ var projectRoot = parsed.projectRoot;
|
|
|
245
626
|
var indexDir = resolveIndexDir(projectRoot, parsed.indexDir);
|
|
246
627
|
var endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
247
628
|
var metadataPath = projectIndexServerMetadataPath(projectRoot, indexDir);
|
|
248
|
-
var idleMsRaw = Number(process.env["WRONGSTACK_INDEX_SERVER_IDLE_MS"]);
|
|
629
|
+
var idleMsRaw = Number(process.env["WRONGSTACK_INDEX_SERVER_IDLE_MS"]?.replaceAll("_", ""));
|
|
249
630
|
var idleMs = Number.isFinite(idleMsRaw) && idleMsRaw >= 100 ? idleMsRaw : DEFAULT_IDLE_MS2;
|
|
250
|
-
var clientLeaseMsRaw = Number(
|
|
631
|
+
var clientLeaseMsRaw = Number(
|
|
632
|
+
process.env["WRONGSTACK_INDEX_SERVER_CLIENT_LEASE_MS"]?.replaceAll("_", "")
|
|
633
|
+
);
|
|
251
634
|
var clientLeaseMs = Number.isFinite(clientLeaseMsRaw) && clientLeaseMsRaw >= 100 ? clientLeaseMsRaw : DEFAULT_CLIENT_LEASE_MS;
|
|
252
635
|
var clientLeaseSweepMs = Math.min(1e4, Math.max(100, Math.floor(clientLeaseMs / 3)));
|
|
253
636
|
var startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
254
637
|
var authToken = randomBytes(16).toString("hex");
|
|
255
638
|
var markMetadataWritten;
|
|
256
|
-
var metadataWritten = new Promise((
|
|
257
|
-
markMetadataWritten =
|
|
639
|
+
var metadataWritten = new Promise((resolve3) => {
|
|
640
|
+
markMetadataWritten = resolve3;
|
|
258
641
|
});
|
|
259
642
|
var serverInfo = {
|
|
260
643
|
protocolVersion: PROJECT_INDEX_SERVER_PROTOCOL_VERSION,
|
|
@@ -269,7 +652,7 @@ var serverInfo = {
|
|
|
269
652
|
// mirrors per-frame, so JSON-only clients keep working unchanged.
|
|
270
653
|
binarySupported: true
|
|
271
654
|
};
|
|
272
|
-
process.title = `wrongstack-codebase-index:${
|
|
655
|
+
process.title = `wrongstack-codebase-index:${path3.basename(projectRoot)}`;
|
|
273
656
|
var clients = /* @__PURE__ */ new Set();
|
|
274
657
|
var idleTimer;
|
|
275
658
|
var stopping = false;
|
|
@@ -298,14 +681,6 @@ var stopMemoryWatchdog = startSharedHeapWatchdog({
|
|
|
298
681
|
})
|
|
299
682
|
});
|
|
300
683
|
var lastProgressBroadcastAt = 0;
|
|
301
|
-
var externalWatcher;
|
|
302
|
-
var DEFAULT_EXTERNAL_DEBOUNCE_MS = 400;
|
|
303
|
-
var DEFAULT_EXTERNAL_COALESCE_WINDOW_MS = 50;
|
|
304
|
-
var externalDebounceMs = DEFAULT_EXTERNAL_DEBOUNCE_MS;
|
|
305
|
-
var externalCoalesceWindowMs = DEFAULT_EXTERNAL_COALESCE_WINDOW_MS;
|
|
306
|
-
var externalDebounceTimers = /* @__PURE__ */ new Map();
|
|
307
|
-
var externalReadyFiles = /* @__PURE__ */ new Set();
|
|
308
|
-
var externalReadyFlush;
|
|
309
684
|
function clearQueryCaches() {
|
|
310
685
|
queryCaches.clear();
|
|
311
686
|
}
|
|
@@ -328,16 +703,8 @@ var walMaintenance = new WalMaintenance({
|
|
|
328
703
|
}
|
|
329
704
|
}
|
|
330
705
|
});
|
|
331
|
-
var MAX_CLIENT_WRITE_BUFFER_BYTES = 8 * 1024 * 1024;
|
|
332
706
|
function send(state, message) {
|
|
333
|
-
|
|
334
|
-
if (state.socket.writableLength > MAX_CLIENT_WRITE_BUFFER_BYTES) {
|
|
335
|
-
state.socket.destroy(new Error("Index client fell too far behind on reads"));
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
state.socket.write(
|
|
339
|
-
state.binary ? encodeBinaryFrame(message) : encodeProjectServerMessage(message)
|
|
340
|
-
);
|
|
707
|
+
sendServerMessage(state, message);
|
|
341
708
|
}
|
|
342
709
|
function withWriteMutex(job) {
|
|
343
710
|
queuedWrites++;
|
|
@@ -357,6 +724,25 @@ function withWriteMutex(job) {
|
|
|
357
724
|
);
|
|
358
725
|
return run;
|
|
359
726
|
}
|
|
727
|
+
var watcherManager = new ProjectServerWatcherManager({
|
|
728
|
+
projectRoot,
|
|
729
|
+
onFilesChanged: (files) => {
|
|
730
|
+
void withIndexWrite(
|
|
731
|
+
(onProgress) => indexService(
|
|
732
|
+
{
|
|
733
|
+
projectRoot,
|
|
734
|
+
indexDir,
|
|
735
|
+
files
|
|
736
|
+
},
|
|
737
|
+
{ onProgress }
|
|
738
|
+
),
|
|
739
|
+
// Watcher runs are targeted file lists — keep the caches so the next
|
|
740
|
+
// refresh's stale-serve window has previous-generation answers.
|
|
741
|
+
{ preserveCaches: true }
|
|
742
|
+
).catch(() => {
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
});
|
|
360
746
|
function serverHealth() {
|
|
361
747
|
const memory = process.memoryUsage();
|
|
362
748
|
const now = Date.now();
|
|
@@ -380,8 +766,8 @@ function serverHealth() {
|
|
|
380
766
|
activeRequests,
|
|
381
767
|
activeWrites,
|
|
382
768
|
queuedWrites,
|
|
383
|
-
pendingExternalFiles:
|
|
384
|
-
watchingExternal:
|
|
769
|
+
pendingExternalFiles: watcherManager.pendingFileCount,
|
|
770
|
+
watchingExternal: watcherManager.isWatching,
|
|
385
771
|
watchingClients,
|
|
386
772
|
clientLeaseTimeoutMs: clientLeaseMs,
|
|
387
773
|
oldestClientIdleMs,
|
|
@@ -401,9 +787,6 @@ function reportIndexProgress(currentFile, totalFiles) {
|
|
|
401
787
|
broadcastIndexActivity();
|
|
402
788
|
}
|
|
403
789
|
}
|
|
404
|
-
function preservesQueryCaches(args) {
|
|
405
|
-
return !args.force && args.files !== void 0 && args.files.length > 0;
|
|
406
|
-
}
|
|
407
790
|
function withIndexWrite(job, options) {
|
|
408
791
|
return withWriteMutex(async () => {
|
|
409
792
|
indexActivity = {
|
|
@@ -443,162 +826,18 @@ function withIndexWrite(job, options) {
|
|
|
443
826
|
}
|
|
444
827
|
});
|
|
445
828
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
if (!active) {
|
|
456
|
-
const controller = new AbortController();
|
|
457
|
-
const subscribers = /* @__PURE__ */ new Set([subscriber]);
|
|
458
|
-
const promise = withIndexWrite(
|
|
459
|
-
(reportProgress) => indexService(fixedArgs(args), {
|
|
460
|
-
signal: controller.signal,
|
|
461
|
-
onProgress: (current, total) => {
|
|
462
|
-
reportProgress(current, total);
|
|
463
|
-
for (const item of subscribers) {
|
|
464
|
-
if (!item.state.cancelled.has(item.id)) {
|
|
465
|
-
send(item.state, { type: "progress", id: item.id, current, total });
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}),
|
|
470
|
-
// Shareable full index: whole-index scope, caches drop on completion.
|
|
471
|
-
{ preserveCaches: false }
|
|
472
|
-
);
|
|
473
|
-
active = { promise, controller, subscribers };
|
|
829
|
+
var operationContext = {
|
|
830
|
+
projectRoot,
|
|
831
|
+
indexDir,
|
|
832
|
+
queryCaches,
|
|
833
|
+
getActivity: () => indexActivity,
|
|
834
|
+
withIndexWrite,
|
|
835
|
+
send,
|
|
836
|
+
getActiveFullIndex: () => activeFullIndex,
|
|
837
|
+
setActiveFullIndex: (active) => {
|
|
474
838
|
activeFullIndex = active;
|
|
475
|
-
void promise.finally(() => {
|
|
476
|
-
if (activeFullIndex === active) activeFullIndex = null;
|
|
477
|
-
}).catch(() => {
|
|
478
|
-
});
|
|
479
|
-
} else {
|
|
480
|
-
active.subscribers.add(subscriber);
|
|
481
|
-
}
|
|
482
|
-
const selected = active;
|
|
483
|
-
state.cancel.set(id, () => {
|
|
484
|
-
state.cancelled.add(id);
|
|
485
|
-
selected.subscribers.delete(subscriber);
|
|
486
|
-
if (selected.subscribers.size === 0) {
|
|
487
|
-
selected.controller.abort(new Error("Indexing cancelled"));
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
try {
|
|
491
|
-
return await selected.promise;
|
|
492
|
-
} finally {
|
|
493
|
-
selected.subscribers.delete(subscriber);
|
|
494
839
|
}
|
|
495
|
-
}
|
|
496
|
-
async function dispatchOperation(state, message) {
|
|
497
|
-
const { id, op } = message;
|
|
498
|
-
switch (op) {
|
|
499
|
-
case "index": {
|
|
500
|
-
const args = message.args;
|
|
501
|
-
if (isShareableFullIndex(args)) return runFullIndex(state, id, args);
|
|
502
|
-
const controller = new AbortController();
|
|
503
|
-
state.cancel.set(id, () => {
|
|
504
|
-
state.cancelled.add(id);
|
|
505
|
-
controller.abort(new Error("Indexing cancelled"));
|
|
506
|
-
});
|
|
507
|
-
return withIndexWrite(
|
|
508
|
-
(reportProgress) => indexService(fixedArgs(args), {
|
|
509
|
-
signal: controller.signal,
|
|
510
|
-
onProgress: (current, total) => {
|
|
511
|
-
reportProgress(current, total);
|
|
512
|
-
if (!state.cancelled.has(id)) send(state, { type: "progress", id, current, total });
|
|
513
|
-
}
|
|
514
|
-
}),
|
|
515
|
-
{ preserveCaches: preservesQueryCaches(args) }
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
|
-
case "search": {
|
|
519
|
-
const { value, stale } = staleAwareRead(
|
|
520
|
-
queryCaches.searchCache,
|
|
521
|
-
JSON.stringify(message.args),
|
|
522
|
-
indexActivity,
|
|
523
|
-
() => searchService(fixedArgs(message.args))
|
|
524
|
-
);
|
|
525
|
-
return stale ? { ...value, stale: true } : value;
|
|
526
|
-
}
|
|
527
|
-
case "stats":
|
|
528
|
-
if (indexActivity.indexing) {
|
|
529
|
-
throw indexRefreshInProgressError(indexActivity.currentFile, indexActivity.totalFiles);
|
|
530
|
-
}
|
|
531
|
-
return staleAwareRead(
|
|
532
|
-
queryCaches.statsCache,
|
|
533
|
-
"stats",
|
|
534
|
-
indexActivity,
|
|
535
|
-
() => statsService(fixedArgs(message.args))
|
|
536
|
-
).value;
|
|
537
|
-
case "packageGraph": {
|
|
538
|
-
const { value, stale } = staleAwareRead(
|
|
539
|
-
queryCaches.packageGraphCache,
|
|
540
|
-
"package",
|
|
541
|
-
indexActivity,
|
|
542
|
-
() => packageGraphService(fixedArgs(message.args))
|
|
543
|
-
);
|
|
544
|
-
return stale ? { ...value, stale: true } : value;
|
|
545
|
-
}
|
|
546
|
-
case "fileGraph": {
|
|
547
|
-
const key = message.args.packageFilter;
|
|
548
|
-
const { value, stale } = staleAwareRead(
|
|
549
|
-
queryCaches.fileGraphCache,
|
|
550
|
-
key,
|
|
551
|
-
indexActivity,
|
|
552
|
-
() => fileGraphService(fixedArgs(message.args))
|
|
553
|
-
);
|
|
554
|
-
return stale ? { ...value, stale: true } : value;
|
|
555
|
-
}
|
|
556
|
-
case "symbolGraph": {
|
|
557
|
-
const key = message.args.fileFilter;
|
|
558
|
-
const { value, stale } = staleAwareRead(
|
|
559
|
-
queryCaches.symbolGraphCache,
|
|
560
|
-
key,
|
|
561
|
-
indexActivity,
|
|
562
|
-
() => symbolGraphService(fixedArgs(message.args))
|
|
563
|
-
);
|
|
564
|
-
return stale ? { ...value, stale: true } : value;
|
|
565
|
-
}
|
|
566
|
-
case "incomingCalls": {
|
|
567
|
-
const callArgs = fixedArgs(message.args);
|
|
568
|
-
const cacheKey = JSON.stringify([
|
|
569
|
-
callArgs.symbol,
|
|
570
|
-
callArgs.file ?? "",
|
|
571
|
-
callArgs.limit ?? 100,
|
|
572
|
-
callArgs.transitive ?? false
|
|
573
|
-
]);
|
|
574
|
-
const { value, stale } = staleAwareRead(
|
|
575
|
-
queryCaches.incomingCallsCache,
|
|
576
|
-
cacheKey,
|
|
577
|
-
indexActivity,
|
|
578
|
-
() => incomingCallsService(callArgs)
|
|
579
|
-
);
|
|
580
|
-
return stale ? { ...value, stale: true } : value;
|
|
581
|
-
}
|
|
582
|
-
case "outgoingCalls": {
|
|
583
|
-
const callArgs = fixedArgs(message.args);
|
|
584
|
-
const cacheKey = JSON.stringify([
|
|
585
|
-
callArgs.symbol,
|
|
586
|
-
callArgs.file ?? "",
|
|
587
|
-
callArgs.limit ?? 100,
|
|
588
|
-
callArgs.transitive ?? false
|
|
589
|
-
]);
|
|
590
|
-
const { value, stale } = staleAwareRead(
|
|
591
|
-
queryCaches.outgoingCallsCache,
|
|
592
|
-
cacheKey,
|
|
593
|
-
indexActivity,
|
|
594
|
-
() => outgoingCallsService(callArgs)
|
|
595
|
-
);
|
|
596
|
-
return stale ? { ...value, stale: true } : value;
|
|
597
|
-
}
|
|
598
|
-
default:
|
|
599
|
-
throw new Error(`unknown index operation: ${String(op)}`);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
840
|
+
};
|
|
602
841
|
async function handleMessage(state, message) {
|
|
603
842
|
if (message.type === "cancel") {
|
|
604
843
|
state.cancel.get(message.id)?.();
|
|
@@ -629,13 +868,13 @@ async function handleMessage(state, message) {
|
|
|
629
868
|
0,
|
|
630
869
|
message.coalesceWindowMs ?? DEFAULT_EXTERNAL_COALESCE_WINDOW_MS
|
|
631
870
|
);
|
|
632
|
-
|
|
871
|
+
watcherManager.reconcile(clients);
|
|
633
872
|
send(state, {
|
|
634
873
|
type: "response",
|
|
635
874
|
id: message.id,
|
|
636
875
|
ok: true,
|
|
637
876
|
result: {
|
|
638
|
-
watching:
|
|
877
|
+
watching: watcherManager.isWatching,
|
|
639
878
|
health: serverHealth()
|
|
640
879
|
}
|
|
641
880
|
});
|
|
@@ -644,7 +883,7 @@ async function handleMessage(state, message) {
|
|
|
644
883
|
state.debounceMs = previousDebounceMs;
|
|
645
884
|
state.coalesceWindowMs = previousCoalesceWindowMs;
|
|
646
885
|
try {
|
|
647
|
-
|
|
886
|
+
watcherManager.reconcile(clients);
|
|
648
887
|
} catch {
|
|
649
888
|
}
|
|
650
889
|
send(state, {
|
|
@@ -669,7 +908,7 @@ async function handleMessage(state, message) {
|
|
|
669
908
|
}
|
|
670
909
|
activeRequests++;
|
|
671
910
|
try {
|
|
672
|
-
const result = await dispatchOperation(state, message);
|
|
911
|
+
const result = await dispatchOperation(operationContext, state, message);
|
|
673
912
|
if (!state.cancelled.has(message.id)) {
|
|
674
913
|
send(state, { type: "response", id: message.id, ok: true, result });
|
|
675
914
|
}
|
|
@@ -689,141 +928,8 @@ async function handleMessage(state, message) {
|
|
|
689
928
|
state.cancelled.delete(message.id);
|
|
690
929
|
}
|
|
691
930
|
}
|
|
692
|
-
function isIgnoredRelativePath(relativePath) {
|
|
693
|
-
return relativePath.split(/[/\\]/u).some((segment) => DEFAULT_WALK_IGNORE_SET.has(segment));
|
|
694
|
-
}
|
|
695
|
-
function enqueueExternalFile(file) {
|
|
696
|
-
const previous = externalDebounceTimers.get(file);
|
|
697
|
-
if (previous) clearTimeout(previous);
|
|
698
|
-
const timer = setTimeout(() => {
|
|
699
|
-
externalDebounceTimers.delete(file);
|
|
700
|
-
externalReadyFiles.add(file);
|
|
701
|
-
if (externalReadyFlush) clearTimeout(externalReadyFlush);
|
|
702
|
-
externalReadyFlush = setTimeout(() => {
|
|
703
|
-
externalReadyFlush = void 0;
|
|
704
|
-
const files = [...externalReadyFiles].sort();
|
|
705
|
-
externalReadyFiles.clear();
|
|
706
|
-
void withIndexWrite(
|
|
707
|
-
(onProgress) => indexService(
|
|
708
|
-
{
|
|
709
|
-
projectRoot,
|
|
710
|
-
indexDir,
|
|
711
|
-
files
|
|
712
|
-
},
|
|
713
|
-
{ onProgress }
|
|
714
|
-
),
|
|
715
|
-
// Watcher runs are targeted file lists — keep the caches so the next
|
|
716
|
-
// refresh's stale-serve window has previous-generation answers.
|
|
717
|
-
{ preserveCaches: true }
|
|
718
|
-
).catch(() => {
|
|
719
|
-
});
|
|
720
|
-
}, externalCoalesceWindowMs);
|
|
721
|
-
externalReadyFlush.unref?.();
|
|
722
|
-
}, externalDebounceMs);
|
|
723
|
-
timer.unref?.();
|
|
724
|
-
externalDebounceTimers.set(file, timer);
|
|
725
|
-
}
|
|
726
|
-
function ensureExternalWatcher() {
|
|
727
|
-
if (externalWatcher) return;
|
|
728
|
-
externalWatcher = watchProjectTree(
|
|
729
|
-
projectRoot,
|
|
730
|
-
({ filename }) => {
|
|
731
|
-
if (!filename || isIgnoredRelativePath(filename)) return;
|
|
732
|
-
const absolute = path.resolve(projectRoot, filename);
|
|
733
|
-
const relative2 = path.relative(projectRoot, absolute);
|
|
734
|
-
if (relative2 === ".." || relative2.startsWith(`..${path.sep}`) || path.isAbsolute(relative2) || !isIndexablePath(absolute)) {
|
|
735
|
-
return;
|
|
736
|
-
}
|
|
737
|
-
enqueueExternalFile(absolute);
|
|
738
|
-
},
|
|
739
|
-
{
|
|
740
|
-
onError: () => {
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
);
|
|
744
|
-
}
|
|
745
|
-
function stopExternalWatcher() {
|
|
746
|
-
try {
|
|
747
|
-
externalWatcher?.close();
|
|
748
|
-
} catch {
|
|
749
|
-
}
|
|
750
|
-
externalWatcher = void 0;
|
|
751
|
-
for (const timer of externalDebounceTimers.values()) clearTimeout(timer);
|
|
752
|
-
externalDebounceTimers.clear();
|
|
753
|
-
if (externalReadyFlush) clearTimeout(externalReadyFlush);
|
|
754
|
-
externalReadyFlush = void 0;
|
|
755
|
-
externalReadyFiles.clear();
|
|
756
|
-
}
|
|
757
|
-
function reconcileExternalWatcher() {
|
|
758
|
-
const owners = [...clients].filter((client) => client.watchExternal);
|
|
759
|
-
if (owners.length === 0) {
|
|
760
|
-
externalDebounceMs = DEFAULT_EXTERNAL_DEBOUNCE_MS;
|
|
761
|
-
externalCoalesceWindowMs = DEFAULT_EXTERNAL_COALESCE_WINDOW_MS;
|
|
762
|
-
stopExternalWatcher();
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
externalDebounceMs = Math.min(...owners.map((client) => client.debounceMs));
|
|
766
|
-
externalCoalesceWindowMs = Math.min(...owners.map((client) => client.coalesceWindowMs));
|
|
767
|
-
ensureExternalWatcher();
|
|
768
|
-
}
|
|
769
931
|
function consume(state, chunk) {
|
|
770
|
-
|
|
771
|
-
let scan = 0;
|
|
772
|
-
while (true) {
|
|
773
|
-
if (buffer.length - scan === 0) break;
|
|
774
|
-
if (isBinaryFrame(buffer[scan])) {
|
|
775
|
-
if (buffer.length - scan < 5) break;
|
|
776
|
-
const frameLen = buffer.readUInt32BE(scan + 1);
|
|
777
|
-
if (frameLen > MAX_INBOUND_BINARY_FRAME_BYTES) {
|
|
778
|
-
state.buffer = Buffer.alloc(0);
|
|
779
|
-
state.socket.destroy(new Error("codebase-index client binary frame exceeds the IPC limit"));
|
|
780
|
-
return;
|
|
781
|
-
}
|
|
782
|
-
if (buffer.length - scan < 5 + frameLen) break;
|
|
783
|
-
const payload = buffer.subarray(scan + 5, scan + 5 + frameLen);
|
|
784
|
-
let message2;
|
|
785
|
-
try {
|
|
786
|
-
message2 = decodeBinaryFrame(payload);
|
|
787
|
-
} catch {
|
|
788
|
-
state.buffer = Buffer.alloc(0);
|
|
789
|
-
state.socket.destroy(new Error("invalid codebase-index client binary frame"));
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
scan += 5 + frameLen;
|
|
793
|
-
state.binary = true;
|
|
794
|
-
state.lastSeenAt = Date.now();
|
|
795
|
-
void handleMessage(state, message2);
|
|
796
|
-
continue;
|
|
797
|
-
}
|
|
798
|
-
const newline = buffer.indexOf(10, scan);
|
|
799
|
-
if (newline < 0) {
|
|
800
|
-
if (buffer.length - scan > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
801
|
-
state.buffer = Buffer.alloc(0);
|
|
802
|
-
state.socket.destroy(new Error("codebase-index client message exceeds the IPC limit"));
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
break;
|
|
806
|
-
}
|
|
807
|
-
if (newline - scan > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
808
|
-
state.buffer = Buffer.alloc(0);
|
|
809
|
-
state.socket.destroy(new Error("codebase-index client message exceeds the IPC limit"));
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
const line = buffer.subarray(scan, newline).toString("utf8");
|
|
813
|
-
scan = newline + 1;
|
|
814
|
-
if (!line) continue;
|
|
815
|
-
let message;
|
|
816
|
-
try {
|
|
817
|
-
message = JSON.parse(line);
|
|
818
|
-
} catch {
|
|
819
|
-
state.buffer = Buffer.alloc(0);
|
|
820
|
-
state.socket.destroy(new Error("invalid codebase-index client message"));
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
state.lastSeenAt = Date.now();
|
|
824
|
-
void handleMessage(state, message);
|
|
825
|
-
}
|
|
826
|
-
state.buffer = scan === 0 ? buffer : Buffer.from(buffer.subarray(scan));
|
|
932
|
+
consumeClientChunk(state, chunk, handleMessage);
|
|
827
933
|
}
|
|
828
934
|
function scheduleIdleStop() {
|
|
829
935
|
if (stopping || clients.size > 0) return;
|
|
@@ -831,24 +937,6 @@ function scheduleIdleStop() {
|
|
|
831
937
|
idleTimer = setTimeout(() => void stop("idle-timeout"), idleMs);
|
|
832
938
|
idleTimer.unref?.();
|
|
833
939
|
}
|
|
834
|
-
function removeMetadataIfOwned() {
|
|
835
|
-
try {
|
|
836
|
-
const current = JSON.parse(fs.readFileSync(metadataPath, "utf8"));
|
|
837
|
-
if (current.pid === process.pid) fs.rmSync(metadataPath, { force: true });
|
|
838
|
-
} catch {
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
async function writeMetadata() {
|
|
842
|
-
fs.mkdirSync(path.dirname(metadataPath), { recursive: true });
|
|
843
|
-
const metadata = { ...serverInfo, authToken };
|
|
844
|
-
await atomicWrite(metadataPath, `${JSON.stringify(metadata, null, 2)}
|
|
845
|
-
`, { mode: 384 });
|
|
846
|
-
await restrictFilePermissions(metadataPath, {
|
|
847
|
-
label: "codebase-index-metadata",
|
|
848
|
-
warn: (message) => process.stderr.write(`${message}
|
|
849
|
-
`)
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
940
|
var server = net.createServer((socket) => {
|
|
853
941
|
if (idleTimer) clearTimeout(idleTimer);
|
|
854
942
|
idleTimer = void 0;
|
|
@@ -876,7 +964,7 @@ var server = net.createServer((socket) => {
|
|
|
876
964
|
state.cancel.clear();
|
|
877
965
|
clients.delete(state);
|
|
878
966
|
try {
|
|
879
|
-
|
|
967
|
+
watcherManager.reconcile(clients);
|
|
880
968
|
} catch {
|
|
881
969
|
}
|
|
882
970
|
scheduleIdleStop();
|
|
@@ -904,21 +992,21 @@ async function stop(_reason) {
|
|
|
904
992
|
state.socket.end();
|
|
905
993
|
}
|
|
906
994
|
activeFullIndex?.controller.abort(new Error("codebase-index server stopping"));
|
|
907
|
-
|
|
995
|
+
watcherManager.stop();
|
|
908
996
|
walMaintenance.dispose();
|
|
909
997
|
indexStorePool.closeAll();
|
|
910
|
-
removeMetadataIfOwned();
|
|
911
|
-
await new Promise((
|
|
912
|
-
server.close(() =>
|
|
998
|
+
removeMetadataIfOwned(metadataPath, process.pid);
|
|
999
|
+
await new Promise((resolve3) => {
|
|
1000
|
+
server.close(() => resolve3());
|
|
913
1001
|
const timer = setTimeout(() => {
|
|
914
1002
|
for (const state of clients) state.socket.destroy();
|
|
915
|
-
|
|
1003
|
+
resolve3();
|
|
916
1004
|
}, 500);
|
|
917
1005
|
timer.unref?.();
|
|
918
1006
|
});
|
|
919
1007
|
if (process.platform !== "win32") {
|
|
920
1008
|
try {
|
|
921
|
-
|
|
1009
|
+
fs2.rmSync(endpoint, { force: true });
|
|
922
1010
|
} catch {
|
|
923
1011
|
}
|
|
924
1012
|
}
|
|
@@ -946,41 +1034,10 @@ void (async () => {
|
|
|
946
1034
|
`);
|
|
947
1035
|
process.exitCode = 1;
|
|
948
1036
|
});
|
|
949
|
-
|
|
1037
|
+
const metadata = { ...serverInfo, authToken };
|
|
1038
|
+
await writeProjectServerMetadata(metadataPath, metadata);
|
|
950
1039
|
markMetadataWritten?.();
|
|
951
1040
|
scheduleIdleStop();
|
|
952
1041
|
})();
|
|
953
|
-
|
|
954
|
-
"wrongstack.codebase-index.project-server.signalGuard"
|
|
955
|
-
);
|
|
956
|
-
var signalGuardStore = globalThis;
|
|
957
|
-
var signalGuard = signalGuardStore[SIGNAL_GUARD];
|
|
958
|
-
if (!signalGuard) {
|
|
959
|
-
let current = async () => void 0;
|
|
960
|
-
let armed = false;
|
|
961
|
-
const handlers = /* @__PURE__ */ new Map();
|
|
962
|
-
const disarm = () => {
|
|
963
|
-
if (!armed) return;
|
|
964
|
-
armed = false;
|
|
965
|
-
for (const [signal, handler] of handlers) process.removeListener(signal, handler);
|
|
966
|
-
handlers.clear();
|
|
967
|
-
};
|
|
968
|
-
signalGuard = {
|
|
969
|
-
arm(next) {
|
|
970
|
-
current = next;
|
|
971
|
-
if (armed) return;
|
|
972
|
-
armed = true;
|
|
973
|
-
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
974
|
-
const handler = () => {
|
|
975
|
-
disarm();
|
|
976
|
-
void current(signal);
|
|
977
|
-
};
|
|
978
|
-
handlers.set(signal, handler);
|
|
979
|
-
process.on(signal, handler);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
};
|
|
983
|
-
signalGuardStore[SIGNAL_GUARD] = signalGuard;
|
|
984
|
-
}
|
|
985
|
-
signalGuard.arm(stop);
|
|
1042
|
+
armCodebaseIndexSignalGuard(stop);
|
|
986
1043
|
//# sourceMappingURL=project-server.js.map
|