aienvmp 0.1.16 → 0.1.18
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/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/commands/dash.js +13 -1
- package/src/commands/plan.js +30 -0
- package/src/render.js +18 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.18
|
|
4
|
+
|
|
5
|
+
- Added a dashboard remediation steps card backed by `.aienvmp/plan.json`.
|
|
6
|
+
- Kept dashboard remediation details bounded to package, severity, fix version, and advisory references.
|
|
7
|
+
- Improved human visibility for AI-generated read-only security plans.
|
|
8
|
+
|
|
9
|
+
## 0.1.17
|
|
10
|
+
|
|
11
|
+
- Added bounded security remediation steps to `aienvmp plan`.
|
|
12
|
+
- Included package fix versions and advisory references in plan JSON and markdown output.
|
|
13
|
+
- Kept remediation output read-only and review-oriented.
|
|
14
|
+
|
|
3
15
|
## 0.1.16
|
|
4
16
|
|
|
5
17
|
- Added scoped strict checks with `doctor --strict security|policy|coordination|all`.
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ AIENV.md
|
|
|
38
38
|
.aienvmp/timeline.jsonl
|
|
39
39
|
.aienvmp/plan.json
|
|
40
40
|
.aienvmp/plan.md
|
|
41
|
-
.aienvmp/dashboard.html
|
|
41
|
+
.aienvmp/dashboard.html # includes plan and remediation cards
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
Trust states are machine-readable: `observed`, `planned`, `changed`, `review`, `verified`, `stale`.
|
|
@@ -59,7 +59,7 @@ npx aienvmp snippet agents
|
|
|
59
59
|
aienvmp sync # update env map, light SBOM, ledger, dashboard
|
|
60
60
|
aienvmp context # AI preflight brief
|
|
61
61
|
aienvmp context --json # machine-readable AI decision context + recommended actions
|
|
62
|
-
aienvmp plan # read-only AI action plan
|
|
62
|
+
aienvmp plan # read-only AI action plan with remediation steps
|
|
63
63
|
aienvmp handoff # next-agent handoff summary + recommended actions
|
|
64
64
|
aienvmp intent # record a planned env change
|
|
65
65
|
aienvmp record # record what changed
|
package/package.json
CHANGED
package/src/commands/dash.js
CHANGED
|
@@ -18,7 +18,8 @@ export async function dashWorkspace(args) {
|
|
|
18
18
|
const policy = await loadPolicy(dir);
|
|
19
19
|
const warnings = [...diagnose(manifest, { timeline, intents }), ...policyWarnings(manifest, policy)];
|
|
20
20
|
const planArtifacts = await detectedPlanArtifacts(dir);
|
|
21
|
-
const
|
|
21
|
+
const planRemediation = await detectedPlanRemediation(dir);
|
|
22
|
+
const html = renderDashboard({ ...manifest, recommendedActions: recommendedActions(manifest, { warnings, intents }), planArtifacts, planRemediation }, timeline, warnings, intents, policy);
|
|
22
23
|
const out = dashboardPath(dir);
|
|
23
24
|
await fs.mkdir(path.dirname(out), { recursive: true });
|
|
24
25
|
await fs.writeFile(out, html, "utf8");
|
|
@@ -36,6 +37,17 @@ async function detectedPlanArtifacts(dir) {
|
|
|
36
37
|
};
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
async function detectedPlanRemediation(dir) {
|
|
41
|
+
const plan = await readJson(planJsonPath(dir), {});
|
|
42
|
+
return (plan.remediationSteps || []).slice(0, 5).map((item) => ({
|
|
43
|
+
package: item.package || "unknown",
|
|
44
|
+
severity: item.severity || "unknown",
|
|
45
|
+
fixVersions: Array.isArray(item.fixVersions) ? item.fixVersions.slice(0, 3) : [],
|
|
46
|
+
fixAvailable: item.fixAvailable === true,
|
|
47
|
+
advisories: Array.isArray(item.advisories) ? item.advisories.map((advisory) => advisory.id || advisory.title).filter(Boolean).slice(0, 2) : []
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
function openFile(file) {
|
|
40
52
|
if (process.platform === "win32") {
|
|
41
53
|
execFile("cmd", ["/c", "start", "", file], { windowsHide: true });
|
package/src/commands/plan.js
CHANGED
|
@@ -56,6 +56,7 @@ export function buildPlan(manifest, warnings = [], intents = [], policy = {}) {
|
|
|
56
56
|
summary: manifest.security?.summary || { total: 0, critical: 0, high: 0, moderate: 0, low: 0, info: 0 },
|
|
57
57
|
topPackages: manifest.security?.topPackages || []
|
|
58
58
|
},
|
|
59
|
+
remediationSteps: remediationSteps(manifest.security),
|
|
59
60
|
notes: [
|
|
60
61
|
"This plan is read-only.",
|
|
61
62
|
"Ask the user before global runtime, package manager, Docker, or global package changes.",
|
|
@@ -64,6 +65,35 @@ export function buildPlan(manifest, warnings = [], intents = [], policy = {}) {
|
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
function remediationSteps(security = {}) {
|
|
69
|
+
if (!security.enabled) return [];
|
|
70
|
+
return (security.topPackages || []).slice(0, 8).map((pkg) => {
|
|
71
|
+
const fixVersions = Array.isArray(pkg.fixVersions) ? pkg.fixVersions.slice(0, 5) : [];
|
|
72
|
+
const advisories = Array.isArray(pkg.advisories)
|
|
73
|
+
? pkg.advisories.map((item) => ({
|
|
74
|
+
id: item.id || "",
|
|
75
|
+
title: item.title || "",
|
|
76
|
+
url: item.url || "",
|
|
77
|
+
severity: item.severity || pkg.severity || "unknown"
|
|
78
|
+
})).slice(0, 5)
|
|
79
|
+
: [];
|
|
80
|
+
return {
|
|
81
|
+
package: pkg.name,
|
|
82
|
+
scanner: pkg.scanner || "unknown",
|
|
83
|
+
severity: pkg.severity || "unknown",
|
|
84
|
+
fixAvailable: pkg.fixAvailable === true,
|
|
85
|
+
fixVersions,
|
|
86
|
+
advisories,
|
|
87
|
+
steps: [
|
|
88
|
+
"Review the package changelog and advisory details.",
|
|
89
|
+
fixVersions.length ? `Prefer an upgrade path to ${fixVersions.join(", ")} if compatible.` : "Identify a compatible patched version before changing dependencies.",
|
|
90
|
+
"Run project tests after dependency changes.",
|
|
91
|
+
"Record the environment or dependency change with aienvmp record."
|
|
92
|
+
]
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
67
97
|
function reviewGates(warnings, intents, security = {}) {
|
|
68
98
|
const gates = [];
|
|
69
99
|
if (warnings.length) gates.push("Review warnings before changing the environment.");
|
package/src/render.js
CHANGED
|
@@ -182,6 +182,9 @@ export function renderPlan(plan) {
|
|
|
182
182
|
"Review gates:",
|
|
183
183
|
...plan.reviewGates.map((item) => `- ${item}`),
|
|
184
184
|
"",
|
|
185
|
+
"Remediation steps:",
|
|
186
|
+
...(plan.remediationSteps?.length ? plan.remediationSteps.slice(0, 5).flatMap(remediationLines) : ["- none"]),
|
|
187
|
+
"",
|
|
185
188
|
"Warnings:",
|
|
186
189
|
...(plan.warnings.length ? plan.warnings.map((warning) => `- [${warning.code}] ${warning.message}`) : ["- none"]),
|
|
187
190
|
""
|
|
@@ -189,6 +192,15 @@ export function renderPlan(plan) {
|
|
|
189
192
|
return lines.join("\n");
|
|
190
193
|
}
|
|
191
194
|
|
|
195
|
+
function remediationLines(item) {
|
|
196
|
+
const fix = item.fixVersions?.length ? `fix ${item.fixVersions.join(", ")}` : item.fixAvailable ? "fix available" : "review required";
|
|
197
|
+
const advisories = (item.advisories || []).map((advisory) => advisory.id || advisory.title).filter(Boolean).slice(0, 2);
|
|
198
|
+
return [
|
|
199
|
+
`- ${item.package}: ${item.severity}; ${fix}${advisories.length ? `; advisories ${advisories.join(", ")}` : ""}`,
|
|
200
|
+
...item.steps.slice(0, 4).map((step) => ` - ${step}`)
|
|
201
|
+
];
|
|
202
|
+
}
|
|
203
|
+
|
|
192
204
|
export function renderDashboard(manifest, timeline = [], warnings = [], intents = [], policy = {}) {
|
|
193
205
|
const data = JSON.stringify({ manifest, timeline, warnings, intents, policy });
|
|
194
206
|
return `<!doctype html>
|
|
@@ -266,6 +278,10 @@ const actions=manifest.recommendedActions||[];
|
|
|
266
278
|
const actionsHtml=actions.length?'<div class="timeline">'+actions.slice(0,6).map(a=>\`<div class="event"><time>\${esc(a.priority)}</time><div><b>\${esc(a.category)}</b> \${esc(a.summary)}\${a.command?\`<div class="path">\${esc(a.command)}</div>\`:''}</div></div>\`).join('')+'</div>':'<div class="okline">No recommended actions. Continue project-local work.</div>';
|
|
267
279
|
const plan=manifest.planArtifacts||{};
|
|
268
280
|
const planHtml=plan.markdown||plan.json?\`<table><tr><th>Markdown</th><td>\${plan.markdown?'<a href="plan.md">plan.md</a>':'not written'}</td></tr><tr><th>JSON</th><td>\${plan.json?'<a href="plan.json">plan.json</a>':'not written'}</td></tr></table>\`:'<div class="okline">No plan artifacts yet. Run <code>aienvmp plan --write</code>.</div>';
|
|
281
|
+
const remediation=manifest.planRemediation||[];
|
|
282
|
+
const remediationFix=r=>r.fixVersions?.length?\`fix \${r.fixVersions.join(', ')}\`:(r.fixAvailable?'fix available':'review required');
|
|
283
|
+
const remediationRefs=r=>r.advisories?.length?\` - \${r.advisories.join(', ')}\`:'';
|
|
284
|
+
const remediationHtml=remediation.length?'<div class="timeline">'+remediation.map(r=>\`<div class="event"><time>\${esc(r.severity)}</time><div><b>\${esc(r.package)}</b> \${esc(remediationFix(r))}\${esc(remediationRefs(r))}</div></div>\`).join('')+'</div>':'<div class="okline">No remediation steps in the current plan.</div>';
|
|
269
285
|
const card=(title,badge,body)=>\`<section class="card"><div class="card-head"><h2>\${title}</h2>\${badge||''}</div>\${body}</section>\`;
|
|
270
286
|
const reviewRequired=warnings.length>0||intents.length>0;
|
|
271
287
|
const recentChanges=timeline.slice(-8).length;
|
|
@@ -309,6 +325,8 @@ document.getElementById('app').innerHTML=\`
|
|
|
309
325
|
<div style="height:14px"></div>
|
|
310
326
|
\${card('AI Plan Artifacts',plan.markdown||plan.json?'<span class="pill">written</span>':'<span class="pill off">not written</span>',planHtml)}
|
|
311
327
|
<div style="height:14px"></div>
|
|
328
|
+
\${card('Remediation Steps',remediation.length?'<span class="pill warn">'+remediation.length+' items</span>':'<span class="pill off">none</span>',remediationHtml)}
|
|
329
|
+
<div style="height:14px"></div>
|
|
312
330
|
\${card('Environment Health',warnings.length?'<span class="pill warn">attention</span>':'<span class="pill">clear</span>',warnHtml)}
|
|
313
331
|
<div style="height:14px"></div>
|
|
314
332
|
\${card('Version Policy','<span class="pill">'+entries(policy).length+' rules</span>',policyHtml)}
|