mcp-aws-manager 0.4.2 → 0.4.4
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 +22 -17
- package/README_KO.md +21 -18
- package/bin/lib/output-presentation.js +20 -1
- package/bin/mcp-aws-manager.js +344 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,14 +4,33 @@ AWS operations CLI + MCP stdio server (SSM-first).
|
|
|
4
4
|
|
|
5
5
|
This package orchestrates AWS operations (inventory/runtime/remediation) with a normalized output schema and `ACTION_REQUIRED` guidance. It is not a plain AWS CLI wrapper.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Agent-First Quick Start
|
|
8
|
+
|
|
9
|
+
Use this as the default flow for agent environments:
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
12
|
npm install -g mcp-aws-manager
|
|
11
13
|
mcp-aws-manager --version
|
|
12
|
-
mcp-aws-manager
|
|
14
|
+
mcp-aws-manager setup --force
|
|
13
15
|
mcp-aws-manager doctor
|
|
14
|
-
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Agent Prompt Examples:
|
|
19
|
+
|
|
20
|
+
Ask in natural language like below; the agent should run fetch/analysis steps as needed.
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
Give me a fresh full AWS server status summary.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then try requests like:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
Compare current AWS server status with the previous check and show only what changed.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
Summarize AWS server status with required actions and high-impact warnings in priority order.
|
|
15
34
|
```
|
|
16
35
|
|
|
17
36
|
## What It Does
|
|
@@ -168,18 +187,6 @@ mcp-aws-manager doctor
|
|
|
168
187
|
|
|
169
188
|
Default behavior (`doctor` without `--clients`) auto-detects installed clients and skips non-installed CLIs.
|
|
170
189
|
|
|
171
|
-
### Agent Instruction Snippet (Any Client)
|
|
172
|
-
|
|
173
|
-
When asking an agent to check AWS status, use this exact sequence first:
|
|
174
|
-
|
|
175
|
-
```bash
|
|
176
|
-
mcp-aws-manager setup --force
|
|
177
|
-
mcp-aws-manager doctor
|
|
178
|
-
mcp-aws-manager discover --profiles default --no-progress
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
Require the agent to include `runId`/`started_at` (live run evidence) in the response. If missing, treat it as file-only/cached summary.
|
|
182
|
-
|
|
183
190
|
3. Configure AWS auth (SSO recommended):
|
|
184
191
|
|
|
185
192
|
```bash
|
|
@@ -991,5 +998,3 @@ Document status:
|
|
|
991
998
|
- `docs/RECORDS_FIELD_REFERENCE_KO.md`: full `records[]` field reference (292 fields)
|
|
992
999
|
- `docs/RESPONSE_COMPATIBILITY_POLICY.md`: response schema/version compatibility rules
|
|
993
1000
|
- `schemas/mcp-tool-response.schema.json`: canonical tool response JSON schema
|
|
994
|
-
|
|
995
|
-
|
package/README_KO.md
CHANGED
|
@@ -4,14 +4,32 @@ SSM 우선(SSM-first) 방식의 AWS 운영 CLI + MCP stdio 서버입니다.
|
|
|
4
4
|
|
|
5
5
|
이 패키지는 AWS 운영 작업(인벤토리/런타임/조치)을 정규화된 응답 스키마와 `ACTION_REQUIRED` 가이드로 제공합니다. 단순 AWS CLI 래퍼가 아니라, 에이전트 친화적인 운영 워크플로우 실행을 목표로 합니다.
|
|
6
6
|
|
|
7
|
-
## 빠른 시작
|
|
7
|
+
## 에이전트 기준 빠른 시작
|
|
8
|
+
|
|
9
|
+
에이전트 환경에서는 아래 순서를 기본 플로우로 사용하세요.
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
12
|
npm install -g mcp-aws-manager
|
|
11
13
|
mcp-aws-manager --version
|
|
12
|
-
mcp-aws-manager
|
|
14
|
+
mcp-aws-manager setup --force
|
|
13
15
|
mcp-aws-manager doctor
|
|
14
|
-
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
에이전트 예시 프롬프트:
|
|
19
|
+
|
|
20
|
+
아래처럼 자연어로 요청하면, 에이전트가 필요 시 조회/분석 단계를 실행합니다.
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
AWS 서버 상태 전체를 최신으로 점검해서 요약해주세요.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
이후에는 이렇게 요청해보세요.
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
AWS 서버 상태를 지난 점검 결과와 비교해서 달라진 항목만 알려주세요.
|
|
30
|
+
```
|
|
31
|
+
```text
|
|
32
|
+
AWS 서버의 상태를 필수 조치 및 주요 경고 사항을 중심으로 우선순위에 따라 요약해주세요.
|
|
15
33
|
```
|
|
16
34
|
|
|
17
35
|
## 무엇을 제공하나요
|
|
@@ -168,18 +186,6 @@ mcp-aws-manager doctor
|
|
|
168
186
|
|
|
169
187
|
기본 동작(`--clients` 없이 `doctor`)은 설치된 클라이언트를 자동 감지하고, 미설치 CLI는 skip 처리합니다.
|
|
170
188
|
|
|
171
|
-
### 에이전트 지시용 공통 스니펫
|
|
172
|
-
|
|
173
|
-
에이전트에게 AWS 상태 점검을 지시할 때는 먼저 아래 순서를 그대로 실행하도록 요청하세요.
|
|
174
|
-
|
|
175
|
-
```bash
|
|
176
|
-
mcp-aws-manager setup --force
|
|
177
|
-
mcp-aws-manager doctor
|
|
178
|
-
mcp-aws-manager discover --profiles default --no-progress
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
응답에는 반드시 `runId`/`started_at`(라이브 실행 증거)를 포함하도록 요구하세요. 없으면 파일 기반/캐시 요약으로 간주하세요.
|
|
182
|
-
|
|
183
189
|
3. AWS 인증 설정(권장: SSO):
|
|
184
190
|
|
|
185
191
|
```bash
|
|
@@ -985,6 +991,3 @@ E2E 러너 검증 항목:
|
|
|
985
991
|
- `docs/RECORDS_FIELD_REFERENCE_KO.md`: `records[]` 전수 필드(292개) 레퍼런스
|
|
986
992
|
- `docs/RESPONSE_COMPATIBILITY_POLICY.md`: 응답 스키마/버전 호환 정책
|
|
987
993
|
- `schemas/mcp-tool-response.schema.json`: 표준 도구 응답 JSON 스키마
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
@@ -329,7 +329,8 @@ function pickSectionPayload(section, context) {
|
|
|
329
329
|
runFinishedAt: runMeta.runFinishedAt || null,
|
|
330
330
|
outputHash: runMeta.outputHash || null,
|
|
331
331
|
recordsHash: runMeta.recordsHash || null,
|
|
332
|
-
htmlHash: runMeta.htmlHash || null
|
|
332
|
+
htmlHash: runMeta.htmlHash || null,
|
|
333
|
+
generatedArtifacts: runMeta.generatedArtifacts || null
|
|
333
334
|
};
|
|
334
335
|
}
|
|
335
336
|
return {};
|
|
@@ -428,6 +429,12 @@ function renderMarkdown(bundle, templateName = "default") {
|
|
|
428
429
|
const fields = bundle && bundle.fields && Array.isArray(bundle.fields.effectiveFields)
|
|
429
430
|
? bundle.fields.effectiveFields
|
|
430
431
|
: ["resourceType", "resourceId", "profile", "region", "state"];
|
|
432
|
+
const runMeta = bundle && bundle.runMeta && typeof bundle.runMeta === "object"
|
|
433
|
+
? bundle.runMeta
|
|
434
|
+
: {};
|
|
435
|
+
const generatedArtifacts = runMeta.generatedArtifacts && typeof runMeta.generatedArtifacts === "object"
|
|
436
|
+
? runMeta.generatedArtifacts
|
|
437
|
+
: {};
|
|
431
438
|
|
|
432
439
|
const lines = [];
|
|
433
440
|
lines.push("# AWS Discovery Output");
|
|
@@ -451,6 +458,18 @@ function renderMarkdown(bundle, templateName = "default") {
|
|
|
451
458
|
lines.push(`| runtimeSnapshotSuccessCount | ${summary.runtimeSnapshotSuccessCount || 0} |`);
|
|
452
459
|
lines.push("");
|
|
453
460
|
|
|
461
|
+
lines.push("## Generated Artifacts");
|
|
462
|
+
lines.push("");
|
|
463
|
+
lines.push("| artifact | path |");
|
|
464
|
+
lines.push("|---|---|");
|
|
465
|
+
lines.push(`| output | ${generatedArtifacts.outputPath || (generatedArtifacts.outputChannel || "stdout")} |`);
|
|
466
|
+
lines.push(`| htmlReport | ${generatedArtifacts.htmlReportPath || "off"} |`);
|
|
467
|
+
lines.push(`| topology | ${generatedArtifacts.topologyPath || "off"} |`);
|
|
468
|
+
lines.push(`| relationships | ${generatedArtifacts.relationshipsPath || "off"} |`);
|
|
469
|
+
lines.push(`| governanceLog | ${generatedArtifacts.governanceLogPath || "off"} |`);
|
|
470
|
+
lines.push(`| incidentPayload | ${generatedArtifacts.incidentPayloadPath || "off"} |`);
|
|
471
|
+
lines.push("");
|
|
472
|
+
|
|
454
473
|
if (template === "default" && sections.length) {
|
|
455
474
|
lines.push("## Sections");
|
|
456
475
|
lines.push("");
|
package/bin/mcp-aws-manager.js
CHANGED
|
@@ -2311,6 +2311,16 @@ function listLocalAwsProfiles() {
|
|
|
2311
2311
|
}
|
|
2312
2312
|
|
|
2313
2313
|
function runCLICommand(cliBin, args, options = {}) {
|
|
2314
|
+
// Cursor 2.x no longer reliably supports `cursor mcp ...` CLI subcommands.
|
|
2315
|
+
// Calling it can open editor tabs named like "mcp", "add", "get".
|
|
2316
|
+
if (String(cliBin).toLowerCase() === "cursor" && Array.isArray(args) && String(args[0] || "").toLowerCase() === "mcp") {
|
|
2317
|
+
return {
|
|
2318
|
+
status: 2,
|
|
2319
|
+
stdout: "",
|
|
2320
|
+
stderr: "cursor mcp subcommand is disabled; use ~/.cursor/mcp.json registration"
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2314
2324
|
const execOptions = {
|
|
2315
2325
|
cwd: process.cwd(),
|
|
2316
2326
|
env: process.env,
|
|
@@ -2351,7 +2361,12 @@ function commandExists(bin, checkArgs) {
|
|
|
2351
2361
|
}
|
|
2352
2362
|
|
|
2353
2363
|
function clientHelpAttempts(cliBin) {
|
|
2354
|
-
if (cliBin === "cursor"
|
|
2364
|
+
if (cliBin === "cursor") {
|
|
2365
|
+
return [
|
|
2366
|
+
["--help"]
|
|
2367
|
+
];
|
|
2368
|
+
}
|
|
2369
|
+
if (cliBin === "windsurf" || cliBin === "antigravity") {
|
|
2355
2370
|
return [
|
|
2356
2371
|
["mcp", "--help"],
|
|
2357
2372
|
["--help"]
|
|
@@ -2360,6 +2375,226 @@ function clientHelpAttempts(cliBin) {
|
|
|
2360
2375
|
return [["mcp", "--help"]];
|
|
2361
2376
|
}
|
|
2362
2377
|
|
|
2378
|
+
function normalizeComparableText(value) {
|
|
2379
|
+
const text = String(value == null ? "" : value).trim();
|
|
2380
|
+
return process.platform === "win32" ? text.toLowerCase() : text;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
function normalizeComparableArgs(args) {
|
|
2384
|
+
return Array.isArray(args) ? args.map((arg) => normalizeComparableText(arg)) : [];
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
function dedupePaths(paths) {
|
|
2388
|
+
const seen = new Set();
|
|
2389
|
+
const out = [];
|
|
2390
|
+
for (const candidate of paths) {
|
|
2391
|
+
const resolved = path.resolve(String(candidate || ""));
|
|
2392
|
+
const key = normalizeComparableText(resolved);
|
|
2393
|
+
if (!key || seen.has(key)) continue;
|
|
2394
|
+
seen.add(key);
|
|
2395
|
+
out.push(resolved);
|
|
2396
|
+
}
|
|
2397
|
+
return out;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
function cursorMcpConfigCandidates() {
|
|
2401
|
+
const explicitPath = envText("CURSOR_MCP_CONFIG_PATH");
|
|
2402
|
+
if (explicitPath) {
|
|
2403
|
+
return dedupePaths([path.resolve(explicitPath)]);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
const home = os.homedir();
|
|
2407
|
+
const candidates = [
|
|
2408
|
+
path.join(home, ".cursor", "mcp.json")
|
|
2409
|
+
];
|
|
2410
|
+
|
|
2411
|
+
if (process.platform === "win32") {
|
|
2412
|
+
const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
2413
|
+
candidates.push(path.join(appData, "Cursor", "User", "mcp.json"));
|
|
2414
|
+
} else if (process.platform === "darwin") {
|
|
2415
|
+
candidates.push(path.join(home, "Library", "Application Support", "Cursor", "User", "mcp.json"));
|
|
2416
|
+
} else {
|
|
2417
|
+
candidates.push(path.join(home, ".config", "Cursor", "User", "mcp.json"));
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
return dedupePaths(candidates);
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
function cursorMcpConfigPath() {
|
|
2424
|
+
const candidates = cursorMcpConfigCandidates();
|
|
2425
|
+
for (const candidate of candidates) {
|
|
2426
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
2427
|
+
}
|
|
2428
|
+
return candidates[0];
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
function parseCursorMcpConfigContent(raw, filePath) {
|
|
2432
|
+
const normalizedRaw = String(raw || "").replace(/^\uFEFF/, "");
|
|
2433
|
+
if (!normalizedRaw.trim()) {
|
|
2434
|
+
return { ok: true, data: { mcpServers: {} } };
|
|
2435
|
+
}
|
|
2436
|
+
try {
|
|
2437
|
+
const parsed = JSON.parse(normalizedRaw);
|
|
2438
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2439
|
+
return { ok: false, error: `Cursor MCP config must be a JSON object. (${filePath})` };
|
|
2440
|
+
}
|
|
2441
|
+
const mcpServers = parsed.mcpServers && typeof parsed.mcpServers === "object" && !Array.isArray(parsed.mcpServers)
|
|
2442
|
+
? parsed.mcpServers
|
|
2443
|
+
: {};
|
|
2444
|
+
return { ok: true, data: { ...parsed, mcpServers } };
|
|
2445
|
+
} catch (error) {
|
|
2446
|
+
return {
|
|
2447
|
+
ok: false,
|
|
2448
|
+
error: `Failed to parse Cursor MCP config: ${error && error.message ? error.message : String(error)} (${filePath})`
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
function readCursorMcpConfig() {
|
|
2454
|
+
const candidates = cursorMcpConfigCandidates();
|
|
2455
|
+
const errors = [];
|
|
2456
|
+
for (const candidate of candidates) {
|
|
2457
|
+
if (!fs.existsSync(candidate)) continue;
|
|
2458
|
+
try {
|
|
2459
|
+
const raw = fs.readFileSync(candidate, "utf8");
|
|
2460
|
+
const parsed = parseCursorMcpConfigContent(raw, candidate);
|
|
2461
|
+
if (!parsed.ok) {
|
|
2462
|
+
errors.push(parsed.error);
|
|
2463
|
+
continue;
|
|
2464
|
+
}
|
|
2465
|
+
return {
|
|
2466
|
+
ok: true,
|
|
2467
|
+
filePath: candidate,
|
|
2468
|
+
exists: true,
|
|
2469
|
+
data: parsed.data,
|
|
2470
|
+
candidates
|
|
2471
|
+
};
|
|
2472
|
+
} catch (error) {
|
|
2473
|
+
errors.push(`Failed to read Cursor MCP config: ${error && error.message ? error.message : String(error)} (${candidate})`);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
if (errors.length) {
|
|
2478
|
+
return {
|
|
2479
|
+
ok: false,
|
|
2480
|
+
filePath: candidates[0],
|
|
2481
|
+
error: errors.join("; ")
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
return { ok: true, filePath: candidates[0], exists: false, data: { mcpServers: {} }, candidates };
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
function writeCursorMcpConfig(filePath, data, mirrorPaths = []) {
|
|
2489
|
+
const allPaths = dedupePaths([filePath, ...(Array.isArray(mirrorPaths) ? mirrorPaths : [])]);
|
|
2490
|
+
const written = [];
|
|
2491
|
+
const mirrorFailures = [];
|
|
2492
|
+
|
|
2493
|
+
for (let i = 0; i < allPaths.length; i += 1) {
|
|
2494
|
+
const currentPath = allPaths[i];
|
|
2495
|
+
try {
|
|
2496
|
+
fs.mkdirSync(path.dirname(currentPath), { recursive: true });
|
|
2497
|
+
fs.writeFileSync(currentPath, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
2498
|
+
written.push(currentPath);
|
|
2499
|
+
} catch (error) {
|
|
2500
|
+
const message = `${currentPath}: ${error && error.message ? error.message : String(error)}`;
|
|
2501
|
+
if (i === 0) {
|
|
2502
|
+
throw new Error(message);
|
|
2503
|
+
}
|
|
2504
|
+
mirrorFailures.push(message);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
return { written, mirrorFailures };
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
function cursorRegistrationMatches(entry, mcpCommand, mcpArgs = []) {
|
|
2512
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return false;
|
|
2513
|
+
const entryCommand = normalizeComparableText(entry.command);
|
|
2514
|
+
if (!entryCommand) return false;
|
|
2515
|
+
if (normalizeComparableText(mcpCommand) && entryCommand !== normalizeComparableText(mcpCommand)) {
|
|
2516
|
+
return false;
|
|
2517
|
+
}
|
|
2518
|
+
const expectedArgs = normalizeComparableArgs(mcpArgs);
|
|
2519
|
+
const actualArgs = normalizeComparableArgs(entry.args);
|
|
2520
|
+
if (expectedArgs.length !== actualArgs.length) return false;
|
|
2521
|
+
for (let i = 0; i < expectedArgs.length; i += 1) {
|
|
2522
|
+
if (expectedArgs[i] !== actualArgs[i]) return false;
|
|
2523
|
+
}
|
|
2524
|
+
return true;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
function ensureCursorRegistered(serverName, mcpCommand, mcpArgs = [], force = false) {
|
|
2528
|
+
const loaded = readCursorMcpConfig();
|
|
2529
|
+
if (!loaded.ok) {
|
|
2530
|
+
return { ok: false, action: "cursor config invalid", detail: `${loaded.error} (${loaded.filePath})` };
|
|
2531
|
+
}
|
|
2532
|
+
const desiredEntry = {
|
|
2533
|
+
command: String(mcpCommand || ""),
|
|
2534
|
+
args: Array.isArray(mcpArgs) ? mcpArgs.map((arg) => String(arg)) : []
|
|
2535
|
+
};
|
|
2536
|
+
const config = loaded.data;
|
|
2537
|
+
const current = config.mcpServers[serverName];
|
|
2538
|
+
if (force || !cursorRegistrationMatches(current, desiredEntry.command, desiredEntry.args)) {
|
|
2539
|
+
config.mcpServers[serverName] = desiredEntry;
|
|
2540
|
+
try {
|
|
2541
|
+
const mirrors = (loaded.candidates || []).filter(
|
|
2542
|
+
(candidate) => normalizeComparableText(candidate) !== normalizeComparableText(loaded.filePath)
|
|
2543
|
+
);
|
|
2544
|
+
const writeResult = writeCursorMcpConfig(loaded.filePath, config, mirrors);
|
|
2545
|
+
const detailParts = [`primary=${loaded.filePath}`];
|
|
2546
|
+
if (writeResult.written.length > 1) {
|
|
2547
|
+
detailParts.push(`mirrored=${writeResult.written.slice(1).join(",")}`);
|
|
2548
|
+
}
|
|
2549
|
+
if (writeResult.mirrorFailures.length) {
|
|
2550
|
+
detailParts.push(`mirror_failures=${writeResult.mirrorFailures.join(" | ")}`);
|
|
2551
|
+
}
|
|
2552
|
+
return { ok: true, action: "registered", detail: detailParts.join("; ") };
|
|
2553
|
+
} catch (error) {
|
|
2554
|
+
return {
|
|
2555
|
+
ok: false,
|
|
2556
|
+
action: "cursor registration failed",
|
|
2557
|
+
detail: `${error && error.message ? error.message : String(error)} (${loaded.filePath})`
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
return { ok: true, action: "already registered", detail: `primary=${loaded.filePath}` };
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
function removeCursorRegistration(serverName) {
|
|
2565
|
+
const loaded = readCursorMcpConfig();
|
|
2566
|
+
if (!loaded.ok || !loaded.exists) return;
|
|
2567
|
+
if (!Object.prototype.hasOwnProperty.call(loaded.data.mcpServers, serverName)) return;
|
|
2568
|
+
delete loaded.data.mcpServers[serverName];
|
|
2569
|
+
try {
|
|
2570
|
+
const mirrors = (loaded.candidates || []).filter(
|
|
2571
|
+
(candidate) => normalizeComparableText(candidate) !== normalizeComparableText(loaded.filePath)
|
|
2572
|
+
);
|
|
2573
|
+
writeCursorMcpConfig(loaded.filePath, loaded.data, mirrors);
|
|
2574
|
+
} catch (_) {
|
|
2575
|
+
// ignore best-effort cleanup
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
function isCursorRegistered(serverName, mcpCommand, mcpArgs = []) {
|
|
2580
|
+
const candidates = cursorMcpConfigCandidates();
|
|
2581
|
+
for (const candidate of candidates) {
|
|
2582
|
+
if (!fs.existsSync(candidate)) continue;
|
|
2583
|
+
try {
|
|
2584
|
+
const raw = fs.readFileSync(candidate, "utf8");
|
|
2585
|
+
const parsed = parseCursorMcpConfigContent(raw, candidate);
|
|
2586
|
+
if (!parsed.ok) continue;
|
|
2587
|
+
const entry = parsed.data && parsed.data.mcpServers ? parsed.data.mcpServers[serverName] : null;
|
|
2588
|
+
if (cursorRegistrationMatches(entry, mcpCommand, mcpArgs)) {
|
|
2589
|
+
return true;
|
|
2590
|
+
}
|
|
2591
|
+
} catch (_) {
|
|
2592
|
+
continue;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
return false;
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2363
2598
|
function clientGetAttempts(cliBin, serverName) {
|
|
2364
2599
|
if (cliBin === "claude") {
|
|
2365
2600
|
return [
|
|
@@ -2427,13 +2662,22 @@ function clientAddAttempts(cliBin, serverName, mcpCommand, mcpArgs = []) {
|
|
|
2427
2662
|
}
|
|
2428
2663
|
|
|
2429
2664
|
function removeRegistration(cliBin, serverName) {
|
|
2665
|
+
if (cliBin === "cursor") {
|
|
2666
|
+
removeCursorRegistration(serverName);
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2430
2669
|
const attempts = clientRemoveAttempts(cliBin, serverName);
|
|
2431
2670
|
for (const args of attempts) {
|
|
2432
2671
|
runCLICommand(cliBin, args, { stdio: "ignore" });
|
|
2433
2672
|
}
|
|
2434
2673
|
}
|
|
2435
2674
|
|
|
2436
|
-
function isRegistered(cliBin, serverName) {
|
|
2675
|
+
function isRegistered(cliBin, serverName, target = null) {
|
|
2676
|
+
if (cliBin === "cursor") {
|
|
2677
|
+
const mcpCommand = target && target.mcpCommand ? target.mcpCommand : "";
|
|
2678
|
+
const mcpArgs = target && Array.isArray(target.mcpArgs) ? target.mcpArgs : [];
|
|
2679
|
+
return isCursorRegistered(serverName, mcpCommand, mcpArgs);
|
|
2680
|
+
}
|
|
2437
2681
|
const attempts = clientGetAttempts(cliBin, serverName);
|
|
2438
2682
|
for (const args of attempts) {
|
|
2439
2683
|
const run = runCLICommand(cliBin, args, { stdio: "ignore" });
|
|
@@ -2531,11 +2775,36 @@ function runSetupInternal(config, options = {}) {
|
|
|
2531
2775
|
|
|
2532
2776
|
for (const cliBin of clients) {
|
|
2533
2777
|
for (const target of activeTargets) {
|
|
2534
|
-
const alreadyRegistered = isRegistered(cliBin, target.serverName);
|
|
2778
|
+
const alreadyRegistered = isRegistered(cliBin, target.serverName, target);
|
|
2535
2779
|
if (config.force || !ensureOnly || alreadyRegistered) {
|
|
2536
2780
|
removeRegistration(cliBin, target.serverName);
|
|
2537
2781
|
}
|
|
2538
2782
|
|
|
2783
|
+
if (cliBin === "cursor") {
|
|
2784
|
+
const registered = ensureCursorRegistered(target.serverName, target.mcpCommand, target.mcpArgs || [], config.force);
|
|
2785
|
+
if (registered.ok) {
|
|
2786
|
+
const action = ensureOnly
|
|
2787
|
+
? (alreadyRegistered ? "updated" : "registered")
|
|
2788
|
+
: (alreadyRegistered ? "re-registered" : "registered");
|
|
2789
|
+
results.push({
|
|
2790
|
+
cliBin,
|
|
2791
|
+
serverName: target.serverName,
|
|
2792
|
+
ok: true,
|
|
2793
|
+
action: `${action} (cursor mcp.json)`,
|
|
2794
|
+
detail: registered.detail || ""
|
|
2795
|
+
});
|
|
2796
|
+
} else {
|
|
2797
|
+
results.push({
|
|
2798
|
+
cliBin,
|
|
2799
|
+
serverName: target.serverName,
|
|
2800
|
+
ok: false,
|
|
2801
|
+
action: registered.action || "registration failed",
|
|
2802
|
+
detail: registered.detail || "unknown"
|
|
2803
|
+
});
|
|
2804
|
+
}
|
|
2805
|
+
continue;
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2539
2808
|
const registered = tryRegister(cliBin, target.serverName, target.mcpCommand, target.mcpArgs || []);
|
|
2540
2809
|
if (registered.ok) {
|
|
2541
2810
|
const action = ensureOnly
|
|
@@ -2554,7 +2823,7 @@ function runSetupInternal(config, options = {}) {
|
|
|
2554
2823
|
|
|
2555
2824
|
for (const row of results) {
|
|
2556
2825
|
process.stdout.write(`${row.cliBin}/${row.serverName}: ${row.action}\n`);
|
|
2557
|
-
if (!row.ok
|
|
2826
|
+
if (row.detail && (!row.ok || /\bcursor mcp\.json\b/i.test(String(row.action || "")))) {
|
|
2558
2827
|
process.stdout.write(` detail: ${row.detail}\n`);
|
|
2559
2828
|
}
|
|
2560
2829
|
}
|
|
@@ -2603,7 +2872,7 @@ function runDoctor(config) {
|
|
|
2603
2872
|
|
|
2604
2873
|
foundClient = true;
|
|
2605
2874
|
for (const target of targets) {
|
|
2606
|
-
const registered = isRegistered(cliBin, target.serverName);
|
|
2875
|
+
const registered = isRegistered(cliBin, target.serverName, target);
|
|
2607
2876
|
if (registered) {
|
|
2608
2877
|
process.stdout.write(`${cliBin}: registered (${target.serverName})\n`);
|
|
2609
2878
|
} else {
|
|
@@ -8775,6 +9044,52 @@ function writeJsonArtifact(targetPath, payload, label, warnings, requiredActions
|
|
|
8775
9044
|
}
|
|
8776
9045
|
}
|
|
8777
9046
|
|
|
9047
|
+
function buildGeneratedArtifacts(config, state = {}) {
|
|
9048
|
+
return {
|
|
9049
|
+
outputPath: config && config.outPath ? config.outPath : null,
|
|
9050
|
+
outputChannel: config && config.outPath ? "file" : "stdout",
|
|
9051
|
+
htmlReportPath: state.htmlReportPath || null,
|
|
9052
|
+
topologyPath: state.topologyPath || null,
|
|
9053
|
+
relationshipsPath: state.relationshipsPath || null,
|
|
9054
|
+
governanceLogPath: state.governanceLogPath || null,
|
|
9055
|
+
incidentPayloadPath: state.incidentPayloadPath || null
|
|
9056
|
+
};
|
|
9057
|
+
}
|
|
9058
|
+
|
|
9059
|
+
function generatedArtifactsSummary(artifacts) {
|
|
9060
|
+
const item = artifacts && typeof artifacts === "object" ? artifacts : {};
|
|
9061
|
+
return [
|
|
9062
|
+
`output=${item.outputPath || (item.outputChannel || "stdout")}`,
|
|
9063
|
+
`html=${item.htmlReportPath || "off"}`,
|
|
9064
|
+
`topology=${item.topologyPath || "off"}`,
|
|
9065
|
+
`relationships=${item.relationshipsPath || "off"}`,
|
|
9066
|
+
`governance=${item.governanceLogPath || "off"}`,
|
|
9067
|
+
`incident=${item.incidentPayloadPath || "off"}`
|
|
9068
|
+
].join(", ");
|
|
9069
|
+
}
|
|
9070
|
+
|
|
9071
|
+
function syncRunMetaToPresentationBundle(presentationBundle, runMeta) {
|
|
9072
|
+
if (!presentationBundle || typeof presentationBundle !== "object") return;
|
|
9073
|
+
const meta = runMeta && typeof runMeta === "object" ? { ...runMeta } : {};
|
|
9074
|
+
presentationBundle.runMeta = meta;
|
|
9075
|
+
|
|
9076
|
+
const sections = presentationBundle.sections;
|
|
9077
|
+
const payload = sections && typeof sections === "object" ? sections.payload : null;
|
|
9078
|
+
const governance = payload && typeof payload === "object" ? payload.governance : null;
|
|
9079
|
+
if (!governance || typeof governance !== "object") return;
|
|
9080
|
+
|
|
9081
|
+
payload.governance = {
|
|
9082
|
+
...governance,
|
|
9083
|
+
runId: meta.runId || null,
|
|
9084
|
+
runStartedAt: meta.runStartedAt || null,
|
|
9085
|
+
runFinishedAt: meta.runFinishedAt || null,
|
|
9086
|
+
outputHash: meta.outputHash || null,
|
|
9087
|
+
recordsHash: meta.recordsHash || null,
|
|
9088
|
+
htmlHash: meta.htmlHash || null,
|
|
9089
|
+
generatedArtifacts: meta.generatedArtifacts || null
|
|
9090
|
+
};
|
|
9091
|
+
}
|
|
9092
|
+
|
|
8778
9093
|
function readLastGovernanceEntryHash(logPath) {
|
|
8779
9094
|
if (!logPath || !fs.existsSync(logPath)) return null;
|
|
8780
9095
|
try {
|
|
@@ -11985,6 +12300,14 @@ async function runWorkflow(config) {
|
|
|
11985
12300
|
incidentArtifactHash: incidentDispatch.artifactHash || null
|
|
11986
12301
|
};
|
|
11987
12302
|
const outputSummary = summarizeOutputRecords(outputRecords);
|
|
12303
|
+
const recordsHash = hashText(JSON.stringify(outputRecords));
|
|
12304
|
+
const runMeta = {
|
|
12305
|
+
...baseRunMeta,
|
|
12306
|
+
recordsHash,
|
|
12307
|
+
htmlOutPath: null,
|
|
12308
|
+
htmlHash: null,
|
|
12309
|
+
generatedArtifacts: null
|
|
12310
|
+
};
|
|
11988
12311
|
const presentationBundle = buildOutputBundle({
|
|
11989
12312
|
rawRecords: outputRecords,
|
|
11990
12313
|
summary: outputSummary,
|
|
@@ -11997,26 +12320,13 @@ async function runWorkflow(config) {
|
|
|
11997
12320
|
excludeFields: config.excludeFields,
|
|
11998
12321
|
sections: config.sections,
|
|
11999
12322
|
clientProfile: config.clientProfile,
|
|
12000
|
-
runMeta
|
|
12323
|
+
runMeta
|
|
12001
12324
|
});
|
|
12002
|
-
|
|
12003
|
-
const outputBody = renderOutput(config, presentationBundle);
|
|
12004
|
-
const outputHash = hashText(outputBody);
|
|
12005
|
-
const recordsHash = hashText(JSON.stringify(outputRecords));
|
|
12006
|
-
const runMeta = { ...baseRunMeta, outputHash, recordsHash };
|
|
12007
|
-
|
|
12008
|
-
presentationBundle.runMeta = {
|
|
12009
|
-
...(presentationBundle.runMeta && typeof presentationBundle.runMeta === "object" ? presentationBundle.runMeta : {}),
|
|
12010
|
-
...runMeta
|
|
12011
|
-
};
|
|
12012
|
-
writeOutput(config, outputBody);
|
|
12325
|
+
syncRunMetaToPresentationBundle(presentationBundle, runMeta);
|
|
12013
12326
|
const htmlPath = writeHtmlReport(config, outputRecords, domainInsights, runMeta, presentationBundle);
|
|
12014
12327
|
if (htmlPath) {
|
|
12015
12328
|
runMeta.htmlOutPath = htmlPath;
|
|
12016
12329
|
runMeta.htmlHash = hashText(fs.readFileSync(htmlPath, "utf8"));
|
|
12017
|
-
} else {
|
|
12018
|
-
runMeta.htmlOutPath = null;
|
|
12019
|
-
runMeta.htmlHash = null;
|
|
12020
12330
|
}
|
|
12021
12331
|
if (htmlPath && shouldAutoOpenHtml(config)) {
|
|
12022
12332
|
const opened = tryOpenFile(htmlPath);
|
|
@@ -12027,6 +12337,19 @@ async function runWorkflow(config) {
|
|
|
12027
12337
|
}
|
|
12028
12338
|
}
|
|
12029
12339
|
|
|
12340
|
+
runMeta.generatedArtifacts = buildGeneratedArtifacts(config, {
|
|
12341
|
+
htmlReportPath: runMeta.htmlOutPath,
|
|
12342
|
+
topologyPath: topologyWrite.path || null,
|
|
12343
|
+
relationshipsPath: relationshipsWrite.path || null,
|
|
12344
|
+
governanceLogPath: governancePersist.path || null,
|
|
12345
|
+
incidentPayloadPath: incidentDispatch.artifactPath || null
|
|
12346
|
+
});
|
|
12347
|
+
syncRunMetaToPresentationBundle(presentationBundle, runMeta);
|
|
12348
|
+
|
|
12349
|
+
const outputBody = renderOutput(config, presentationBundle);
|
|
12350
|
+
runMeta.outputHash = hashText(outputBody);
|
|
12351
|
+
writeOutput(config, outputBody);
|
|
12352
|
+
|
|
12030
12353
|
progress(config, 19, "END: emit execution summary and evidence metadata");
|
|
12031
12354
|
const outputEc2 = outputRecords.filter((r) => r.resourceType === "ec2");
|
|
12032
12355
|
const outputLambda = outputRecords.filter((r) => r.resourceType === "lambda");
|
|
@@ -12078,6 +12401,7 @@ async function runWorkflow(config) {
|
|
|
12078
12401
|
const ssmManaged = outputEc2.filter((r) => r.ssmManaged).length;
|
|
12079
12402
|
const ssmOnline = outputEc2.filter((r) => r.ssmOnline).length;
|
|
12080
12403
|
const publicCount = outputEc2.filter((r) => Boolean(r.publicIp)).length;
|
|
12404
|
+
eprint(`Artifacts: ${generatedArtifactsSummary(runMeta.generatedArtifacts)}`);
|
|
12081
12405
|
eprint(`Summary: execution_mode=${INTERNAL_BACKEND_ID}, manual_mode=${config.manualServerListPath ? "on" : "off"}, selected_surface=${selectedSurface}, selected_schema_tier=${selectedSchemaTier}, mode=${routing.modeInfo.mode}, mode_source=${routing.modeInfo.source}, profiles=${stats.profiles}, regions_scanned=${stats.regions}, region_errors=${stats.regionErrors}, ec2_scanned=${stats.instancesScanned}, lambda_scanned=${stats.lambdaFunctionsScanned}, alb_scanned=${stats.loadBalancersScanned}, targetgroup_scanned=${stats.targetGroupsScanned}, asg_scanned=${stats.autoScalingGroupsScanned}, rds_scanned=${stats.rdsInstancesScanned}, elasticache_scanned=${stats.elasticacheClustersScanned}, route53_zone_scanned=${stats.route53ZonesScanned}, vpc_scanned=${stats.vpcsScanned || 0}, subnet_scanned=${stats.subnetsScanned || 0}, security_group_scanned=${stats.securityGroupsScanned || 0}, ecs_cluster_scanned=${stats.ecsClustersScanned || 0}, ecs_service_scanned=${stats.ecsServicesScanned || 0}, s3_bucket_scanned=${stats.s3BucketsScanned || 0}, iam_role_scanned=${stats.iamRolesScanned || 0}, kms_key_scanned=${stats.kmsKeysScanned || 0}, cloudwatch_alarm_scanned=${stats.cloudwatchAlarmsScanned || 0}, cloudtrail_trail_scanned=${stats.cloudtrailTrailsScanned || 0}, config_recorder_scanned=${stats.configRecordersScanned || 0}, secret_scanned=${stats.secretsScanned || 0}, ssm_parameter_scanned=${stats.parametersScanned || 0}, ecr_repository_scanned=${stats.ecrRepositoriesScanned || 0}, dynamodb_table_scanned=${stats.dynamodbTablesScanned || 0}, sns_topic_scanned=${stats.snsTopicsScanned || 0}, eventbridge_bus_scanned=${stats.eventbridgeBusesScanned || 0}, sqs_queue_scanned=${stats.sqsQueuesScanned || 0}, acm_certificate_scanned=${stats.acmCertificatesScanned || 0}, kinesis_stream_scanned=${stats.kinesisStreamsScanned || 0}, msk_cluster_scanned=${stats.mskClustersScanned || 0}, budget_scanned=${stats.budgetsScanned || 0}, cost_anomaly_scanned=${stats.costAnomaliesScanned || 0}, ebs_volume_scanned=${stats.ebsVolumesScanned || 0}, efs_filesystem_scanned=${stats.efsFileSystemsScanned || 0}, eks_cluster_scanned=${stats.eksClustersScanned || 0}, apigateway_rest_api_scanned=${stats.apiGatewayRestApisScanned || 0}, apigatewayv2_api_scanned=${stats.apiGatewayV2ApisScanned || 0}, cloudfront_distribution_scanned=${stats.cloudFrontDistributionsScanned || 0}, waf_web_acl_scanned=${stats.wafWebAclsScanned || 0}, shield_protection_scanned=${stats.shieldProtectionsScanned || 0}, stepfunctions_scanned=${stats.stepFunctionsScanned || 0}, cloudwatch_log_group_scanned=${stats.cloudwatchLogGroupsScanned || 0}, xray_group_scanned=${stats.xrayGroupsScanned || 0}, inspector2_finding_scanned=${stats.inspector2FindingsScanned || 0}, redshift_cluster_scanned=${stats.redshiftClustersScanned || 0}, opensearch_domain_scanned=${stats.opensearchDomainsScanned || 0}, organizations_account_scanned=${stats.organizationsAccountsScanned || 0}, controltower_landing_zone_scanned=${stats.controlTowerLandingZonesScanned || 0}, manual_servers_scanned=${stats.manualServersScanned || 0}, manual_servers_loaded=${stats.manualServersLoaded || 0}, output_records=${outputRecords.length}, output_ec2=${outputEc2.length}, output_lambda=${outputLambda.length}, output_alb=${outputAlb.length}, output_target_group=${outputTargetGroups.length}, output_asg=${outputAsg.length}, output_rds=${outputRds.length}, output_elasticache=${outputElastiCache.length}, output_route53_zone=${outputRoute53.length}, output_vpc=${outputVpc.length}, output_subnet=${outputSubnet.length}, output_security_group=${outputSecurityGroup.length}, output_ecs_cluster=${outputEcsCluster.length}, output_ecs_service=${outputEcsService.length}, output_s3_bucket=${outputS3.length}, output_iam_role=${outputIam.length}, output_kms_key=${outputKms.length}, output_cloudwatch_alarm=${outputCloudwatch.length}, output_cloudtrail_trail=${outputCloudtrail.length}, output_config_recorder=${outputConfig.length}, output_secret=${outputSecret.length}, output_ssm_parameter=${outputParameter.length}, output_ecr_repository=${outputEcr.length}, output_dynamodb_table=${outputDynamodb.length}, output_sns_topic=${outputSns.length}, output_eventbridge_bus=${outputEventbridge.length}, output_sqs_queue=${outputSqs.length}, output_acm_certificate=${outputAcm.length}, output_kinesis_stream=${outputKinesis.length}, output_msk_cluster=${outputMsk.length}, output_budget=${outputBudget.length}, output_cost_anomaly=${outputCostAnomaly.length}, output_ebs_volume=${outputEbs.length}, output_efs_filesystem=${outputEfs.length}, output_eks_cluster=${outputEks.length}, output_apigateway_rest_api=${outputApiGateway.length}, output_apigatewayv2_api=${outputApiGatewayV2.length}, output_cloudfront_distribution=${outputCloudfront.length}, output_waf_web_acl=${outputWaf.length}, output_shield_protection=${outputShield.length}, output_stepfunctions=${outputStepFunctions.length}, output_cloudwatch_log_group=${outputCloudwatchLogs.length}, output_xray_group=${outputXray.length}, output_inspector2_finding=${outputInspector2.length}, output_redshift_cluster=${outputRedshift.length}, output_opensearch_domain=${outputOpensearch.length}, output_organizations_account=${outputOrganizations.length}, output_controltower_landing_zone=${outputControlTower.length}, ec2_public_ip_records=${publicCount}, ec2_ssm_managed=${ssmManaged}, ec2_ssm_online=${ssmOnline}, remediation_attempts=${runtimeStats.remediationAttempts}, remediation_changed=${runtimeStats.remediationChanged}, runtime_snapshot_attempted=${runtimeStats.snapshotAttempted}, runtime_snapshot_succeeded=${runtimeStats.snapshotSucceeded}, runtime_snapshot_ssm_attempted=${runtimeStats.snapshotSsmAttempted}, runtime_snapshot_ssm_succeeded=${runtimeStats.snapshotSsmSucceeded}, runtime_snapshot_ssh_attempted=${runtimeStats.snapshotSshAttempted}, runtime_snapshot_ssh_succeeded=${runtimeStats.snapshotSshSucceeded}, snapshot_profile=${config.snapshotProfile}, output_profile=${presentationBundle.outputProfile || "operator"}, output_profile_source=${presentationBundle.outputProfileSource || "default"}, client_profile=${presentationBundle.clientProfile || "none"}, renderer_template=${config.rendererTemplate || "default"}, topology_path=${topologyWrite.path || "off"}, relationships_path=${relationshipsWrite.path || "off"}, governance_persisted=${governancePersist.persisted ? "on" : "off"}, governance_chain_checked=${governanceVerify.checked ? "on" : "off"}, governance_chain_valid=${governanceVerify.valid == null ? "unknown" : (governanceVerify.valid ? "yes" : "no")}, incident_webhook_dispatched=${incidentDispatch.webhookDispatched ? "on" : "off"}, policy_pack=${integrationDispatch.activePolicyPack || "balanced"}, policy_gate=${policyDecision.allowed !== false ? "allow" : "blocked"}, integration_connectors_enabled=${integrationDispatch.enabledConnectors}, integration_incident_success=${integrationDispatch.incident ? integrationDispatch.incident.successCount : 0}, integration_incident_failure=${integrationDispatch.incident ? integrationDispatch.incident.failureCount : 0}, integration_action_success=${integrationDispatch.actionRequired ? integrationDispatch.actionRequired.successCount : 0}, integration_action_failure=${integrationDispatch.actionRequired ? integrationDispatch.actionRequired.failureCount : 0}, view_sections=${presentationBundle.sections && Array.isArray(presentationBundle.sections.effective) ? presentationBundle.sections.effective.join(",") : "none"}, view_fields=${presentationBundle.fields && Array.isArray(presentationBundle.fields.effectiveFields) ? presentationBundle.fields.effectiveFields.length : 0}, iac_stacks=${domainInsights.iac.scannedStacks || 0}, iac_drifted=${domainInsights.iac.driftedStacks || 0}, cicd_pipelines=${domainInsights.cicd.pipelinesScanned || 0}, cicd_failing=${domainInsights.cicd.pipelinesFailing || 0}, backup_plans=${domainInsights.backupDr.backupPlans || 0}, backup_protected=${domainInsights.backupDr.protectedResources || 0}, security_high_critical=${domainInsights.security.highOrCritical || 0}, finops_cost_7d_usd=${domainInsights.finops.totalCost7dUsd || 0}, app_runtime_coverage_pct=${domainInsights.application.runtimeCoveragePct == null ? "n/a" : domainInsights.application.runtimeCoveragePct}, app_degraded_hosts=${domainInsights.application.degradedHosts || 0}, app_ecs_degraded=${domainInsights.application.ecsServicesDegraded || 0}, app_managed_degraded=${domainInsights.application.managedServicesDegraded || 0}, app_lambda_degraded=${domainInsights.application.lambdaHealth && domainInsights.application.lambdaHealth.degraded ? domainInsights.application.lambdaHealth.degraded : 0}, incident_severity=${domainInsights.incident.severity || "none"}, warnings=${warnings.length}, required_actions=${requiredActions.length}`);
|
|
12082
12406
|
eprint(`EvidenceMeta: workflow_id=b60f0f18-cc45-4af9-a7c7-217284457759; schema=gesia.orflow.contract.v2; step_count=${TOTAL_STEPS}; execution_mode=${INTERNAL_BACKEND_ID}; run_id=${runId}; output_hash=${runMeta.outputHash}; records_hash=${runMeta.recordsHash}; html_hash=${runMeta.htmlHash || "n/a"}; topology_hash=${runMeta.topologyHash || "n/a"}; relationships_hash=${runMeta.relationshipsHash || "n/a"}; governance_entry_hash=${runMeta.governanceEntryHash || "n/a"}; governance_chain_valid=${runMeta.governanceChainValid == null ? "unknown" : (runMeta.governanceChainValid ? "yes" : "no")}; incident_artifact_hash=${runMeta.incidentArtifactHash || "n/a"}; policy_pack=${integrationDispatch.activePolicyPack || "balanced"}; policy_gate=${policyDecision.allowed !== false ? "allow" : "blocked"}; integration_connectors_enabled=${integrationDispatch.enabledConnectors}; inventory_ops=${operationPlan.inventoryOps.length}; runtime_ops=${operationPlan.runtimeOps.length}; analysis_ops=${operationPlan.analysisOps.length}`);
|
|
12083
12407
|
|
package/package.json
CHANGED