auditor-lambda 0.3.30 → 0.3.32
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/audit-code-wrapper-lib.mjs +179 -171
- package/dist/cli.js +36 -7
- package/dist/extractors/risk.js +6 -4
- package/dist/io/artifacts.d.ts +2 -0
- package/dist/io/artifacts.js +1 -0
- package/dist/io/toolingManifest.d.ts +1 -0
- package/dist/io/toolingManifest.js +1 -1
- package/dist/mcp/server.d.ts +71 -0
- package/dist/mcp/server.js +252 -213
- package/dist/orchestrator/artifactFreshness.d.ts +4 -0
- package/dist/orchestrator/artifactFreshness.js +45 -0
- package/dist/orchestrator/artifactMetadata.js +2 -51
- package/dist/orchestrator/dependencyMap.js +14 -0
- package/dist/orchestrator/internalExecutors.js +8 -0
- package/dist/orchestrator/staleness.js +2 -46
- package/dist/orchestrator/state.js +1 -1
- package/dist/orchestrator/syntaxResolutionExecutor.js +121 -13
- package/dist/orchestrator/unitBuilder.js +2 -1
- package/dist/providers/spawnLoggedCommand.js +71 -18
- package/dist/providers/types.d.ts +5 -0
- package/dist/quota/scheduler.js +10 -2
- package/dist/quota/state.js +6 -2
- package/dist/types/externalAnalyzer.d.ts +10 -0
- package/dist/types/workerSession.js +1 -2
- package/dist/validation/artifacts.js +36 -0
- package/package.json +1 -1
- package/schemas/audit_task.schema.json +2 -2
- package/schemas/risk_register.schema.json +1 -1
- package/schemas/unit_manifest.schema.json +2 -1
- package/scripts/postinstall.mjs +10 -41
package/dist/mcp/server.js
CHANGED
|
@@ -69,7 +69,7 @@ function failure(id, code, message, data) {
|
|
|
69
69
|
},
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
-
function parseContentLength(headerBlock) {
|
|
72
|
+
export function parseContentLength(headerBlock) {
|
|
73
73
|
const headers = headerBlock.split("\r\n");
|
|
74
74
|
const contentLengthHeader = headers.find((header) => header.toLowerCase().startsWith("content-length:"));
|
|
75
75
|
if (!contentLengthHeader) {
|
|
@@ -186,72 +186,73 @@ function toolResult(value) {
|
|
|
186
186
|
structuredContent: value && typeof value === "object" ? value : { value },
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
export const resourceRegistry = [
|
|
190
|
+
{
|
|
191
|
+
uri: "audit-code://artifacts/current",
|
|
192
|
+
name: "current_artifacts",
|
|
193
|
+
description: "Current artifact bundle as JSON.",
|
|
194
|
+
mimeType: "application/json",
|
|
195
|
+
async read(context) {
|
|
192
196
|
const bundle = await loadArtifactBundle(context.artifactsDir);
|
|
193
|
-
return {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
return { mimeType: this.mimeType, text: JSON.stringify(bundle) };
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
uri: "audit-code://handoff/current",
|
|
202
|
+
name: "operator_handoff",
|
|
203
|
+
description: "Current operator handoff payload as JSON.",
|
|
204
|
+
mimeType: "application/json",
|
|
205
|
+
async read(context) {
|
|
199
206
|
const status = (await getStatusPayload(context)).handoff;
|
|
200
|
-
return {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
207
|
+
return { mimeType: this.mimeType, text: JSON.stringify(status) };
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
uri: "audit-code://install/guide",
|
|
212
|
+
name: "install_guide",
|
|
213
|
+
description: "Repo-local install guide for supported IDE hosts.",
|
|
214
|
+
mimeType: "text/markdown",
|
|
215
|
+
async read(context) {
|
|
206
216
|
const path = join(context.root, ".audit-code", "install", "GETTING-STARTED.md");
|
|
207
217
|
const guide = (await readOptionalTextFile(path)) ??
|
|
208
218
|
"Run `audit-code install` from the repository root to generate the repo-local setup guide.";
|
|
209
|
-
return {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
219
|
+
return { mimeType: this.mimeType, text: guide };
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
uri: "audit-code://report/current",
|
|
224
|
+
name: "audit_report",
|
|
225
|
+
description: "Current deterministic audit report if available.",
|
|
226
|
+
mimeType: "text/markdown",
|
|
227
|
+
async read(context) {
|
|
215
228
|
const report = (await readOptionalTextFile(join(context.artifactsDir, "audit-report.md"))) ??
|
|
216
229
|
(await readOptionalTextFile(join(context.root, "audit-report.md"))) ??
|
|
217
230
|
"The audit report has not been rendered yet.";
|
|
218
|
-
return {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
231
|
+
return { mimeType: this.mimeType, text: report };
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
];
|
|
235
|
+
async function readResource(uri, context) {
|
|
236
|
+
const entry = resourceRegistry.find((r) => r.uri === uri);
|
|
237
|
+
if (!entry) {
|
|
238
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
225
239
|
}
|
|
240
|
+
return entry.read(context);
|
|
226
241
|
}
|
|
227
|
-
function
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
{
|
|
235
|
-
name: "review-task",
|
|
236
|
-
description: "Inspect one audit task with explain_task and the current artifacts before reviewing code.",
|
|
237
|
-
arguments: [
|
|
238
|
-
{
|
|
239
|
-
name: "task_id",
|
|
240
|
-
required: true,
|
|
241
|
-
description: "Audit task id to inspect.",
|
|
242
|
-
},
|
|
243
|
-
],
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
name: "synthesize-report",
|
|
247
|
-
description: "Read the current audit report resource and summarize the highest-signal findings.",
|
|
248
|
-
arguments: [],
|
|
249
|
-
},
|
|
250
|
-
];
|
|
242
|
+
function resourceListPayload() {
|
|
243
|
+
return resourceRegistry.map((entry) => ({
|
|
244
|
+
uri: entry.uri,
|
|
245
|
+
name: entry.name,
|
|
246
|
+
description: entry.description,
|
|
247
|
+
mimeType: entry.mimeType,
|
|
248
|
+
}));
|
|
251
249
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
250
|
+
export const promptRegistry = [
|
|
251
|
+
{
|
|
252
|
+
name: "audit-code",
|
|
253
|
+
description: "Start or continue the autonomous audit loop through the auditor MCP tools.",
|
|
254
|
+
arguments: [],
|
|
255
|
+
render() {
|
|
255
256
|
return [
|
|
256
257
|
"Use the auditor MCP tools as the primary interface to the backend wrapper.",
|
|
257
258
|
"1. Call `start_audit`.",
|
|
@@ -260,19 +261,50 @@ function renderPrompt(name, args) {
|
|
|
260
261
|
"3. When the user provides additional evidence, call `import_results` or `import_runtime_updates`.",
|
|
261
262
|
"4. Call `continue_audit` until the status is complete or explicitly blocked for operator input.",
|
|
262
263
|
].join("\n");
|
|
263
|
-
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: "review-task",
|
|
268
|
+
description: "Inspect one audit task with explain_task and the current artifacts before reviewing code.",
|
|
269
|
+
arguments: [
|
|
270
|
+
{
|
|
271
|
+
name: "task_id",
|
|
272
|
+
required: true,
|
|
273
|
+
description: "Audit task id to inspect.",
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
render(args) {
|
|
264
277
|
return [
|
|
265
278
|
`Use \`explain_task\` for task \`${String(args?.task_id ?? "")}\` before you inspect code manually.`,
|
|
266
279
|
"Do not read the full `audit-code://artifacts/current` bundle unless specifically needed, as it is massive.",
|
|
267
280
|
].join("\n");
|
|
268
|
-
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: "synthesize-report",
|
|
285
|
+
description: "Read the current audit report resource and summarize the highest-signal findings.",
|
|
286
|
+
arguments: [],
|
|
287
|
+
render() {
|
|
269
288
|
return [
|
|
270
289
|
"Read `audit-code://report/current`.",
|
|
271
290
|
"Summarize the final audit report as work blocks first, then highlight the most important risks and remediation priorities.",
|
|
272
291
|
].join("\n");
|
|
273
|
-
|
|
274
|
-
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
];
|
|
295
|
+
function promptDefinitions() {
|
|
296
|
+
return promptRegistry.map((entry) => ({
|
|
297
|
+
name: entry.name,
|
|
298
|
+
description: entry.description,
|
|
299
|
+
arguments: entry.arguments,
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
function renderPrompt(name, args) {
|
|
303
|
+
const entry = promptRegistry.find((p) => p.name === name);
|
|
304
|
+
if (!entry) {
|
|
305
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
275
306
|
}
|
|
307
|
+
return entry.render(args);
|
|
276
308
|
}
|
|
277
309
|
async function runContinueAudit(context, extraArgs = []) {
|
|
278
310
|
const step = await parseCliJson(extraArgs, context);
|
|
@@ -542,6 +574,153 @@ function toolDefinitions() {
|
|
|
542
574
|
},
|
|
543
575
|
];
|
|
544
576
|
}
|
|
577
|
+
/**
|
|
578
|
+
* Extract zero or more complete Content-Length framed messages from a buffer.
|
|
579
|
+
* Returns an array of parsed body strings and the remaining unconsumed buffer.
|
|
580
|
+
* On framing errors, emits a framing error response via `emit` and resets the buffer.
|
|
581
|
+
*/
|
|
582
|
+
export function extractFrames(buffer, emit) {
|
|
583
|
+
const bodies = [];
|
|
584
|
+
let current = buffer;
|
|
585
|
+
while (true) {
|
|
586
|
+
const separator = current.indexOf("\r\n\r\n");
|
|
587
|
+
if (separator < 0) {
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
let contentLength;
|
|
591
|
+
try {
|
|
592
|
+
const headerBlock = current.slice(0, separator).toString("utf8");
|
|
593
|
+
contentLength = parseContentLength(headerBlock);
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
current = Buffer.alloc(0);
|
|
597
|
+
emit(failure(null, -32700, `Invalid MCP framing: ${error instanceof Error ? error.message : String(error)}.`));
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
const frameLength = separator + 4 + contentLength;
|
|
601
|
+
if (current.length < frameLength) {
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
bodies.push(current.slice(separator + 4, frameLength).toString("utf8"));
|
|
605
|
+
current = current.slice(frameLength);
|
|
606
|
+
}
|
|
607
|
+
return { bodies, remaining: current };
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Dispatch a single JSON-RPC request and return the response(s) to send,
|
|
611
|
+
* plus updated shutdown state.
|
|
612
|
+
*/
|
|
613
|
+
export async function dispatchRequest(request, ctx) {
|
|
614
|
+
const responses = [];
|
|
615
|
+
let { shutdownRequested } = ctx;
|
|
616
|
+
if (!request.method) {
|
|
617
|
+
responses.push(failure(request.id ?? null, -32600, "Missing method."));
|
|
618
|
+
return { responses, shutdownRequested };
|
|
619
|
+
}
|
|
620
|
+
try {
|
|
621
|
+
switch (request.method) {
|
|
622
|
+
case "initialize": {
|
|
623
|
+
const requestedVersion = typeof request.params?.protocolVersion === "string"
|
|
624
|
+
? request.params.protocolVersion
|
|
625
|
+
: PROTOCOL_VERSION;
|
|
626
|
+
const negotiatedVersion = requestedVersion <= PROTOCOL_VERSION
|
|
627
|
+
? requestedVersion
|
|
628
|
+
: PROTOCOL_VERSION;
|
|
629
|
+
responses.push(success(request.id ?? null, {
|
|
630
|
+
protocolVersion: negotiatedVersion,
|
|
631
|
+
serverInfo: {
|
|
632
|
+
name: "audit-code",
|
|
633
|
+
version: ctx.version,
|
|
634
|
+
},
|
|
635
|
+
instructions: "Use the audit-code MCP tools as the primary interface to the backend wrapper. Prefer start_audit, get_status, continue_audit, and the audit-code resources over ad hoc shell commands.",
|
|
636
|
+
capabilities: {
|
|
637
|
+
tools: { listChanged: false },
|
|
638
|
+
resources: { subscribe: false, listChanged: false },
|
|
639
|
+
prompts: { listChanged: false },
|
|
640
|
+
},
|
|
641
|
+
}));
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
case "notifications/initialized":
|
|
645
|
+
break;
|
|
646
|
+
case "ping":
|
|
647
|
+
if (request.id !== undefined) {
|
|
648
|
+
responses.push(success(request.id, {}));
|
|
649
|
+
}
|
|
650
|
+
break;
|
|
651
|
+
case "tools/list":
|
|
652
|
+
responses.push(success(request.id ?? null, { tools: toolDefinitions() }));
|
|
653
|
+
break;
|
|
654
|
+
case "tools/call": {
|
|
655
|
+
const params = parseObject(request.params);
|
|
656
|
+
const toolName = params.name;
|
|
657
|
+
if (!hasValue(toolName)) {
|
|
658
|
+
throw new Error("tools/call requires a tool name.");
|
|
659
|
+
}
|
|
660
|
+
responses.push(success(request.id ?? null, await handleToolCall(toolName, parseObject(params.arguments), ctx.defaults)));
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
case "resources/list":
|
|
664
|
+
responses.push(success(request.id ?? null, {
|
|
665
|
+
resources: resourceListPayload(),
|
|
666
|
+
}));
|
|
667
|
+
break;
|
|
668
|
+
case "resources/read": {
|
|
669
|
+
const params = parseObject(request.params);
|
|
670
|
+
if (!hasValue(params.uri)) {
|
|
671
|
+
throw new Error("resources/read requires uri.");
|
|
672
|
+
}
|
|
673
|
+
const resource = await readResource(params.uri, ctx.defaults);
|
|
674
|
+
responses.push(success(request.id ?? null, {
|
|
675
|
+
contents: [
|
|
676
|
+
{
|
|
677
|
+
uri: params.uri,
|
|
678
|
+
mimeType: resource.mimeType,
|
|
679
|
+
text: resource.text,
|
|
680
|
+
},
|
|
681
|
+
],
|
|
682
|
+
}));
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
case "prompts/list":
|
|
686
|
+
responses.push(success(request.id ?? null, {
|
|
687
|
+
prompts: promptDefinitions(),
|
|
688
|
+
}));
|
|
689
|
+
break;
|
|
690
|
+
case "prompts/get": {
|
|
691
|
+
const params = parseObject(request.params);
|
|
692
|
+
if (!hasValue(params.name)) {
|
|
693
|
+
throw new Error("prompts/get requires name.");
|
|
694
|
+
}
|
|
695
|
+
responses.push(success(request.id ?? null, {
|
|
696
|
+
description: promptDefinitions().find((prompt) => prompt.name === params.name)?.description,
|
|
697
|
+
messages: [
|
|
698
|
+
{
|
|
699
|
+
role: "user",
|
|
700
|
+
content: {
|
|
701
|
+
type: "text",
|
|
702
|
+
text: renderPrompt(params.name, parseObject(params.arguments)),
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
],
|
|
706
|
+
}));
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
case "shutdown":
|
|
710
|
+
shutdownRequested = true;
|
|
711
|
+
responses.push(success(request.id ?? null, {}));
|
|
712
|
+
break;
|
|
713
|
+
case "exit":
|
|
714
|
+
return { responses, shutdownRequested, exit: shutdownRequested ? 0 : 1 };
|
|
715
|
+
default:
|
|
716
|
+
responses.push(failure(request.id ?? null, -32601, `Unknown method: ${request.method}`));
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
catch (error) {
|
|
720
|
+
responses.push(failure(request.id ?? null, -32000, error instanceof Error ? error.message : String(error)));
|
|
721
|
+
}
|
|
722
|
+
return { responses, shutdownRequested };
|
|
723
|
+
}
|
|
545
724
|
export async function runAuditCodeMcpServer(argv) {
|
|
546
725
|
const defaults = parseServerOptions(argv);
|
|
547
726
|
const version = await packageVersion();
|
|
@@ -549,29 +728,9 @@ export async function runAuditCodeMcpServer(argv) {
|
|
|
549
728
|
let buffer = Buffer.alloc(0);
|
|
550
729
|
process.stdin.on("data", async (chunk) => {
|
|
551
730
|
buffer = Buffer.concat([buffer, chunk]);
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
let contentLength;
|
|
558
|
-
try {
|
|
559
|
-
const headerBlock = buffer.slice(0, separator).toString("utf8");
|
|
560
|
-
contentLength = parseContentLength(headerBlock);
|
|
561
|
-
}
|
|
562
|
-
catch (error) {
|
|
563
|
-
buffer = Buffer.alloc(0);
|
|
564
|
-
writeMessage(failure(null, -32700, `Invalid MCP framing: ${error instanceof Error ? error.message : String(error)}.`));
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
const frameLength = separator + 4 + contentLength;
|
|
568
|
-
if (buffer.length < frameLength) {
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
const body = buffer
|
|
572
|
-
.slice(separator + 4, frameLength)
|
|
573
|
-
.toString("utf8");
|
|
574
|
-
buffer = buffer.slice(frameLength);
|
|
731
|
+
const { bodies, remaining } = extractFrames(buffer, writeMessage);
|
|
732
|
+
buffer = remaining;
|
|
733
|
+
for (const body of bodies) {
|
|
575
734
|
let request;
|
|
576
735
|
try {
|
|
577
736
|
request = JSON.parse(body);
|
|
@@ -580,137 +739,17 @@ export async function runAuditCodeMcpServer(argv) {
|
|
|
580
739
|
writeMessage(failure(null, -32700, "Invalid JSON-RPC payload.", error instanceof Error ? error.message : String(error)));
|
|
581
740
|
continue;
|
|
582
741
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
? request.params.protocolVersion
|
|
592
|
-
: PROTOCOL_VERSION;
|
|
593
|
-
const negotiatedVersion = requestedVersion <= PROTOCOL_VERSION
|
|
594
|
-
? requestedVersion
|
|
595
|
-
: PROTOCOL_VERSION;
|
|
596
|
-
writeMessage(success(request.id ?? null, {
|
|
597
|
-
protocolVersion: negotiatedVersion,
|
|
598
|
-
serverInfo: {
|
|
599
|
-
name: "audit-code",
|
|
600
|
-
version,
|
|
601
|
-
},
|
|
602
|
-
instructions: "Use the audit-code MCP tools as the primary interface to the backend wrapper. Prefer start_audit, get_status, continue_audit, and the audit-code resources over ad hoc shell commands.",
|
|
603
|
-
capabilities: {
|
|
604
|
-
tools: { listChanged: false },
|
|
605
|
-
resources: { subscribe: false, listChanged: false },
|
|
606
|
-
prompts: { listChanged: false },
|
|
607
|
-
},
|
|
608
|
-
}));
|
|
609
|
-
break;
|
|
610
|
-
}
|
|
611
|
-
case "notifications/initialized":
|
|
612
|
-
break;
|
|
613
|
-
case "ping":
|
|
614
|
-
if (request.id !== undefined) {
|
|
615
|
-
writeMessage(success(request.id, {}));
|
|
616
|
-
}
|
|
617
|
-
break;
|
|
618
|
-
case "tools/list":
|
|
619
|
-
writeMessage(success(request.id ?? null, { tools: toolDefinitions() }));
|
|
620
|
-
break;
|
|
621
|
-
case "tools/call": {
|
|
622
|
-
const params = parseObject(request.params);
|
|
623
|
-
const toolName = params.name;
|
|
624
|
-
if (!hasValue(toolName)) {
|
|
625
|
-
throw new Error("tools/call requires a tool name.");
|
|
626
|
-
}
|
|
627
|
-
writeMessage(success(request.id ?? null, await handleToolCall(toolName, parseObject(params.arguments), defaults)));
|
|
628
|
-
break;
|
|
629
|
-
}
|
|
630
|
-
case "resources/list":
|
|
631
|
-
writeMessage(success(request.id ?? null, {
|
|
632
|
-
resources: [
|
|
633
|
-
{
|
|
634
|
-
uri: "audit-code://artifacts/current",
|
|
635
|
-
name: "current_artifacts",
|
|
636
|
-
description: "Current artifact bundle as JSON.",
|
|
637
|
-
mimeType: "application/json",
|
|
638
|
-
},
|
|
639
|
-
{
|
|
640
|
-
uri: "audit-code://handoff/current",
|
|
641
|
-
name: "operator_handoff",
|
|
642
|
-
description: "Current operator handoff payload as JSON.",
|
|
643
|
-
mimeType: "application/json",
|
|
644
|
-
},
|
|
645
|
-
{
|
|
646
|
-
uri: "audit-code://install/guide",
|
|
647
|
-
name: "install_guide",
|
|
648
|
-
description: "Repo-local install guide for supported IDE hosts.",
|
|
649
|
-
mimeType: "text/markdown",
|
|
650
|
-
},
|
|
651
|
-
{
|
|
652
|
-
uri: "audit-code://report/current",
|
|
653
|
-
name: "audit_report",
|
|
654
|
-
description: "Current deterministic audit report if available.",
|
|
655
|
-
mimeType: "text/markdown",
|
|
656
|
-
},
|
|
657
|
-
],
|
|
658
|
-
}));
|
|
659
|
-
break;
|
|
660
|
-
case "resources/read": {
|
|
661
|
-
const params = parseObject(request.params);
|
|
662
|
-
if (!hasValue(params.uri)) {
|
|
663
|
-
throw new Error("resources/read requires uri.");
|
|
664
|
-
}
|
|
665
|
-
const resource = await readResource(params.uri, defaults);
|
|
666
|
-
writeMessage(success(request.id ?? null, {
|
|
667
|
-
contents: [
|
|
668
|
-
{
|
|
669
|
-
uri: params.uri,
|
|
670
|
-
mimeType: resource.mimeType,
|
|
671
|
-
text: resource.text,
|
|
672
|
-
},
|
|
673
|
-
],
|
|
674
|
-
}));
|
|
675
|
-
break;
|
|
676
|
-
}
|
|
677
|
-
case "prompts/list":
|
|
678
|
-
writeMessage(success(request.id ?? null, {
|
|
679
|
-
prompts: promptDefinitions(),
|
|
680
|
-
}));
|
|
681
|
-
break;
|
|
682
|
-
case "prompts/get": {
|
|
683
|
-
const params = parseObject(request.params);
|
|
684
|
-
if (!hasValue(params.name)) {
|
|
685
|
-
throw new Error("prompts/get requires name.");
|
|
686
|
-
}
|
|
687
|
-
writeMessage(success(request.id ?? null, {
|
|
688
|
-
description: promptDefinitions().find((prompt) => prompt.name === params.name)?.description,
|
|
689
|
-
messages: [
|
|
690
|
-
{
|
|
691
|
-
role: "user",
|
|
692
|
-
content: {
|
|
693
|
-
type: "text",
|
|
694
|
-
text: renderPrompt(params.name, parseObject(params.arguments)),
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
],
|
|
698
|
-
}));
|
|
699
|
-
break;
|
|
700
|
-
}
|
|
701
|
-
case "shutdown":
|
|
702
|
-
shutdownRequested = true;
|
|
703
|
-
writeMessage(success(request.id ?? null, {}));
|
|
704
|
-
break;
|
|
705
|
-
case "exit":
|
|
706
|
-
process.exit(shutdownRequested ? 0 : 1);
|
|
707
|
-
break;
|
|
708
|
-
default:
|
|
709
|
-
writeMessage(failure(request.id ?? null, -32601, `Unknown method: ${request.method}`));
|
|
710
|
-
}
|
|
742
|
+
const result = await dispatchRequest(request, {
|
|
743
|
+
version,
|
|
744
|
+
defaults,
|
|
745
|
+
shutdownRequested,
|
|
746
|
+
});
|
|
747
|
+
shutdownRequested = result.shutdownRequested;
|
|
748
|
+
for (const response of result.responses) {
|
|
749
|
+
writeMessage(response);
|
|
711
750
|
}
|
|
712
|
-
|
|
713
|
-
|
|
751
|
+
if (result.exit !== undefined) {
|
|
752
|
+
process.exit(result.exit);
|
|
714
753
|
}
|
|
715
754
|
}
|
|
716
755
|
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function stableStringify(value: unknown): string;
|
|
2
|
+
export declare function normalizeForMetadataHash(artifactName: string, value: unknown): unknown;
|
|
3
|
+
export declare function hashArtifactValue(artifactName: string, value: unknown): string;
|
|
4
|
+
export declare function buildReverseDependencyMap(): Record<string, string[]>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { ARTIFACT_DEPENDENCY_MAP } from "./dependencyMap.js";
|
|
3
|
+
export function stableStringify(value) {
|
|
4
|
+
if (value === undefined) {
|
|
5
|
+
return "null";
|
|
6
|
+
}
|
|
7
|
+
if (value === null || typeof value !== "object") {
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
return `[${value.map((item) => stableStringify(item ?? null)).join(",")}]`;
|
|
12
|
+
}
|
|
13
|
+
const entries = Object.entries(value)
|
|
14
|
+
.filter(([, item]) => item !== undefined)
|
|
15
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
16
|
+
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
|
|
17
|
+
}
|
|
18
|
+
export function normalizeForMetadataHash(artifactName, value) {
|
|
19
|
+
if ((artifactName === "repo_manifest.json" ||
|
|
20
|
+
artifactName === "tooling_manifest.json") &&
|
|
21
|
+
value &&
|
|
22
|
+
typeof value === "object" &&
|
|
23
|
+
!Array.isArray(value)) {
|
|
24
|
+
const record = value;
|
|
25
|
+
const { generated_at: _generatedAt, ...rest } = record;
|
|
26
|
+
return rest;
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
export function hashArtifactValue(artifactName, value) {
|
|
31
|
+
return createHash("sha256")
|
|
32
|
+
.update(stableStringify(normalizeForMetadataHash(artifactName, value)))
|
|
33
|
+
.digest("hex");
|
|
34
|
+
}
|
|
35
|
+
export function buildReverseDependencyMap() {
|
|
36
|
+
const reverse = {};
|
|
37
|
+
for (const [upstream, downstreamList] of Object.entries(ARTIFACT_DEPENDENCY_MAP)) {
|
|
38
|
+
reverse[upstream] ??= [];
|
|
39
|
+
for (const downstream of downstreamList) {
|
|
40
|
+
reverse[downstream] ??= [];
|
|
41
|
+
reverse[downstream].push(upstream);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return reverse;
|
|
45
|
+
}
|
|
@@ -1,54 +1,5 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { ARTIFACT_DEPENDENCY_MAP } from "./dependencyMap.js";
|
|
3
1
|
import { getArtifactValue } from "../io/artifacts.js";
|
|
4
|
-
|
|
5
|
-
if (value === undefined) {
|
|
6
|
-
return "null";
|
|
7
|
-
}
|
|
8
|
-
if (value === null || typeof value !== "object") {
|
|
9
|
-
return JSON.stringify(value);
|
|
10
|
-
}
|
|
11
|
-
if (Array.isArray(value)) {
|
|
12
|
-
return `[${value.map((item) => stableStringify(item ?? null)).join(",")}]`;
|
|
13
|
-
}
|
|
14
|
-
const entries = Object.entries(value)
|
|
15
|
-
.filter(([, item]) => item !== undefined)
|
|
16
|
-
.sort(([a], [b]) => a.localeCompare(b));
|
|
17
|
-
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
|
|
18
|
-
}
|
|
19
|
-
function hashValue(value) {
|
|
20
|
-
return createHash("sha256").update(stableStringify(value)).digest("hex");
|
|
21
|
-
}
|
|
22
|
-
function normalizeForMetadataHash(artifactName, value) {
|
|
23
|
-
if (artifactName === "repo_manifest.json" &&
|
|
24
|
-
value &&
|
|
25
|
-
typeof value === "object" &&
|
|
26
|
-
!Array.isArray(value)) {
|
|
27
|
-
const record = value;
|
|
28
|
-
const { generated_at: _generatedAt, ...rest } = record;
|
|
29
|
-
return rest;
|
|
30
|
-
}
|
|
31
|
-
if (artifactName === "tooling_manifest.json" &&
|
|
32
|
-
value &&
|
|
33
|
-
typeof value === "object" &&
|
|
34
|
-
!Array.isArray(value)) {
|
|
35
|
-
const record = value;
|
|
36
|
-
const { generated_at: _generatedAt, ...rest } = record;
|
|
37
|
-
return rest;
|
|
38
|
-
}
|
|
39
|
-
return value;
|
|
40
|
-
}
|
|
41
|
-
function buildReverseDependencyMap() {
|
|
42
|
-
const reverse = {};
|
|
43
|
-
for (const [upstream, downstreamList] of Object.entries(ARTIFACT_DEPENDENCY_MAP)) {
|
|
44
|
-
reverse[upstream] ??= [];
|
|
45
|
-
for (const downstream of downstreamList) {
|
|
46
|
-
reverse[downstream] ??= [];
|
|
47
|
-
reverse[downstream].push(upstream);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return reverse;
|
|
51
|
-
}
|
|
2
|
+
import { buildReverseDependencyMap, hashArtifactValue, stableStringify, } from "./artifactFreshness.js";
|
|
52
3
|
const REVERSE_DEPENDENCY_MAP = buildReverseDependencyMap();
|
|
53
4
|
function computeDependencyFirstOrder(artifactNames) {
|
|
54
5
|
const target = new Set(artifactNames);
|
|
@@ -97,7 +48,7 @@ export function computeArtifactMetadata(bundle, previous, updatedArtifacts = [])
|
|
|
97
48
|
artifacts[artifactName] = previousEntry;
|
|
98
49
|
continue;
|
|
99
50
|
}
|
|
100
|
-
const contentHash =
|
|
51
|
+
const contentHash = hashArtifactValue(artifactName, value);
|
|
101
52
|
const dependencyRevisions = Object.fromEntries((REVERSE_DEPENDENCY_MAP[artifactName] ?? [])
|
|
102
53
|
.filter((dependencyName) => dependencyName !== "artifact_metadata.json")
|
|
103
54
|
.sort()
|
|
@@ -62,6 +62,20 @@ export const ARTIFACT_DEPENDENCY_MAP = {
|
|
|
62
62
|
"runtime_validation_report.json",
|
|
63
63
|
"audit-report.md",
|
|
64
64
|
],
|
|
65
|
+
"external_analyzer_results.json": [
|
|
66
|
+
"coverage_matrix.json",
|
|
67
|
+
"flow_coverage.json",
|
|
68
|
+
"audit_tasks.json",
|
|
69
|
+
"audit_plan_metrics.json",
|
|
70
|
+
"review_packets.json",
|
|
71
|
+
"requeue_tasks.json",
|
|
72
|
+
"runtime_validation_tasks.json",
|
|
73
|
+
"runtime_validation_report.json",
|
|
74
|
+
"audit-report.md",
|
|
75
|
+
],
|
|
76
|
+
"syntax_resolution_status.json": [
|
|
77
|
+
"audit-report.md",
|
|
78
|
+
],
|
|
65
79
|
"audit_results.jsonl": [
|
|
66
80
|
"coverage_matrix.json",
|
|
67
81
|
"flow_coverage.json",
|
|
@@ -426,6 +426,14 @@ export function runExternalAnalyzerImportExecutor(bundle, externalResults) {
|
|
|
426
426
|
updated: {
|
|
427
427
|
...bundle,
|
|
428
428
|
external_analyzer_results: externalResults,
|
|
429
|
+
coverage_matrix: undefined,
|
|
430
|
+
flow_coverage: undefined,
|
|
431
|
+
runtime_validation_tasks: undefined,
|
|
432
|
+
runtime_validation_report: undefined,
|
|
433
|
+
audit_tasks: undefined,
|
|
434
|
+
audit_plan_metrics: undefined,
|
|
435
|
+
review_packets: undefined,
|
|
436
|
+
requeue_tasks: undefined,
|
|
429
437
|
audit_report: undefined,
|
|
430
438
|
},
|
|
431
439
|
artifacts_written: ["external_analyzer_results.json"],
|