@coderule/mcp 1.8.0 → 2.0.1
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/cli.cjs +224 -45
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +220 -42
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +203 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +199 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp-cli.cjs +264 -44
- package/dist/mcp-cli.cjs.map +1 -1
- package/dist/mcp-cli.js +260 -41
- package/dist/mcp-cli.js.map +1 -1
- package/package.json +6 -4
package/dist/mcp-cli.cjs
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var
|
|
4
|
+
var fs5 = require('fs/promises');
|
|
5
|
+
var path2 = require('path');
|
|
6
6
|
var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
7
7
|
var crypto = require('crypto');
|
|
8
8
|
var envPaths = require('env-paths');
|
|
9
9
|
var pino = require('pino');
|
|
10
|
+
var os = require('os');
|
|
10
11
|
var Database = require('better-sqlite3');
|
|
11
12
|
var qulite = require('@coderule/qulite');
|
|
12
13
|
var clients = require('@coderule/clients');
|
|
13
|
-
var
|
|
14
|
+
var fs3 = require('fs');
|
|
14
15
|
var worker_threads = require('worker_threads');
|
|
15
16
|
var chokidar = require('chokidar');
|
|
16
17
|
var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
@@ -18,12 +19,13 @@ var zod = require('zod');
|
|
|
18
19
|
|
|
19
20
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
20
21
|
|
|
21
|
-
var
|
|
22
|
-
var
|
|
22
|
+
var fs5__default = /*#__PURE__*/_interopDefault(fs5);
|
|
23
|
+
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
23
24
|
var envPaths__default = /*#__PURE__*/_interopDefault(envPaths);
|
|
24
25
|
var pino__default = /*#__PURE__*/_interopDefault(pino);
|
|
26
|
+
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
25
27
|
var Database__default = /*#__PURE__*/_interopDefault(Database);
|
|
26
|
-
var
|
|
28
|
+
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
27
29
|
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
28
30
|
|
|
29
31
|
// node_modules/tsup/assets/cjs_shims.js
|
|
@@ -50,6 +52,166 @@ var DEFAULT_MAX_SNAPSHOT_ATTEMPTS = 5;
|
|
|
50
52
|
var DEFAULT_HTTP_TIMEOUT_MS = 12e4;
|
|
51
53
|
var DEFAULT_UPLOAD_CHUNK_SIZE = 1;
|
|
52
54
|
var DEFAULT_MAX_QUERY_WAIT_MS = 5e4;
|
|
55
|
+
function collectCandidateSources(options) {
|
|
56
|
+
const sources = [];
|
|
57
|
+
if (options?.cliRoot) {
|
|
58
|
+
sources.push({
|
|
59
|
+
value: options.cliRoot,
|
|
60
|
+
source: "cli-arg",
|
|
61
|
+
baseConfidence: 1
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (process.env.CODERULE_ROOT) {
|
|
65
|
+
sources.push({
|
|
66
|
+
value: process.env.CODERULE_ROOT,
|
|
67
|
+
source: "env-coderule-root",
|
|
68
|
+
baseConfidence: 0.9
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (process.env.WORKSPACE_FOLDER_PATHS) {
|
|
72
|
+
sources.push({
|
|
73
|
+
value: process.env.WORKSPACE_FOLDER_PATHS,
|
|
74
|
+
source: "env-workspace",
|
|
75
|
+
baseConfidence: 0.8
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
sources.push({
|
|
79
|
+
value: process.cwd(),
|
|
80
|
+
source: "process-cwd",
|
|
81
|
+
baseConfidence: 0.6
|
|
82
|
+
});
|
|
83
|
+
return sources;
|
|
84
|
+
}
|
|
85
|
+
async function pathExists(targetPath) {
|
|
86
|
+
try {
|
|
87
|
+
await fs5__default.default.access(targetPath);
|
|
88
|
+
return true;
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function isDirectory(targetPath) {
|
|
94
|
+
try {
|
|
95
|
+
const stat = await fs5__default.default.stat(targetPath);
|
|
96
|
+
return stat.isDirectory();
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function hasGitDirectory(rootPath) {
|
|
102
|
+
const gitPath = path2__default.default.join(rootPath, ".git");
|
|
103
|
+
return pathExists(gitPath);
|
|
104
|
+
}
|
|
105
|
+
function isShallowPath(absolutePath) {
|
|
106
|
+
const normalized = absolutePath.replace(/\\/g, "/");
|
|
107
|
+
const separatorCount = (normalized.match(/\//g) || []).length;
|
|
108
|
+
return separatorCount <= 1;
|
|
109
|
+
}
|
|
110
|
+
function isHomeDirectory(candidatePath) {
|
|
111
|
+
const home = os__default.default.homedir();
|
|
112
|
+
const normalizedCandidate = path2__default.default.normalize(candidatePath);
|
|
113
|
+
const normalizedHome = path2__default.default.normalize(home);
|
|
114
|
+
return normalizedCandidate === normalizedHome;
|
|
115
|
+
}
|
|
116
|
+
async function applyModifiers(candidate) {
|
|
117
|
+
const modifiers = [];
|
|
118
|
+
if (await hasGitDirectory(candidate.path)) {
|
|
119
|
+
modifiers.push({ reason: "has .git directory", delta: 0.1 });
|
|
120
|
+
}
|
|
121
|
+
if (isShallowPath(candidate.path)) {
|
|
122
|
+
modifiers.push({ reason: "shallow path (likely root)", delta: -0.2 });
|
|
123
|
+
}
|
|
124
|
+
if (isHomeDirectory(candidate.path)) {
|
|
125
|
+
modifiers.push({ reason: "is home directory", delta: -0.3 });
|
|
126
|
+
}
|
|
127
|
+
const totalModifier = modifiers.reduce((sum, m) => sum + m.delta, 0);
|
|
128
|
+
const finalConfidence = candidate.baseConfidence + totalModifier;
|
|
129
|
+
return {
|
|
130
|
+
...candidate,
|
|
131
|
+
modifiers,
|
|
132
|
+
finalConfidence
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async function createCandidate(source) {
|
|
136
|
+
const absolutePath = path2__default.default.resolve(source.value);
|
|
137
|
+
const exists = await pathExists(absolutePath);
|
|
138
|
+
const isDir = exists ? await isDirectory(absolutePath) : false;
|
|
139
|
+
const candidate = {
|
|
140
|
+
path: absolutePath,
|
|
141
|
+
source: source.source,
|
|
142
|
+
baseConfidence: source.baseConfidence,
|
|
143
|
+
modifiers: [],
|
|
144
|
+
finalConfidence: source.baseConfidence,
|
|
145
|
+
exists,
|
|
146
|
+
isDirectory: isDir
|
|
147
|
+
};
|
|
148
|
+
if (exists && isDir) {
|
|
149
|
+
return applyModifiers(candidate);
|
|
150
|
+
}
|
|
151
|
+
return candidate;
|
|
152
|
+
}
|
|
153
|
+
async function resolveRootPath(options) {
|
|
154
|
+
const logger2 = options?.logger;
|
|
155
|
+
const sources = collectCandidateSources(options);
|
|
156
|
+
const candidates = await Promise.all(sources.map(createCandidate));
|
|
157
|
+
logger2?.debug(
|
|
158
|
+
{
|
|
159
|
+
candidates: candidates.map((c) => ({
|
|
160
|
+
path: c.path,
|
|
161
|
+
source: c.source,
|
|
162
|
+
baseConfidence: c.baseConfidence,
|
|
163
|
+
finalConfidence: c.finalConfidence,
|
|
164
|
+
exists: c.exists,
|
|
165
|
+
isDirectory: c.isDirectory,
|
|
166
|
+
modifiers: c.modifiers
|
|
167
|
+
}))
|
|
168
|
+
},
|
|
169
|
+
"Collected root path candidates"
|
|
170
|
+
);
|
|
171
|
+
const validCandidates = candidates.filter((c) => c.exists && c.isDirectory);
|
|
172
|
+
if (validCandidates.length === 0) {
|
|
173
|
+
const attempted = candidates.map((c) => c.path).join(", ");
|
|
174
|
+
throw new Error(
|
|
175
|
+
`No valid root directory found. Attempted: ${attempted}. Ensure the directory exists and is accessible.`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
const sourcePriority = {
|
|
179
|
+
"cli-arg": 1,
|
|
180
|
+
"env-coderule-root": 2,
|
|
181
|
+
"env-workspace": 3,
|
|
182
|
+
"process-cwd": 4
|
|
183
|
+
};
|
|
184
|
+
validCandidates.sort((a, b) => {
|
|
185
|
+
const confidenceDiff = b.finalConfidence - a.finalConfidence;
|
|
186
|
+
if (confidenceDiff !== 0) {
|
|
187
|
+
return confidenceDiff;
|
|
188
|
+
}
|
|
189
|
+
return sourcePriority[a.source] - sourcePriority[b.source];
|
|
190
|
+
});
|
|
191
|
+
const best = validCandidates[0];
|
|
192
|
+
if (best.finalConfidence < 0.7) {
|
|
193
|
+
logger2?.warn(
|
|
194
|
+
{
|
|
195
|
+
path: best.path,
|
|
196
|
+
source: best.source,
|
|
197
|
+
confidence: best.finalConfidence,
|
|
198
|
+
modifiers: best.modifiers
|
|
199
|
+
},
|
|
200
|
+
"Selected root path has low confidence"
|
|
201
|
+
);
|
|
202
|
+
} else {
|
|
203
|
+
logger2?.info(
|
|
204
|
+
{
|
|
205
|
+
path: best.path,
|
|
206
|
+
source: best.source,
|
|
207
|
+
confidence: best.finalConfidence,
|
|
208
|
+
modifiers: best.modifiers
|
|
209
|
+
},
|
|
210
|
+
"Resolved root path"
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return best;
|
|
214
|
+
}
|
|
53
215
|
|
|
54
216
|
// src/config/Configurator.ts
|
|
55
217
|
var DEFAULT_RETRIEVAL_FORMATTER = "standard";
|
|
@@ -62,9 +224,9 @@ var DEFAULTS = {
|
|
|
62
224
|
maxSnapshotAttempts: DEFAULT_MAX_SNAPSHOT_ATTEMPTS
|
|
63
225
|
};
|
|
64
226
|
function normalizeRoot(root) {
|
|
65
|
-
const resolved =
|
|
66
|
-
const normalized =
|
|
67
|
-
return normalized.split(
|
|
227
|
+
const resolved = path2__default.default.resolve(root);
|
|
228
|
+
const normalized = path2__default.default.normalize(resolved);
|
|
229
|
+
return normalized.split(path2__default.default.sep).join("/");
|
|
68
230
|
}
|
|
69
231
|
function sha256(input) {
|
|
70
232
|
return crypto.createHash("sha256").update(input).digest("hex");
|
|
@@ -96,7 +258,8 @@ function parseFormatter(value) {
|
|
|
96
258
|
);
|
|
97
259
|
}
|
|
98
260
|
async function resolveConfig({
|
|
99
|
-
token
|
|
261
|
+
token,
|
|
262
|
+
rootPath: cliRoot
|
|
100
263
|
}) {
|
|
101
264
|
const resolvedToken = token ?? process.env.CODERULE_TOKEN;
|
|
102
265
|
if (!resolvedToken) {
|
|
@@ -104,14 +267,17 @@ async function resolveConfig({
|
|
|
104
267
|
"Missing token: provide params.token or CODERULE_TOKEN env"
|
|
105
268
|
);
|
|
106
269
|
}
|
|
107
|
-
const rootCandidate =
|
|
108
|
-
|
|
270
|
+
const rootCandidate = await resolveRootPath({
|
|
271
|
+
cliRoot,
|
|
272
|
+
logger: logger.child({ scope: "root-resolver" })
|
|
273
|
+
});
|
|
274
|
+
const rootPath = rootCandidate.path;
|
|
109
275
|
const normalized = normalizeRoot(rootPath);
|
|
110
276
|
const rootId = sha256(normalized);
|
|
111
277
|
const dataDir = process.env.CODERULE_DATA_DIR || envPaths__default.default("coderule").data;
|
|
112
|
-
const watchDir =
|
|
113
|
-
await
|
|
114
|
-
const dbPath =
|
|
278
|
+
const watchDir = path2__default.default.join(dataDir, "watch");
|
|
279
|
+
await fs5__default.default.mkdir(watchDir, { recursive: true });
|
|
280
|
+
const dbPath = path2__default.default.join(watchDir, `${rootId}.sqlite`);
|
|
115
281
|
const baseConfig = {
|
|
116
282
|
token: resolvedToken,
|
|
117
283
|
rootPath,
|
|
@@ -188,6 +354,9 @@ async function resolveConfig({
|
|
|
188
354
|
logger.debug(
|
|
189
355
|
{
|
|
190
356
|
rootPath,
|
|
357
|
+
rootSource: rootCandidate.source,
|
|
358
|
+
rootConfidence: rootCandidate.finalConfidence,
|
|
359
|
+
rootModifiers: rootCandidate.modifiers,
|
|
191
360
|
dbPath,
|
|
192
361
|
dataDir,
|
|
193
362
|
authBaseUrl: baseConfig.authBaseUrl,
|
|
@@ -688,7 +857,7 @@ async function fetchVisitorRules(clients, logger2) {
|
|
|
688
857
|
return rules;
|
|
689
858
|
}
|
|
690
859
|
function toPosix(input) {
|
|
691
|
-
return input.split(
|
|
860
|
+
return input.split(path2__default.default.sep).join("/");
|
|
692
861
|
}
|
|
693
862
|
function getLowerBasename(input) {
|
|
694
863
|
const base = input.split("/").pop();
|
|
@@ -707,7 +876,7 @@ function compileRulesBundle(rules) {
|
|
|
707
876
|
if (!info) {
|
|
708
877
|
logger.debug({ path: fullPath }, "Predicate fallback lstat");
|
|
709
878
|
try {
|
|
710
|
-
info =
|
|
879
|
+
info = fs3__default.default.lstatSync(fullPath);
|
|
711
880
|
} catch (error) {
|
|
712
881
|
logger.warn(
|
|
713
882
|
{ err: error, path: fullPath },
|
|
@@ -856,14 +1025,14 @@ var Hasher = class {
|
|
|
856
1025
|
await new Promise((resolve) => setImmediate(resolve));
|
|
857
1026
|
}
|
|
858
1027
|
resolveAbsolutePath(record) {
|
|
859
|
-
if (
|
|
1028
|
+
if (path2__default.default.isAbsolute(record.display_path)) {
|
|
860
1029
|
return record.display_path;
|
|
861
1030
|
}
|
|
862
|
-
return
|
|
1031
|
+
return path2__default.default.join(this.options.rootPath, record.rel_path);
|
|
863
1032
|
}
|
|
864
1033
|
async ensureExists(absPath, record) {
|
|
865
1034
|
try {
|
|
866
|
-
await
|
|
1035
|
+
await fs5__default.default.access(absPath);
|
|
867
1036
|
return true;
|
|
868
1037
|
} catch (error) {
|
|
869
1038
|
this.log.warn(
|
|
@@ -886,7 +1055,7 @@ var Hasher = class {
|
|
|
886
1055
|
const service = crypto.createHash("sha256");
|
|
887
1056
|
service.update(relPath);
|
|
888
1057
|
service.update("\n");
|
|
889
|
-
const stream =
|
|
1058
|
+
const stream = fs3__default.default.createReadStream(absPath);
|
|
890
1059
|
stream.on("data", (chunk) => {
|
|
891
1060
|
content.update(chunk);
|
|
892
1061
|
service.update(chunk);
|
|
@@ -1001,13 +1170,13 @@ async function bootstrap(params) {
|
|
|
1001
1170
|
return runtime;
|
|
1002
1171
|
}
|
|
1003
1172
|
function toPosixRelative(root, target) {
|
|
1004
|
-
const rel =
|
|
1173
|
+
const rel = path2__default.default.relative(root, target);
|
|
1005
1174
|
if (!rel || rel === "") return "";
|
|
1006
|
-
return rel.split(
|
|
1175
|
+
return rel.split(path2__default.default.sep).join("/");
|
|
1007
1176
|
}
|
|
1008
1177
|
function isInsideRoot(root, target) {
|
|
1009
|
-
const rel =
|
|
1010
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
1178
|
+
const rel = path2__default.default.relative(root, target);
|
|
1179
|
+
return rel === "" || !rel.startsWith("..") && !path2__default.default.isAbsolute(rel);
|
|
1011
1180
|
}
|
|
1012
1181
|
|
|
1013
1182
|
// src/fs/Walker.ts
|
|
@@ -1022,7 +1191,7 @@ function cloneStats(stats) {
|
|
|
1022
1191
|
}
|
|
1023
1192
|
async function readSymlinkTarget(absPath, log) {
|
|
1024
1193
|
try {
|
|
1025
|
-
return await
|
|
1194
|
+
return await fs5__default.default.readlink(absPath);
|
|
1026
1195
|
} catch (error) {
|
|
1027
1196
|
log.warn({ err: error, path: absPath }, "Failed to read symlink target");
|
|
1028
1197
|
return null;
|
|
@@ -1032,13 +1201,13 @@ async function walkDirectory(current, opts, stats) {
|
|
|
1032
1201
|
const dirLogger = opts.logger;
|
|
1033
1202
|
let dirents;
|
|
1034
1203
|
try {
|
|
1035
|
-
dirents = await
|
|
1204
|
+
dirents = await fs5__default.default.readdir(current, { withFileTypes: true });
|
|
1036
1205
|
} catch (error) {
|
|
1037
1206
|
dirLogger.warn({ err: error, path: current }, "Failed to read directory");
|
|
1038
1207
|
return;
|
|
1039
1208
|
}
|
|
1040
1209
|
for (const dirent of dirents) {
|
|
1041
|
-
const absPath =
|
|
1210
|
+
const absPath = path2__default.default.join(current, dirent.name);
|
|
1042
1211
|
const relPath = toPosixRelative(opts.rootPath, absPath);
|
|
1043
1212
|
if (dirent.isDirectory()) {
|
|
1044
1213
|
if (shouldPruneDirectory(relPath, opts.bundle)) {
|
|
@@ -1051,7 +1220,7 @@ async function walkDirectory(current, opts, stats) {
|
|
|
1051
1220
|
if (dirent.isSymbolicLink() || dirent.isFile()) {
|
|
1052
1221
|
let stat;
|
|
1053
1222
|
try {
|
|
1054
|
-
stat = await
|
|
1223
|
+
stat = await fs5__default.default.lstat(absPath);
|
|
1055
1224
|
} catch (error) {
|
|
1056
1225
|
dirLogger.warn({ err: error, path: absPath }, "Failed to stat file");
|
|
1057
1226
|
continue;
|
|
@@ -1143,9 +1312,9 @@ async function uploadMissing(rootPath, missing, syncClient, logger2, maxAttempts
|
|
|
1143
1312
|
const list = chunks[idx];
|
|
1144
1313
|
const map = /* @__PURE__ */ new Map();
|
|
1145
1314
|
for (const missingFile of list) {
|
|
1146
|
-
const absPath =
|
|
1315
|
+
const absPath = path2__default.default.join(rootPath, missingFile.file_path);
|
|
1147
1316
|
try {
|
|
1148
|
-
const buffer = await
|
|
1317
|
+
const buffer = await fs5__default.default.readFile(absPath);
|
|
1149
1318
|
map.set(missingFile.file_hash, {
|
|
1150
1319
|
path: missingFile.file_path,
|
|
1151
1320
|
content: buffer
|
|
@@ -1272,6 +1441,21 @@ async function runInitialSyncPipeline(runtime, options) {
|
|
|
1272
1441
|
hashLogger.debug("Hasher processed batch");
|
|
1273
1442
|
}
|
|
1274
1443
|
}
|
|
1444
|
+
if (options?.blockUntilReady !== false) {
|
|
1445
|
+
const syncLogger = runtime.logger.child({ scope: "snapshot" });
|
|
1446
|
+
const result = await publishSnapshot(
|
|
1447
|
+
runtime.config.rootPath,
|
|
1448
|
+
runtime.filesRepo,
|
|
1449
|
+
runtime.snapshotsRepo,
|
|
1450
|
+
runtime.clients.sync,
|
|
1451
|
+
syncLogger,
|
|
1452
|
+
{
|
|
1453
|
+
maxAttempts: runtime.config.maxSnapshotAttempts,
|
|
1454
|
+
uploadChunkSize: runtime.config.uploadChunkSize
|
|
1455
|
+
}
|
|
1456
|
+
);
|
|
1457
|
+
return result;
|
|
1458
|
+
}
|
|
1275
1459
|
const computation = computeSnapshot(runtime.filesRepo);
|
|
1276
1460
|
const createdAt = Date.now();
|
|
1277
1461
|
runtime.snapshotsRepo.insert(
|
|
@@ -1410,7 +1594,7 @@ function computeBackoff(attempts) {
|
|
|
1410
1594
|
}
|
|
1411
1595
|
async function readSymlinkTarget2(absPath) {
|
|
1412
1596
|
try {
|
|
1413
|
-
return await
|
|
1597
|
+
return await fs5__default.default.readlink(absPath);
|
|
1414
1598
|
} catch {
|
|
1415
1599
|
return null;
|
|
1416
1600
|
}
|
|
@@ -1551,7 +1735,7 @@ var ServiceRunner = class {
|
|
|
1551
1735
|
async handleEvent(event, absPath, stats) {
|
|
1552
1736
|
if (!this.running) return;
|
|
1553
1737
|
const root = this.runtime.config.rootPath;
|
|
1554
|
-
const absolute =
|
|
1738
|
+
const absolute = path2__default.default.isAbsolute(absPath) ? absPath : path2__default.default.join(root, absPath);
|
|
1555
1739
|
if (!isInsideRoot(root, absolute)) {
|
|
1556
1740
|
return;
|
|
1557
1741
|
}
|
|
@@ -1571,7 +1755,7 @@ var ServiceRunner = class {
|
|
|
1571
1755
|
async handleAddChange(absPath, _stats) {
|
|
1572
1756
|
let fileStats;
|
|
1573
1757
|
try {
|
|
1574
|
-
fileStats = await
|
|
1758
|
+
fileStats = await fs5__default.default.lstat(absPath);
|
|
1575
1759
|
} catch (error) {
|
|
1576
1760
|
this.runtime.logger.warn(
|
|
1577
1761
|
{ err: error, path: absPath },
|
|
@@ -2072,7 +2256,6 @@ ${statusText}`;
|
|
|
2072
2256
|
|
|
2073
2257
|
// src/mcp-cli.ts
|
|
2074
2258
|
var ENV_FLAG_MAP = {
|
|
2075
|
-
root: "CODERULE_ROOT",
|
|
2076
2259
|
"data-dir": "CODERULE_DATA_DIR",
|
|
2077
2260
|
"auth-url": "CODERULE_AUTH_URL",
|
|
2078
2261
|
"sync-url": "CODERULE_SYNC_URL",
|
|
@@ -2098,6 +2281,9 @@ function printUsage() {
|
|
|
2098
2281
|
console.log(
|
|
2099
2282
|
" --clean, --reindex Remove existing local state before running"
|
|
2100
2283
|
);
|
|
2284
|
+
console.log(
|
|
2285
|
+
" --exit Exit after reindex (use with --reindex)"
|
|
2286
|
+
);
|
|
2101
2287
|
console.log(
|
|
2102
2288
|
" --inline-hasher Force inline hashing (debug only)"
|
|
2103
2289
|
);
|
|
@@ -2143,7 +2329,9 @@ function printUsage() {
|
|
|
2143
2329
|
}
|
|
2144
2330
|
function parseArgs(argv) {
|
|
2145
2331
|
let token = process.env.CODERULE_TOKEN;
|
|
2332
|
+
let rootPath;
|
|
2146
2333
|
let clean = false;
|
|
2334
|
+
let exit = false;
|
|
2147
2335
|
let inlineHasher = false;
|
|
2148
2336
|
const env = {};
|
|
2149
2337
|
const args = [...argv];
|
|
@@ -2157,6 +2345,10 @@ function parseArgs(argv) {
|
|
|
2157
2345
|
clean = true;
|
|
2158
2346
|
continue;
|
|
2159
2347
|
}
|
|
2348
|
+
if (arg === "--exit") {
|
|
2349
|
+
exit = true;
|
|
2350
|
+
continue;
|
|
2351
|
+
}
|
|
2160
2352
|
if (arg === "--inline-hasher") {
|
|
2161
2353
|
inlineHasher = true;
|
|
2162
2354
|
continue;
|
|
@@ -2173,6 +2365,14 @@ function parseArgs(argv) {
|
|
|
2173
2365
|
token = arg.slice("--token=".length);
|
|
2174
2366
|
continue;
|
|
2175
2367
|
}
|
|
2368
|
+
if (arg === "--root") {
|
|
2369
|
+
const value = args.shift();
|
|
2370
|
+
if (!value) {
|
|
2371
|
+
throw new Error("Missing value for --root");
|
|
2372
|
+
}
|
|
2373
|
+
rootPath = value;
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2176
2376
|
if (arg.startsWith("--")) {
|
|
2177
2377
|
const flag = arg.slice(2);
|
|
2178
2378
|
const envKey = ENV_FLAG_MAP[flag];
|
|
@@ -2205,22 +2405,22 @@ function parseArgs(argv) {
|
|
|
2205
2405
|
"Missing token. Provide via argument or CODERULE_TOKEN environment variable."
|
|
2206
2406
|
);
|
|
2207
2407
|
}
|
|
2208
|
-
return { token, clean, inlineHasher, env };
|
|
2408
|
+
return { token, rootPath, clean, exit, inlineHasher, env };
|
|
2209
2409
|
}
|
|
2210
|
-
async function ensureClean(configToken) {
|
|
2211
|
-
const config = await resolveConfig({ token: configToken });
|
|
2410
|
+
async function ensureClean(configToken, rootPath) {
|
|
2411
|
+
const config = await resolveConfig({ token: configToken, rootPath });
|
|
2212
2412
|
const targets = [
|
|
2213
2413
|
config.dbPath,
|
|
2214
2414
|
`${config.dbPath}-shm`,
|
|
2215
2415
|
`${config.dbPath}-wal`
|
|
2216
2416
|
];
|
|
2217
|
-
await Promise.all(targets.map((target) =>
|
|
2218
|
-
await
|
|
2417
|
+
await Promise.all(targets.map((target) => fs5__default.default.rm(target, { force: true })));
|
|
2418
|
+
await fs5__default.default.rm(path2__default.default.join(config.dataDir, "watch", `${config.rootId}.sqlite-shm`), {
|
|
2219
2419
|
force: true
|
|
2220
2420
|
}).catch(() => {
|
|
2221
2421
|
});
|
|
2222
|
-
const dir =
|
|
2223
|
-
await
|
|
2422
|
+
const dir = path2__default.default.dirname(config.dbPath);
|
|
2423
|
+
await fs5__default.default.mkdir(dir, { recursive: true });
|
|
2224
2424
|
console.log(`Removed scanner database at ${config.dbPath}`);
|
|
2225
2425
|
}
|
|
2226
2426
|
function awaitShutdownSignals() {
|
|
@@ -2251,10 +2451,30 @@ async function main() {
|
|
|
2251
2451
|
process.env[key] = value;
|
|
2252
2452
|
}
|
|
2253
2453
|
if (options.clean) {
|
|
2254
|
-
await ensureClean(options.token);
|
|
2454
|
+
await ensureClean(options.token, options.rootPath);
|
|
2255
2455
|
}
|
|
2256
|
-
const runtime = await bootstrap({
|
|
2456
|
+
const runtime = await bootstrap({
|
|
2457
|
+
token: options.token,
|
|
2458
|
+
rootPath: options.rootPath
|
|
2459
|
+
});
|
|
2257
2460
|
const runner = new ServiceRunner(runtime);
|
|
2461
|
+
if (options.exit && options.clean) {
|
|
2462
|
+
try {
|
|
2463
|
+
const initial = await runInitialSyncPipeline(runtime, {
|
|
2464
|
+
blockUntilReady: true
|
|
2465
|
+
});
|
|
2466
|
+
runtime.logger.info(
|
|
2467
|
+
{
|
|
2468
|
+
snapshotHash: initial.snapshotHash,
|
|
2469
|
+
filesCount: initial.filesCount
|
|
2470
|
+
},
|
|
2471
|
+
"Reindex completed; exiting"
|
|
2472
|
+
);
|
|
2473
|
+
} finally {
|
|
2474
|
+
await runner.stop();
|
|
2475
|
+
}
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2258
2478
|
try {
|
|
2259
2479
|
await runner.prepareWatcher(true);
|
|
2260
2480
|
const server = createMcpServer({ runtime, runner });
|