nexarch 0.1.31 → 0.1.33
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/dist/commands/add-relationship.js +50 -2
- package/dist/commands/check-in.js +97 -0
- package/dist/commands/command-done.js +37 -0
- package/dist/commands/command-fail.js +44 -0
- package/dist/commands/init-agent.js +10 -1
- package/dist/commands/init-project.js +9 -3
- package/dist/index.js +22 -0
- package/package.json +1 -1
|
@@ -17,6 +17,29 @@ function parseToolText(result) {
|
|
|
17
17
|
const text = result.content?.[0]?.text ?? "{}";
|
|
18
18
|
return JSON.parse(text);
|
|
19
19
|
}
|
|
20
|
+
async function tryCreateStubEntity(externalKey, mcpOpts, agentContext, policyContext) {
|
|
21
|
+
// Only auto-create global reference entities — company entities must be created explicitly
|
|
22
|
+
if (!externalKey.startsWith("global:"))
|
|
23
|
+
return false;
|
|
24
|
+
// Derive a normalised alias from the external key (e.g. "global:platform:vercel" → "vercel")
|
|
25
|
+
const parts = externalKey.split(":");
|
|
26
|
+
const alias = parts[parts.length - 1];
|
|
27
|
+
const resolveRaw = await callMcpTool("nexarch_resolve_reference", { names: [alias], companyId: mcpOpts.companyId }, mcpOpts);
|
|
28
|
+
const resolveResult = parseToolText(resolveRaw);
|
|
29
|
+
const match = resolveResult.results?.find((r) => r.resolved && r.canonicalExternalRef === externalKey);
|
|
30
|
+
if (!match)
|
|
31
|
+
return false;
|
|
32
|
+
const entity = {
|
|
33
|
+
externalKey,
|
|
34
|
+
entityTypeCode: match.entityTypeCode,
|
|
35
|
+
name: match.canonicalName,
|
|
36
|
+
confidence: 1,
|
|
37
|
+
};
|
|
38
|
+
if (match.entitySubtypeCode)
|
|
39
|
+
entity.entitySubtypeCode = match.entitySubtypeCode;
|
|
40
|
+
await callMcpTool("nexarch_upsert_entities", { entities: [entity], agentContext, policyContext }, mcpOpts);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
20
43
|
export async function addRelationship(args) {
|
|
21
44
|
const asJson = parseFlag(args, "--json");
|
|
22
45
|
const fromKey = parseOptionValue(args, "--from");
|
|
@@ -58,8 +81,33 @@ export async function addRelationship(args) {
|
|
|
58
81
|
toEntityExternalKey: toKey,
|
|
59
82
|
confidence: 1,
|
|
60
83
|
};
|
|
61
|
-
|
|
62
|
-
|
|
84
|
+
let raw = await callMcpTool("nexarch_upsert_relationships", { relationships: [relationship], agentContext, policyContext }, mcpOpts);
|
|
85
|
+
let result = parseToolText(raw);
|
|
86
|
+
// Auto-create global reference stubs if endpoints are missing, then retry once
|
|
87
|
+
const missingEndpoint = result.errors?.some((e) => e.error === "RELATIONSHIP_ENDPOINT_NOT_FOUND");
|
|
88
|
+
if (missingEndpoint) {
|
|
89
|
+
const failedFromKeys = result.errors
|
|
90
|
+
?.filter((e) => e.error === "RELATIONSHIP_ENDPOINT_NOT_FOUND" && e.fromEntityExternalKey)
|
|
91
|
+
.map((e) => e.fromEntityExternalKey);
|
|
92
|
+
const failedToKeys = result.errors
|
|
93
|
+
?.filter((e) => e.error === "RELATIONSHIP_ENDPOINT_NOT_FOUND" && e.toEntityExternalKey)
|
|
94
|
+
.map((e) => e.toEntityExternalKey);
|
|
95
|
+
const keysToCreate = [...new Set([...(failedFromKeys ?? []), ...(failedToKeys ?? [])])];
|
|
96
|
+
// If no specific endpoint keys returned, fall back to trying both
|
|
97
|
+
if (keysToCreate.length === 0) {
|
|
98
|
+
keysToCreate.push(fromKey, toKey);
|
|
99
|
+
}
|
|
100
|
+
let createdAny = false;
|
|
101
|
+
for (const key of keysToCreate) {
|
|
102
|
+
const created = await tryCreateStubEntity(key, mcpOpts, agentContext, policyContext);
|
|
103
|
+
if (created)
|
|
104
|
+
createdAny = true;
|
|
105
|
+
}
|
|
106
|
+
if (createdAny) {
|
|
107
|
+
raw = await callMcpTool("nexarch_upsert_relationships", { relationships: [relationship], agentContext, policyContext }, mcpOpts);
|
|
108
|
+
result = parseToolText(raw);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
63
111
|
if (asJson) {
|
|
64
112
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
65
113
|
if (Number(result.summary?.failed ?? 0) > 0)
|
|
@@ -0,0 +1,97 @@
|
|
|
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 parseToolText(result) {
|
|
20
|
+
const text = result.content?.[0]?.text ?? "{}";
|
|
21
|
+
return JSON.parse(text);
|
|
22
|
+
}
|
|
23
|
+
function loadStoredAgentKey() {
|
|
24
|
+
const identityPath = join(homedir(), ".nexarch", "identity.json");
|
|
25
|
+
if (!existsSync(identityPath))
|
|
26
|
+
return null;
|
|
27
|
+
try {
|
|
28
|
+
const data = JSON.parse(readFileSync(identityPath, "utf8"));
|
|
29
|
+
return data.agentKey ?? null;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const COMMAND_TYPE_HINTS = {
|
|
36
|
+
update_project: "Re-scan the project with `npx nexarch init-project` to update the architecture graph.",
|
|
37
|
+
drift_audit: "Compare the current codebase to the architecture graph and report any drift or missing entities.",
|
|
38
|
+
access_audit: "Audit which agents and users have access to this application and report findings.",
|
|
39
|
+
gap_check: "Run a gap analysis: identify architecturally significant platforms or infrastructure not yet in the graph.",
|
|
40
|
+
policy_check: "Use nexarch_get_entity_policy_controls with the target entity key to fetch the policy controls linked to this application, evaluate the application against each control, and report findings. If no controls are returned, inform the user that policy_control entities must be linked to the application via 'governs' relationships in the workspace.",
|
|
41
|
+
enrich_entity: "Update the entity description, subtype, and metadata using information from the README or codebase.",
|
|
42
|
+
register_aliases: "Find internal package names that are unresolved and register them as company aliases.",
|
|
43
|
+
custom: "Execute the custom instructions provided.",
|
|
44
|
+
};
|
|
45
|
+
export async function checkIn(args) {
|
|
46
|
+
const asJson = parseFlag(args, "--json");
|
|
47
|
+
const agentKeyArg = parseOptionValue(args, "--agent-key");
|
|
48
|
+
const projectKey = parseOptionValue(args, "--project");
|
|
49
|
+
const creds = requireCredentials();
|
|
50
|
+
const agentKey = agentKeyArg ?? loadStoredAgentKey();
|
|
51
|
+
if (!agentKey) {
|
|
52
|
+
if (asJson) {
|
|
53
|
+
process.stdout.write(JSON.stringify({ command: null, reason: "no-agent-key" }) + "\n");
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.log("No agent key found. Pass --agent-key or run nexarch init-agent first.");
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const mcpOpts = { companyId: creds.companyId };
|
|
61
|
+
const raw = await callMcpTool("nexarch_claim_command", {
|
|
62
|
+
agentKey,
|
|
63
|
+
...(projectKey ? { projectKey } : {}),
|
|
64
|
+
companyId: creds.companyId,
|
|
65
|
+
}, mcpOpts);
|
|
66
|
+
const result = parseToolText(raw);
|
|
67
|
+
if (asJson) {
|
|
68
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (!result.command) {
|
|
72
|
+
console.log("No pending commands. Nothing to do.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const cmd = result.command;
|
|
76
|
+
const hint = COMMAND_TYPE_HINTS[cmd.command_type] ?? "";
|
|
77
|
+
console.log(`\n╔══════════════════════════════════════════════════════════════════╗`);
|
|
78
|
+
console.log(`║ COMMAND QUEUED — action required ║`);
|
|
79
|
+
console.log(`╚══════════════════════════════════════════════════════════════════╝`);
|
|
80
|
+
console.log(`\nCommand ID : ${cmd.id}`);
|
|
81
|
+
console.log(`Type : ${cmd.command_type}`);
|
|
82
|
+
console.log(`Target : ${cmd.target_entity_key ?? "(any)"}`);
|
|
83
|
+
console.log(`Priority : ${cmd.priority}`);
|
|
84
|
+
if (Object.keys(cmd.command_params).length > 0) {
|
|
85
|
+
console.log(`Params : ${JSON.stringify(cmd.command_params)}`);
|
|
86
|
+
}
|
|
87
|
+
if (hint) {
|
|
88
|
+
console.log(`\n${hint}`);
|
|
89
|
+
}
|
|
90
|
+
if (cmd.instructions) {
|
|
91
|
+
console.log(`\nInstructions:\n${cmd.instructions}`);
|
|
92
|
+
}
|
|
93
|
+
console.log(`\nWhen done, run:`);
|
|
94
|
+
console.log(` npx nexarch command-done --id "${cmd.id}" --summary "..."`);
|
|
95
|
+
console.log(`\nIf it fails, run:`);
|
|
96
|
+
console.log(` npx nexarch command-fail --id "${cmd.id}" --error "..."`);
|
|
97
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
return JSON.parse(result.content?.[0]?.text ?? "{}");
|
|
18
|
+
}
|
|
19
|
+
export async function commandDone(args) {
|
|
20
|
+
const asJson = parseFlag(args, "--json");
|
|
21
|
+
const id = parseOptionValue(args, "--id");
|
|
22
|
+
const summary = parseOptionValue(args, "--summary");
|
|
23
|
+
if (!id) {
|
|
24
|
+
console.error("error: --id <commandId> is required");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const creds = requireCredentials();
|
|
28
|
+
const raw = await callMcpTool("nexarch_complete_command", { commandId: id, ...(summary ? { summary } : {}), 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
|
+
console.log(`Command ${id} marked completed.`);
|
|
35
|
+
if (summary)
|
|
36
|
+
console.log(`Summary: ${summary}`);
|
|
37
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
return JSON.parse(result.content?.[0]?.text ?? "{}");
|
|
18
|
+
}
|
|
19
|
+
export async function commandFail(args) {
|
|
20
|
+
const asJson = parseFlag(args, "--json");
|
|
21
|
+
const id = parseOptionValue(args, "--id");
|
|
22
|
+
const error = parseOptionValue(args, "--error");
|
|
23
|
+
if (!id) {
|
|
24
|
+
console.error("error: --id <commandId> is required");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
if (!error) {
|
|
28
|
+
console.error("error: --error <message> is required");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const creds = requireCredentials();
|
|
32
|
+
const raw = await callMcpTool("nexarch_fail_command", { commandId: id, errorMessage: error, companyId: creds.companyId }, { companyId: creds.companyId });
|
|
33
|
+
const result = parseToolText(raw);
|
|
34
|
+
if (asJson) {
|
|
35
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (result.retrying) {
|
|
39
|
+
console.log(`Command ${id} failed — will be retried.`);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.log(`Command ${id} marked failed.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -4,7 +4,7 @@ import { join } from "path";
|
|
|
4
4
|
import process from "process";
|
|
5
5
|
import { requireCredentials } from "../lib/credentials.js";
|
|
6
6
|
import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
|
|
7
|
-
const CLI_VERSION = "0.1.
|
|
7
|
+
const CLI_VERSION = "0.1.33";
|
|
8
8
|
const AGENT_ENTITY_TYPE = "agent";
|
|
9
9
|
const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
|
|
10
10
|
function parseFlag(args, flag) {
|
|
@@ -572,6 +572,15 @@ export async function initAgent(args) {
|
|
|
572
572
|
let bootstrapFilePath = null;
|
|
573
573
|
let agentConfigResults = [];
|
|
574
574
|
if (registration.ok) {
|
|
575
|
+
try {
|
|
576
|
+
// Save identity so check-in can find the agent key
|
|
577
|
+
const identityDir = join(homedir(), ".nexarch");
|
|
578
|
+
mkdirSync(identityDir, { recursive: true });
|
|
579
|
+
writeFileSync(join(identityDir, "identity.json"), JSON.stringify({ agentKey: `agent:${agentId}` }, null, 2), { mode: 0o600 });
|
|
580
|
+
}
|
|
581
|
+
catch {
|
|
582
|
+
// non-fatal
|
|
583
|
+
}
|
|
575
584
|
try {
|
|
576
585
|
bootstrapFilePath = writeAgentBootstrap({
|
|
577
586
|
agentId,
|
|
@@ -585,16 +585,22 @@ ${finalStep} — Identify architecturally significant components not auto-detect
|
|
|
585
585
|
For each one you identify:
|
|
586
586
|
|
|
587
587
|
1. Resolve it to a canonical reference:
|
|
588
|
-
npx nexarch resolve-names --names "<platform name>"
|
|
588
|
+
npx nexarch resolve-names --names "<platform name>" --json
|
|
589
589
|
|
|
590
|
-
2. If resolved,
|
|
590
|
+
2. If resolved, instantiate the entity in the graph (REQUIRED before add-relationship):
|
|
591
|
+
npx nexarch update-entity \\
|
|
592
|
+
--key "<resolved-ref>" \\
|
|
593
|
+
--entity-type "<entityTypeCode from resolve-names>" \\
|
|
594
|
+
--name "<canonicalName from resolve-names>"
|
|
595
|
+
|
|
596
|
+
3. Wire the relationship:
|
|
591
597
|
npx nexarch add-relationship \\
|
|
592
598
|
--from "${projectExternalKey}" \\
|
|
593
599
|
--to "<resolved-ref>" \\
|
|
594
600
|
--type "runs_on" # for platforms (Vercel, Neon, etc.)
|
|
595
601
|
# use --type "depends_on" for external APIs/SaaS (Stripe, Resend, etc.)
|
|
596
602
|
|
|
597
|
-
|
|
603
|
+
4. If unresolved (not in the reference library), skip it for now — it will appear
|
|
598
604
|
as a reference candidate on the next scan once you register an alias for it.
|
|
599
605
|
`;
|
|
600
606
|
return `
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,9 @@ import { updateEntity } from "./commands/update-entity.js";
|
|
|
15
15
|
import { addRelationship } from "./commands/add-relationship.js";
|
|
16
16
|
import { registerAlias } from "./commands/register-alias.js";
|
|
17
17
|
import { resolveNames } from "./commands/resolve-names.js";
|
|
18
|
+
import { checkIn } from "./commands/check-in.js";
|
|
19
|
+
import { commandDone } from "./commands/command-done.js";
|
|
20
|
+
import { commandFail } from "./commands/command-fail.js";
|
|
18
21
|
const [, , command, ...args] = process.argv;
|
|
19
22
|
const commands = {
|
|
20
23
|
login,
|
|
@@ -30,6 +33,9 @@ const commands = {
|
|
|
30
33
|
"add-relationship": addRelationship,
|
|
31
34
|
"register-alias": registerAlias,
|
|
32
35
|
"resolve-names": resolveNames,
|
|
36
|
+
"check-in": checkIn,
|
|
37
|
+
"command-done": commandDone,
|
|
38
|
+
"command-fail": commandFail,
|
|
33
39
|
};
|
|
34
40
|
async function main() {
|
|
35
41
|
if (command === "agent") {
|
|
@@ -123,6 +129,22 @@ Usage:
|
|
|
123
129
|
results before calling add-relationship.
|
|
124
130
|
Options: --names <csv> (required, e.g. "vercel,neon")
|
|
125
131
|
--json
|
|
132
|
+
nexarch check-in Poll the command queue and claim the next pending command
|
|
133
|
+
for this agent. Reads agent key from ~/.nexarch/identity.json
|
|
134
|
+
(written by init-agent) or via --agent-key.
|
|
135
|
+
Options: --agent-key <key> override stored agent key
|
|
136
|
+
--project <key> application entity key
|
|
137
|
+
--json
|
|
138
|
+
nexarch command-done
|
|
139
|
+
Mark a claimed command as completed.
|
|
140
|
+
Options: --id <commandId> (required)
|
|
141
|
+
--summary <text> short summary of what was done
|
|
142
|
+
--json
|
|
143
|
+
nexarch command-fail
|
|
144
|
+
Mark a claimed command as failed.
|
|
145
|
+
Options: --id <commandId> (required)
|
|
146
|
+
--error <message> (required)
|
|
147
|
+
--json
|
|
126
148
|
`);
|
|
127
149
|
process.exit(command ? 1 : 0);
|
|
128
150
|
}
|