@neurcode-ai/cli 0.9.66 → 0.10.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/commands/governance.d.ts +3 -0
- package/dist/commands/governance.d.ts.map +1 -0
- package/dist/commands/governance.js +390 -0
- package/dist/commands/governance.js.map +1 -0
- package/dist/commands/quickstart.js +3 -3
- package/dist/commands/quickstart.js.map +1 -1
- package/dist/commands/remediate-export.d.ts +5 -0
- package/dist/commands/remediate-export.d.ts.map +1 -1
- package/dist/commands/remediate-export.js +803 -14
- package/dist/commands/remediate-export.js.map +1 -1
- package/dist/commands/replay.d.ts.map +1 -1
- package/dist/commands/replay.js +14 -0
- package/dist/commands/replay.js.map +1 -1
- package/dist/commands/session.d.ts +7 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +156 -0
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/start-intent.d.ts.map +1 -1
- package/dist/commands/start-intent.js +61 -11
- package/dist/commands/start-intent.js.map +1 -1
- package/dist/commands/verify-guidance.d.ts +5 -0
- package/dist/commands/verify-guidance.d.ts.map +1 -0
- package/dist/commands/verify-guidance.js +49 -0
- package/dist/commands/verify-guidance.js.map +1 -0
- package/dist/commands/verify-output.d.ts +37 -0
- package/dist/commands/verify-output.d.ts.map +1 -0
- package/dist/commands/verify-output.js +572 -0
- package/dist/commands/verify-output.js.map +1 -0
- package/dist/commands/verify-render.d.ts +41 -0
- package/dist/commands/verify-render.d.ts.map +1 -0
- package/dist/commands/verify-render.js +457 -0
- package/dist/commands/verify-render.js.map +1 -0
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +278 -1081
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/workspace.d.ts.map +1 -1
- package/dist/commands/workspace.js +3 -14
- package/dist/commands/workspace.js.map +1 -1
- package/dist/context-engine/graph.d.ts.map +1 -1
- package/dist/context-engine/graph.js +69 -7
- package/dist/context-engine/graph.js.map +1 -1
- package/dist/context-engine/scanner.d.ts.map +1 -1
- package/dist/context-engine/scanner.js +9 -2
- package/dist/context-engine/scanner.js.map +1 -1
- package/dist/daemon/compatibility/execution.d.ts +42 -0
- package/dist/daemon/compatibility/execution.d.ts.map +1 -0
- package/dist/daemon/compatibility/execution.js +183 -0
- package/dist/daemon/compatibility/execution.js.map +1 -0
- package/dist/daemon/compatibility/mutation.d.ts +24 -0
- package/dist/daemon/compatibility/mutation.d.ts.map +1 -0
- package/dist/daemon/compatibility/mutation.js +724 -0
- package/dist/daemon/compatibility/mutation.js.map +1 -0
- package/dist/daemon/routes.d.ts +19 -0
- package/dist/daemon/routes.d.ts.map +1 -0
- package/dist/daemon/routes.js +123 -0
- package/dist/daemon/routes.js.map +1 -0
- package/dist/daemon/runtime/execution-bus.d.ts +217 -0
- package/dist/daemon/runtime/execution-bus.d.ts.map +1 -0
- package/dist/daemon/runtime/execution-bus.js +1420 -0
- package/dist/daemon/runtime/execution-bus.js.map +1 -0
- package/dist/daemon/runtime/workspace-runtime.d.ts +280 -0
- package/dist/daemon/runtime/workspace-runtime.d.ts.map +1 -0
- package/dist/daemon/runtime/workspace-runtime.js +1473 -0
- package/dist/daemon/runtime/workspace-runtime.js.map +1 -0
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +171 -874
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/shaping.d.ts +11 -0
- package/dist/daemon/shaping.d.ts.map +1 -0
- package/dist/daemon/shaping.js +240 -0
- package/dist/daemon/shaping.js.map +1 -0
- package/dist/governance/canonical-pipeline.d.ts +2 -1
- package/dist/governance/canonical-pipeline.d.ts.map +1 -1
- package/dist/governance/canonical-pipeline.js +259 -84
- package/dist/governance/canonical-pipeline.js.map +1 -1
- package/dist/governance/structural-cache.d.ts.map +1 -1
- package/dist/governance/structural-cache.js +2 -7
- package/dist/governance/structural-cache.js.map +1 -1
- package/dist/index.js +230 -66
- package/dist/index.js.map +1 -1
- package/dist/utils/active-engineering-context.d.ts +12 -0
- package/dist/utils/active-engineering-context.d.ts.map +1 -0
- package/dist/utils/active-engineering-context.js +67 -0
- package/dist/utils/active-engineering-context.js.map +1 -0
- package/dist/utils/artifact-io.d.ts +33 -0
- package/dist/utils/artifact-io.d.ts.map +1 -0
- package/dist/utils/artifact-io.js +183 -0
- package/dist/utils/artifact-io.js.map +1 -0
- package/dist/utils/change-contract.d.ts +6 -2
- package/dist/utils/change-contract.d.ts.map +1 -1
- package/dist/utils/change-contract.js +175 -0
- package/dist/utils/change-contract.js.map +1 -1
- package/dist/utils/context-pack.d.ts +12 -0
- package/dist/utils/context-pack.d.ts.map +1 -0
- package/dist/utils/context-pack.js +147 -0
- package/dist/utils/context-pack.js.map +1 -0
- package/dist/utils/control-plane.d.ts +18 -0
- package/dist/utils/control-plane.d.ts.map +1 -1
- package/dist/utils/control-plane.js +31 -4
- package/dist/utils/control-plane.js.map +1 -1
- package/dist/utils/drift-intelligence.d.ts +47 -0
- package/dist/utils/drift-intelligence.d.ts.map +1 -0
- package/dist/utils/drift-intelligence.js +2099 -0
- package/dist/utils/drift-intelligence.js.map +1 -0
- package/dist/utils/execution-actions.d.ts +22 -0
- package/dist/utils/execution-actions.d.ts.map +1 -0
- package/dist/utils/execution-actions.js +103 -0
- package/dist/utils/execution-actions.js.map +1 -0
- package/dist/utils/execution-bus.d.ts +1 -214
- package/dist/utils/execution-bus.d.ts.map +1 -1
- package/dist/utils/execution-bus.js +15 -1359
- package/dist/utils/execution-bus.js.map +1 -1
- package/dist/utils/git.d.ts +1 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +13 -3
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/governance-decisions.d.ts +75 -0
- package/dist/utils/governance-decisions.d.ts.map +1 -0
- package/dist/utils/governance-decisions.js +412 -0
- package/dist/utils/governance-decisions.js.map +1 -0
- package/dist/utils/governance-provenance.d.ts +1 -1
- package/dist/utils/governance-provenance.d.ts.map +1 -1
- package/dist/utils/governance-provenance.js +5 -7
- package/dist/utils/governance-provenance.js.map +1 -1
- package/dist/utils/governance.d.ts +108 -0
- package/dist/utils/governance.d.ts.map +1 -1
- package/dist/utils/governance.js +209 -7
- package/dist/utils/governance.js.map +1 -1
- package/dist/utils/intelligence-runtime-common.d.ts +30 -0
- package/dist/utils/intelligence-runtime-common.d.ts.map +1 -0
- package/dist/utils/intelligence-runtime-common.js +156 -0
- package/dist/utils/intelligence-runtime-common.js.map +1 -0
- package/dist/utils/intent-contract-diagnostics.d.ts +9 -0
- package/dist/utils/intent-contract-diagnostics.d.ts.map +1 -0
- package/dist/utils/intent-contract-diagnostics.js +322 -0
- package/dist/utils/intent-contract-diagnostics.js.map +1 -0
- package/dist/utils/intent-pack.d.ts +15 -0
- package/dist/utils/intent-pack.d.ts.map +1 -0
- package/dist/utils/intent-pack.js +196 -0
- package/dist/utils/intent-pack.js.map +1 -0
- package/dist/utils/plan-sync.d.ts +1 -0
- package/dist/utils/plan-sync.d.ts.map +1 -1
- package/dist/utils/plan-sync.js +23 -0
- package/dist/utils/plan-sync.js.map +1 -1
- package/dist/utils/policy-decision.d.ts +5 -0
- package/dist/utils/policy-decision.d.ts.map +1 -0
- package/dist/utils/policy-decision.js +17 -0
- package/dist/utils/policy-decision.js.map +1 -0
- package/dist/utils/replay-custody.d.ts +43 -0
- package/dist/utils/replay-custody.d.ts.map +1 -0
- package/dist/utils/replay-custody.js +168 -0
- package/dist/utils/replay-custody.js.map +1 -0
- package/dist/utils/replay-runtime.d.ts +13 -0
- package/dist/utils/replay-runtime.d.ts.map +1 -1
- package/dist/utils/replay-runtime.js +96 -9
- package/dist/utils/replay-runtime.js.map +1 -1
- package/dist/utils/repository-intelligence.d.ts +9 -0
- package/dist/utils/repository-intelligence.d.ts.map +1 -0
- package/dist/utils/repository-intelligence.js +372 -0
- package/dist/utils/repository-intelligence.js.map +1 -0
- package/dist/utils/runtime-events.d.ts.map +1 -1
- package/dist/utils/runtime-events.js +25 -6
- package/dist/utils/runtime-events.js.map +1 -1
- package/dist/utils/semantic-contract-intelligence.d.ts +20 -0
- package/dist/utils/semantic-contract-intelligence.d.ts.map +1 -0
- package/dist/utils/semantic-contract-intelligence.js +825 -0
- package/dist/utils/semantic-contract-intelligence.js.map +1 -0
- package/dist/utils/session-continuity.d.ts +56 -0
- package/dist/utils/session-continuity.d.ts.map +1 -0
- package/dist/utils/session-continuity.js +318 -0
- package/dist/utils/session-continuity.js.map +1 -0
- package/dist/utils/verification-evidence.d.ts.map +1 -1
- package/dist/utils/verification-evidence.js +4 -1
- package/dist/utils/verification-evidence.js.map +1 -1
- package/dist/utils/workspace-runtime.d.ts +1 -266
- package/dist/utils/workspace-runtime.d.ts.map +1 -1
- package/dist/utils/workspace-runtime.js +15 -1412
- package/dist/utils/workspace-runtime.js.map +1 -1
- package/package.json +11 -10
- package/LICENSE +0 -201
package/dist/daemon/server.js
CHANGED
|
@@ -39,14 +39,15 @@ exports.startDaemon = startDaemon;
|
|
|
39
39
|
const http = __importStar(require("node:http"));
|
|
40
40
|
const path = __importStar(require("node:path"));
|
|
41
41
|
const fs = __importStar(require("node:fs"));
|
|
42
|
-
const node_crypto_1 = require("node:crypto");
|
|
43
|
-
const node_child_process_1 = require("node:child_process");
|
|
44
|
-
const patch_engine_1 = require("../patch-engine");
|
|
45
|
-
const diff_1 = require("../patch-engine/diff");
|
|
46
42
|
const execution_bus_1 = require("../utils/execution-bus");
|
|
43
|
+
const execution_actions_1 = require("../utils/execution-actions");
|
|
47
44
|
const runtime_events_1 = require("../utils/runtime-events");
|
|
48
45
|
const control_plane_1 = require("../utils/control-plane");
|
|
49
46
|
const workspace_runtime_1 = require("../utils/workspace-runtime");
|
|
47
|
+
const routes_1 = require("./routes");
|
|
48
|
+
const shaping_1 = require("./shaping");
|
|
49
|
+
const execution_1 = require("./compatibility/execution");
|
|
50
|
+
const mutation_1 = require("./compatibility/mutation");
|
|
50
51
|
const workspace_1 = require("../workspace");
|
|
51
52
|
const semantic_1 = require("../semantic");
|
|
52
53
|
const intent_engine_1 = require("../intent-engine");
|
|
@@ -73,6 +74,16 @@ const daemonOpsMetrics = {
|
|
|
73
74
|
requestsTotal: 0,
|
|
74
75
|
requestsByMethod: {},
|
|
75
76
|
requestsByRoute: {},
|
|
77
|
+
requestsBySubsystem: {
|
|
78
|
+
'canonical-governance': 0,
|
|
79
|
+
'compatibility-mutation': 0,
|
|
80
|
+
'runtime-execution': 0,
|
|
81
|
+
'workspace-orchestration': 0,
|
|
82
|
+
'replay-evidence': 0,
|
|
83
|
+
'operational-status': 0,
|
|
84
|
+
'docs-transport': 0,
|
|
85
|
+
unknown: 0,
|
|
86
|
+
},
|
|
76
87
|
failuresTotal: 0,
|
|
77
88
|
retriableFailuresTotal: 0,
|
|
78
89
|
stalePreviewRejections: 0,
|
|
@@ -84,18 +95,15 @@ const daemonOpsMetrics = {
|
|
|
84
95
|
rollbackRejected: 0,
|
|
85
96
|
recentErrors: [],
|
|
86
97
|
};
|
|
87
|
-
function normalizeRoutePath(url) {
|
|
88
|
-
const pathOnly = url.split('?')[0]?.trim() || '/';
|
|
89
|
-
return pathOnly.startsWith('/') ? pathOnly : `/${pathOnly}`;
|
|
90
|
-
}
|
|
91
98
|
function incrementMetricCounter(record, key) {
|
|
92
99
|
record[key] = (record[key] || 0) + 1;
|
|
93
100
|
}
|
|
94
101
|
function recordDaemonRequest(url, method) {
|
|
95
102
|
daemonOpsMetrics.requestsTotal += 1;
|
|
96
103
|
incrementMetricCounter(daemonOpsMetrics.requestsByMethod, method.toUpperCase());
|
|
97
|
-
const route = normalizeRoutePath(url);
|
|
104
|
+
const route = (0, routes_1.normalizeRoutePath)(url);
|
|
98
105
|
incrementMetricCounter(daemonOpsMetrics.requestsByRoute, route);
|
|
106
|
+
incrementMetricCounter(daemonOpsMetrics.requestsBySubsystem, (0, routes_1.classifyDaemonRoute)(method, route));
|
|
99
107
|
// Keep route cardinality bounded for long-lived daemon sessions.
|
|
100
108
|
const routeKeys = Object.keys(daemonOpsMetrics.requestsByRoute);
|
|
101
109
|
if (routeKeys.length > DAEMON_MAX_ROUTE_SAMPLE) {
|
|
@@ -191,6 +199,7 @@ function buildDaemonOperationalSummary(cwd) {
|
|
|
191
199
|
failures: daemonOpsMetrics.failuresTotal,
|
|
192
200
|
retriableFailures: daemonOpsMetrics.retriableFailuresTotal,
|
|
193
201
|
byMethod: daemonOpsMetrics.requestsByMethod,
|
|
202
|
+
bySubsystem: daemonOpsMetrics.requestsBySubsystem,
|
|
194
203
|
topRoutes: Object.entries(daemonOpsMetrics.requestsByRoute)
|
|
195
204
|
.sort((left, right) => right[1] - left[1])
|
|
196
205
|
.slice(0, 12)
|
|
@@ -313,408 +322,6 @@ function addCorsHeaders(res, req) {
|
|
|
313
322
|
res.setHeader('Access-Control-Allow-Headers', [...allowedHeaders].join(', '));
|
|
314
323
|
res.setHeader('Access-Control-Max-Age', '86400');
|
|
315
324
|
}
|
|
316
|
-
function resolveGitRoot(cwd) {
|
|
317
|
-
const result = (0, node_child_process_1.spawnSync)('git', ['-C', cwd, 'rev-parse', '--show-toplevel'], {
|
|
318
|
-
encoding: 'utf-8',
|
|
319
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
320
|
-
});
|
|
321
|
-
if (result.status !== 0)
|
|
322
|
-
return null;
|
|
323
|
-
const value = typeof result.stdout === 'string' ? result.stdout.trim() : '';
|
|
324
|
-
return value.length > 0 ? value : null;
|
|
325
|
-
}
|
|
326
|
-
function captureGitDirtyPaths(cwd) {
|
|
327
|
-
const gitRoot = resolveGitRoot(cwd);
|
|
328
|
-
if (!gitRoot)
|
|
329
|
-
return null;
|
|
330
|
-
const statusResult = (0, node_child_process_1.spawnSync)('git', ['-C', cwd, 'status', '--porcelain=1', '-z', '--untracked-files=all'], {
|
|
331
|
-
encoding: 'utf-8',
|
|
332
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
333
|
-
});
|
|
334
|
-
if (statusResult.status !== 0 || typeof statusResult.stdout !== 'string')
|
|
335
|
-
return null;
|
|
336
|
-
const tokens = statusResult.stdout.split('\0').filter((entry) => entry.length > 0);
|
|
337
|
-
const dirty = new Set();
|
|
338
|
-
for (let index = 0; index < tokens.length; index += 1) {
|
|
339
|
-
const token = tokens[index];
|
|
340
|
-
if (token.length < 4)
|
|
341
|
-
continue;
|
|
342
|
-
const status = token.slice(0, 2);
|
|
343
|
-
const filePath = token.slice(3).trim();
|
|
344
|
-
if (filePath.length > 0) {
|
|
345
|
-
dirty.add(path.resolve(gitRoot, filePath));
|
|
346
|
-
}
|
|
347
|
-
const renamedOrCopied = status.includes('R') || status.includes('C');
|
|
348
|
-
if (renamedOrCopied && index + 1 < tokens.length) {
|
|
349
|
-
index += 1;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
return dirty;
|
|
353
|
-
}
|
|
354
|
-
function hashFileForDiff(absPath) {
|
|
355
|
-
try {
|
|
356
|
-
const stat = fs.statSync(absPath);
|
|
357
|
-
if (!stat.isFile())
|
|
358
|
-
return '<non-file>';
|
|
359
|
-
const content = fs.readFileSync(absPath);
|
|
360
|
-
return (0, node_crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
361
|
-
}
|
|
362
|
-
catch {
|
|
363
|
-
return '<missing>';
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
function captureDirtyFileFingerprints(cwd) {
|
|
367
|
-
const dirtyPaths = captureGitDirtyPaths(cwd);
|
|
368
|
-
if (!dirtyPaths)
|
|
369
|
-
return null;
|
|
370
|
-
const map = new Map();
|
|
371
|
-
for (const dirtyPath of dirtyPaths) {
|
|
372
|
-
map.set(dirtyPath, hashFileForDiff(dirtyPath));
|
|
373
|
-
}
|
|
374
|
-
return map;
|
|
375
|
-
}
|
|
376
|
-
function isAllowedPatchSideEffect(absPath, targetAbsPath, cwd) {
|
|
377
|
-
if (absPath === targetAbsPath)
|
|
378
|
-
return true;
|
|
379
|
-
const rel = path.relative(cwd, absPath);
|
|
380
|
-
if (!rel || rel.startsWith('..'))
|
|
381
|
-
return false;
|
|
382
|
-
if (rel === 'neurcode.policy.compiled.json')
|
|
383
|
-
return true;
|
|
384
|
-
return rel === '.neurcode' || rel.startsWith(`.neurcode${path.sep}`);
|
|
385
|
-
}
|
|
386
|
-
function collectUnexpectedPatchSideEffects(before, after, targetAbsPath, cwd) {
|
|
387
|
-
if (!before || !after)
|
|
388
|
-
return [];
|
|
389
|
-
const added = [...after].filter((entry) => !before.has(entry));
|
|
390
|
-
const unexpected = added
|
|
391
|
-
.filter((entry) => !isAllowedPatchSideEffect(entry, targetAbsPath, cwd))
|
|
392
|
-
.map((entry) => path.relative(cwd, entry).replace(/\\/g, '/'))
|
|
393
|
-
.filter((entry) => entry.length > 0)
|
|
394
|
-
.sort();
|
|
395
|
-
return unexpected;
|
|
396
|
-
}
|
|
397
|
-
function collectUnexpectedPatchMutations(before, after, targetAbsPath, cwd) {
|
|
398
|
-
if (!before || !after)
|
|
399
|
-
return [];
|
|
400
|
-
const keys = new Set([...before.keys(), ...after.keys()]);
|
|
401
|
-
const unexpected = [];
|
|
402
|
-
for (const key of keys) {
|
|
403
|
-
const beforeHash = before.get(key) ?? '<missing>';
|
|
404
|
-
const afterHash = after.get(key) ?? '<missing>';
|
|
405
|
-
if (beforeHash === afterHash)
|
|
406
|
-
continue;
|
|
407
|
-
if (isAllowedPatchSideEffect(key, targetAbsPath, cwd))
|
|
408
|
-
continue;
|
|
409
|
-
const rel = path.relative(cwd, key).replace(/\\/g, '/');
|
|
410
|
-
if (rel.length > 0 && !rel.startsWith('..')) {
|
|
411
|
-
unexpected.push(rel);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return unexpected.sort();
|
|
415
|
-
}
|
|
416
|
-
function patternDescriptor(kind, confidence, manualReviewRequired) {
|
|
417
|
-
const labelByKind = {
|
|
418
|
-
missing_validation: 'API input validation guard',
|
|
419
|
-
missing_timeout_handling: 'Outbound request timeout guard',
|
|
420
|
-
unsafe_fetch_without_retries: 'Outbound request retry guard',
|
|
421
|
-
missing_idempotency_keys: 'Mutation idempotency-key guard',
|
|
422
|
-
unsafe_file_uploads: 'Upload MIME/size validation guard',
|
|
423
|
-
missing_auth_middleware: 'Route authentication middleware',
|
|
424
|
-
missing_rate_limiting: 'Route rate limiting middleware',
|
|
425
|
-
missing_token_expiry: 'JWT expiry enforcement',
|
|
426
|
-
unsafe_inner_html_usage: 'Unsafe DOM sink replacement',
|
|
427
|
-
unsafe_sensitive_logging: 'Sensitive log redaction',
|
|
428
|
-
db_in_ui: 'Service-layer boundary placeholder',
|
|
429
|
-
todo_fixme: 'TODO/FIXME debt marker removal',
|
|
430
|
-
};
|
|
431
|
-
const confidenceModel = confidence === 'high'
|
|
432
|
-
? 'high'
|
|
433
|
-
: confidence === 'medium'
|
|
434
|
-
? 'medium'
|
|
435
|
-
: 'low';
|
|
436
|
-
return {
|
|
437
|
-
kind,
|
|
438
|
-
label: labelByKind[kind] || kind,
|
|
439
|
-
deterministic: true,
|
|
440
|
-
confidenceModel,
|
|
441
|
-
advisoryOnly: confidenceModel === 'low',
|
|
442
|
-
manualReviewRequired,
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
function summarizeDiff(diff) {
|
|
446
|
-
let addedLines = 0;
|
|
447
|
-
let removedLines = 0;
|
|
448
|
-
for (const line of diff.split('\n')) {
|
|
449
|
-
if (line.startsWith('+++') || line.startsWith('---') || line.startsWith('@@'))
|
|
450
|
-
continue;
|
|
451
|
-
if (line.startsWith('+'))
|
|
452
|
-
addedLines += 1;
|
|
453
|
-
if (line.startsWith('-'))
|
|
454
|
-
removedLines += 1;
|
|
455
|
-
}
|
|
456
|
-
const changedLines = addedLines + removedLines;
|
|
457
|
-
return {
|
|
458
|
-
addedLines,
|
|
459
|
-
removedLines,
|
|
460
|
-
changedLines,
|
|
461
|
-
summary: `${changedLines} changed line(s): +${addedLines} / -${removedLines}`,
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
function extractRequestInputUsage(content) {
|
|
465
|
-
const accessMatch = content.match(/\b(req|request)\.(body|params|query)\b/);
|
|
466
|
-
if (!accessMatch)
|
|
467
|
-
return null;
|
|
468
|
-
const receiver = accessMatch[1];
|
|
469
|
-
const field = accessMatch[2];
|
|
470
|
-
const fieldRegex = new RegExp(`\\b${receiver}\\.${field}\\.([A-Za-z_$][\\w$]*)\\b`, 'g');
|
|
471
|
-
const fields = [];
|
|
472
|
-
const seen = new Set();
|
|
473
|
-
let match = fieldRegex.exec(content);
|
|
474
|
-
while (match) {
|
|
475
|
-
const fieldName = match[1];
|
|
476
|
-
if (!seen.has(fieldName)) {
|
|
477
|
-
seen.add(fieldName);
|
|
478
|
-
fields.push(fieldName);
|
|
479
|
-
}
|
|
480
|
-
match = fieldRegex.exec(content);
|
|
481
|
-
}
|
|
482
|
-
return { receiver, field, fields };
|
|
483
|
-
}
|
|
484
|
-
function buildPatchPreviewReasoning(patternKind, targetPath, beforeContent) {
|
|
485
|
-
if (!patternKind)
|
|
486
|
-
return null;
|
|
487
|
-
if (patternKind === 'missing_validation') {
|
|
488
|
-
const usage = extractRequestInputUsage(beforeContent);
|
|
489
|
-
if (!usage) {
|
|
490
|
-
return {
|
|
491
|
-
summary: 'Adds deterministic API input validation guard.',
|
|
492
|
-
why: `This file accesses request input without a validation boundary check.`,
|
|
493
|
-
risk: 'Malformed input can cause runtime errors or unsafe processing paths.',
|
|
494
|
-
expectedOutcome: 'Invalid requests fail fast and valid requests continue unchanged.',
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
const noun = usage.field === 'body' ? 'request body' : usage.field === 'params' ? 'route params' : 'query params';
|
|
498
|
-
const fieldSummary = usage.fields.length > 0 ? usage.fields.join(', ') : 'no explicit property access detected';
|
|
499
|
-
return {
|
|
500
|
-
summary: `Adds deterministic validation before reading ${usage.receiver}.${usage.field}.`,
|
|
501
|
-
why: `${targetPath} reads ${noun} fields (${fieldSummary}) before validation.`,
|
|
502
|
-
risk: `Without boundary validation, malformed ${noun} may propagate into handler logic.`,
|
|
503
|
-
expectedOutcome: `Invalid ${noun} returns HTTP 400 early; valid requests keep existing behavior.`,
|
|
504
|
-
fields: usage.fields,
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
if (patternKind === 'db_in_ui') {
|
|
508
|
-
return {
|
|
509
|
-
summary: 'Suggests moving direct DB access behind a service boundary.',
|
|
510
|
-
why: `${targetPath} appears to perform direct data access in a non-service layer.`,
|
|
511
|
-
risk: 'Layering violations increase coupling and make behavior harder to govern.',
|
|
512
|
-
expectedOutcome: 'Patch inserts a deterministic placeholder to redirect to service-layer logic.',
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
if (patternKind === 'missing_auth_middleware') {
|
|
516
|
-
return {
|
|
517
|
-
summary: 'Adds deterministic authentication middleware to the route definition.',
|
|
518
|
-
why: `${targetPath} appears to expose a request handler without an auth middleware guard.`,
|
|
519
|
-
risk: 'Unauthenticated routes can expose sensitive behavior to unauthorized clients.',
|
|
520
|
-
expectedOutcome: 'Route execution is gated by requireAuth before handler logic runs.',
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
if (patternKind === 'missing_rate_limiting') {
|
|
524
|
-
return {
|
|
525
|
-
summary: 'Adds deterministic rate-limit middleware to the route definition.',
|
|
526
|
-
why: `${targetPath} appears to expose a request handler without rate limiting controls.`,
|
|
527
|
-
risk: 'Unbounded request rates can increase abuse, cost, and availability risks.',
|
|
528
|
-
expectedOutcome: 'Route applies rateLimitGuard before handler execution.',
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
if (patternKind === 'missing_timeout_handling') {
|
|
532
|
-
return {
|
|
533
|
-
summary: 'Adds deterministic timeout guard to outbound fetch call.',
|
|
534
|
-
why: `${targetPath} issues a fetch request without timeout protection.`,
|
|
535
|
-
risk: 'Unbounded network calls can hang request execution and degrade reliability under upstream latency.',
|
|
536
|
-
expectedOutcome: 'Fetch call aborts after timeout and fails fast instead of hanging.',
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
if (patternKind === 'unsafe_fetch_without_retries') {
|
|
540
|
-
return {
|
|
541
|
-
summary: 'Wraps outbound fetch call in deterministic retry guard.',
|
|
542
|
-
why: `${targetPath} makes outbound network calls without transient failure retry handling.`,
|
|
543
|
-
risk: 'Single transient failures can become user-facing errors and increase instability.',
|
|
544
|
-
expectedOutcome: 'Transient upstream failures retry deterministically before failing.',
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
if (patternKind === 'missing_idempotency_keys') {
|
|
548
|
-
return {
|
|
549
|
-
summary: 'Adds deterministic idempotency-key guard for side-effecting requests.',
|
|
550
|
-
why: `${targetPath} appears to process payment/order-like mutations without idempotency key enforcement.`,
|
|
551
|
-
risk: 'Duplicate requests can cause repeated side effects (double charges/orders).',
|
|
552
|
-
expectedOutcome: 'Requests missing idempotency key fail early with explicit error.',
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
if (patternKind === 'unsafe_file_uploads') {
|
|
556
|
-
return {
|
|
557
|
-
summary: 'Adds deterministic MIME and size guards for uploaded files.',
|
|
558
|
-
why: `${targetPath} appears to process uploaded files without boundary checks.`,
|
|
559
|
-
risk: 'Unbounded or unsafe uploads increase security and stability risk.',
|
|
560
|
-
expectedOutcome: 'Invalid upload payloads are rejected before processing.',
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
if (patternKind === 'missing_token_expiry') {
|
|
564
|
-
return {
|
|
565
|
-
summary: 'Adds deterministic token expiry to JWT signing call.',
|
|
566
|
-
why: `${targetPath} signs JWT tokens without an expiresIn option.`,
|
|
567
|
-
risk: 'Long-lived tokens increase replay and account-compromise blast radius.',
|
|
568
|
-
expectedOutcome: 'Tokens gain explicit expiry to enforce credential rotation windows.',
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
if (patternKind === 'unsafe_inner_html_usage') {
|
|
572
|
-
return {
|
|
573
|
-
summary: 'Replaces unsafe innerHTML assignment with textContent.',
|
|
574
|
-
why: `${targetPath} writes HTML content directly into the DOM using innerHTML.`,
|
|
575
|
-
risk: 'innerHTML assignments can expose XSS vectors when input is not trusted.',
|
|
576
|
-
expectedOutcome: 'DOM assignment becomes text-only rendering with reduced injection risk.',
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
if (patternKind === 'unsafe_sensitive_logging') {
|
|
580
|
-
return {
|
|
581
|
-
summary: 'Removes deterministic sensitive logging line.',
|
|
582
|
-
why: `${targetPath} appears to log secret-bearing fields (token/authorization/password).`,
|
|
583
|
-
risk: 'Sensitive log content can leak credentials to observability or audit sinks.',
|
|
584
|
-
expectedOutcome: 'Sensitive logging path is replaced with a neutral warning placeholder.',
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
if (patternKind === 'todo_fixme') {
|
|
588
|
-
return {
|
|
589
|
-
summary: 'Removes TODO/FIXME marker matched by policy.',
|
|
590
|
-
why: `${targetPath} includes TODO/FIXME comments tracked as governance debt.`,
|
|
591
|
-
risk: 'Unresolved TODO markers can hide missing implementation or review debt.',
|
|
592
|
-
expectedOutcome: 'Patch removes the marker; implementation must still be verified separately.',
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
return null;
|
|
596
|
-
}
|
|
597
|
-
function isExecutionActionType(value) {
|
|
598
|
-
if (typeof value !== 'string')
|
|
599
|
-
return false;
|
|
600
|
-
return (value === 'verify'
|
|
601
|
-
|| value === 'fix'
|
|
602
|
-
|| value === 'patch'
|
|
603
|
-
|| value === 'apply-safe'
|
|
604
|
-
|| value === 'reverify'
|
|
605
|
-
|| value === 'policy-sync'
|
|
606
|
-
|| value === 'intent-update');
|
|
607
|
-
}
|
|
608
|
-
function asObjectRecord(value) {
|
|
609
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
610
|
-
return null;
|
|
611
|
-
return value;
|
|
612
|
-
}
|
|
613
|
-
function asObjectArray(value) {
|
|
614
|
-
if (!Array.isArray(value))
|
|
615
|
-
return [];
|
|
616
|
-
return value
|
|
617
|
-
.map((entry) => asObjectRecord(entry))
|
|
618
|
-
.filter((entry) => entry !== null);
|
|
619
|
-
}
|
|
620
|
-
function toLegacyViolation(entry, fallbackSeverity) {
|
|
621
|
-
const file = typeof entry.file === 'string' && entry.file.trim().length > 0
|
|
622
|
-
? entry.file.trim()
|
|
623
|
-
: '';
|
|
624
|
-
const message = typeof entry.message === 'string' && entry.message.trim().length > 0
|
|
625
|
-
? entry.message.trim()
|
|
626
|
-
: '';
|
|
627
|
-
if (!file || !message)
|
|
628
|
-
return null;
|
|
629
|
-
const severity = typeof entry.severity === 'string' && entry.severity.trim().length > 0
|
|
630
|
-
? entry.severity.trim()
|
|
631
|
-
: fallbackSeverity;
|
|
632
|
-
const rule = typeof entry.rule === 'string' && entry.rule.trim().length > 0
|
|
633
|
-
? entry.rule.trim()
|
|
634
|
-
: typeof entry.policy === 'string' && entry.policy.trim().length > 0
|
|
635
|
-
? entry.policy.trim()
|
|
636
|
-
: '';
|
|
637
|
-
return { file, message, severity, rule };
|
|
638
|
-
}
|
|
639
|
-
function normalizeVerifyPayloadForLegacyClients(payload) {
|
|
640
|
-
if (!payload)
|
|
641
|
-
return null;
|
|
642
|
-
const existingViolations = asObjectArray(payload.violations)
|
|
643
|
-
.map((entry) => toLegacyViolation(entry, 'warn'))
|
|
644
|
-
.filter((entry) => entry !== null);
|
|
645
|
-
const blockingItems = asObjectArray(payload.blockingItems)
|
|
646
|
-
.map((entry) => toLegacyViolation(entry, 'block'))
|
|
647
|
-
.filter((entry) => entry !== null);
|
|
648
|
-
const advisoryItems = asObjectArray(payload.advisoryItems)
|
|
649
|
-
.map((entry) => toLegacyViolation(entry, 'warn'))
|
|
650
|
-
.filter((entry) => entry !== null);
|
|
651
|
-
const warnings = asObjectArray(payload.warnings)
|
|
652
|
-
.map((entry) => toLegacyViolation(entry, 'warn'))
|
|
653
|
-
.filter((entry) => entry !== null);
|
|
654
|
-
const merged = [...existingViolations];
|
|
655
|
-
const canonicalSeverity = (value) => {
|
|
656
|
-
const normalized = value.trim().toLowerCase();
|
|
657
|
-
if (normalized === 'block' || normalized === 'critical' || normalized === 'high')
|
|
658
|
-
return 'block';
|
|
659
|
-
if (normalized === 'warn' || normalized === 'warning' || normalized === 'advisory' || normalized === 'medium' || normalized === 'low')
|
|
660
|
-
return 'warn';
|
|
661
|
-
return normalized;
|
|
662
|
-
};
|
|
663
|
-
const canonicalKey = (entry) => `${entry.file}::${entry.rule}::${entry.message}::${canonicalSeverity(entry.severity)}`;
|
|
664
|
-
const seen = new Set(merged.map((entry) => canonicalKey(entry)));
|
|
665
|
-
for (const item of [...blockingItems, ...advisoryItems, ...warnings]) {
|
|
666
|
-
const key = canonicalKey(item);
|
|
667
|
-
if (seen.has(key))
|
|
668
|
-
continue;
|
|
669
|
-
seen.add(key);
|
|
670
|
-
merged.push(item);
|
|
671
|
-
}
|
|
672
|
-
if (merged.length === 0)
|
|
673
|
-
return payload;
|
|
674
|
-
return {
|
|
675
|
-
...payload,
|
|
676
|
-
violations: merged,
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
function normalizeFixPayloadForLegacyClients(payload) {
|
|
680
|
-
if (!payload)
|
|
681
|
-
return null;
|
|
682
|
-
const suggestions = asObjectArray(payload.suggestions);
|
|
683
|
-
if (suggestions.length === 0)
|
|
684
|
-
return payload;
|
|
685
|
-
const deduped = [];
|
|
686
|
-
const seen = new Set();
|
|
687
|
-
for (const suggestion of suggestions) {
|
|
688
|
-
const file = typeof suggestion.file === 'string' ? suggestion.file.trim() : '';
|
|
689
|
-
const line = typeof suggestion.line === 'number' && Number.isFinite(suggestion.line)
|
|
690
|
-
? String(Math.floor(suggestion.line))
|
|
691
|
-
: '';
|
|
692
|
-
const message = typeof suggestion.message === 'string' ? suggestion.message.trim() : '';
|
|
693
|
-
const rule = typeof suggestion.rule === 'string'
|
|
694
|
-
? suggestion.rule.trim()
|
|
695
|
-
: typeof suggestion.policy === 'string'
|
|
696
|
-
? suggestion.policy.trim()
|
|
697
|
-
: '';
|
|
698
|
-
const confidence = typeof suggestion.confidence === 'string' ? suggestion.confidence.trim().toLowerCase() : '';
|
|
699
|
-
const patch = asObjectRecord(suggestion.patch);
|
|
700
|
-
const patchDiff = patch && typeof patch.diff === 'string' ? patch.diff : '';
|
|
701
|
-
const key = `${file}::${line}::${rule}::${message}::${confidence}::${(0, node_crypto_1.createHash)('sha1').update(patchDiff).digest('hex')}`;
|
|
702
|
-
if (seen.has(key))
|
|
703
|
-
continue;
|
|
704
|
-
seen.add(key);
|
|
705
|
-
deduped.push(suggestion);
|
|
706
|
-
}
|
|
707
|
-
if (deduped.length === suggestions.length)
|
|
708
|
-
return payload;
|
|
709
|
-
return {
|
|
710
|
-
...payload,
|
|
711
|
-
suggestions: deduped,
|
|
712
|
-
_normalization: {
|
|
713
|
-
...(asObjectRecord(payload._normalization) || {}),
|
|
714
|
-
suggestionsDeduped: suggestions.length - deduped.length,
|
|
715
|
-
},
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
325
|
function isLoopback(req) {
|
|
719
326
|
if (ALLOW_NON_LOOPBACK)
|
|
720
327
|
return true;
|
|
@@ -965,71 +572,15 @@ async function handleVerify(req, res) {
|
|
|
965
572
|
failure(res, run.execution.result?.message || 'verify execution produced no payload');
|
|
966
573
|
return;
|
|
967
574
|
}
|
|
968
|
-
const normalizedPayload = normalizeVerifyPayloadForLegacyClients(run.primaryPayload ?? null) ?? run.primaryPayload;
|
|
575
|
+
const normalizedPayload = (0, shaping_1.normalizeVerifyPayloadForLegacyClients)(run.primaryPayload ?? null) ?? run.primaryPayload;
|
|
576
|
+
const governanceEnvelope = (0, shaping_1.buildGovernanceEnvelope)(run);
|
|
969
577
|
success(res, {
|
|
970
578
|
...normalizedPayload,
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
type: run.execution.type,
|
|
974
|
-
source: run.execution.source,
|
|
975
|
-
actor: run.execution.actor,
|
|
976
|
-
status: run.execution.status,
|
|
977
|
-
trend: run.execution.verification.diff.trend,
|
|
978
|
-
evidence: run.execution.evidence.references,
|
|
979
|
-
durationMs: run.execution.durationMs,
|
|
980
|
-
},
|
|
981
|
-
});
|
|
982
|
-
}
|
|
983
|
-
async function handleFix(req, res) {
|
|
984
|
-
const run = await (0, execution_bus_1.runExecution)({
|
|
985
|
-
type: 'fix',
|
|
986
|
-
source: toSource(req),
|
|
987
|
-
actor: toActor(req),
|
|
988
|
-
cwd: process.cwd(),
|
|
989
|
-
reverify: true,
|
|
990
|
-
});
|
|
991
|
-
if (!run.primaryPayload) {
|
|
992
|
-
failure(res, run.execution.result?.message || 'fix execution produced no payload');
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
|
-
const normalizedFixPayload = normalizeFixPayloadForLegacyClients(run.primaryPayload) ?? run.primaryPayload;
|
|
996
|
-
const normalizedVerifyAfter = normalizeVerifyPayloadForLegacyClients(run.verificationPayload);
|
|
997
|
-
success(res, {
|
|
998
|
-
...normalizedFixPayload,
|
|
999
|
-
verifyAfter: normalizedVerifyAfter ?? null,
|
|
1000
|
-
_execution: {
|
|
1001
|
-
id: run.execution.id,
|
|
1002
|
-
type: run.execution.type,
|
|
1003
|
-
source: run.execution.source,
|
|
1004
|
-
actor: run.execution.actor,
|
|
1005
|
-
status: run.execution.status,
|
|
1006
|
-
trend: run.execution.verification.diff.trend,
|
|
1007
|
-
evidence: run.execution.evidence.references,
|
|
1008
|
-
durationMs: run.execution.durationMs,
|
|
1009
|
-
},
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
async function handleFixApplySafe(req, res) {
|
|
1013
|
-
const run = await (0, execution_bus_1.runExecution)({
|
|
1014
|
-
type: 'apply-safe',
|
|
1015
|
-
source: toSource(req),
|
|
1016
|
-
actor: toActor(req),
|
|
1017
|
-
cwd: process.cwd(),
|
|
1018
|
-
reverify: true,
|
|
1019
|
-
});
|
|
1020
|
-
if (!run.primaryPayload) {
|
|
1021
|
-
failure(res, run.execution.result?.message || 'fix --apply-safe execution produced no payload');
|
|
1022
|
-
return;
|
|
1023
|
-
}
|
|
1024
|
-
const normalizedFixPayload = normalizeFixPayloadForLegacyClients(run.primaryPayload) ?? run.primaryPayload;
|
|
1025
|
-
const normalizedVerifyAfter = normalizeVerifyPayloadForLegacyClients(run.verificationPayload);
|
|
1026
|
-
success(res, {
|
|
1027
|
-
...normalizedFixPayload,
|
|
1028
|
-
verifyAfter: normalizedVerifyAfter ?? null,
|
|
1029
|
-
execution: run.execution,
|
|
579
|
+
governanceEnvelope,
|
|
580
|
+
_execution: (0, shaping_1.buildExecutionResponseMeta)(run),
|
|
1030
581
|
});
|
|
1031
582
|
}
|
|
1032
|
-
async function
|
|
583
|
+
async function handleExecute(req, res, compatibilityExecutionHandlers) {
|
|
1033
584
|
let body = {};
|
|
1034
585
|
try {
|
|
1035
586
|
body = JSON.parse(await readBody(req));
|
|
@@ -1038,358 +589,18 @@ async function handlePatch(req, res) {
|
|
|
1038
589
|
failure(res, 'Invalid JSON body', 400);
|
|
1039
590
|
return;
|
|
1040
591
|
}
|
|
1041
|
-
const
|
|
1042
|
-
if (!
|
|
1043
|
-
failure(res, '
|
|
1044
|
-
return;
|
|
1045
|
-
}
|
|
1046
|
-
const previewToken = typeof body.previewToken === 'string' && body.previewToken.trim().length > 0
|
|
1047
|
-
? body.previewToken.trim()
|
|
1048
|
-
: undefined;
|
|
1049
|
-
const cwd = process.cwd();
|
|
1050
|
-
const targetPath = file.trim();
|
|
1051
|
-
const absPath = path.resolve(cwd, targetPath);
|
|
1052
|
-
const beforeDirtyPaths = captureGitDirtyPaths(cwd);
|
|
1053
|
-
const beforeDirtyFingerprints = captureDirtyFileFingerprints(cwd);
|
|
1054
|
-
// Capture file content before patch to detect real change
|
|
1055
|
-
let contentBefore = null;
|
|
1056
|
-
try {
|
|
1057
|
-
contentBefore = fs.readFileSync(absPath, 'utf-8');
|
|
1058
|
-
}
|
|
1059
|
-
catch { /* file may not exist */ }
|
|
1060
|
-
const primaryArgs = ['patch', '--file', targetPath];
|
|
1061
|
-
if (previewToken) {
|
|
1062
|
-
primaryArgs.push('--preview-token', previewToken);
|
|
1063
|
-
}
|
|
1064
|
-
const run = await (0, execution_bus_1.runExecution)({
|
|
1065
|
-
type: 'patch',
|
|
1066
|
-
source: toSource(req),
|
|
1067
|
-
actor: toActor(req),
|
|
1068
|
-
target: targetPath,
|
|
1069
|
-
cwd,
|
|
1070
|
-
reverify: true,
|
|
1071
|
-
primaryArgs,
|
|
1072
|
-
});
|
|
1073
|
-
const patchData = run.primaryPayload ?? {
|
|
1074
|
-
success: false,
|
|
1075
|
-
file: targetPath,
|
|
1076
|
-
message: run.execution.result?.message || 'No applicable patch found',
|
|
1077
|
-
};
|
|
1078
|
-
// Validate that the file actually changed on disk
|
|
1079
|
-
let changed = false;
|
|
1080
|
-
if (patchData.success && contentBefore !== null) {
|
|
1081
|
-
try {
|
|
1082
|
-
const contentAfter = fs.readFileSync(absPath, 'utf-8');
|
|
1083
|
-
changed = contentAfter !== contentBefore;
|
|
1084
|
-
}
|
|
1085
|
-
catch { /* ignore read error */ }
|
|
1086
|
-
}
|
|
1087
|
-
const afterDirtyPaths = captureGitDirtyPaths(cwd);
|
|
1088
|
-
const afterDirtyFingerprints = captureDirtyFileFingerprints(cwd);
|
|
1089
|
-
const sideEffects = collectUnexpectedPatchSideEffects(beforeDirtyPaths, afterDirtyPaths, absPath, cwd);
|
|
1090
|
-
const mutatedSideEffects = collectUnexpectedPatchMutations(beforeDirtyFingerprints, afterDirtyFingerprints, absPath, cwd);
|
|
1091
|
-
const combinedSideEffects = [...new Set([...sideEffects, ...mutatedSideEffects])].sort();
|
|
1092
|
-
const payloadFile = typeof patchData.file === 'string' ? patchData.file : '';
|
|
1093
|
-
const payloadTargetMatch = payloadFile.length > 0
|
|
1094
|
-
? path.resolve(cwd, payloadFile) === absPath
|
|
1095
|
-
: true;
|
|
1096
|
-
const patchSucceeded = patchData.success === true;
|
|
1097
|
-
const rawPatchStatus = typeof patchData.status === 'string' ? patchData.status : '';
|
|
1098
|
-
const patchStatus = rawPatchStatus === 'filesystem_changed_since_preview'
|
|
1099
|
-
? 'stale_preview'
|
|
1100
|
-
: !patchSucceeded
|
|
1101
|
-
? 'rejected'
|
|
1102
|
-
: changed && payloadTargetMatch && combinedSideEffects.length === 0
|
|
1103
|
-
? 'applied'
|
|
1104
|
-
: changed
|
|
1105
|
-
? 'partial'
|
|
1106
|
-
: 'rejected';
|
|
1107
|
-
const patchMessage = (() => {
|
|
1108
|
-
if (patchStatus === 'applied') {
|
|
1109
|
-
return (typeof patchData.message === 'string' && patchData.message.trim().length > 0)
|
|
1110
|
-
? patchData.message
|
|
1111
|
-
: `${contracts_1.STATUS_TERMS.safePatchApplied}`;
|
|
1112
|
-
}
|
|
1113
|
-
if (patchStatus === 'partial') {
|
|
1114
|
-
if (!payloadTargetMatch) {
|
|
1115
|
-
return `${contracts_1.STATUS_TERMS.patchRejected}: patch target mismatch detected between requested file and daemon payload file.`;
|
|
1116
|
-
}
|
|
1117
|
-
if (combinedSideEffects.length > 0) {
|
|
1118
|
-
return `${contracts_1.STATUS_TERMS.patchRejected}: patch introduced side effects in ${combinedSideEffects.length} additional file(s).`;
|
|
1119
|
-
}
|
|
1120
|
-
return `${contracts_1.STATUS_TERMS.safePatchApplied}. ${contracts_1.STATUS_TERMS.manualReviewRecommended}.`;
|
|
1121
|
-
}
|
|
1122
|
-
if (patchStatus === 'stale_preview') {
|
|
1123
|
-
return `${contracts_1.STATUS_TERMS.filesystemChangedSincePreview}. Regenerate patch preview and retry. ${contracts_1.STATUS_TERMS.retrySafe}.`;
|
|
1124
|
-
}
|
|
1125
|
-
return (typeof patchData.message === 'string' && patchData.message.trim().length > 0)
|
|
1126
|
-
? patchData.message
|
|
1127
|
-
: `${contracts_1.STATUS_TERMS.patchRejected}; no deterministic file-scoped change applied`;
|
|
1128
|
-
})();
|
|
1129
|
-
const reverifyRequired = patchStatus === 'applied' || patchStatus === 'partial';
|
|
1130
|
-
const stateLabel = patchStatus === 'stale_preview'
|
|
1131
|
-
? contracts_1.STATUS_TERMS.filesystemChangedSincePreview.toLowerCase()
|
|
1132
|
-
: (0, contracts_1.toPatchStateLabel)(patchStatus).toLowerCase();
|
|
1133
|
-
recordPatchOutcome(patchStatus);
|
|
1134
|
-
const normalizedVerifyPayload = normalizeVerifyPayloadForLegacyClients(run.verificationPayload);
|
|
1135
|
-
success(res, {
|
|
1136
|
-
patch: {
|
|
1137
|
-
...patchData,
|
|
1138
|
-
file: payloadFile || targetPath,
|
|
1139
|
-
success: patchStatus === 'applied',
|
|
1140
|
-
rawSuccess: patchData.success === true,
|
|
1141
|
-
changed,
|
|
1142
|
-
status: patchStatus,
|
|
1143
|
-
targetMatch: payloadTargetMatch,
|
|
1144
|
-
sideEffects: combinedSideEffects,
|
|
1145
|
-
message: patchMessage,
|
|
1146
|
-
reverifyRequired,
|
|
1147
|
-
stateLabel,
|
|
1148
|
-
previewTokenUsed: previewToken ? true : false,
|
|
1149
|
-
},
|
|
1150
|
-
verify: normalizedVerifyPayload ?? null,
|
|
1151
|
-
execution: run.execution,
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
async function handlePatchRollback(req, res) {
|
|
1155
|
-
let body = {};
|
|
1156
|
-
try {
|
|
1157
|
-
body = JSON.parse(await readBody(req));
|
|
1158
|
-
}
|
|
1159
|
-
catch {
|
|
1160
|
-
failure(res, 'Invalid JSON body', 400);
|
|
1161
|
-
return;
|
|
1162
|
-
}
|
|
1163
|
-
const file = body.file;
|
|
1164
|
-
const receiptId = typeof body.receiptId === 'string' ? body.receiptId.trim() : '';
|
|
1165
|
-
if (!file || typeof file !== 'string' || file.includes('..')) {
|
|
1166
|
-
failure(res, 'Missing or unsafe "file" field', 400);
|
|
1167
|
-
return;
|
|
1168
|
-
}
|
|
1169
|
-
if (!receiptId) {
|
|
1170
|
-
failure(res, 'Missing "receiptId" field', 400);
|
|
1171
|
-
return;
|
|
1172
|
-
}
|
|
1173
|
-
const cwd = process.cwd();
|
|
1174
|
-
const targetPath = file.trim();
|
|
1175
|
-
const absPath = path.resolve(cwd, targetPath);
|
|
1176
|
-
const beforeDirtyPaths = captureGitDirtyPaths(cwd);
|
|
1177
|
-
const beforeDirtyFingerprints = captureDirtyFileFingerprints(cwd);
|
|
1178
|
-
let contentBefore = null;
|
|
1179
|
-
try {
|
|
1180
|
-
contentBefore = fs.readFileSync(absPath, 'utf-8');
|
|
1181
|
-
}
|
|
1182
|
-
catch { /* file may not exist */ }
|
|
1183
|
-
const run = await (0, execution_bus_1.runExecution)({
|
|
1184
|
-
type: 'patch',
|
|
1185
|
-
source: toSource(req),
|
|
1186
|
-
actor: toActor(req),
|
|
1187
|
-
target: targetPath,
|
|
1188
|
-
cwd,
|
|
1189
|
-
reverify: true,
|
|
1190
|
-
primaryArgs: ['patch', '--file', targetPath, '--rollback-receipt', receiptId, '--json'],
|
|
1191
|
-
});
|
|
1192
|
-
const patchData = run.primaryPayload ?? {
|
|
1193
|
-
success: false,
|
|
1194
|
-
file: targetPath,
|
|
1195
|
-
message: run.execution.result?.message || 'No rollback receipt could be applied',
|
|
1196
|
-
};
|
|
1197
|
-
let changed = false;
|
|
1198
|
-
if (patchData.success && contentBefore !== null) {
|
|
1199
|
-
try {
|
|
1200
|
-
const contentAfter = fs.readFileSync(absPath, 'utf-8');
|
|
1201
|
-
changed = contentAfter !== contentBefore;
|
|
1202
|
-
}
|
|
1203
|
-
catch {
|
|
1204
|
-
// ignore read error
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
const afterDirtyPaths = captureGitDirtyPaths(cwd);
|
|
1208
|
-
const afterDirtyFingerprints = captureDirtyFileFingerprints(cwd);
|
|
1209
|
-
const sideEffects = collectUnexpectedPatchSideEffects(beforeDirtyPaths, afterDirtyPaths, absPath, cwd);
|
|
1210
|
-
const mutatedSideEffects = collectUnexpectedPatchMutations(beforeDirtyFingerprints, afterDirtyFingerprints, absPath, cwd);
|
|
1211
|
-
const combinedSideEffects = [...new Set([...sideEffects, ...mutatedSideEffects])].sort();
|
|
1212
|
-
const payloadFile = typeof patchData.file === 'string' ? patchData.file : '';
|
|
1213
|
-
const payloadTargetMatch = payloadFile.length > 0
|
|
1214
|
-
? path.resolve(cwd, payloadFile) === absPath
|
|
1215
|
-
: true;
|
|
1216
|
-
const rawStatus = typeof patchData.status === 'string' ? patchData.status : '';
|
|
1217
|
-
const rollbackStatus = rawStatus === 'rollback_applied'
|
|
1218
|
-
? 'rollback_applied'
|
|
1219
|
-
: rawStatus === 'rollback_stale' || rawStatus === 'filesystem_changed_since_patch'
|
|
1220
|
-
? 'rollback_stale'
|
|
1221
|
-
: 'rollback_rejected';
|
|
1222
|
-
const rollbackSucceeded = patchData.success === true && rollbackStatus === 'rollback_applied' && payloadTargetMatch && combinedSideEffects.length === 0;
|
|
1223
|
-
const rollbackMessage = (() => {
|
|
1224
|
-
if (rollbackSucceeded) {
|
|
1225
|
-
return (typeof patchData.message === 'string' && patchData.message.trim().length > 0)
|
|
1226
|
-
? patchData.message
|
|
1227
|
-
: contracts_1.STATUS_TERMS.rollbackApplied;
|
|
1228
|
-
}
|
|
1229
|
-
if (!payloadTargetMatch) {
|
|
1230
|
-
return `${contracts_1.STATUS_TERMS.patchRejected}: rollback receipt target mismatch detected.`;
|
|
1231
|
-
}
|
|
1232
|
-
if (combinedSideEffects.length > 0) {
|
|
1233
|
-
return `${contracts_1.STATUS_TERMS.patchRejected}: rollback side effects detected in ${combinedSideEffects.length} additional file(s).`;
|
|
1234
|
-
}
|
|
1235
|
-
return (typeof patchData.message === 'string' && patchData.message.trim().length > 0)
|
|
1236
|
-
? patchData.message
|
|
1237
|
-
: contracts_1.STATUS_TERMS.patchRejected;
|
|
1238
|
-
})();
|
|
1239
|
-
recordPatchOutcome(rollbackStatus);
|
|
1240
|
-
success(res, {
|
|
1241
|
-
patch: {
|
|
1242
|
-
...patchData,
|
|
1243
|
-
file: payloadFile || targetPath,
|
|
1244
|
-
success: rollbackSucceeded,
|
|
1245
|
-
rawSuccess: patchData.success === true,
|
|
1246
|
-
changed,
|
|
1247
|
-
status: rollbackStatus,
|
|
1248
|
-
targetMatch: payloadTargetMatch,
|
|
1249
|
-
sideEffects: combinedSideEffects,
|
|
1250
|
-
message: rollbackMessage,
|
|
1251
|
-
reverifyRequired: rollbackSucceeded,
|
|
1252
|
-
stateLabel: rollbackSucceeded
|
|
1253
|
-
? contracts_1.STATUS_TERMS.rollbackApplied.toLowerCase()
|
|
1254
|
-
: rollbackStatus === 'rollback_stale'
|
|
1255
|
-
? contracts_1.STATUS_TERMS.filesystemChangedSincePreview.toLowerCase()
|
|
1256
|
-
: contracts_1.STATUS_TERMS.patchRejected.toLowerCase(),
|
|
1257
|
-
previewTokenUsed: false,
|
|
1258
|
-
},
|
|
1259
|
-
verify: normalizeVerifyPayloadForLegacyClients(run.verificationPayload) ?? null,
|
|
1260
|
-
execution: run.execution,
|
|
1261
|
-
});
|
|
1262
|
-
}
|
|
1263
|
-
async function handlePatchPreview(req, res) {
|
|
1264
|
-
let body = {};
|
|
1265
|
-
try {
|
|
1266
|
-
body = JSON.parse(await readBody(req));
|
|
1267
|
-
}
|
|
1268
|
-
catch {
|
|
1269
|
-
failure(res, 'Invalid JSON body', 400);
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
const file = body.file;
|
|
1273
|
-
if (!file || typeof file !== 'string' || file.includes('..')) {
|
|
1274
|
-
failure(res, 'Missing or unsafe "file" field', 400);
|
|
1275
|
-
return;
|
|
1276
|
-
}
|
|
1277
|
-
const cwd = process.cwd();
|
|
1278
|
-
const targetPath = file.trim();
|
|
1279
|
-
const absPath = path.resolve(cwd, targetPath);
|
|
1280
|
-
let contentBefore = '';
|
|
1281
|
-
try {
|
|
1282
|
-
contentBefore = fs.readFileSync(absPath, 'utf-8');
|
|
1283
|
-
}
|
|
1284
|
-
catch {
|
|
1285
|
-
failure(res, `File not found: ${targetPath}`, 404);
|
|
1286
|
-
return;
|
|
1287
|
-
}
|
|
1288
|
-
const preview = (0, patch_engine_1.applyFirstMatchingPatch)(targetPath, contentBefore);
|
|
1289
|
-
if (!preview) {
|
|
1290
|
-
success(res, {
|
|
1291
|
-
success: false,
|
|
1292
|
-
file: targetPath,
|
|
1293
|
-
status: 'rejected',
|
|
1294
|
-
message: `No deterministic patch preview available for ${targetPath}`,
|
|
1295
|
-
beforeContent: contentBefore,
|
|
1296
|
-
afterContent: null,
|
|
1297
|
-
diff: null,
|
|
1298
|
-
changed: false,
|
|
1299
|
-
patternKind: null,
|
|
1300
|
-
patchConfidence: null,
|
|
1301
|
-
patchHash: null,
|
|
1302
|
-
previewToken: null,
|
|
1303
|
-
validation: null,
|
|
1304
|
-
recipe: null,
|
|
1305
|
-
pattern: null,
|
|
1306
|
-
whatChanges: null,
|
|
1307
|
-
rollbackPreviewDiff: null,
|
|
1308
|
-
whySafe: null,
|
|
1309
|
-
manualReviewRequired: true,
|
|
1310
|
-
supportedDeterministicPattern: false,
|
|
1311
|
-
reasoning: null,
|
|
1312
|
-
});
|
|
592
|
+
const type = body.type;
|
|
593
|
+
if (!(0, execution_actions_1.isExecutionActionType)(type)) {
|
|
594
|
+
failure(res, 'Invalid or missing "type" field', 400);
|
|
1313
595
|
return;
|
|
1314
596
|
}
|
|
1315
|
-
const
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|| preview.validation.safe !== true
|
|
1320
|
-
|| preview.recipe.requiresManualReview === true;
|
|
1321
|
-
const pattern = patternDescriptor(preview.patternKind, preview.patchConfidence, manualReviewRequired);
|
|
1322
|
-
const whySafe = {
|
|
1323
|
-
deterministic: true,
|
|
1324
|
-
validationPassed: preview.validation.safe === true,
|
|
1325
|
-
confidence: preview.patchConfidence,
|
|
1326
|
-
checks: preview.validation.checks,
|
|
1327
|
-
reasonCodes: preview.validation.reasonCodes,
|
|
1328
|
-
};
|
|
1329
|
-
if (!preview.validation.safe) {
|
|
1330
|
-
success(res, {
|
|
1331
|
-
success: false,
|
|
1332
|
-
file: targetPath,
|
|
1333
|
-
status: 'rejected',
|
|
1334
|
-
message: `Patch preview rejected by deterministic safety validation (${preview.validation.reasonCodes.join(', ') || 'unknown'}).`,
|
|
1335
|
-
beforeContent: contentBefore,
|
|
1336
|
-
afterContent: null,
|
|
1337
|
-
diff: preview.diff,
|
|
1338
|
-
changed: false,
|
|
1339
|
-
patternKind: preview.patternKind,
|
|
1340
|
-
patchConfidence: preview.patchConfidence,
|
|
1341
|
-
patchHash: preview.patchHash,
|
|
1342
|
-
previewToken: preview.previewToken,
|
|
1343
|
-
validation: preview.validation,
|
|
1344
|
-
recipe: preview.recipe,
|
|
1345
|
-
pattern,
|
|
1346
|
-
whatChanges: changeSummary,
|
|
1347
|
-
rollbackPreviewDiff,
|
|
1348
|
-
whySafe,
|
|
1349
|
-
manualReviewRequired,
|
|
1350
|
-
supportedDeterministicPattern: true,
|
|
1351
|
-
reasoning,
|
|
597
|
+
const actionClass = (0, execution_actions_1.getExecutionActionClass)(type);
|
|
598
|
+
if ((0, execution_actions_1.isCompatibilityExecutionActionType)(type) && compatibilityExecutionHandlers) {
|
|
599
|
+
await compatibilityExecutionHandlers.handleExecuteBody(req, res, body, {
|
|
600
|
+
dispatchMode: 'legacy-generic-route',
|
|
1352
601
|
});
|
|
1353
602
|
return;
|
|
1354
603
|
}
|
|
1355
|
-
success(res, {
|
|
1356
|
-
success: true,
|
|
1357
|
-
file: targetPath,
|
|
1358
|
-
status: 'preview',
|
|
1359
|
-
message: 'Patch preview generated',
|
|
1360
|
-
beforeContent: contentBefore,
|
|
1361
|
-
afterContent: preview.updatedContent,
|
|
1362
|
-
diff: preview.diff,
|
|
1363
|
-
changed: contentBefore !== preview.updatedContent,
|
|
1364
|
-
patternKind: preview.patternKind,
|
|
1365
|
-
patchConfidence: preview.patchConfidence,
|
|
1366
|
-
patchHash: preview.patchHash,
|
|
1367
|
-
previewToken: preview.previewToken,
|
|
1368
|
-
validation: preview.validation,
|
|
1369
|
-
recipe: preview.recipe,
|
|
1370
|
-
pattern,
|
|
1371
|
-
whatChanges: changeSummary,
|
|
1372
|
-
rollbackPreviewDiff,
|
|
1373
|
-
whySafe,
|
|
1374
|
-
manualReviewRequired,
|
|
1375
|
-
supportedDeterministicPattern: true,
|
|
1376
|
-
reasoning,
|
|
1377
|
-
});
|
|
1378
|
-
}
|
|
1379
|
-
async function handleExecute(req, res) {
|
|
1380
|
-
let body = {};
|
|
1381
|
-
try {
|
|
1382
|
-
body = JSON.parse(await readBody(req));
|
|
1383
|
-
}
|
|
1384
|
-
catch {
|
|
1385
|
-
failure(res, 'Invalid JSON body', 400);
|
|
1386
|
-
return;
|
|
1387
|
-
}
|
|
1388
|
-
const type = body.type;
|
|
1389
|
-
if (!isExecutionActionType(type)) {
|
|
1390
|
-
failure(res, 'Invalid or missing "type" field', 400);
|
|
1391
|
-
return;
|
|
1392
|
-
}
|
|
1393
604
|
const run = await (0, execution_bus_1.runExecution)({
|
|
1394
605
|
type,
|
|
1395
606
|
source: toSource(req),
|
|
@@ -1397,13 +608,28 @@ async function handleExecute(req, res) {
|
|
|
1397
608
|
target: body.target ?? null,
|
|
1398
609
|
intentText: body.intentText ?? null,
|
|
1399
610
|
cwd: process.cwd(),
|
|
1400
|
-
reverify: body.reverify
|
|
611
|
+
reverify: typeof body.reverify === 'boolean'
|
|
612
|
+
? body.reverify
|
|
613
|
+
: actionClass === 'compatibility-mutation',
|
|
1401
614
|
ciMode: typeof body.ciMode === 'boolean' ? body.ciMode : undefined,
|
|
1402
615
|
evidenceDir: typeof body.evidenceDir === 'string' ? body.evidenceDir : undefined,
|
|
1403
616
|
dedupeWindowMs: typeof body.dedupeWindowMs === 'number' ? body.dedupeWindowMs : undefined,
|
|
1404
617
|
});
|
|
618
|
+
const executionBoundary = {
|
|
619
|
+
routeScope: actionClass === 'canonical-governance' ? 'canonical-governance' : 'runtime-operation',
|
|
620
|
+
actionClass,
|
|
621
|
+
compatibilityAction: false,
|
|
622
|
+
canonicalRuntime: actionClass === 'canonical-governance',
|
|
623
|
+
genericExecution: true,
|
|
624
|
+
};
|
|
625
|
+
const governanceEnvelope = (0, shaping_1.buildGovernanceEnvelope)(run, { executionBoundary });
|
|
1405
626
|
success(res, {
|
|
1406
627
|
execution: run.execution,
|
|
628
|
+
_execution: (0, shaping_1.buildExecutionResponseMeta)(run, { executionBoundary }),
|
|
629
|
+
actionClass,
|
|
630
|
+
compatibilityAction: false,
|
|
631
|
+
executionBoundary,
|
|
632
|
+
governanceEnvelope,
|
|
1407
633
|
payload: run.primaryPayload,
|
|
1408
634
|
verification: run.verificationPayload,
|
|
1409
635
|
});
|
|
@@ -1742,7 +968,7 @@ async function handleUpdateWorkspace(req, res, workspaceId) {
|
|
|
1742
968
|
});
|
|
1743
969
|
success(res, result);
|
|
1744
970
|
}
|
|
1745
|
-
async function handleExecuteWorkspace(req, res) {
|
|
971
|
+
async function handleExecuteWorkspace(req, res, compatibilityExecutionHandlers) {
|
|
1746
972
|
let body = {};
|
|
1747
973
|
try {
|
|
1748
974
|
body = JSON.parse(await readBody(req));
|
|
@@ -1751,10 +977,16 @@ async function handleExecuteWorkspace(req, res) {
|
|
|
1751
977
|
failure(res, 'Invalid JSON body', 400);
|
|
1752
978
|
return;
|
|
1753
979
|
}
|
|
1754
|
-
if (!isExecutionActionType(body.type)) {
|
|
980
|
+
if (!(0, execution_actions_1.isExecutionActionType)(body.type)) {
|
|
1755
981
|
failure(res, 'Invalid or missing execution type', 400);
|
|
1756
982
|
return;
|
|
1757
983
|
}
|
|
984
|
+
if ((0, execution_actions_1.isCompatibilityExecutionActionType)(body.type) && compatibilityExecutionHandlers) {
|
|
985
|
+
await compatibilityExecutionHandlers.handleWorkspaceExecuteBody(req, res, body, {
|
|
986
|
+
dispatchMode: 'legacy-generic-route',
|
|
987
|
+
});
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
1758
990
|
const request = {
|
|
1759
991
|
workspaceId: asNonEmptyString(body.workspaceId),
|
|
1760
992
|
repositoryIds: asStringArray(body.repositoryIds),
|
|
@@ -1763,7 +995,9 @@ async function handleExecuteWorkspace(req, res) {
|
|
|
1763
995
|
actor: toActor(req),
|
|
1764
996
|
target: body.target === null ? null : asNonEmptyString(body.target) || null,
|
|
1765
997
|
intentText: body.intentText === null ? null : asNonEmptyString(body.intentText) || null,
|
|
1766
|
-
reverify: typeof body.reverify === 'boolean'
|
|
998
|
+
reverify: typeof body.reverify === 'boolean'
|
|
999
|
+
? body.reverify
|
|
1000
|
+
: (0, execution_actions_1.getExecutionActionClass)(body.type) === 'compatibility-mutation',
|
|
1767
1001
|
ciMode: typeof body.ciMode === 'boolean' ? body.ciMode : undefined,
|
|
1768
1002
|
evidenceDir: asNonEmptyString(body.evidenceDir),
|
|
1769
1003
|
dedupeWindowMs: typeof body.dedupeWindowMs === 'number' ? body.dedupeWindowMs : undefined,
|
|
@@ -1771,7 +1005,54 @@ async function handleExecuteWorkspace(req, res) {
|
|
|
1771
1005
|
const result = await (0, workspace_runtime_1.executeWorkspaceAction)(request, {
|
|
1772
1006
|
cwd: process.cwd(),
|
|
1773
1007
|
});
|
|
1774
|
-
|
|
1008
|
+
const workspaceActionClass = (0, execution_actions_1.getExecutionActionClass)(body.type);
|
|
1009
|
+
const executionBoundary = {
|
|
1010
|
+
routeScope: workspaceActionClass === 'canonical-governance'
|
|
1011
|
+
? 'workspace-canonical-governance'
|
|
1012
|
+
: 'workspace-runtime-operation',
|
|
1013
|
+
actionClass: workspaceActionClass,
|
|
1014
|
+
compatibilityAction: false,
|
|
1015
|
+
canonicalRuntime: workspaceActionClass === 'canonical-governance',
|
|
1016
|
+
workspaceExecution: true,
|
|
1017
|
+
};
|
|
1018
|
+
success(res, {
|
|
1019
|
+
...result,
|
|
1020
|
+
actionClass: workspaceActionClass,
|
|
1021
|
+
compatibilityAction: false,
|
|
1022
|
+
executionBoundary,
|
|
1023
|
+
governanceEnvelope: {
|
|
1024
|
+
schemaVersion: 'neurcode.governance-envelope.v1',
|
|
1025
|
+
identity: {
|
|
1026
|
+
executionId: result.executionId,
|
|
1027
|
+
executionType: result.type,
|
|
1028
|
+
source: result.source,
|
|
1029
|
+
actor: result.actor,
|
|
1030
|
+
completedAt: result.completedAt,
|
|
1031
|
+
},
|
|
1032
|
+
boundary: {
|
|
1033
|
+
actionClass: workspaceActionClass,
|
|
1034
|
+
runtimeBoundary: workspaceActionClass,
|
|
1035
|
+
mutatesCode: false,
|
|
1036
|
+
compatibilityAction: false,
|
|
1037
|
+
executionBoundary,
|
|
1038
|
+
compatibilityBoundary: null,
|
|
1039
|
+
},
|
|
1040
|
+
custody: {
|
|
1041
|
+
evidence: { generated: false, references: [], retentionLimit: null },
|
|
1042
|
+
replay: { checksum: null, mode: null, integrity: null },
|
|
1043
|
+
provenance: { runId: null, generatedAt: null },
|
|
1044
|
+
policy: { policyLockFingerprint: null, compiledPolicyFingerprint: null },
|
|
1045
|
+
receipts: { ids: [] },
|
|
1046
|
+
},
|
|
1047
|
+
lineage: {
|
|
1048
|
+
verificationTrend: result.totals.failed > 0 ? 'regressed' : 'unchanged',
|
|
1049
|
+
repositories: result.totals.repositories,
|
|
1050
|
+
attempted: result.totals.attempted,
|
|
1051
|
+
succeeded: result.totals.succeeded,
|
|
1052
|
+
failed: result.totals.failed,
|
|
1053
|
+
},
|
|
1054
|
+
},
|
|
1055
|
+
});
|
|
1775
1056
|
}
|
|
1776
1057
|
// ── Semantic Search Handlers ──────────────────────────────────────────────────
|
|
1777
1058
|
/**
|
|
@@ -2213,7 +1494,10 @@ async function handleGovernanceFindings(req, res) {
|
|
|
2213
1494
|
const parsed = JSON.parse(fs.readFileSync(candidate, 'utf8'));
|
|
2214
1495
|
if (parsed && typeof parsed === 'object') {
|
|
2215
1496
|
envelope = parsed;
|
|
2216
|
-
const
|
|
1497
|
+
const governanceVerification = parsed.governanceVerification;
|
|
1498
|
+
const findings = parsed.governanceFindings
|
|
1499
|
+
|| governanceVerification?.findings
|
|
1500
|
+
|| parsed.findings;
|
|
2217
1501
|
if (Array.isArray(findings)) {
|
|
2218
1502
|
rawFindings = findings;
|
|
2219
1503
|
}
|
|
@@ -2286,6 +1570,8 @@ async function handleGovernanceFindings(req, res) {
|
|
|
2286
1570
|
advisoryCount,
|
|
2287
1571
|
structuralCount: rawFindings.filter((f) => f.category === 'structural').length,
|
|
2288
1572
|
semanticCount: rawFindings.filter((f) => f.category === 'semantic').length,
|
|
1573
|
+
intentGovernance: envelope.intentGovernance || null,
|
|
1574
|
+
intentGovernanceCount: rawFindings.filter((f) => f.sourceSystem === 'intent-engine').length,
|
|
2289
1575
|
sourceFile: sourceFile || null,
|
|
2290
1576
|
filtered: filtered.length,
|
|
2291
1577
|
returned: paginated.length,
|
|
@@ -2884,6 +2170,21 @@ async function handlePilotReport(_req, res) {
|
|
|
2884
2170
|
}
|
|
2885
2171
|
// ── Server factory ─────────────────────────────────────────────────────────────
|
|
2886
2172
|
function createDaemonServer() {
|
|
2173
|
+
const compatibilityExecutionHandlers = (0, execution_1.createCompatibilityExecutionHandlers)({
|
|
2174
|
+
readBody,
|
|
2175
|
+
success,
|
|
2176
|
+
failure,
|
|
2177
|
+
toSource,
|
|
2178
|
+
toActor,
|
|
2179
|
+
});
|
|
2180
|
+
const compatibilityMutationHandlers = (0, mutation_1.createCompatibilityMutationHandlers)({
|
|
2181
|
+
readBody,
|
|
2182
|
+
success,
|
|
2183
|
+
failure,
|
|
2184
|
+
toSource,
|
|
2185
|
+
toActor,
|
|
2186
|
+
recordPatchOutcome,
|
|
2187
|
+
});
|
|
2887
2188
|
const server = http.createServer(async (req, res) => {
|
|
2888
2189
|
const incomingRequestIdRaw = req.headers[REQUEST_ID_HEADER];
|
|
2889
2190
|
const incomingRequestId = Array.isArray(incomingRequestIdRaw)
|
|
@@ -2906,7 +2207,7 @@ function createDaemonServer() {
|
|
|
2906
2207
|
}
|
|
2907
2208
|
const url = req.url ?? '/';
|
|
2908
2209
|
const method = req.method ?? 'GET';
|
|
2909
|
-
const normalizedRoutePath = normalizeRoutePath(url);
|
|
2210
|
+
const normalizedRoutePath = (0, routes_1.normalizeRoutePath)(url);
|
|
2910
2211
|
recordDaemonRequest(normalizedRoutePath, method);
|
|
2911
2212
|
res.__neurcodeRoutePath = normalizedRoutePath;
|
|
2912
2213
|
try {
|
|
@@ -2925,7 +2226,19 @@ function createDaemonServer() {
|
|
|
2925
2226
|
operational,
|
|
2926
2227
|
executionBus: {
|
|
2927
2228
|
schemaVersion: 'neurcode.execution.v1',
|
|
2928
|
-
supportedActions:
|
|
2229
|
+
supportedActions: execution_actions_1.EXECUTION_ACTION_TYPES,
|
|
2230
|
+
genericActions: [
|
|
2231
|
+
...execution_actions_1.CANONICAL_EXECUTION_ACTION_TYPES,
|
|
2232
|
+
...execution_actions_1.RUNTIME_OPERATION_EXECUTION_ACTION_TYPES,
|
|
2233
|
+
],
|
|
2234
|
+
canonicalActions: execution_actions_1.CANONICAL_EXECUTION_ACTION_TYPES,
|
|
2235
|
+
runtimeOperationActions: execution_actions_1.RUNTIME_OPERATION_EXECUTION_ACTION_TYPES,
|
|
2236
|
+
compatibilityActions: execution_actions_1.COMPATIBILITY_EXECUTION_ACTION_TYPES,
|
|
2237
|
+
compatibilityBoundary: {
|
|
2238
|
+
explicitRoute: '/execute/compatibility',
|
|
2239
|
+
workspaceExplicitRoute: '/workspaces/execute/compatibility',
|
|
2240
|
+
legacyGenericDispatch: 'accepted-but-quarantined',
|
|
2241
|
+
},
|
|
2929
2242
|
},
|
|
2930
2243
|
runtimeEvents: {
|
|
2931
2244
|
schemaVersion: 'neurcode.runtime-event.v1',
|
|
@@ -2948,6 +2261,15 @@ function createDaemonServer() {
|
|
|
2948
2261
|
schemaVersion: 'neurcode.replay.state.v1',
|
|
2949
2262
|
path: '/replay/state',
|
|
2950
2263
|
},
|
|
2264
|
+
routeGroups: {
|
|
2265
|
+
canonicalGovernance: routes_1.CANONICAL_GOVERNANCE_ROUTE_DESCRIPTIONS,
|
|
2266
|
+
compatibilityMutation: routes_1.COMPATIBILITY_MUTATION_ROUTE_DESCRIPTIONS,
|
|
2267
|
+
runtimeExecution: routes_1.DAEMON_ROUTE_GROUPS['runtime-execution'],
|
|
2268
|
+
workspaceOrchestration: routes_1.DAEMON_ROUTE_GROUPS['workspace-orchestration'],
|
|
2269
|
+
replayEvidence: routes_1.DAEMON_ROUTE_GROUPS['replay-evidence'],
|
|
2270
|
+
operationalStatus: routes_1.DAEMON_ROUTE_GROUPS['operational-status'],
|
|
2271
|
+
docsTransport: routes_1.DAEMON_ROUTE_GROUPS['docs-transport'],
|
|
2272
|
+
},
|
|
2951
2273
|
});
|
|
2952
2274
|
return;
|
|
2953
2275
|
}
|
|
@@ -3077,7 +2399,11 @@ function createDaemonServer() {
|
|
|
3077
2399
|
return;
|
|
3078
2400
|
}
|
|
3079
2401
|
if (method === 'POST' && (url === '/workspaces/execute' || url.startsWith('/workspaces/execute?'))) {
|
|
3080
|
-
await handleExecuteWorkspace(req, res);
|
|
2402
|
+
await handleExecuteWorkspace(req, res, compatibilityExecutionHandlers);
|
|
2403
|
+
return;
|
|
2404
|
+
}
|
|
2405
|
+
if (method === 'POST' && (url === '/workspaces/execute/compatibility' || url.startsWith('/workspaces/execute/compatibility?'))) {
|
|
2406
|
+
await compatibilityExecutionHandlers.handleWorkspaceExecute(req, res);
|
|
3081
2407
|
return;
|
|
3082
2408
|
}
|
|
3083
2409
|
if (method === 'GET' && url.startsWith('/replay')) {
|
|
@@ -3135,8 +2461,12 @@ function createDaemonServer() {
|
|
|
3135
2461
|
await handleDocsContent(res, decodeURIComponent(docsContentMatch[1]));
|
|
3136
2462
|
return;
|
|
3137
2463
|
}
|
|
2464
|
+
if (method === 'POST' && (url === '/execute/compatibility' || url.startsWith('/execute/compatibility?'))) {
|
|
2465
|
+
await compatibilityExecutionHandlers.handleExecute(req, res);
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
3138
2468
|
if (method === 'POST' && (url === '/execute' || url.startsWith('/execute?'))) {
|
|
3139
|
-
await handleExecute(req, res);
|
|
2469
|
+
await handleExecute(req, res, compatibilityExecutionHandlers);
|
|
3140
2470
|
return;
|
|
3141
2471
|
}
|
|
3142
2472
|
if (method === 'POST' && (url === '/verify' || url.startsWith('/verify?'))) {
|
|
@@ -3144,23 +2474,23 @@ function createDaemonServer() {
|
|
|
3144
2474
|
return;
|
|
3145
2475
|
}
|
|
3146
2476
|
if (method === 'POST' && (url === '/fix' || url.startsWith('/fix?'))) {
|
|
3147
|
-
await handleFix(req, res);
|
|
2477
|
+
await compatibilityMutationHandlers.handleFix(req, res);
|
|
3148
2478
|
return;
|
|
3149
2479
|
}
|
|
3150
2480
|
if (method === 'POST' && (url === '/fix/apply-safe' || url.startsWith('/fix/apply-safe?'))) {
|
|
3151
|
-
await handleFixApplySafe(req, res);
|
|
2481
|
+
await compatibilityMutationHandlers.handleFixApplySafe(req, res);
|
|
3152
2482
|
return;
|
|
3153
2483
|
}
|
|
3154
2484
|
if (method === 'POST' && (url === '/patch/preview' || url.startsWith('/patch/preview?'))) {
|
|
3155
|
-
await handlePatchPreview(req, res);
|
|
2485
|
+
await compatibilityMutationHandlers.handlePatchPreview(req, res);
|
|
3156
2486
|
return;
|
|
3157
2487
|
}
|
|
3158
2488
|
if (method === 'POST' && (url === '/patch/rollback' || url.startsWith('/patch/rollback?'))) {
|
|
3159
|
-
await handlePatchRollback(req, res);
|
|
2489
|
+
await compatibilityMutationHandlers.handlePatchRollback(req, res);
|
|
3160
2490
|
return;
|
|
3161
2491
|
}
|
|
3162
2492
|
if (method === 'POST' && (url === '/patch' || url.startsWith('/patch?'))) {
|
|
3163
|
-
await handlePatch(req, res);
|
|
2493
|
+
await compatibilityMutationHandlers.handlePatch(req, res);
|
|
3164
2494
|
return;
|
|
3165
2495
|
}
|
|
3166
2496
|
failure(res, `No route for ${method} ${url}`, 404);
|
|
@@ -3246,46 +2576,13 @@ function startDaemon() {
|
|
|
3246
2576
|
});
|
|
3247
2577
|
server.listen(exports.DAEMON_PORT, exports.DAEMON_HOST, () => {
|
|
3248
2578
|
console.log(`\nNeurcode daemon v2 running on http://localhost:${exports.DAEMON_PORT}`);
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
console.log(` GET /executions → execution history`);
|
|
3257
|
-
console.log(` GET /executions/:id → execution detail`);
|
|
3258
|
-
console.log(` GET /executions/:id/timeline → phase timeline + durations`);
|
|
3259
|
-
console.log(` GET /executions/:id/diff → verification + patch inspection`);
|
|
3260
|
-
console.log(` GET /events → runtime event history`);
|
|
3261
|
-
console.log(` GET /events/stream → SSE deterministic governance runtime`);
|
|
3262
|
-
console.log(` GET /ops/summary → daemon operational health + reliability metrics`);
|
|
3263
|
-
console.log(` GET /control-plane → governance control-plane state + snapshots`);
|
|
3264
|
-
console.log(` POST /control-plane/preview → deterministic config impact preview`);
|
|
3265
|
-
console.log(` PUT /control-plane → apply deterministic governance config update`);
|
|
3266
|
-
console.log(` GET /workspaces → workspace catalog + active pointer`);
|
|
3267
|
-
console.log(` GET /workspaces/runtime → workspace governance runtime snapshot`);
|
|
3268
|
-
console.log(` GET /workspaces/:id/runtime → workspace-specific runtime snapshot`);
|
|
3269
|
-
console.log(` GET /workspaces/:id → workspace definition`);
|
|
3270
|
-
console.log(` POST /workspaces → create workspace`);
|
|
3271
|
-
console.log(` PUT /workspaces/:id → update workspace`);
|
|
3272
|
-
console.log(` POST /workspaces/:id/activate → set active workspace`);
|
|
3273
|
-
console.log(` POST /workspaces/:id/repositories → add repository to workspace`);
|
|
3274
|
-
console.log(` POST /workspaces/execute → workspace-scoped deterministic execution`);
|
|
3275
|
-
console.log(` GET /workspaces/:id/cross-repo-graph → detected cross-repo dependency edges`);
|
|
3276
|
-
console.log(` POST /workspaces/:id/federated-context → multi-repo blast radius analysis`);
|
|
3277
|
-
console.log(` POST /workspaces/:id/semantic-search → TF-IDF vector similarity file search`);
|
|
3278
|
-
console.log(` POST /workspaces/:id/semantic-index/build → rebuild semantic index from brain context`);
|
|
3279
|
-
console.log(` POST /workspaces/:id/intent-expand → signed semantic intent governance artifact`);
|
|
3280
|
-
console.log(` GET /replay/state → deterministic governance state replay`);
|
|
3281
|
-
console.log(` GET /replay/execution/:id → deterministic execution replay`);
|
|
3282
|
-
console.log(` GET /replay/workspace/:id → deterministic workspace replay`);
|
|
3283
|
-
console.log(` GET /replay/timeline → deterministic governance timeline replay`);
|
|
3284
|
-
console.log(` GET /governance/findings → canonical governance findings (last verify output)`);
|
|
3285
|
-
console.log(` GET /governance/overview → governance posture summary`);
|
|
3286
|
-
console.log(` GET /brain/cache-status → brain cache manifest and freshness`);
|
|
3287
|
-
console.log(` GET /remediation/status → remediation artifacts and receipts`);
|
|
3288
|
-
console.log(` GET /pilot-report → governance health metrics and trend`);
|
|
2579
|
+
(0, routes_1.logDaemonRouteGroup)('\nCanonical governance routes:', routes_1.CANONICAL_GOVERNANCE_ROUTE_DESCRIPTIONS);
|
|
2580
|
+
(0, routes_1.logDaemonRouteGroup)('\nCompatibility mutation routes (legacy):', routes_1.COMPATIBILITY_MUTATION_ROUTE_DESCRIPTIONS);
|
|
2581
|
+
(0, routes_1.logDaemonRouteGroup)('\nRuntime execution routes:', routes_1.DAEMON_ROUTE_GROUPS['runtime-execution']);
|
|
2582
|
+
(0, routes_1.logDaemonRouteGroup)('\nWorkspace orchestration routes:', routes_1.DAEMON_ROUTE_GROUPS['workspace-orchestration']);
|
|
2583
|
+
(0, routes_1.logDaemonRouteGroup)('\nReplay and evidence routes:', routes_1.DAEMON_ROUTE_GROUPS['replay-evidence']);
|
|
2584
|
+
(0, routes_1.logDaemonRouteGroup)('\nOperational status routes:', routes_1.DAEMON_ROUTE_GROUPS['operational-status']);
|
|
2585
|
+
(0, routes_1.logDaemonRouteGroup)('\nDocs transport routes:', routes_1.DAEMON_ROUTE_GROUPS['docs-transport']);
|
|
3289
2586
|
console.log(`\n CWD: ${cwd}`);
|
|
3290
2587
|
console.log(` Press Ctrl+C to stop.\n`);
|
|
3291
2588
|
});
|