@neurcode-ai/cli 0.9.45 → 0.9.47
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/README.md +7 -3
- package/dist/commands/ship.js +10 -3
- package/dist/commands/verify.js +20 -7
- package/dist/utils/governance.d.ts +1 -0
- package/dist/utils/governance.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,14 +49,17 @@ neurcode brain mode --storage-mode no-code
|
|
|
49
49
|
neurcode brain doctor "is userid used instead of org id"
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
## Enterprise Governance Signing
|
|
52
|
+
## Enterprise Governance Signing (Optional Hardening)
|
|
53
53
|
|
|
54
54
|
Use signed AI change logs for fail-closed governance in `verify`/`ship`.
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
#
|
|
57
|
+
# Optional strict mode: require signed logs (fail closed when key material is missing)
|
|
58
58
|
export NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS=1
|
|
59
59
|
|
|
60
|
+
# Optional: honor org-level signed log requirement from control plane
|
|
61
|
+
export NEURCODE_GOVERNANCE_ENFORCE_ORG_SIGNED_LOG_REQUIREMENT=1
|
|
62
|
+
|
|
60
63
|
# Single-key mode
|
|
61
64
|
export NEURCODE_GOVERNANCE_SIGNING_KEY="<strong-random-secret>"
|
|
62
65
|
export NEURCODE_GOVERNANCE_SIGNING_KEY_ID="kid-prod-2026-03"
|
|
@@ -68,10 +71,11 @@ export NEURCODE_GOVERNANCE_SIGNING_KEY_ID="kid-prod-2026-03"
|
|
|
68
71
|
|
|
69
72
|
Notes:
|
|
70
73
|
- `verify` writes and verifies `.neurcode/ai-change-log.json` with integrity chain checks.
|
|
71
|
-
- If signed
|
|
74
|
+
- If strict signed-log mode is enabled and integrity/signature checks fail, `verify` exits non-zero.
|
|
72
75
|
- `ship` will block deployment when required signed AI logs are missing/invalid.
|
|
73
76
|
- `policy compile` and `plan` auto-sign deterministic artifacts when governance signing keys are configured.
|
|
74
77
|
- Use `--require-signed-artifacts` (or `NEURCODE_VERIFY_REQUIRE_SIGNED_ARTIFACTS=1`) to fail closed on unsigned/tampered artifacts.
|
|
78
|
+
- Default onboarding flow is non-blocking unless strict signing is explicitly enabled.
|
|
75
79
|
|
|
76
80
|
## Docs
|
|
77
81
|
|
package/dist/commands/ship.js
CHANGED
|
@@ -825,6 +825,15 @@ function isEnabledFlag(value) {
|
|
|
825
825
|
const normalized = value.trim().toLowerCase();
|
|
826
826
|
return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
|
|
827
827
|
}
|
|
828
|
+
function isSignedAiLogsRequired(orgGovernance) {
|
|
829
|
+
const explicitRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
|
|
830
|
+
isEnabledFlag(process.env.NEURCODE_AI_LOG_REQUIRE_SIGNED);
|
|
831
|
+
if (explicitRequirement) {
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
const honorOrgRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_ENFORCE_ORG_SIGNED_LOG_REQUIREMENT);
|
|
835
|
+
return honorOrgRequirement && orgGovernance?.requireSignedAiLogs === true;
|
|
836
|
+
}
|
|
828
837
|
function collectApplyWrittenFiles(output) {
|
|
829
838
|
const clean = stripAnsi(output);
|
|
830
839
|
const files = [];
|
|
@@ -1846,9 +1855,7 @@ async function shipCommand(goal, options) {
|
|
|
1846
1855
|
const manualApprovalBypass = options.manualApproveHighRisk === true || process.env.NEURCODE_MANUAL_APPROVE_HIGH_RISK === '1';
|
|
1847
1856
|
const governanceDecision = finalVerifyPayload.governanceDecision?.decision;
|
|
1848
1857
|
const orgGovernance = finalVerifyPayload.orgGovernance || null;
|
|
1849
|
-
const signedAiLogsRequired = orgGovernance
|
|
1850
|
-
isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
|
|
1851
|
-
isEnabledFlag(process.env.NEURCODE_AI_LOG_REQUIRE_SIGNED);
|
|
1858
|
+
const signedAiLogsRequired = isSignedAiLogsRequired(orgGovernance);
|
|
1852
1859
|
const aiLogIntegrity = finalVerifyPayload.aiChangeLog?.integrity;
|
|
1853
1860
|
const signedAiLogsValid = aiLogIntegrity?.valid === true && aiLogIntegrity?.signed === true;
|
|
1854
1861
|
const orgManualApprovalRequired = orgGovernance?.requireManualApproval === true;
|
package/dist/commands/verify.js
CHANGED
|
@@ -101,9 +101,9 @@ function toArtifactSignatureSummary(status) {
|
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
103
|
function resolveCliComponentVersion() {
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
return
|
|
104
|
+
const explicitEnvVersion = process.env.NEURCODE_CLI_VERSION;
|
|
105
|
+
if (explicitEnvVersion && explicitEnvVersion.trim()) {
|
|
106
|
+
return explicitEnvVersion.trim();
|
|
107
107
|
}
|
|
108
108
|
try {
|
|
109
109
|
const packagePath = (0, path_1.join)(__dirname, '../../package.json');
|
|
@@ -116,6 +116,12 @@ function resolveCliComponentVersion() {
|
|
|
116
116
|
catch {
|
|
117
117
|
// Ignore and fall back.
|
|
118
118
|
}
|
|
119
|
+
const npmContextVersion = process.env.npm_package_version;
|
|
120
|
+
if (npmContextVersion
|
|
121
|
+
&& npmContextVersion.trim()
|
|
122
|
+
&& npmContextVersion.trim() !== '0.0.0') {
|
|
123
|
+
return npmContextVersion.trim();
|
|
124
|
+
}
|
|
119
125
|
return '0.0.0';
|
|
120
126
|
}
|
|
121
127
|
const CLI_COMPONENT_VERSION = resolveCliComponentVersion();
|
|
@@ -821,9 +827,13 @@ function isGitRepository(cwd) {
|
|
|
821
827
|
}
|
|
822
828
|
}
|
|
823
829
|
function isSignedAiLogsRequired(orgGovernanceSettings) {
|
|
824
|
-
|
|
825
|
-
isEnabledFlag(process.env.
|
|
826
|
-
|
|
830
|
+
const explicitRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
|
|
831
|
+
isEnabledFlag(process.env.NEURCODE_AI_LOG_REQUIRE_SIGNED);
|
|
832
|
+
if (explicitRequirement) {
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
835
|
+
const honorOrgRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_ENFORCE_ORG_SIGNED_LOG_REQUIREMENT);
|
|
836
|
+
return honorOrgRequirement && orgGovernanceSettings?.requireSignedAiLogs === true;
|
|
827
837
|
}
|
|
828
838
|
function policyLockMismatchMessage(mismatches) {
|
|
829
839
|
if (mismatches.length === 0) {
|
|
@@ -1012,6 +1022,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
1012
1022
|
if (!options.json) {
|
|
1013
1023
|
console.log(chalk.cyan('🛡️ General Governance mode (policy only, no plan linked)\n'));
|
|
1014
1024
|
}
|
|
1025
|
+
const signedLogsRequired = isSignedAiLogsRequired(orgGovernanceSettings);
|
|
1015
1026
|
const governanceAnalysis = (0, governance_1.evaluateGovernance)({
|
|
1016
1027
|
projectRoot,
|
|
1017
1028
|
task: 'Policy-only verification',
|
|
@@ -1019,6 +1030,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
1019
1030
|
diffFiles,
|
|
1020
1031
|
contextCandidates: diffFiles.map((file) => file.path),
|
|
1021
1032
|
orgGovernance: orgGovernanceSettings,
|
|
1033
|
+
requireSignedAiLogs: signedLogsRequired,
|
|
1022
1034
|
signingKey: aiLogSigningKey,
|
|
1023
1035
|
signingKeyId: aiLogSigningKeyId,
|
|
1024
1036
|
signingKeys: aiLogSigningKeys,
|
|
@@ -1029,7 +1041,6 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
1029
1041
|
changeContract: changeContractSummary,
|
|
1030
1042
|
});
|
|
1031
1043
|
const contextPolicyViolations = governanceAnalysis.contextPolicy.violations.filter((item) => !ignoreFilter(item.file));
|
|
1032
|
-
const signedLogsRequired = isSignedAiLogsRequired(orgGovernanceSettings);
|
|
1033
1044
|
if (signedLogsRequired && !governanceAnalysis.aiChangeLogIntegrity.valid) {
|
|
1034
1045
|
const message = `AI change-log integrity check failed: ${governanceAnalysis.aiChangeLogIntegrity.issues.join('; ') || 'unknown issue'}`;
|
|
1035
1046
|
if (options.json) {
|
|
@@ -2246,6 +2257,7 @@ async function verifyCommand(options) {
|
|
|
2246
2257
|
diffFiles,
|
|
2247
2258
|
contextCandidates: diffFiles.map((file) => file.path),
|
|
2248
2259
|
orgGovernance: orgGovernanceSettings,
|
|
2260
|
+
requireSignedAiLogs: signedLogsRequired,
|
|
2249
2261
|
signingKey: aiLogSigningKey,
|
|
2250
2262
|
signingKeyId: aiLogSigningKeyId,
|
|
2251
2263
|
signingKeys: aiLogSigningKeys,
|
|
@@ -2547,6 +2559,7 @@ async function verifyCommand(options) {
|
|
|
2547
2559
|
diffFiles,
|
|
2548
2560
|
contextCandidates: planFiles,
|
|
2549
2561
|
orgGovernance: orgGovernanceSettings,
|
|
2562
|
+
requireSignedAiLogs: signedLogsRequired,
|
|
2550
2563
|
signingKey: aiLogSigningKey,
|
|
2551
2564
|
signingKeyId: aiLogSigningKeyId,
|
|
2552
2565
|
signingKeys: aiLogSigningKeys,
|
|
@@ -8,6 +8,7 @@ export interface GovernanceEvaluationInput {
|
|
|
8
8
|
diffFiles: DiffFile[];
|
|
9
9
|
contextCandidates?: string[];
|
|
10
10
|
orgGovernance?: OrgGovernanceSettings | null;
|
|
11
|
+
requireSignedAiLogs?: boolean;
|
|
11
12
|
signingKey?: string | null;
|
|
12
13
|
signingKeyId?: string | null;
|
|
13
14
|
signingKeys?: Record<string, string> | null;
|
package/dist/utils/governance.js
CHANGED
|
@@ -35,7 +35,7 @@ function evaluateGovernance(input) {
|
|
|
35
35
|
});
|
|
36
36
|
let aiChangeLogIntegrity = writtenChangeLog.integrity;
|
|
37
37
|
let governanceDecisionFinal = governanceDecision;
|
|
38
|
-
const requireSignedAiLogs = input.
|
|
38
|
+
const requireSignedAiLogs = input.requireSignedAiLogs === true;
|
|
39
39
|
if (requireSignedAiLogs) {
|
|
40
40
|
aiChangeLogIntegrity = (0, analysis_1.verifyAiChangeLogIntegrity)(input.projectRoot, {
|
|
41
41
|
requiredSigned: true,
|