@nexrall/code-core 1.4.71 → 1.4.73
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/agent/agentRegistry.d.ts.map +1 -1
- package/dist/agent/agentRegistry.js +28 -10
- package/dist/agent/agentTypes.d.ts.map +1 -1
- package/dist/agent/agentTypes.js +159 -48
- package/dist/agent/askOnce.d.ts.map +1 -1
- package/dist/agent/askOnce.js +14 -5
- package/dist/agent/backgroundAgents.d.ts.map +1 -1
- package/dist/agent/backgroundAgents.js +22 -8
- package/dist/agent/claimEvidence.d.ts.map +1 -1
- package/dist/agent/claimEvidence.js +2 -1
- package/dist/agent/compaction.d.ts.map +1 -1
- package/dist/agent/compaction.js +31 -10
- package/dist/agent/crossProcessLock.d.ts.map +1 -1
- package/dist/agent/crossProcessLock.js +14 -6
- package/dist/agent/editCompleteness.d.ts.map +1 -1
- package/dist/agent/fileLocks.d.ts.map +1 -1
- package/dist/agent/fileLocks.js +14 -5
- package/dist/agent/flaky.d.ts.map +1 -1
- package/dist/agent/hooks.d.ts.map +1 -1
- package/dist/agent/hooks.js +284 -75
- package/dist/agent/iterationPolicy.d.ts.map +1 -1
- package/dist/agent/iterationPolicy.js +26 -23
- package/dist/agent/lifecycleHost.d.ts.map +1 -1
- package/dist/agent/lifecycleHost.js +35 -11
- package/dist/agent/loop.d.ts +22 -4
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +238 -51
- package/dist/agent/memory.d.ts.map +1 -1
- package/dist/agent/memory.js +13 -7
- package/dist/agent/modelCatalogue.d.ts.map +1 -1
- package/dist/agent/modelCatalogue.js +4 -1
- package/dist/agent/nestedInstructions.d.ts +122 -0
- package/dist/agent/nestedInstructions.d.ts.map +1 -0
- package/dist/agent/nestedInstructions.js +696 -0
- package/dist/agent/peerRegistry.d.ts.map +1 -1
- package/dist/agent/peerRegistry.js +44 -6
- package/dist/agent/peerTransport.d.ts.map +1 -1
- package/dist/agent/peerTransport.js +18 -6
- package/dist/agent/planMode.d.ts.map +1 -1
- package/dist/agent/planMode.js +194 -41
- package/dist/agent/readDedupe.d.ts.map +1 -1
- package/dist/agent/readDedupe.js +3 -1
- package/dist/agent/securityLint.d.ts.map +1 -1
- package/dist/agent/sharedTasks.d.ts.map +1 -1
- package/dist/agent/sharedTasks.js +20 -5
- package/dist/agent/skills.d.ts +79 -0
- package/dist/agent/skills.d.ts.map +1 -1
- package/dist/agent/skills.js +403 -14
- package/dist/agent/subAgentBudget.d.ts.map +1 -1
- package/dist/agent/subTask.d.ts.map +1 -1
- package/dist/agent/subTask.js +87 -37
- package/dist/agent/subTaskSupport.d.ts.map +1 -1
- package/dist/agent/subTaskSupport.js +43 -14
- package/dist/agent/testIntegrity.d.ts.map +1 -1
- package/dist/agent/testIntegrity.js +4 -2
- package/dist/agent/toolDescriptions.d.ts.map +1 -1
- package/dist/agent/toolPrefetch.d.ts.map +1 -1
- package/dist/agent/toolPrefetch.js +15 -11
- package/dist/agent/trust.d.ts.map +1 -1
- package/dist/agent/trust.js +6 -2
- package/dist/agent/worktree.d.ts.map +1 -1
- package/dist/agent/worktree.js +70 -16
- package/dist/agent/worktreeEnforcement.d.ts.map +1 -1
- package/dist/agent/worktreeEnforcement.js +9 -4
- package/dist/api/client.d.ts +6 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +64 -31
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -1
- package/dist/checkpoint/manager.d.ts.map +1 -1
- package/dist/checkpoint/manager.js +22 -7
- package/dist/commands/loader.d.ts.map +1 -1
- package/dist/commands/loader.js +6 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/mcp/client.d.ts +42 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +89 -24
- package/dist/mcp/httpClient.d.ts +24 -1
- package/dist/mcp/httpClient.d.ts.map +1 -1
- package/dist/mcp/httpClient.js +80 -14
- package/dist/mcp/instructions.d.ts +11 -0
- package/dist/mcp/instructions.d.ts.map +1 -0
- package/dist/mcp/instructions.js +42 -0
- package/dist/mcp/manager.d.ts +35 -0
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +135 -24
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +48 -14
- package/dist/mcp/sseClient.d.ts +9 -1
- package/dist/mcp/sseClient.d.ts.map +1 -1
- package/dist/mcp/sseClient.js +43 -10
- package/dist/mcp/stats.d.ts.map +1 -1
- package/dist/permissions/bashClassify.d.ts.map +1 -1
- package/dist/permissions/bashClassify.js +25 -3
- package/dist/permissions/destructive.d.ts.map +1 -1
- package/dist/permissions/destructive.js +7 -2
- package/dist/permissions/destructiveTokens.d.ts.map +1 -1
- package/dist/permissions/destructiveTokens.js +104 -24
- package/dist/permissions/modePolicy.d.ts.map +1 -1
- package/dist/permissions/modePolicy.js +40 -17
- package/dist/permissions/rules.d.ts.map +1 -1
- package/dist/permissions/rules.js +7 -5
- package/dist/plugins/data.d.ts.map +1 -1
- package/dist/plugins/data.js +6 -2
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +18 -7
- package/dist/plugins/installer.d.ts.map +1 -1
- package/dist/plugins/installer.js +17 -10
- package/dist/plugins/sources.d.ts.map +1 -1
- package/dist/plugins/sources.js +9 -3
- package/dist/tools/executor.d.ts.map +1 -1
- package/dist/tools/executor.js +454 -168
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +5 -1
- package/dist/tools/symbols.d.ts.map +1 -1
- package/dist/tools/symbols.js +30 -8
- package/dist/tools/tsLangService.d.ts.map +1 -1
- package/dist/tools/tsLangService.js +12 -4
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/frontmatter.d.ts.map +1 -1
- package/dist/util/miniYaml.d.ts.map +1 -1
- package/dist/util/miniYaml.js +3 -1
- package/package.json +1 -1
package/dist/mcp/manager.js
CHANGED
|
@@ -76,6 +76,36 @@ class McpManager {
|
|
|
76
76
|
for (const client of this._clients.values())
|
|
77
77
|
client.setElicitationHandler(handler);
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Route server→client notifications for one client: `tools/list_changed` re-fetches
|
|
81
|
+
* the tool table and rebuilds the prefixed map in place (new tools appear on the next
|
|
82
|
+
* turn — no restart), `prompts/list_changed` drops the cached prompt list. A failed
|
|
83
|
+
* refresh keeps the OLD table: a transient error must not empty the toolset.
|
|
84
|
+
*/
|
|
85
|
+
_wireNotifications(name, client) {
|
|
86
|
+
client.setNotificationHandler((method) => {
|
|
87
|
+
if (method === 'notifications/tools/list_changed') {
|
|
88
|
+
void client
|
|
89
|
+
.refreshTools()
|
|
90
|
+
.then(() => this._refreshToolMap(name, client.tools))
|
|
91
|
+
.catch(() => {
|
|
92
|
+
/* keep the old table on failure */
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
else if (method === 'notifications/prompts/list_changed') {
|
|
96
|
+
this._promptCache.delete(name);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/** Replace one server's entries in the prefixed tool map with its current list. */
|
|
101
|
+
_refreshToolMap(name, tools) {
|
|
102
|
+
for (const key of [...this._toolMap.keys()]) {
|
|
103
|
+
if (this._toolMap.get(key) === name)
|
|
104
|
+
this._toolMap.delete(key);
|
|
105
|
+
}
|
|
106
|
+
for (const tool of tools)
|
|
107
|
+
this._toolMap.set(`${name}__${tool.name}`, name);
|
|
108
|
+
}
|
|
79
109
|
/** Pick the transport for a server config: stdio (command), Streamable HTTP, or legacy SSE. */
|
|
80
110
|
static makeClient(name, cfg) {
|
|
81
111
|
if (cfg.type === 'sse')
|
|
@@ -91,11 +121,10 @@ class McpManager {
|
|
|
91
121
|
try {
|
|
92
122
|
Object.assign(merged.mcpServers, (0, index_1.pluginMcpServers)(workDir));
|
|
93
123
|
}
|
|
94
|
-
catch {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
];
|
|
124
|
+
catch {
|
|
125
|
+
/* plugin scan is best-effort */
|
|
126
|
+
}
|
|
127
|
+
const locations = [path.join(os.homedir(), '.nexrall', 'mcp.json'), path.join(workDir, '.nexrall', 'mcp.json')];
|
|
99
128
|
// A list, so the two scopes UNION rather than override: switching a server off in
|
|
100
129
|
// the project must add to whatever the user file already silences.
|
|
101
130
|
const disabled = new Set();
|
|
@@ -128,16 +157,14 @@ class McpManager {
|
|
|
128
157
|
}
|
|
129
158
|
const client = McpManager.makeClient(name, serverCfg);
|
|
130
159
|
client.setElicitationHandler(this._elicitationHandler);
|
|
160
|
+
this._wireNotifications(name, client);
|
|
131
161
|
try {
|
|
132
162
|
await client.connect();
|
|
133
163
|
this._clients.set(name, client);
|
|
134
164
|
this._errors.delete(name);
|
|
135
165
|
this._needsAuth.delete(name);
|
|
136
166
|
// Register tools with prefixed names: serverName__toolName
|
|
137
|
-
|
|
138
|
-
const prefixed = `${name}__${tool.name}`;
|
|
139
|
-
this._toolMap.set(prefixed, name);
|
|
140
|
-
}
|
|
167
|
+
this._refreshToolMap(name, client.tools);
|
|
141
168
|
}
|
|
142
169
|
catch (err) {
|
|
143
170
|
if (err instanceof httpClient_1.McpOAuthRequiredError) {
|
|
@@ -204,7 +231,21 @@ class McpManager {
|
|
|
204
231
|
// stats of two servers with a same-named tool never merge.
|
|
205
232
|
const startedAt = Date.now();
|
|
206
233
|
this.stats.begin();
|
|
207
|
-
const timed = (p) => p.then((v) => {
|
|
234
|
+
const timed = (p) => p.then((v) => {
|
|
235
|
+
this.stats.ok(prefixedName, Date.now() - startedAt);
|
|
236
|
+
return v;
|
|
237
|
+
}, (e) => {
|
|
238
|
+
this.stats.fail(prefixedName, Date.now() - startedAt, e.message || String(e));
|
|
239
|
+
throw e;
|
|
240
|
+
});
|
|
241
|
+
// A 401 mid-CALL is different from one at connect: the server is already up and
|
|
242
|
+
// some tools work — record it so /mcp flips to needs-auth (with the missing
|
|
243
|
+
// scope) and let the enriched message reach the caller (the model sees WHY).
|
|
244
|
+
const trackAuth = (p) => p.catch((err) => {
|
|
245
|
+
if (err instanceof httpClient_1.McpOAuthRequiredError)
|
|
246
|
+
this._needsAuth.set(serverName, err.wwwAuthenticate);
|
|
247
|
+
throw err;
|
|
248
|
+
});
|
|
208
249
|
const racers = [timed(client.callTool(toolName, input)), timed(timeoutPromise)];
|
|
209
250
|
if (abortSignal) {
|
|
210
251
|
let pollId;
|
|
@@ -220,9 +261,9 @@ class McpManager {
|
|
|
220
261
|
// Clearing the poll on whichever branch wins avoids leaving a live interval
|
|
221
262
|
// (and the closure it holds) running for the rest of the process lifetime —
|
|
222
263
|
// the same leak-prevention pattern client.ts uses for its own abort poll.
|
|
223
|
-
return Promise.race(racers).finally(() => clearInterval(pollId));
|
|
264
|
+
return trackAuth(Promise.race(racers).finally(() => clearInterval(pollId)));
|
|
224
265
|
}
|
|
225
|
-
return Promise.race(racers);
|
|
266
|
+
return trackAuth(Promise.race(racers));
|
|
226
267
|
}
|
|
227
268
|
/** Resources across connected, enabled servers. One server failing never hides the others. */
|
|
228
269
|
async listResources(server) {
|
|
@@ -241,7 +282,9 @@ class McpManager {
|
|
|
241
282
|
if (!client)
|
|
242
283
|
throw new Error(`No connected MCP server named "${server}". Connected: ${[...this._clients.keys()].join(', ') || '(none)'}`);
|
|
243
284
|
let timer;
|
|
244
|
-
const timeout = new Promise((_, rej) => {
|
|
285
|
+
const timeout = new Promise((_, rej) => {
|
|
286
|
+
timer = setTimeout(() => rej(new Error(`Reading ${uri} timed out after 60s`)), 60000);
|
|
287
|
+
});
|
|
245
288
|
try {
|
|
246
289
|
return await Promise.race([client.readResource(uri), timeout]);
|
|
247
290
|
}
|
|
@@ -300,13 +343,18 @@ class McpManager {
|
|
|
300
343
|
throw new Error(`MCP prompt "${server}:${name}" takes ${declared.length} argument(s)${declared.length ? ` (${declared.map((a) => a.name).join(', ')})` : ''} — got ${values.length}.`);
|
|
301
344
|
}
|
|
302
345
|
const args = {};
|
|
303
|
-
declared.forEach((a, i) => {
|
|
304
|
-
|
|
346
|
+
declared.forEach((a, i) => {
|
|
347
|
+
const v = values[i];
|
|
348
|
+
if (v !== undefined && v !== '')
|
|
349
|
+
args[a.name] = v;
|
|
350
|
+
});
|
|
305
351
|
const missing = declared.filter((a) => a.required && args[a.name] === undefined);
|
|
306
352
|
if (missing.length)
|
|
307
353
|
throw new Error(`MCP prompt "${server}:${name}" is missing required argument(s): ${missing.map((a) => a.name).join(', ')}.`);
|
|
308
354
|
let timer;
|
|
309
|
-
const timeout = new Promise((_, rej) => {
|
|
355
|
+
const timeout = new Promise((_, rej) => {
|
|
356
|
+
timer = setTimeout(() => rej(new Error(`MCP prompt ${server}:${name} timed out after 60s`)), 60000);
|
|
357
|
+
});
|
|
310
358
|
try {
|
|
311
359
|
return await Promise.race([client.getPrompt(name, args), timeout]);
|
|
312
360
|
}
|
|
@@ -325,6 +373,21 @@ class McpManager {
|
|
|
325
373
|
get toolCount() {
|
|
326
374
|
return this._toolMap.size;
|
|
327
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Names of configured servers whose entry sets `alwaysLoad: true` (CC parity).
|
|
378
|
+
* The agent loop sends this per request; the backend keeps those servers' tools
|
|
379
|
+
* out of BOTH deferral paths (tool-search `defer_loading` and the cap selector's
|
|
380
|
+
* droppable set). Config-level — a server that failed to connect still contributes
|
|
381
|
+
* an inert name, since it sends no tools to protect. Sorted because the list rides
|
|
382
|
+
* in the request body, which is part of the tools-cache key.
|
|
383
|
+
*/
|
|
384
|
+
alwaysLoadServers() {
|
|
385
|
+
const names = [];
|
|
386
|
+
for (const [name, cfg] of this._configs)
|
|
387
|
+
if (cfg.alwaysLoad === true)
|
|
388
|
+
names.push(name);
|
|
389
|
+
return names.sort();
|
|
390
|
+
}
|
|
328
391
|
static transportOf(cfg) {
|
|
329
392
|
if (cfg?.type)
|
|
330
393
|
return cfg.type;
|
|
@@ -334,7 +397,19 @@ class McpManager {
|
|
|
334
397
|
getStatus() {
|
|
335
398
|
const out = [];
|
|
336
399
|
for (const [name, client] of this._clients) {
|
|
337
|
-
out.push({
|
|
400
|
+
out.push({
|
|
401
|
+
name,
|
|
402
|
+
transport: McpManager.transportOf(this._configs.get(name)),
|
|
403
|
+
tools: client.tools,
|
|
404
|
+
connected: true,
|
|
405
|
+
disabled: false,
|
|
406
|
+
// A server can be UP and still have recorded a 401 from a mid-call
|
|
407
|
+
// insufficient_scope challenge — the row says so, the detail says which scope.
|
|
408
|
+
needsAuth: this._needsAuth.has(name),
|
|
409
|
+
missingScope: this.missingScope(name) || undefined,
|
|
410
|
+
protocolVersion: client.protocolVersion,
|
|
411
|
+
alwaysLoad: this._configs.get(name)?.alwaysLoad === true,
|
|
412
|
+
});
|
|
338
413
|
}
|
|
339
414
|
for (const name of this._configs.keys()) {
|
|
340
415
|
if (this._clients.has(name))
|
|
@@ -344,12 +419,26 @@ class McpManager {
|
|
|
344
419
|
// A disabled server that never connected has no error to report — say nothing
|
|
345
420
|
// rather than repeat "disconnected" as if it were a failure.
|
|
346
421
|
if (disabled && !error) {
|
|
347
|
-
out.push({
|
|
422
|
+
out.push({
|
|
423
|
+
name,
|
|
424
|
+
transport: McpManager.transportOf(this._configs.get(name)),
|
|
425
|
+
tools: [],
|
|
426
|
+
connected: false,
|
|
427
|
+
disabled: true,
|
|
428
|
+
alwaysLoad: this._configs.get(name)?.alwaysLoad === true,
|
|
429
|
+
});
|
|
348
430
|
continue;
|
|
349
431
|
}
|
|
350
432
|
out.push({
|
|
351
|
-
name,
|
|
352
|
-
|
|
433
|
+
name,
|
|
434
|
+
transport: McpManager.transportOf(this._configs.get(name)),
|
|
435
|
+
tools: [],
|
|
436
|
+
connected: false,
|
|
437
|
+
disabled,
|
|
438
|
+
error,
|
|
439
|
+
needsAuth: this._needsAuth.has(name),
|
|
440
|
+
missingScope: this.missingScope(name) || undefined,
|
|
441
|
+
alwaysLoad: this._configs.get(name)?.alwaysLoad === true,
|
|
353
442
|
});
|
|
354
443
|
}
|
|
355
444
|
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -358,6 +447,23 @@ class McpManager {
|
|
|
358
447
|
authChallenge(name) {
|
|
359
448
|
return this._needsAuth.get(name) ?? '';
|
|
360
449
|
}
|
|
450
|
+
/** Permissions an auth-required server says are missing ('' when unknown/plain 401). */
|
|
451
|
+
missingScope(name) {
|
|
452
|
+
return (0, httpClient_1.parseOAuthChallenge)(this._needsAuth.get(name) ?? '').scope ?? '';
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* `initialize.instructions` from every connected server that sent one — static
|
|
456
|
+
* reference notes (workflow rules, repo layout) the conversation should carry.
|
|
457
|
+
* The caller decides where they go; ordering is by server name for stable output.
|
|
458
|
+
*/
|
|
459
|
+
serverInstructions() {
|
|
460
|
+
const out = [];
|
|
461
|
+
for (const [name, client] of this._clients) {
|
|
462
|
+
if (client.instructions)
|
|
463
|
+
out.push({ server: name, instructions: client.instructions });
|
|
464
|
+
}
|
|
465
|
+
return out.sort((a, b) => a.server.localeCompare(b.server));
|
|
466
|
+
}
|
|
361
467
|
/** True when the user has switched this server off for the session. */
|
|
362
468
|
isDisabled(name) {
|
|
363
469
|
return this._disabled.has(name);
|
|
@@ -386,6 +492,7 @@ class McpManager {
|
|
|
386
492
|
this._dropClient(name);
|
|
387
493
|
const client = McpManager.makeClient(name, cfg);
|
|
388
494
|
client.setElicitationHandler(this._elicitationHandler);
|
|
495
|
+
this._wireNotifications(name, client);
|
|
389
496
|
try {
|
|
390
497
|
await client.connect();
|
|
391
498
|
}
|
|
@@ -399,8 +506,7 @@ class McpManager {
|
|
|
399
506
|
this._clients.set(name, client);
|
|
400
507
|
this._errors.delete(name);
|
|
401
508
|
this._needsAuth.delete(name);
|
|
402
|
-
|
|
403
|
-
this._toolMap.set(`${name}__${tool.name}`, name);
|
|
509
|
+
this._refreshToolMap(name, client.tools);
|
|
404
510
|
return null;
|
|
405
511
|
}
|
|
406
512
|
/** Disconnect one server for the session (its config stays; `enable` brings it back). */
|
|
@@ -415,9 +521,12 @@ class McpManager {
|
|
|
415
521
|
const client = this._clients.get(name);
|
|
416
522
|
if (client) {
|
|
417
523
|
try {
|
|
524
|
+
client.setNotificationHandler(null);
|
|
418
525
|
client.disconnect();
|
|
419
526
|
}
|
|
420
|
-
catch {
|
|
527
|
+
catch {
|
|
528
|
+
/* best effort */
|
|
529
|
+
}
|
|
421
530
|
}
|
|
422
531
|
this._clients.delete(name);
|
|
423
532
|
for (const key of [...this._toolMap.keys()]) {
|
|
@@ -491,7 +600,9 @@ class McpManager {
|
|
|
491
600
|
if ((0, index_1.pluginMcpServers)(workDir)[name])
|
|
492
601
|
return 'plugin';
|
|
493
602
|
}
|
|
494
|
-
catch {
|
|
603
|
+
catch {
|
|
604
|
+
/* plugin scan is best-effort */
|
|
605
|
+
}
|
|
495
606
|
return 'none';
|
|
496
607
|
}
|
|
497
608
|
/** Remove a server from whichever scope(s) define it. Returns true if anything was removed. */
|
package/dist/mcp/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAI3C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,wDAAwD;IACxD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAaD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,EAAE,CA8ClD;AAcD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyIhE"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.readOnlyMcpTools = readOnlyMcpTools;
|
|
16
16
|
exports.serveMcp = serveMcp;
|
|
17
17
|
const executor_1 = require("../tools/executor");
|
|
18
|
+
const client_1 = require("./client");
|
|
18
19
|
// ─── The toolset ──────────────────────────────────────────────────────────────
|
|
19
20
|
/** Wrap one executor tool. The executor is the single source of behaviour — same caps, same
|
|
20
21
|
* error messages, same path resolution as the CLI's own turns. */
|
|
@@ -31,32 +32,52 @@ const fromExecutor = (name, description, inputSchema) => ({
|
|
|
31
32
|
*/
|
|
32
33
|
function readOnlyMcpTools() {
|
|
33
34
|
return [
|
|
34
|
-
fromExecutor('read_file', 'Read a text file. Returns numbered lines so later edits can cite them. Binary files are refused.', {
|
|
35
|
+
fromExecutor('read_file', 'Read a text file. Returns numbered lines so later edits can cite them. Binary files are refused.', {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
35
38
|
path: { type: 'string', description: 'File path, absolute or relative to the workspace root' },
|
|
36
39
|
offset: { type: 'number', description: '0-based line to start from (default 0)' },
|
|
37
40
|
limit: { type: 'number', description: 'How many lines to return' },
|
|
38
|
-
},
|
|
39
|
-
|
|
41
|
+
},
|
|
42
|
+
required: ['path'],
|
|
43
|
+
}),
|
|
44
|
+
fromExecutor('list_directory', 'List the entries of a directory, each marked as file or directory.', {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
40
47
|
path: { type: 'string', description: 'Directory path (default: the workspace root)' },
|
|
41
|
-
}
|
|
42
|
-
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
fromExecutor('glob', 'Find files by glob pattern, e.g. "**/*.ts" or "src/**/*.test.js".', {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
43
53
|
pattern: { type: 'string', description: 'Glob pattern' },
|
|
44
54
|
path: { type: 'string', description: 'Directory to search from (default: the workspace root)' },
|
|
45
|
-
},
|
|
46
|
-
|
|
55
|
+
},
|
|
56
|
+
required: ['pattern'],
|
|
57
|
+
}),
|
|
58
|
+
fromExecutor('search_files', 'Search file contents with a regular expression. Returns matching file paths by default; ask for context lines to get the matches themselves.', {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
47
61
|
pattern: { type: 'string', description: 'Regular expression' },
|
|
48
62
|
path: { type: 'string', description: 'Directory to search (default: the workspace root)' },
|
|
49
63
|
type: { type: 'string', description: '"files" (paths only) or "content" (matching lines)' },
|
|
50
64
|
include: { type: 'string', description: 'Only files matching this glob, e.g. "*.ts"' },
|
|
51
65
|
case_insensitive: { type: 'boolean', description: 'Case-insensitive match' },
|
|
52
66
|
context_lines: { type: 'number', description: 'Lines of context around each match (implies type "content")' },
|
|
53
|
-
},
|
|
67
|
+
},
|
|
68
|
+
required: ['pattern'],
|
|
69
|
+
}),
|
|
54
70
|
];
|
|
55
71
|
}
|
|
56
72
|
// ─── Protocol ─────────────────────────────────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
// Shared with the client half of the module so the two cannot drift. All listed
|
|
74
|
+
// revisions have the same framing for our surface (initialize/tools/resources);
|
|
75
|
+
// when a client asks for a revision we do not implement, the answer is the LATEST
|
|
76
|
+
// we do — that is what the MCP spec requires ("another protocol version it
|
|
77
|
+
// supports, preferably the latest"), and it keeps modern clients on a modern
|
|
78
|
+
// negotiation instead of downgrading them to the 2024 framing by default.
|
|
79
|
+
const SUPPORTED_PROTOCOLS = client_1.MCP_PROTOCOL_VERSIONS;
|
|
80
|
+
const DEFAULT_PROTOCOL = client_1.MCP_PROTOCOL_VERSION;
|
|
60
81
|
const SERVER_NAME = 'nexrall';
|
|
61
82
|
/**
|
|
62
83
|
* Serve MCP on stdio until the client closes the pipe. Resolves (never rejects) at EOF — the
|
|
@@ -69,7 +90,9 @@ async function serveMcp(opts) {
|
|
|
69
90
|
const tools = opts.tools ?? readOnlyMcpTools();
|
|
70
91
|
const version = opts.version ?? '0.0.0';
|
|
71
92
|
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
72
|
-
const send = (msg) => {
|
|
93
|
+
const send = (msg) => {
|
|
94
|
+
output.write(JSON.stringify(msg) + '\n');
|
|
95
|
+
};
|
|
73
96
|
const reply = (id, result) => send({ jsonrpc: '2.0', id, result });
|
|
74
97
|
const replyError = (id, code, message) => send({ jsonrpc: '2.0', id, error: { code, message } });
|
|
75
98
|
async function handle(message) {
|
|
@@ -110,7 +133,15 @@ async function serveMcp(opts) {
|
|
|
110
133
|
if (!tool) {
|
|
111
134
|
// A RESULT, not a protocol error: the calling model should read "not offered" as tool
|
|
112
135
|
// output and pick another route, not see the whole call fail.
|
|
113
|
-
reply(id, {
|
|
136
|
+
reply(id, {
|
|
137
|
+
content: [
|
|
138
|
+
{
|
|
139
|
+
type: 'text',
|
|
140
|
+
text: `Tool "${name}" is not offered by this server (read-only). Available: ${[...byName.keys()].join(', ')}.`,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
isError: true,
|
|
144
|
+
});
|
|
114
145
|
return;
|
|
115
146
|
}
|
|
116
147
|
try {
|
|
@@ -122,7 +153,10 @@ async function serveMcp(opts) {
|
|
|
122
153
|
}
|
|
123
154
|
catch (err) {
|
|
124
155
|
// A throwing tool is the tool's problem, not the session's — answer and keep serving.
|
|
125
|
-
reply(id, {
|
|
156
|
+
reply(id, {
|
|
157
|
+
content: [{ type: 'text', text: err instanceof Error ? err.message : String(err) }],
|
|
158
|
+
isError: true,
|
|
159
|
+
});
|
|
126
160
|
}
|
|
127
161
|
return;
|
|
128
162
|
}
|
package/dist/mcp/sseClient.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpServerConfig, McpToolDefinition, McpTransport, McpResource, McpPrompt, McpElicitationHandler } from './client';
|
|
1
|
+
import type { McpServerConfig, McpToolDefinition, McpTransport, McpResource, McpPrompt, McpElicitationHandler, McpNotificationHandler } from './client';
|
|
2
2
|
export declare class McpSseClient implements McpTransport {
|
|
3
3
|
private readonly config;
|
|
4
4
|
private _nextId;
|
|
@@ -10,11 +10,19 @@ export declare class McpSseClient implements McpTransport {
|
|
|
10
10
|
private readonly _pending;
|
|
11
11
|
private _endpointWaiter;
|
|
12
12
|
private _elicitationHandler;
|
|
13
|
+
private _notificationHandler;
|
|
14
|
+
private _protocolVersion;
|
|
15
|
+
private _instructions;
|
|
13
16
|
readonly name: string;
|
|
14
17
|
constructor(name: string, config: McpServerConfig);
|
|
15
18
|
get tools(): McpToolDefinition[];
|
|
19
|
+
get protocolVersion(): string | undefined;
|
|
20
|
+
get instructions(): string | undefined;
|
|
16
21
|
setElicitationHandler(handler: McpElicitationHandler | null): void;
|
|
22
|
+
setNotificationHandler(handler: McpNotificationHandler | null): void;
|
|
17
23
|
connect(): Promise<void>;
|
|
24
|
+
/** Re-fetch `tools/list` (see McpClient.refreshTools for the failure rule). */
|
|
25
|
+
refreshTools(): Promise<void>;
|
|
18
26
|
listResources(): Promise<McpResource[]>;
|
|
19
27
|
readResource(uri: string): Promise<string>;
|
|
20
28
|
listPrompts(): Promise<McpPrompt[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sseClient.d.ts","sourceRoot":"","sources":["../../src/mcp/sseClient.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sseClient.d.ts","sourceRoot":"","sources":["../../src/mcp/sseClient.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,SAAS,EACT,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,UAAU,CAAC;AA6BlB,qBAAa,YAAa,YAAW,YAAY;IAoB7C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAnBzB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAGrB;IACJ,OAAO,CAAC,eAAe,CAAoE;IAC3F,OAAO,CAAC,mBAAmB,CAAsC;IACjE,OAAO,CAAC,oBAAoB,CAAuC;IACnE,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,aAAa,CAAqB;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAGpB,IAAI,EAAE,MAAM,EACK,MAAM,EAAE,eAAe;IAK1C,IAAI,KAAK,IAAI,iBAAiB,EAAE,CAE/B;IAED,IAAI,eAAe,IAAI,MAAM,GAAG,SAAS,CAExC;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAErC;IAED,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,GAAG,IAAI;IAIlE,sBAAsB,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,GAAG,IAAI;IAI9D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B,+EAA+E;IACzE,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAK7B,aAAa,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IASvC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1C,WAAW,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IASnC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAMtE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAc5E,UAAU,IAAI,IAAI;IAalB,4EAA4E;YAC9D,WAAW;IA4CzB,OAAO,CAAC,eAAe;IAYvB,oFAAoF;IACpF,OAAO,CAAC,KAAK;IAWb,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,cAAc;IA0DtB,OAAO,CAAC,IAAI;YAoBE,OAAO;IAIrB,6EAA6E;YAC/D,YAAY;CAwB3B"}
|
package/dist/mcp/sseClient.js
CHANGED
|
@@ -21,22 +21,42 @@ class McpSseClient {
|
|
|
21
21
|
this._pending = new Map();
|
|
22
22
|
this._endpointWaiter = null;
|
|
23
23
|
this._elicitationHandler = null;
|
|
24
|
+
this._notificationHandler = null;
|
|
24
25
|
this.name = name;
|
|
25
26
|
}
|
|
26
27
|
get tools() {
|
|
27
28
|
return this._tools;
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
+
get protocolVersion() {
|
|
31
|
+
return this._protocolVersion;
|
|
32
|
+
}
|
|
33
|
+
get instructions() {
|
|
34
|
+
return this._instructions;
|
|
35
|
+
}
|
|
36
|
+
setElicitationHandler(handler) {
|
|
37
|
+
this._elicitationHandler = handler;
|
|
38
|
+
}
|
|
39
|
+
setNotificationHandler(handler) {
|
|
40
|
+
this._notificationHandler = handler;
|
|
41
|
+
}
|
|
30
42
|
async connect() {
|
|
31
43
|
if (!this.config.url)
|
|
32
44
|
throw new Error(`MCP server "${this.name}" has no url (sse transport requires one).`);
|
|
33
45
|
await this._openStream();
|
|
34
|
-
await this._rpc('initialize', {
|
|
35
|
-
protocolVersion:
|
|
46
|
+
const init = (await this._rpc('initialize', {
|
|
47
|
+
protocolVersion: client_1.MCP_PROTOCOL_VERSION,
|
|
36
48
|
capabilities: (0, client_1.elicitationCapability)(this.config.bareElicitationCapability),
|
|
37
49
|
clientInfo: { name: 'nexrall-code', version: '1.0' },
|
|
38
|
-
});
|
|
50
|
+
}));
|
|
51
|
+
this._protocolVersion =
|
|
52
|
+
typeof init?.protocolVersion === 'string' && init.protocolVersion ? init.protocolVersion : undefined;
|
|
53
|
+
this._instructions =
|
|
54
|
+
typeof init?.instructions === 'string' && init.instructions.trim() ? init.instructions : undefined;
|
|
39
55
|
await this._notify('notifications/initialized');
|
|
56
|
+
await this.refreshTools();
|
|
57
|
+
}
|
|
58
|
+
/** Re-fetch `tools/list` (see McpClient.refreshTools for the failure rule). */
|
|
59
|
+
async refreshTools() {
|
|
40
60
|
const res = (await this._rpc('tools/list', {}));
|
|
41
61
|
this._tools = res?.tools ?? [];
|
|
42
62
|
}
|
|
@@ -111,8 +131,10 @@ class McpSseClient {
|
|
|
111
131
|
const stream = resp.body;
|
|
112
132
|
stream.on('data', (chunk) => this._feed(chunk.toString('utf-8')));
|
|
113
133
|
stream.on('end', () => this._onStreamClosed(new Error('MCP SSE stream ended')));
|
|
114
|
-
stream.on('error', (err) => {
|
|
115
|
-
this.
|
|
134
|
+
stream.on('error', (err) => {
|
|
135
|
+
if (!this._closed)
|
|
136
|
+
this._onStreamClosed(err);
|
|
137
|
+
});
|
|
116
138
|
// Wait for the `endpoint` event so we know where to POST messages.
|
|
117
139
|
await new Promise((resolve, reject) => {
|
|
118
140
|
const timer = setTimeout(() => {
|
|
@@ -120,8 +142,14 @@ class McpSseClient {
|
|
|
120
142
|
reject(new Error('Timed out waiting for SSE endpoint event'));
|
|
121
143
|
}, ENDPOINT_TIMEOUT_MS);
|
|
122
144
|
this._endpointWaiter = {
|
|
123
|
-
resolve: () => {
|
|
124
|
-
|
|
145
|
+
resolve: () => {
|
|
146
|
+
clearTimeout(timer);
|
|
147
|
+
resolve();
|
|
148
|
+
},
|
|
149
|
+
reject: (e) => {
|
|
150
|
+
clearTimeout(timer);
|
|
151
|
+
reject(e);
|
|
152
|
+
},
|
|
125
153
|
};
|
|
126
154
|
});
|
|
127
155
|
}
|
|
@@ -199,7 +227,9 @@ class McpSseClient {
|
|
|
199
227
|
void (async () => {
|
|
200
228
|
const reply = await (0, client_1.serverRequestReply)(this.name, this._elicitationHandler, method, params);
|
|
201
229
|
await this._postMessage({ jsonrpc: '2.0', id: reqId, ...reply });
|
|
202
|
-
})().catch(() => {
|
|
230
|
+
})().catch(() => {
|
|
231
|
+
/* server times the request out itself */
|
|
232
|
+
});
|
|
203
233
|
return;
|
|
204
234
|
}
|
|
205
235
|
if (typeof msg.id === 'number' && this._pending.has(msg.id)) {
|
|
@@ -212,7 +242,10 @@ class McpSseClient {
|
|
|
212
242
|
resolve(msg.result ?? null);
|
|
213
243
|
return;
|
|
214
244
|
}
|
|
215
|
-
// Server-initiated
|
|
245
|
+
// Server-initiated notification (no id): list_changed for tools/prompts/resources.
|
|
246
|
+
if (typeof msg.method === 'string' && msg.id == null) {
|
|
247
|
+
this._notificationHandler?.(msg.method, msg.params);
|
|
248
|
+
}
|
|
216
249
|
}
|
|
217
250
|
_rpc(method, params) {
|
|
218
251
|
const id = this._nextId++;
|
package/dist/mcp/stats.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/mcp/stats.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAK;IAErB,6DAA6D;IAC7D,KAAK,IAAI,IAAI;IAIb,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IAUpD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IAWrE,6EAA6E;IAC7E,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvC,6FAA6F;IAC7F,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAYjF,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,YAAY;IAS5D,iFAAiF;IACjF,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/mcp/stats.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAK;IAErB,6DAA6D;IAC7D,KAAK,IAAI,IAAI;IAIb,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IAUpD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IAWrE,6EAA6E;IAC7E,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvC,6FAA6F;IAC7F,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAYjF,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,YAAY;IAS5D,iFAAiF;IACjF,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAQ1B,OAAO,CAAC,MAAM;CAQf;AAED,6DAA6D;AAC7D,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,GAAG,UAAO,GAAG,MAAM,CAS5E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bashClassify.d.ts","sourceRoot":"","sources":["../../src/permissions/bashClassify.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bashClassify.d.ts","sourceRoot":"","sources":["../../src/permissions/bashClassify.ts"],"names":[],"mappings":"AA2DA;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAkB1D"}
|
|
@@ -32,9 +32,31 @@ exports.bashNeedsRepoLock = bashNeedsRepoLock;
|
|
|
32
32
|
* a lockfile, or mutate a shared build/dependency directory.
|
|
33
33
|
*/
|
|
34
34
|
const MUTATING_BASH_VERBS = new Set([
|
|
35
|
-
'git',
|
|
36
|
-
'
|
|
37
|
-
'
|
|
35
|
+
'git',
|
|
36
|
+
'npm',
|
|
37
|
+
'pnpm',
|
|
38
|
+
'yarn',
|
|
39
|
+
'bun',
|
|
40
|
+
'cargo',
|
|
41
|
+
'go',
|
|
42
|
+
'pip',
|
|
43
|
+
'pip3',
|
|
44
|
+
'poetry',
|
|
45
|
+
'uv',
|
|
46
|
+
'bundle',
|
|
47
|
+
'composer',
|
|
48
|
+
'gradle',
|
|
49
|
+
'mvn',
|
|
50
|
+
'make',
|
|
51
|
+
'terraform',
|
|
52
|
+
'helm',
|
|
53
|
+
'docker',
|
|
54
|
+
'vsce',
|
|
55
|
+
'tsc',
|
|
56
|
+
'pytest',
|
|
57
|
+
'dotnet',
|
|
58
|
+
'swift',
|
|
59
|
+
'gem',
|
|
38
60
|
]);
|
|
39
61
|
/**
|
|
40
62
|
* Whether a bash command should take the repo-state lock.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"destructive.d.ts","sourceRoot":"","sources":["../../src/permissions/destructive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,0FAA0F;IAC1F,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"destructive.d.ts","sourceRoot":"","sources":["../../src/permissions/destructive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,0FAA0F;IAC1F,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAsRD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAGnF;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,GAAG,IAAI,CASvG"}
|
|
@@ -60,7 +60,11 @@ const RAW_RULES = [
|
|
|
60
60
|
{
|
|
61
61
|
category: 'database',
|
|
62
62
|
reason: 'DELETE without a WHERE clause (deletes every row)',
|
|
63
|
-
re: new RegExp(CMD_POS +
|
|
63
|
+
re: new RegExp(CMD_POS +
|
|
64
|
+
DB_CLIENT +
|
|
65
|
+
String.raw `\b[\s\S]*?\bdelete\s+from\s+[\w.` +
|
|
66
|
+
'`' +
|
|
67
|
+
String.raw `"]+\s*(?:;|"|'|$)(?![\s\S]*\bwhere\b)`, 'i'),
|
|
64
68
|
},
|
|
65
69
|
{
|
|
66
70
|
category: 'database',
|
|
@@ -94,7 +98,8 @@ const STRIPPED_RULES = [
|
|
|
94
98
|
{
|
|
95
99
|
category: 'database',
|
|
96
100
|
reason: 'reverses or resets database migrations (may drop columns/tables)',
|
|
97
|
-
re: new RegExp(CMD_POS +
|
|
101
|
+
re: new RegExp(CMD_POS +
|
|
102
|
+
String.raw `(?:rails\s+db:(?:drop|reset)|rake\s+db:(?:drop|reset)|(?:npx\s+)?prisma\s+migrate\s+reset|(?:npx\s+)?sequelize(?:-cli)?\s+db:drop|python\s+manage\.py\s+flush|knex\s+migrate:rollback\s+--all|alembic\s+downgrade\s+base)`, 'i'),
|
|
98
103
|
},
|
|
99
104
|
// ── Git history / remotes ───────────────────────────────────────────────────
|
|
100
105
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"destructiveTokens.d.ts","sourceRoot":"","sources":["../../src/permissions/destructiveTokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAID,uEAAuE;AACvE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"destructiveTokens.d.ts","sourceRoot":"","sources":["../../src/permissions/destructiveTokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAID,uEAAuE;AACvE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CA6DhD;AAsVD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,EAC/C,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,GAC7C,UAAU,GAAG,IAAI,CA+HnB"}
|