nexarch 0.6.1 → 0.6.3
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.
|
@@ -138,8 +138,6 @@ export async function checkIn(args) {
|
|
|
138
138
|
console.log(`Type : ${cmd.command_type}`);
|
|
139
139
|
console.log(`Target : ${cmd.target_entity_key ?? "(any)"}`);
|
|
140
140
|
console.log(`Priority : ${cmd.priority}`);
|
|
141
|
-
if (cmd.resolved_execution_mode)
|
|
142
|
-
console.log(`Mode : ${cmd.resolved_execution_mode}`);
|
|
143
141
|
if (cmd.resolved_runtime_handler)
|
|
144
142
|
console.log(`Runtime : ${cmd.resolved_runtime_handler}`);
|
|
145
143
|
if (cmd.command_type_version != null)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import process from "process";
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { requireCredentials } from "../lib/credentials.js";
|
|
6
|
+
import { callMcpTool } from "../lib/mcp.js";
|
|
7
|
+
function parseFlag(args, flag) {
|
|
8
|
+
return args.includes(flag);
|
|
9
|
+
}
|
|
10
|
+
function parseOptionValue(args, option) {
|
|
11
|
+
const idx = args.indexOf(option);
|
|
12
|
+
if (idx === -1)
|
|
13
|
+
return null;
|
|
14
|
+
const v = args[idx + 1];
|
|
15
|
+
if (!v || v.startsWith("--"))
|
|
16
|
+
return null;
|
|
17
|
+
return v;
|
|
18
|
+
}
|
|
19
|
+
function parseMultiOptionValues(args, option) {
|
|
20
|
+
const values = [];
|
|
21
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
22
|
+
if (args[i] !== option)
|
|
23
|
+
continue;
|
|
24
|
+
const v = args[i + 1];
|
|
25
|
+
if (!v || v.startsWith("--"))
|
|
26
|
+
continue;
|
|
27
|
+
values.push(v);
|
|
28
|
+
}
|
|
29
|
+
return values;
|
|
30
|
+
}
|
|
31
|
+
function parseToolText(result) {
|
|
32
|
+
const text = result.content?.[0]?.text ?? "{}";
|
|
33
|
+
return JSON.parse(text);
|
|
34
|
+
}
|
|
35
|
+
function loadIdentityAgentKey() {
|
|
36
|
+
const identityPath = join(homedir(), ".nexarch", "identity.json");
|
|
37
|
+
if (!existsSync(identityPath))
|
|
38
|
+
return null;
|
|
39
|
+
try {
|
|
40
|
+
const data = JSON.parse(readFileSync(identityPath, "utf8"));
|
|
41
|
+
return data.agentKey ?? null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function parseFindingToken(token) {
|
|
48
|
+
const [policyControlId, policyRuleId, resultRaw, rationaleRaw, missingRaw] = token.split("|");
|
|
49
|
+
const result = (resultRaw ?? "").trim().toLowerCase();
|
|
50
|
+
if (!policyControlId?.trim() || !policyRuleId?.trim() || !result) {
|
|
51
|
+
throw new Error("Each --finding must be: <policyControlId>|<policyRuleId>|<pass|partial|fail>|<rationale>|<missing1;missing2>");
|
|
52
|
+
}
|
|
53
|
+
if (result !== "pass" && result !== "partial" && result !== "fail") {
|
|
54
|
+
throw new Error(`Invalid finding result '${resultRaw}'. Use pass|partial|fail.`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
policyControlId: policyControlId.trim(),
|
|
58
|
+
policyRuleId: policyRuleId.trim(),
|
|
59
|
+
result,
|
|
60
|
+
...(rationaleRaw?.trim() ? { rationale: rationaleRaw.trim() } : {}),
|
|
61
|
+
...(missingRaw?.trim() ? { missingRequirements: missingRaw.split(";").map((m) => m.trim()).filter(Boolean) } : {}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function parseFindings(args) {
|
|
65
|
+
const findingsJson = parseOptionValue(args, "--findings-json");
|
|
66
|
+
if (findingsJson) {
|
|
67
|
+
let parsed;
|
|
68
|
+
try {
|
|
69
|
+
parsed = JSON.parse(findingsJson);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new Error("--findings-json must be valid JSON array");
|
|
73
|
+
}
|
|
74
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
75
|
+
throw new Error("--findings-json must be a non-empty JSON array");
|
|
76
|
+
}
|
|
77
|
+
return parsed.map((item) => {
|
|
78
|
+
const value = item;
|
|
79
|
+
const result = String(value.result ?? "").toLowerCase();
|
|
80
|
+
if (!value.policyControlId || !value.policyRuleId || !result) {
|
|
81
|
+
throw new Error("Each finding must include policyControlId, policyRuleId, result");
|
|
82
|
+
}
|
|
83
|
+
if (result !== "pass" && result !== "partial" && result !== "fail") {
|
|
84
|
+
throw new Error(`Invalid finding result '${String(value.result)}'. Use pass|partial|fail.`);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
policyControlId: String(value.policyControlId),
|
|
88
|
+
policyRuleId: String(value.policyRuleId),
|
|
89
|
+
result,
|
|
90
|
+
...(value.rationale ? { rationale: String(value.rationale) } : {}),
|
|
91
|
+
...(Array.isArray(value.missingRequirements) ? { missingRequirements: value.missingRequirements.map(String) } : {}),
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const tokens = parseMultiOptionValues(args, "--finding");
|
|
96
|
+
if (tokens.length === 0) {
|
|
97
|
+
throw new Error("Provide findings via --finding (repeatable) or --findings-json '<json-array>'");
|
|
98
|
+
}
|
|
99
|
+
return tokens.map(parseFindingToken);
|
|
100
|
+
}
|
|
101
|
+
export async function policyAuditSubmit(args) {
|
|
102
|
+
const asJson = parseFlag(args, "--json");
|
|
103
|
+
const commandId = parseOptionValue(args, "--command-id") ?? parseOptionValue(args, "--id");
|
|
104
|
+
const applicationEntityKey = parseOptionValue(args, "--application-key") ?? parseOptionValue(args, "--entity");
|
|
105
|
+
const agentKey = parseOptionValue(args, "--agent-key") ?? loadIdentityAgentKey();
|
|
106
|
+
if (!commandId) {
|
|
107
|
+
console.error("error: --command-id <uuid> is required");
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
if (!applicationEntityKey) {
|
|
111
|
+
console.error("error: --application-key <externalKey> is required (e.g. application:bad-driving)");
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
const findings = parseFindings(args);
|
|
115
|
+
const creds = requireCredentials();
|
|
116
|
+
const raw = await callMcpTool("nexarch_submit_policy_audit", {
|
|
117
|
+
commandId,
|
|
118
|
+
applicationEntityKey,
|
|
119
|
+
...(agentKey ? { agentKey } : {}),
|
|
120
|
+
findings,
|
|
121
|
+
companyId: creds.companyId,
|
|
122
|
+
}, { companyId: creds.companyId });
|
|
123
|
+
const result = parseToolText(raw);
|
|
124
|
+
if (asJson) {
|
|
125
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!result.ok) {
|
|
129
|
+
console.error(`Policy audit submit failed${result.error ? `: ${result.error}` : ""}`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
console.log(`Policy audit submitted for ${applicationEntityKey}.`);
|
|
133
|
+
if (result.runId)
|
|
134
|
+
console.log(`Run ID: ${result.runId}`);
|
|
135
|
+
if (result.summary) {
|
|
136
|
+
console.log(`Summary: ${result.summary.passCount ?? 0} pass, ${result.summary.partialCount ?? 0} partial, ${result.summary.failCount ?? 0} fail`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import process from "process";
|
|
2
|
+
import { requireCredentials } from "../lib/credentials.js";
|
|
3
|
+
import { callMcpTool } from "../lib/mcp.js";
|
|
4
|
+
function parseFlag(args, flag) {
|
|
5
|
+
return args.includes(flag);
|
|
6
|
+
}
|
|
7
|
+
function parseOptionValue(args, option) {
|
|
8
|
+
const idx = args.indexOf(option);
|
|
9
|
+
if (idx === -1)
|
|
10
|
+
return null;
|
|
11
|
+
const v = args[idx + 1];
|
|
12
|
+
if (!v || v.startsWith("--"))
|
|
13
|
+
return null;
|
|
14
|
+
return v;
|
|
15
|
+
}
|
|
16
|
+
function parseToolText(result) {
|
|
17
|
+
const text = result.content?.[0]?.text ?? "{}";
|
|
18
|
+
return JSON.parse(text);
|
|
19
|
+
}
|
|
20
|
+
export async function policyControls(args) {
|
|
21
|
+
const asJson = parseFlag(args, "--json");
|
|
22
|
+
const entity = parseOptionValue(args, "--entity") ?? parseOptionValue(args, "--application-key");
|
|
23
|
+
if (!entity) {
|
|
24
|
+
console.error("error: --entity <externalKey> is required (e.g. application:bad-driving)");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const creds = requireCredentials();
|
|
28
|
+
const raw = await callMcpTool("nexarch_get_entity_policy_controls", { entityExternalKey: entity, companyId: creds.companyId }, { companyId: creds.companyId });
|
|
29
|
+
const result = parseToolText(raw);
|
|
30
|
+
if (asJson) {
|
|
31
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (!result.found) {
|
|
35
|
+
console.log(`Entity not found: ${entity}`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
console.log(`Policy controls for ${result.entityExternalKey ?? entity}`);
|
|
39
|
+
console.log(`Controls: ${result.controlCount ?? 0}`);
|
|
40
|
+
for (const control of result.controls ?? []) {
|
|
41
|
+
const rules = control.rules ?? [];
|
|
42
|
+
console.log(`\n- ${control.name} (${control.id})`);
|
|
43
|
+
if (rules.length === 0) {
|
|
44
|
+
console.log(" (no rules)");
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
for (const rule of rules) {
|
|
48
|
+
const level = rule.requirementLevel ? ` [${rule.requirementLevel}]` : "";
|
|
49
|
+
console.log(` • ${rule.name} (${rule.id})${level}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,8 @@ import { resolveNames } from "./commands/resolve-names.js";
|
|
|
15
15
|
import { checkIn } from "./commands/check-in.js";
|
|
16
16
|
import { commandDone } from "./commands/command-done.js";
|
|
17
17
|
import { commandFail } from "./commands/command-fail.js";
|
|
18
|
+
import { policyControls } from "./commands/policy-controls.js";
|
|
19
|
+
import { policyAuditSubmit } from "./commands/policy-audit-submit.js";
|
|
18
20
|
const [, , command, ...args] = process.argv;
|
|
19
21
|
const commands = {
|
|
20
22
|
login,
|
|
@@ -33,6 +35,8 @@ const commands = {
|
|
|
33
35
|
"check-in": checkIn,
|
|
34
36
|
"command-done": commandDone,
|
|
35
37
|
"command-fail": commandFail,
|
|
38
|
+
"policy-controls": policyControls,
|
|
39
|
+
"policy-audit-submit": policyAuditSubmit,
|
|
36
40
|
};
|
|
37
41
|
async function main() {
|
|
38
42
|
if (command === "agent") {
|
|
@@ -129,6 +133,18 @@ Usage:
|
|
|
129
133
|
Options: --id <commandId> (required)
|
|
130
134
|
--error <message> (required)
|
|
131
135
|
--json
|
|
136
|
+
nexarch policy-controls
|
|
137
|
+
Fetch policy controls/rules assigned to an entity (for policy audits).
|
|
138
|
+
Options: --entity <externalKey> (required, e.g. application:bad-driving)
|
|
139
|
+
--json
|
|
140
|
+
nexarch policy-audit-submit
|
|
141
|
+
Submit structured policy findings (writes policy_audit_finding rows).
|
|
142
|
+
Options: --command-id <id> (required)
|
|
143
|
+
--application-key <key> (required)
|
|
144
|
+
--agent-key <key> (optional; defaults from identity)
|
|
145
|
+
--finding <controlId|ruleId|result|rationale|missing1;missing2> (repeatable)
|
|
146
|
+
--findings-json <json-array>
|
|
147
|
+
--json
|
|
132
148
|
`);
|
|
133
149
|
process.exit(command ? 1 : 0);
|
|
134
150
|
}
|