@neurcode-ai/cli 0.9.38 → 0.9.40
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/LICENSE +201 -0
- package/dist/api-client.d.ts +54 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +36 -0
- package/dist/api-client.js.map +1 -1
- package/dist/commands/feedback.d.ts.map +1 -1
- package/dist/commands/feedback.js +145 -0
- package/dist/commands/feedback.js.map +1 -1
- package/dist/commands/guard.d.ts +3 -0
- package/dist/commands/guard.d.ts.map +1 -0
- package/dist/commands/guard.js +390 -0
- package/dist/commands/guard.js.map +1 -0
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +106 -1
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/remediate.d.ts +3 -0
- package/dist/commands/remediate.d.ts.map +1 -1
- package/dist/commands/remediate.js +221 -67
- package/dist/commands/remediate.js.map +1 -1
- package/dist/commands/verify.d.ts +4 -0
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +201 -0
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/policy-compiler.d.ts +4 -0
- package/dist/utils/policy-compiler.d.ts.map +1 -1
- package/dist/utils/policy-compiler.js +43 -0
- package/dist/utils/policy-compiler.js.map +1 -1
- package/dist/utils/runtime-guard.d.ts +93 -0
- package/dist/utils/runtime-guard.d.ts.map +1 -0
- package/dist/utils/runtime-guard.js +327 -0
- package/dist/utils/runtime-guard.js.map +1 -0
- package/package.json +9 -10
package/dist/commands/verify.js
CHANGED
|
@@ -64,6 +64,7 @@ const policy_audit_1 = require("../utils/policy-audit");
|
|
|
64
64
|
const governance_1 = require("../utils/governance");
|
|
65
65
|
const policy_compiler_1 = require("../utils/policy-compiler");
|
|
66
66
|
const change_contract_1 = require("../utils/change-contract");
|
|
67
|
+
const runtime_guard_1 = require("../utils/runtime-guard");
|
|
67
68
|
const artifact_signature_1 = require("../utils/artifact-signature");
|
|
68
69
|
const policy_1 = require("@neurcode-ai/policy");
|
|
69
70
|
// Import chalk with fallback
|
|
@@ -434,6 +435,51 @@ function isEnabledFlag(value) {
|
|
|
434
435
|
const normalized = value.trim().toLowerCase();
|
|
435
436
|
return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
|
|
436
437
|
}
|
|
438
|
+
function createRuntimeGuardSummary(required, projectRoot, runtimeGuardPath) {
|
|
439
|
+
return {
|
|
440
|
+
required,
|
|
441
|
+
path: (0, runtime_guard_1.resolveRuntimeGuardPath)(projectRoot, runtimeGuardPath),
|
|
442
|
+
exists: false,
|
|
443
|
+
valid: false,
|
|
444
|
+
active: false,
|
|
445
|
+
pass: !required,
|
|
446
|
+
changedFiles: 0,
|
|
447
|
+
outOfScopeFiles: [],
|
|
448
|
+
constraintViolations: [],
|
|
449
|
+
violations: [],
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function runtimeGuardViolationsToReport(summary) {
|
|
453
|
+
if (!summary.required || summary.pass) {
|
|
454
|
+
return [];
|
|
455
|
+
}
|
|
456
|
+
if (!summary.exists) {
|
|
457
|
+
return [
|
|
458
|
+
{
|
|
459
|
+
file: summary.path,
|
|
460
|
+
rule: 'runtime_guard_missing',
|
|
461
|
+
severity: 'block',
|
|
462
|
+
message: 'Runtime guard artifact is missing. Run `neurcode guard start` before verify.',
|
|
463
|
+
},
|
|
464
|
+
];
|
|
465
|
+
}
|
|
466
|
+
if (!summary.valid) {
|
|
467
|
+
return [
|
|
468
|
+
{
|
|
469
|
+
file: summary.path,
|
|
470
|
+
rule: 'runtime_guard_invalid',
|
|
471
|
+
severity: 'block',
|
|
472
|
+
message: 'Runtime guard artifact is invalid. Regenerate with `neurcode guard start`.',
|
|
473
|
+
},
|
|
474
|
+
];
|
|
475
|
+
}
|
|
476
|
+
return summary.violations.map((item) => ({
|
|
477
|
+
file: item.file || summary.path,
|
|
478
|
+
rule: `runtime_guard:${item.code.toLowerCase()}`,
|
|
479
|
+
severity: 'block',
|
|
480
|
+
message: item.message,
|
|
481
|
+
}));
|
|
482
|
+
}
|
|
437
483
|
function parseSigningKeyRing(raw) {
|
|
438
484
|
if (!raw || !raw.trim()) {
|
|
439
485
|
return {};
|
|
@@ -1188,6 +1234,8 @@ async function verifyCommand(options) {
|
|
|
1188
1234
|
&& !isEnabledFlag(process.env.NEURCODE_VERIFY_ALLOW_NON_STRICT_CI)
|
|
1189
1235
|
&& Boolean(options.apiKey || process.env.NEURCODE_API_KEY);
|
|
1190
1236
|
const strictArtifactMode = explicitStrictArtifactMode || ciEnterpriseDefaultStrict;
|
|
1237
|
+
const requireRuntimeGuard = options.requireRuntimeGuard === true
|
|
1238
|
+
|| isEnabledFlag(process.env.NEURCODE_VERIFY_REQUIRE_RUNTIME_GUARD);
|
|
1191
1239
|
const signingConfig = resolveGovernanceSigningConfig();
|
|
1192
1240
|
const aiLogSigningKey = signingConfig.signingKey;
|
|
1193
1241
|
const aiLogSigningKeyId = signingConfig.signingKeyId;
|
|
@@ -1201,6 +1249,7 @@ async function verifyCommand(options) {
|
|
|
1201
1249
|
|| (!allowUnsignedArtifacts && strictArtifactMode && hasSigningMaterial);
|
|
1202
1250
|
const changeContractRead = (0, change_contract_1.readChangeContract)(projectRoot, options.changeContract);
|
|
1203
1251
|
const compiledPolicyRead = (0, policy_compiler_1.readCompiledPolicyArtifact)(projectRoot, options.compiledPolicy);
|
|
1252
|
+
let runtimeGuardSummary = createRuntimeGuardSummary(requireRuntimeGuard, projectRoot, options.runtimeGuard);
|
|
1204
1253
|
let compiledPolicyMetadata = resolveCompiledPolicyMetadata(compiledPolicyRead.artifact, compiledPolicyRead.exists ? compiledPolicyRead.path : null);
|
|
1205
1254
|
const compiledPolicySignatureStatus = compiledPolicyRead.artifact
|
|
1206
1255
|
? (0, artifact_signature_1.verifyGovernanceArtifactSignature)({
|
|
@@ -1705,6 +1754,140 @@ async function verifyCommand(options) {
|
|
|
1705
1754
|
const normalized = toUnixPath(filePath || '');
|
|
1706
1755
|
return ignoreFilter(normalized) || runtimeIgnoreSet.has(normalized);
|
|
1707
1756
|
};
|
|
1757
|
+
if (requireRuntimeGuard) {
|
|
1758
|
+
const guardRead = (0, runtime_guard_1.readRuntimeGuardArtifact)(projectRoot, options.runtimeGuard);
|
|
1759
|
+
runtimeGuardSummary = {
|
|
1760
|
+
...runtimeGuardSummary,
|
|
1761
|
+
path: guardRead.path,
|
|
1762
|
+
exists: guardRead.exists,
|
|
1763
|
+
valid: Boolean(guardRead.artifact),
|
|
1764
|
+
};
|
|
1765
|
+
if (!guardRead.artifact) {
|
|
1766
|
+
const message = guardRead.error
|
|
1767
|
+
? `Runtime guard artifact is invalid: ${guardRead.error}`
|
|
1768
|
+
: 'Runtime guard artifact missing. Run `neurcode guard start` before verify.';
|
|
1769
|
+
runtimeGuardSummary = {
|
|
1770
|
+
...runtimeGuardSummary,
|
|
1771
|
+
active: false,
|
|
1772
|
+
pass: false,
|
|
1773
|
+
violations: [
|
|
1774
|
+
{
|
|
1775
|
+
code: guardRead.error ? 'RUNTIME_GUARD_INACTIVE' : 'RUNTIME_GUARD_INACTIVE',
|
|
1776
|
+
message,
|
|
1777
|
+
},
|
|
1778
|
+
],
|
|
1779
|
+
};
|
|
1780
|
+
const runtimeGuardViolationItems = runtimeGuardViolationsToReport(runtimeGuardSummary);
|
|
1781
|
+
recordVerifyEvent('FAIL', 'runtime_guard_missing_or_invalid', diffFiles.map((f) => f.path));
|
|
1782
|
+
if (options.json) {
|
|
1783
|
+
emitVerifyJson({
|
|
1784
|
+
grade: 'F',
|
|
1785
|
+
score: 0,
|
|
1786
|
+
verdict: 'FAIL',
|
|
1787
|
+
violations: runtimeGuardViolationItems,
|
|
1788
|
+
adherenceScore: 0,
|
|
1789
|
+
bloatCount: 0,
|
|
1790
|
+
bloatFiles: [],
|
|
1791
|
+
plannedFilesModified: 0,
|
|
1792
|
+
totalPlannedFiles: 0,
|
|
1793
|
+
message,
|
|
1794
|
+
scopeGuardPassed: false,
|
|
1795
|
+
mode: 'runtime_guard_required',
|
|
1796
|
+
policyOnly: Boolean(options.policyOnly),
|
|
1797
|
+
runtimeGuard: runtimeGuardSummary,
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
else {
|
|
1801
|
+
console.log(chalk.red('\n⛔ Runtime Guard Required'));
|
|
1802
|
+
console.log(chalk.red(` ${message}`));
|
|
1803
|
+
console.log(chalk.dim(` Path: ${runtimeGuardSummary.path}`));
|
|
1804
|
+
console.log(chalk.dim(' Start guard: neurcode guard start --strict\n'));
|
|
1805
|
+
}
|
|
1806
|
+
await recordVerificationIfRequested(options, config, {
|
|
1807
|
+
grade: 'F',
|
|
1808
|
+
violations: runtimeGuardViolationItems,
|
|
1809
|
+
verifyResult: {
|
|
1810
|
+
adherenceScore: 0,
|
|
1811
|
+
verdict: 'FAIL',
|
|
1812
|
+
bloatCount: 0,
|
|
1813
|
+
bloatFiles: [],
|
|
1814
|
+
message,
|
|
1815
|
+
},
|
|
1816
|
+
projectId: projectId || undefined,
|
|
1817
|
+
jsonMode: Boolean(options.json),
|
|
1818
|
+
});
|
|
1819
|
+
process.exit(2);
|
|
1820
|
+
}
|
|
1821
|
+
const runtimeGuardEvaluation = (0, runtime_guard_1.evaluateRuntimeGuardArtifact)(guardRead.artifact, diffFiles.filter((file) => !shouldIgnore(file.path)));
|
|
1822
|
+
runtimeGuardSummary = {
|
|
1823
|
+
...runtimeGuardSummary,
|
|
1824
|
+
active: guardRead.artifact.active,
|
|
1825
|
+
pass: runtimeGuardEvaluation.pass,
|
|
1826
|
+
changedFiles: runtimeGuardEvaluation.changedFiles.length,
|
|
1827
|
+
outOfScopeFiles: runtimeGuardEvaluation.outOfScopeFiles,
|
|
1828
|
+
constraintViolations: runtimeGuardEvaluation.constraintViolations,
|
|
1829
|
+
violations: runtimeGuardEvaluation.violations.map((item) => ({
|
|
1830
|
+
code: item.code,
|
|
1831
|
+
message: item.message,
|
|
1832
|
+
...(item.file ? { file: item.file } : {}),
|
|
1833
|
+
})),
|
|
1834
|
+
};
|
|
1835
|
+
const runtimeGuardUpdated = (0, runtime_guard_1.withRuntimeGuardCheckStats)(guardRead.artifact, {
|
|
1836
|
+
blocked: !runtimeGuardEvaluation.pass,
|
|
1837
|
+
});
|
|
1838
|
+
(0, runtime_guard_1.writeRuntimeGuardArtifact)(projectRoot, runtimeGuardUpdated, options.runtimeGuard);
|
|
1839
|
+
if (!runtimeGuardEvaluation.pass) {
|
|
1840
|
+
const message = runtimeGuardEvaluation.violations.length > 0
|
|
1841
|
+
? `Runtime guard blocked ${runtimeGuardEvaluation.violations.length} violation(s).`
|
|
1842
|
+
: 'Runtime guard blocked verification.';
|
|
1843
|
+
const runtimeGuardViolationItems = runtimeGuardViolationsToReport(runtimeGuardSummary);
|
|
1844
|
+
recordVerifyEvent('FAIL', `runtime_guard_violations=${runtimeGuardEvaluation.violations.length}`, diffFiles.map((f) => f.path));
|
|
1845
|
+
if (options.json) {
|
|
1846
|
+
emitVerifyJson({
|
|
1847
|
+
grade: 'F',
|
|
1848
|
+
score: 0,
|
|
1849
|
+
verdict: 'FAIL',
|
|
1850
|
+
violations: runtimeGuardViolationItems,
|
|
1851
|
+
adherenceScore: 0,
|
|
1852
|
+
bloatCount: runtimeGuardEvaluation.outOfScopeFiles.length,
|
|
1853
|
+
bloatFiles: runtimeGuardEvaluation.outOfScopeFiles,
|
|
1854
|
+
plannedFilesModified: runtimeGuardEvaluation.plannedFilesModified,
|
|
1855
|
+
totalPlannedFiles: runtimeGuardEvaluation.totalPlannedFiles,
|
|
1856
|
+
message,
|
|
1857
|
+
scopeGuardPassed: false,
|
|
1858
|
+
mode: 'runtime_guard_blocked',
|
|
1859
|
+
policyOnly: Boolean(options.policyOnly),
|
|
1860
|
+
runtimeGuard: runtimeGuardSummary,
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
else {
|
|
1864
|
+
console.log(chalk.red('\n⛔ Runtime Guard Blocked Verification'));
|
|
1865
|
+
runtimeGuardEvaluation.violations.forEach((item) => {
|
|
1866
|
+
const file = item.file ? `${item.file}: ` : '';
|
|
1867
|
+
console.log(chalk.red(` • ${file}${item.message}`));
|
|
1868
|
+
});
|
|
1869
|
+
console.log(chalk.dim(`\n Guard artifact: ${runtimeGuardSummary.path}\n`));
|
|
1870
|
+
}
|
|
1871
|
+
await recordVerificationIfRequested(options, config, {
|
|
1872
|
+
grade: 'F',
|
|
1873
|
+
violations: runtimeGuardViolationItems,
|
|
1874
|
+
verifyResult: {
|
|
1875
|
+
adherenceScore: runtimeGuardEvaluation.adherenceScore,
|
|
1876
|
+
verdict: 'FAIL',
|
|
1877
|
+
bloatCount: runtimeGuardEvaluation.outOfScopeFiles.length,
|
|
1878
|
+
bloatFiles: runtimeGuardEvaluation.outOfScopeFiles,
|
|
1879
|
+
message,
|
|
1880
|
+
},
|
|
1881
|
+
projectId: projectId || undefined,
|
|
1882
|
+
jsonMode: Boolean(options.json),
|
|
1883
|
+
});
|
|
1884
|
+
process.exit(2);
|
|
1885
|
+
}
|
|
1886
|
+
if (!options.json) {
|
|
1887
|
+
console.log(chalk.dim(` Runtime guard passed (${runtimeGuardSummary.changedFiles} changed file(s), ` +
|
|
1888
|
+
`${runtimeGuardSummary.violations.length} violation(s))`));
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1708
1891
|
const baselineContextPolicyLocal = (0, policy_1.loadContextPolicy)(projectRoot);
|
|
1709
1892
|
const baselineContextPolicy = orgGovernanceSettings?.contextPolicy
|
|
1710
1893
|
? (0, policy_1.mergeContextPolicies)(baselineContextPolicyLocal, orgGovernanceSettings.contextPolicy)
|
|
@@ -2550,6 +2733,19 @@ async function verifyCommand(options) {
|
|
|
2550
2733
|
console.log(chalk.yellow('⚠️ Verify API unavailable, using local deterministic fallback.'));
|
|
2551
2734
|
console.log(chalk.dim(` Reason: ${fallbackReason}`));
|
|
2552
2735
|
}
|
|
2736
|
+
const localFileContents = {};
|
|
2737
|
+
for (const file of changedFiles) {
|
|
2738
|
+
const absolutePath = (0, path_1.join)(projectRoot, file.path);
|
|
2739
|
+
if (!(0, fs_1.existsSync)(absolutePath)) {
|
|
2740
|
+
continue;
|
|
2741
|
+
}
|
|
2742
|
+
try {
|
|
2743
|
+
localFileContents[file.path] = (0, fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
2744
|
+
}
|
|
2745
|
+
catch {
|
|
2746
|
+
// Best effort only; fallback can still run on diff-only context.
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2553
2749
|
const localEvaluation = (0, governance_runtime_1.evaluatePlanVerification)({
|
|
2554
2750
|
planFiles: planFilesForVerification.map((path) => ({
|
|
2555
2751
|
path,
|
|
@@ -2560,6 +2756,7 @@ async function verifyCommand(options) {
|
|
|
2560
2756
|
intentConstraints: intentConstraintsForVerification,
|
|
2561
2757
|
policyRules: deterministicPolicyRules,
|
|
2562
2758
|
extraConstraintRules: hydratedCompiledPolicyRules.length > 0 ? hydratedCompiledPolicyRules : undefined,
|
|
2759
|
+
fileContents: localFileContents,
|
|
2563
2760
|
});
|
|
2564
2761
|
verifyResult = {
|
|
2565
2762
|
verificationId: `local-fallback-${Date.now()}`,
|
|
@@ -2693,6 +2890,7 @@ async function verifyCommand(options) {
|
|
|
2693
2890
|
},
|
|
2694
2891
|
policyExceptions: policyExceptionsSummary,
|
|
2695
2892
|
policyGovernance: policyGovernanceSummary,
|
|
2893
|
+
...(runtimeGuardSummary.required ? { runtimeGuard: runtimeGuardSummary } : {}),
|
|
2696
2894
|
...(policyViolations.length > 0 && { policyDecision }),
|
|
2697
2895
|
...(effectiveRules.policyPack
|
|
2698
2896
|
? {
|
|
@@ -2774,6 +2972,9 @@ async function verifyCommand(options) {
|
|
|
2774
2972
|
console.log(chalk.red(` • ${issue}`));
|
|
2775
2973
|
});
|
|
2776
2974
|
}
|
|
2975
|
+
if (runtimeGuardSummary.required) {
|
|
2976
|
+
console.log(chalk.dim(`\n Runtime guard: ${runtimeGuardSummary.pass ? 'pass' : 'block'} (${runtimeGuardSummary.path})`));
|
|
2977
|
+
}
|
|
2777
2978
|
}
|
|
2778
2979
|
// Report to Neurcode Cloud if --record flag is set
|
|
2779
2980
|
const filteredBloatForReport = (verifyResult.bloatFiles || []).filter((f) => !shouldIgnore(f));
|