nexarch 0.3.1 → 0.4.1
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 +6 -3
- package/dist/commands/init-agent.js +55 -25
- package/dist/commands/mcp-config.js +36 -55
- package/dist/commands/mcp-proxy.js +1 -1
- package/dist/commands/setup.js +39 -27
- package/dist/commands/status.js +11 -2
- package/dist/index.js +5 -4
- package/dist/lib/agent-registry.js +49 -0
- package/dist/lib/clients.js +57 -52
- package/dist/lib/mcp.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# nexarch
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Your architecture workspace for AI delivery.
|
|
4
4
|
|
|
5
5
|
## Quick start
|
|
6
6
|
|
|
@@ -9,15 +9,18 @@ npx nexarch login
|
|
|
9
9
|
npx nexarch setup
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
+
> `setup`, `mcp-config`, and `init-agent` are registry-driven and fail closed.
|
|
13
|
+
> If the published integration registry is unavailable/invalid, these commands stop until service is restored.
|
|
14
|
+
|
|
12
15
|
## Commands
|
|
13
16
|
|
|
14
17
|
| Command | Description |
|
|
15
18
|
|---|---|
|
|
16
19
|
| `nexarch login [--company <id>]` | Authenticate via browser and store company-scoped credentials; prompts for company when multiple memberships exist |
|
|
17
20
|
| `nexarch logout` | Remove local credentials |
|
|
18
|
-
| `nexarch status` | Check connection and
|
|
21
|
+
| `nexarch status` | Check connection, registry health/version, and architecture summary for the company selected at login |
|
|
19
22
|
| `nexarch setup` | Auto-configure detected MCP clients |
|
|
20
|
-
| `nexarch mcp-config [client]` | Print config block for manual setup (
|
|
23
|
+
| `nexarch mcp-config [client]` | Print config block for manual setup (supported client codes are registry-managed) |
|
|
21
24
|
| `nexarch mcp-proxy` | stdio→HTTP bridge used internally by MCP clients |
|
|
22
25
|
| `nexarch init-agent [--json] [--strict] [--agent-id <id>] [--redact-hostname]` | Run onboarding handshake and mandatory `agent` entity registration in graph |
|
|
23
26
|
| `nexarch init-agent --bind-to-external-key <key> [--bind-relationship-type <code>]` | Optionally bind the agent node to an existing graph external key |
|
|
@@ -3,8 +3,9 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import process from "process";
|
|
5
5
|
import { requireCredentials } from "../lib/credentials.js";
|
|
6
|
+
import { fetchAgentRegistryOrThrow } from "../lib/agent-registry.js";
|
|
6
7
|
import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
|
|
7
|
-
const CLI_VERSION = "0.
|
|
8
|
+
const CLI_VERSION = "0.4.1";
|
|
8
9
|
const AGENT_ENTITY_TYPE = "agent";
|
|
9
10
|
const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
|
|
10
11
|
function parseFlag(args, flag) {
|
|
@@ -145,36 +146,58 @@ After running, confirm \`"ok": true\` in the JSON output. No further action is n
|
|
|
145
146
|
writeFileSync(filePath, content, "utf8");
|
|
146
147
|
return filePath;
|
|
147
148
|
}
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
function injectAgentConfigs() {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
"AGENTS.md", "agents.md", // OpenAI Codex CLI / generic
|
|
160
|
-
".github/copilot-instructions.md", // GitHub Copilot
|
|
161
|
-
".cursorrules", // Cursor (legacy)
|
|
162
|
-
".continue/rules.md", // Continue.dev
|
|
163
|
-
".windsurfrules", // Windsurf
|
|
164
|
-
];
|
|
149
|
+
function replaceInjectedSection(existing, sectionHeading, sectionBody) {
|
|
150
|
+
const headingRegex = new RegExp(`^${sectionHeading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?(?=^##\\s|\\Z)`, "m");
|
|
151
|
+
if (headingRegex.test(existing)) {
|
|
152
|
+
const replaced = existing.replace(headingRegex, sectionBody.trim());
|
|
153
|
+
return replaced.endsWith("\n") ? replaced : `${replaced}\n`;
|
|
154
|
+
}
|
|
155
|
+
return existing;
|
|
156
|
+
}
|
|
157
|
+
function injectAgentConfigs(registry) {
|
|
158
|
+
const templateByCode = new Map(registry.instructionTemplates.map((t) => [t.code, t]));
|
|
159
|
+
const targets = [...registry.instructionTargets].sort((a, b) => a.sortOrder - b.sortOrder || a.filePathPattern.localeCompare(b.filePathPattern));
|
|
165
160
|
const results = [];
|
|
166
|
-
for (const
|
|
167
|
-
|
|
161
|
+
for (const target of targets) {
|
|
162
|
+
if (target.matchMode !== "exact")
|
|
163
|
+
continue;
|
|
164
|
+
const template = templateByCode.get(target.templateCode);
|
|
165
|
+
if (!template)
|
|
166
|
+
continue;
|
|
167
|
+
const filePath = join(process.cwd(), target.filePathPattern);
|
|
168
168
|
if (!existsSync(filePath))
|
|
169
169
|
continue;
|
|
170
170
|
const existing = readFileSync(filePath, "utf8");
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
const sectionBody = template.body.trim();
|
|
172
|
+
const sectionHeading = target.sectionHeading ?? "## Nexarch Agent Registration";
|
|
173
|
+
const sectionMarker = target.sectionMarker ?? sectionHeading;
|
|
174
|
+
if (target.insertionMode === "replace_section") {
|
|
175
|
+
let replaced = replaceInjectedSection(existing, sectionHeading, sectionBody);
|
|
176
|
+
if (replaced === existing && existing.includes(sectionMarker)) {
|
|
177
|
+
const markerIndex = existing.indexOf(sectionMarker);
|
|
178
|
+
const before = existing.slice(0, markerIndex).replace(/\s*$/, "\n\n");
|
|
179
|
+
replaced = `${before}${sectionBody}\n`;
|
|
180
|
+
}
|
|
181
|
+
if (replaced !== existing) {
|
|
182
|
+
writeFileSync(filePath, replaced, "utf8");
|
|
183
|
+
results.push({ path: filePath, status: "updated" });
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
187
|
+
writeFileSync(filePath, existing + separator + sectionBody + "\n", "utf8");
|
|
188
|
+
results.push({ path: filePath, status: "injected" });
|
|
189
|
+
}
|
|
173
190
|
continue;
|
|
174
191
|
}
|
|
175
192
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
const next = existing + separator + sectionBody + "\n";
|
|
194
|
+
if (next === existing) {
|
|
195
|
+
results.push({ path: filePath, status: "already_present" });
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
writeFileSync(filePath, next, "utf8");
|
|
199
|
+
results.push({ path: filePath, status: "injected" });
|
|
200
|
+
}
|
|
178
201
|
}
|
|
179
202
|
return results;
|
|
180
203
|
}
|
|
@@ -198,6 +221,7 @@ export async function initAgent(args) {
|
|
|
198
221
|
const checks = [];
|
|
199
222
|
const creds = requireCredentials();
|
|
200
223
|
const selectedCompanyId = creds.companyId;
|
|
224
|
+
const registry = await fetchAgentRegistryOrThrow();
|
|
201
225
|
const init = await mcpInitialize({ companyId: selectedCompanyId });
|
|
202
226
|
checks.push({
|
|
203
227
|
name: "mcp.initialize",
|
|
@@ -207,6 +231,7 @@ export async function initAgent(args) {
|
|
|
207
231
|
const tools = await mcpListTools({ companyId: selectedCompanyId });
|
|
208
232
|
const toolNames = new Set(tools.map((t) => t.name));
|
|
209
233
|
const required = [
|
|
234
|
+
"nexarch_get_agent_registry",
|
|
210
235
|
"nexarch_get_applied_policies",
|
|
211
236
|
"nexarch_get_ingest_contract",
|
|
212
237
|
"nexarch_upsert_entities",
|
|
@@ -639,7 +664,7 @@ export async function initAgent(args) {
|
|
|
639
664
|
// non-fatal
|
|
640
665
|
}
|
|
641
666
|
try {
|
|
642
|
-
agentConfigResults = injectAgentConfigs();
|
|
667
|
+
agentConfigResults = injectAgentConfigs(registry);
|
|
643
668
|
}
|
|
644
669
|
catch {
|
|
645
670
|
// non-fatal
|
|
@@ -703,6 +728,7 @@ export async function initAgent(args) {
|
|
|
703
728
|
submitCommandTemplate: `nexarch agent identify --agent-id \"${agentId}\" --provider \"<provider>\" --model \"<model>\" --client \"<client>\" --framework \"<openclaw|n8n|m365-agent-framework|other>\" --session-id \"<session-id>\" --tool-version \"<tool-version>\" --capabilities \"<capability1,capability2>\" --notes \"<optional notes>\" --json`,
|
|
704
729
|
},
|
|
705
730
|
companyId: creds.companyId,
|
|
731
|
+
registry: { version: registry.release.version, registryVersion: registry.registryVersion, publishedAt: registry.release.publishedAt },
|
|
706
732
|
bootstrapFile: bootstrapFilePath,
|
|
707
733
|
agentConfigs: agentConfigResults,
|
|
708
734
|
};
|
|
@@ -711,6 +737,7 @@ export async function initAgent(args) {
|
|
|
711
737
|
process.exitCode = 1;
|
|
712
738
|
return;
|
|
713
739
|
}
|
|
740
|
+
console.log(`Using integration registry release v${registry.release.version}.`);
|
|
714
741
|
console.log("Running agent onboarding handshake…\n");
|
|
715
742
|
for (const check of checks) {
|
|
716
743
|
console.log(`${check.ok ? "✓" : "✗"} ${check.name}`);
|
|
@@ -733,6 +760,9 @@ export async function initAgent(args) {
|
|
|
733
760
|
if (r.status === "injected") {
|
|
734
761
|
console.log(`📝 Nexarch registration instructions added to: ${r.path}`);
|
|
735
762
|
}
|
|
763
|
+
if (r.status === "updated") {
|
|
764
|
+
console.log(`📝 Nexarch registration instructions updated in: ${r.path}`);
|
|
765
|
+
}
|
|
736
766
|
}
|
|
737
767
|
console.log("\n Tell your agent: \"run npx nexarch agent prompt and follow the instructions\"");
|
|
738
768
|
}
|
|
@@ -1,53 +1,23 @@
|
|
|
1
1
|
import { loadCredentials } from "../lib/credentials.js";
|
|
2
|
-
import {
|
|
2
|
+
import { fetchAgentRegistryOrThrow } from "../lib/agent-registry.js";
|
|
3
|
+
import { findClientProfile } from "../lib/clients.js";
|
|
4
|
+
function renderConfigForProfile(profile) {
|
|
5
|
+
const serverBlock = { command: profile.serverCommand, args: profile.serverArgs };
|
|
6
|
+
if (profile.mergeStrategy === "continue_style") {
|
|
7
|
+
return { mcpServers: [{ name: "nexarch", ...serverBlock }] };
|
|
8
|
+
}
|
|
9
|
+
return { mcpServers: { nexarch: serverBlock } };
|
|
10
|
+
}
|
|
3
11
|
export async function mcpConfig(args) {
|
|
4
|
-
const clientFlag = args.find((a) => !a.startsWith("-")) ?? "claude-desktop";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Locations:
|
|
14
|
-
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
15
|
-
Windows: %APPDATA%\\Claude\\claude_desktop_config.json
|
|
16
|
-
`);
|
|
17
|
-
break;
|
|
18
|
-
}
|
|
19
|
-
case "cursor":
|
|
20
|
-
case "windsurf": {
|
|
21
|
-
const config = { mcpServers: { nexarch: serverBlock } };
|
|
22
|
-
console.log(`Add the following to your ${clientFlag === "cursor" ? ".cursor/mcp.json" : ".windsurf/mcp.json"}:\n`);
|
|
23
|
-
console.log(JSON.stringify(config, null, 2));
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
case "continue":
|
|
27
|
-
case "continue-dev": {
|
|
28
|
-
// Continue.dev uses an array-style mcpServers with a "name" field per entry
|
|
29
|
-
const config = { mcpServers: [{ name: "nexarch", ...serverBlock }] };
|
|
30
|
-
console.log("Add (or merge) the following into your ~/.continue/config.json:\n");
|
|
31
|
-
console.log(JSON.stringify(config, null, 2));
|
|
32
|
-
console.log(`
|
|
33
|
-
Locations:
|
|
34
|
-
macOS/Linux: ~/.continue/config.json
|
|
35
|
-
Windows: %USERPROFILE%\\.continue\\config.json
|
|
36
|
-
`);
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
case "http":
|
|
40
|
-
case "direct": {
|
|
41
|
-
// Direct HTTP connection — for MCP clients that support Streamable HTTP transport.
|
|
42
|
-
// The gateway accepts JSON-RPC 2.0 POST requests at the /mcp endpoint.
|
|
43
|
-
// Auth is via Bearer token (your nexarch token from ~/.nexarch/credentials.json).
|
|
44
|
-
const creds = loadCredentials();
|
|
45
|
-
const tokenHint = creds ? creds.token : "<your-nexarch-token>";
|
|
46
|
-
console.log("Direct HTTP MCP connection (Streamable HTTP / SSE transport):\n");
|
|
47
|
-
console.log(` Endpoint : https://mcp.nexarch.ai/mcp`);
|
|
48
|
-
console.log(` Auth : Authorization: Bearer ${tokenHint}`);
|
|
49
|
-
console.log(` Protocol : MCP JSON-RPC 2.0 over HTTP POST`);
|
|
50
|
-
console.log(`
|
|
12
|
+
const clientFlag = (args.find((a) => !a.startsWith("-")) ?? "claude-desktop").toLowerCase();
|
|
13
|
+
if (clientFlag === "http" || clientFlag === "direct") {
|
|
14
|
+
const creds = loadCredentials();
|
|
15
|
+
const tokenHint = creds ? creds.token : "<your-nexarch-token>";
|
|
16
|
+
console.log("Direct HTTP MCP connection (Streamable HTTP / SSE transport):\n");
|
|
17
|
+
console.log(` Endpoint : https://mcp.nexarch.ai/mcp`);
|
|
18
|
+
console.log(` Auth : Authorization: Bearer ${tokenHint}`);
|
|
19
|
+
console.log(` Protocol : MCP JSON-RPC 2.0 over HTTP POST`);
|
|
20
|
+
console.log(`
|
|
51
21
|
Use this when your MCP client supports connecting to a remote HTTP MCP server directly
|
|
52
22
|
(no stdio proxy required). Examples: custom agent frameworks, LangChain MCP integration,
|
|
53
23
|
or any client that speaks the MCP Streamable HTTP or SSE transport.
|
|
@@ -62,13 +32,24 @@ Example curl to verify connectivity:
|
|
|
62
32
|
If your client requires an MCP server URL instead of command/args, use:
|
|
63
33
|
https://mcp.nexarch.ai/mcp
|
|
64
34
|
`);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const registry = await fetchAgentRegistryOrThrow();
|
|
38
|
+
const profile = findClientProfile(registry, clientFlag);
|
|
39
|
+
if (!profile) {
|
|
40
|
+
const known = registry.mcpClientProfiles.map((c) => c.code).join(", ");
|
|
41
|
+
throw new Error(`Unknown MCP client '${clientFlag}'. Supported clients from registry: ${known}`);
|
|
42
|
+
}
|
|
43
|
+
const config = renderConfigForProfile(profile);
|
|
44
|
+
console.log(`\nUsing integration registry release v${registry.release.version}.`);
|
|
45
|
+
console.log(`\nMCP server config for ${profile.code} (${profile.name}):\n`);
|
|
46
|
+
console.log(JSON.stringify(config, null, 2));
|
|
47
|
+
const platformKey = process.platform === "darwin" ? "darwin" : process.platform === "win32" ? "win32" : "linux";
|
|
48
|
+
const locations = profile.osPaths?.[platformKey] ?? [];
|
|
49
|
+
if (locations.length > 0) {
|
|
50
|
+
console.log("\nKnown config locations for this platform:");
|
|
51
|
+
for (const p of locations)
|
|
52
|
+
console.log(` ${p}`);
|
|
72
53
|
}
|
|
73
54
|
console.log("\nOr run `nexarch setup` to have the CLI write the config automatically.");
|
|
74
55
|
}
|
|
@@ -2,7 +2,7 @@ import * as readline from "readline";
|
|
|
2
2
|
import { requireCredentials } from "../lib/credentials.js";
|
|
3
3
|
import { forwardToGateway } from "../lib/mcp.js";
|
|
4
4
|
/**
|
|
5
|
-
* stdio MCP proxy — bridges MCP clients
|
|
5
|
+
* stdio MCP proxy — bridges registry-managed MCP clients that
|
|
6
6
|
* use stdio transport to the Nexarch HTTP MCP gateway.
|
|
7
7
|
*
|
|
8
8
|
* The MCP protocol over stdio is newline-delimited JSON-RPC 2.0.
|
package/dist/commands/setup.js
CHANGED
|
@@ -1,37 +1,49 @@
|
|
|
1
1
|
import { requireCredentials } from "../lib/credentials.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { detectClientsFromRegistry, writeClientConfig, nexarchServerBlockFromRegistry } from "../lib/clients.js";
|
|
3
|
+
import { fetchAgentRegistryOrThrow } from "../lib/agent-registry.js";
|
|
4
|
+
import { initAgent } from "./init-agent.js";
|
|
5
|
+
import { login } from "./login.js";
|
|
6
|
+
export async function setup(args) {
|
|
7
|
+
try {
|
|
8
|
+
requireCredentials();
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
console.log("No active Nexarch session found. Starting login flow…\n");
|
|
12
|
+
await login(args);
|
|
13
|
+
}
|
|
14
|
+
requireCredentials(); // ensure login succeeded before continuing
|
|
15
|
+
const registry = await fetchAgentRegistryOrThrow();
|
|
16
|
+
console.log(`Using integration registry release v${registry.release.version}.`);
|
|
5
17
|
console.log("Detecting installed MCP clients…\n");
|
|
6
|
-
const clients =
|
|
18
|
+
const clients = detectClientsFromRegistry(registry);
|
|
7
19
|
if (clients.length === 0) {
|
|
8
|
-
console.log("No supported MCP clients detected.");
|
|
9
|
-
|
|
20
|
+
console.log("No supported MCP clients detected for this platform.");
|
|
21
|
+
const supported = registry.mcpClientProfiles.map((c) => c.name).join(", ");
|
|
22
|
+
console.log(`\nSupported clients (registry): ${supported}`);
|
|
10
23
|
console.log("\nTo configure manually, run:");
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
24
|
+
for (const cp of registry.mcpClientProfiles) {
|
|
25
|
+
console.log(` nexarch mcp-config --client ${cp.code}`);
|
|
26
|
+
}
|
|
14
27
|
console.log("\nFor clients that support direct HTTP MCP connections:");
|
|
15
28
|
console.log(" nexarch mcp-config --client http");
|
|
16
|
-
return;
|
|
17
29
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
else {
|
|
31
|
+
const serverBlock = nexarchServerBlockFromRegistry(registry);
|
|
32
|
+
for (const client of clients) {
|
|
33
|
+
process.stdout.write(` ${client.name.padEnd(20)} ${client.configPath}\n`);
|
|
34
|
+
process.stdout.write(` ${"".padEnd(20)} `);
|
|
35
|
+
try {
|
|
36
|
+
writeClientConfig(client, serverBlock);
|
|
37
|
+
console.log("✓ configured");
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
41
|
+
console.log(`✗ failed — ${message}`);
|
|
42
|
+
}
|
|
29
43
|
}
|
|
44
|
+
console.log("\nDone. Restart your MCP client for the changes to take effect.");
|
|
30
45
|
}
|
|
31
|
-
console.log("\
|
|
32
|
-
|
|
33
|
-
console.log("
|
|
34
|
-
console.log(" nexarch_get_snapshot_facts");
|
|
35
|
-
console.log(" nexarch_get_governance_summary");
|
|
36
|
-
console.log(" nexarch_get_applied_policies");
|
|
46
|
+
console.log("\nRegistering this runtime as a Nexarch agent…\n");
|
|
47
|
+
await initAgent(args.includes("--strict") ? args : [...args, "--strict"]);
|
|
48
|
+
console.log("\nSetup complete: MCP client config + agent registration are now in place.");
|
|
37
49
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { requireCredentials } from "../lib/credentials.js";
|
|
2
|
+
import { fetchAgentRegistryOrThrow } from "../lib/agent-registry.js";
|
|
2
3
|
import { callMcpTool } from "../lib/mcp.js";
|
|
3
4
|
export async function status(_args) {
|
|
4
5
|
const creds = requireCredentials();
|
|
@@ -7,9 +8,14 @@ export async function status(_args) {
|
|
|
7
8
|
const daysLeft = Math.ceil((expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
8
9
|
process.stdout.write("Connecting to mcp.nexarch.ai… ");
|
|
9
10
|
let governance;
|
|
11
|
+
let registryInfo = null;
|
|
10
12
|
try {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
+
const [governanceResult, registry] = await Promise.all([
|
|
14
|
+
callMcpTool("nexarch_get_governance_summary", { companyId: selectedCompanyId || undefined }, { companyId: selectedCompanyId || undefined }),
|
|
15
|
+
fetchAgentRegistryOrThrow(),
|
|
16
|
+
]);
|
|
17
|
+
governance = JSON.parse(governanceResult.content[0]?.text ?? "{}");
|
|
18
|
+
registryInfo = { version: registry.release.version, publishedAt: registry.release.publishedAt };
|
|
13
19
|
}
|
|
14
20
|
catch (err) {
|
|
15
21
|
console.error("failed\n");
|
|
@@ -22,6 +28,9 @@ export async function status(_args) {
|
|
|
22
28
|
if (creds.company) {
|
|
23
29
|
console.log(` Workspace: ${creds.company}`);
|
|
24
30
|
}
|
|
31
|
+
if (registryInfo) {
|
|
32
|
+
console.log(`\n Integration registry: v${registryInfo.version} (published ${new Date(registryInfo.publishedAt).toLocaleString()})`);
|
|
33
|
+
}
|
|
25
34
|
console.log(`\n Architecture facts: ${governance.canonicalFactCount}`);
|
|
26
35
|
console.log(` Pending review: ${governance.reviewQueue.pending_entities} entities, ${governance.reviewQueue.pending_relationships} relationships`);
|
|
27
36
|
if (governance.latestSnapshot) {
|
package/dist/index.js
CHANGED
|
@@ -50,7 +50,7 @@ async function main() {
|
|
|
50
50
|
process.stdout.write(readFileSync(filePath, "utf8") + "\n");
|
|
51
51
|
}
|
|
52
52
|
catch {
|
|
53
|
-
console.error("No bootstrap file found. Run 'nexarch init-agent' first.");
|
|
53
|
+
console.error("No bootstrap file found. Run 'nexarch setup' (or 'nexarch init-agent') first.");
|
|
54
54
|
process.exit(1);
|
|
55
55
|
}
|
|
56
56
|
return;
|
|
@@ -59,17 +59,18 @@ async function main() {
|
|
|
59
59
|
const handler = commands[command ?? ""];
|
|
60
60
|
if (!handler) {
|
|
61
61
|
console.log(`
|
|
62
|
-
nexarch —
|
|
62
|
+
nexarch — Your architecture workspace for AI delivery.
|
|
63
63
|
|
|
64
64
|
Usage:
|
|
65
65
|
nexarch login Authenticate in browser and store company-scoped credentials
|
|
66
66
|
Option: --company <id>
|
|
67
67
|
nexarch logout Remove stored credentials
|
|
68
68
|
nexarch status Check connection and show architecture summary
|
|
69
|
-
nexarch setup
|
|
69
|
+
nexarch setup One-step onboarding: login (if needed) + MCP config + register agent
|
|
70
70
|
nexarch mcp-config Print MCP server config block for manual setup
|
|
71
|
+
Client list is registry-managed (see 'nexarch mcp-config --client <code>')
|
|
71
72
|
nexarch mcp-proxy Run as stdio MCP proxy (used by MCP clients)
|
|
72
|
-
nexarch init-agent Run handshake + mandatory agent registration in graph
|
|
73
|
+
nexarch init-agent Run handshake + mandatory agent registration in graph (advanced/manual)
|
|
73
74
|
Options: --agent-id <id> --bind-to-external-key <key>
|
|
74
75
|
--bind-relationship-type <code> --redact-hostname
|
|
75
76
|
--json --strict
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { callMcpTool } from "./mcp.js";
|
|
2
|
+
import { requireCredentials } from "./credentials.js";
|
|
3
|
+
function parseToolText(result) {
|
|
4
|
+
const text = result.content?.[0]?.text ?? "{}";
|
|
5
|
+
return JSON.parse(text);
|
|
6
|
+
}
|
|
7
|
+
function isString(v) {
|
|
8
|
+
return typeof v === "string";
|
|
9
|
+
}
|
|
10
|
+
function ensureRegistryShape(raw) {
|
|
11
|
+
const r = raw;
|
|
12
|
+
if (!r || typeof r !== "object")
|
|
13
|
+
throw new Error("invalid payload");
|
|
14
|
+
if (typeof r.registryVersion !== "number")
|
|
15
|
+
throw new Error("missing registryVersion");
|
|
16
|
+
if (!r.release || !isString(r.release.id) || typeof r.release.version !== "number" || !isString(r.release.publishedAt)) {
|
|
17
|
+
throw new Error("missing release metadata");
|
|
18
|
+
}
|
|
19
|
+
if (!Array.isArray(r.instructionTemplates) || !Array.isArray(r.instructionTargets) || !Array.isArray(r.mcpClientProfiles)) {
|
|
20
|
+
throw new Error("missing registry collections");
|
|
21
|
+
}
|
|
22
|
+
for (const t of r.instructionTemplates) {
|
|
23
|
+
if (!isString(t.code) || !isString(t.body))
|
|
24
|
+
throw new Error("invalid instruction template");
|
|
25
|
+
}
|
|
26
|
+
for (const it of r.instructionTargets) {
|
|
27
|
+
if (!isString(it.filePathPattern) || !isString(it.runtimeCode) || !isString(it.templateCode)) {
|
|
28
|
+
throw new Error("invalid instruction target");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const cp of r.mcpClientProfiles) {
|
|
32
|
+
if (!isString(cp.code) || !isString(cp.name) || !isString(cp.serverCommand) || !Array.isArray(cp.serverArgs)) {
|
|
33
|
+
throw new Error("invalid mcp client profile");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return r;
|
|
37
|
+
}
|
|
38
|
+
export async function fetchAgentRegistryOrThrow() {
|
|
39
|
+
const creds = requireCredentials();
|
|
40
|
+
try {
|
|
41
|
+
const raw = await callMcpTool("nexarch_get_agent_registry", {}, { companyId: creds.companyId });
|
|
42
|
+
const parsed = parseToolText(raw);
|
|
43
|
+
return ensureRegistryShape(parsed);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
47
|
+
throw new Error(`Nexarch integration registry unavailable. This command is blocked until service is restored. (${message})`);
|
|
48
|
+
}
|
|
49
|
+
}
|
package/dist/lib/clients.js
CHANGED
|
@@ -4,45 +4,15 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
|
4
4
|
function appData() {
|
|
5
5
|
return process.env.APPDATA ?? null;
|
|
6
6
|
}
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return [join(homedir(), ".config", "Claude", "claude_desktop_config.json")];
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function cursorPaths() {
|
|
20
|
-
switch (process.platform) {
|
|
21
|
-
case "darwin":
|
|
22
|
-
return [join(homedir(), ".cursor", "mcp.json")];
|
|
23
|
-
case "win32": {
|
|
24
|
-
const ad = appData();
|
|
25
|
-
return ad ? [join(ad, "Cursor", "User", "mcp.json")] : [];
|
|
26
|
-
}
|
|
27
|
-
default:
|
|
28
|
-
return [join(homedir(), ".cursor", "mcp.json")];
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function windsurfPaths() {
|
|
32
|
-
switch (process.platform) {
|
|
33
|
-
case "darwin":
|
|
34
|
-
return [join(homedir(), ".windsurf", "mcp.json")];
|
|
35
|
-
case "win32": {
|
|
36
|
-
const ad = appData();
|
|
37
|
-
return ad ? [join(ad, "Windsurf", "User", "mcp.json")] : [];
|
|
38
|
-
}
|
|
39
|
-
default:
|
|
40
|
-
return [join(homedir(), ".windsurf", "mcp.json")];
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function continueDevPaths() {
|
|
44
|
-
// Continue.dev stores config at ~/.continue/config.json on all platforms
|
|
45
|
-
return [join(homedir(), ".continue", "config.json")];
|
|
7
|
+
function expandPath(p) {
|
|
8
|
+
let out = p;
|
|
9
|
+
if (out.startsWith("~/"))
|
|
10
|
+
out = join(homedir(), out.slice(2));
|
|
11
|
+
const ad = appData();
|
|
12
|
+
if (ad)
|
|
13
|
+
out = out.replace(/%APPDATA%/g, ad);
|
|
14
|
+
out = out.replace(/%USERPROFILE%/g, homedir());
|
|
15
|
+
return out;
|
|
46
16
|
}
|
|
47
17
|
function mergeClaudeStyle(existing, serverBlock) {
|
|
48
18
|
let config = {};
|
|
@@ -57,8 +27,6 @@ function mergeClaudeStyle(existing, serverBlock) {
|
|
|
57
27
|
config.mcpServers = servers;
|
|
58
28
|
return JSON.stringify(config, null, 2);
|
|
59
29
|
}
|
|
60
|
-
// Continue.dev uses an array under mcpServers (each entry has a "name" field).
|
|
61
|
-
// We upsert by name so re-running setup is idempotent.
|
|
62
30
|
function mergeContinueStyle(existing, serverBlock) {
|
|
63
31
|
let config = {};
|
|
64
32
|
try {
|
|
@@ -92,14 +60,40 @@ function mergeFlatStyle(existing, serverBlock) {
|
|
|
92
60
|
config.mcpServers = servers;
|
|
93
61
|
return JSON.stringify(config, null, 2);
|
|
94
62
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
63
|
+
function mergeForStrategy(strategy) {
|
|
64
|
+
switch (strategy) {
|
|
65
|
+
case "claude_style":
|
|
66
|
+
return mergeClaudeStyle;
|
|
67
|
+
case "continue_style":
|
|
68
|
+
return mergeContinueStyle;
|
|
69
|
+
case "flat_style":
|
|
70
|
+
default:
|
|
71
|
+
return mergeFlatStyle;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function osKey() {
|
|
75
|
+
if (process.platform === "darwin")
|
|
76
|
+
return "darwin";
|
|
77
|
+
if (process.platform === "win32")
|
|
78
|
+
return "win32";
|
|
79
|
+
return "linux";
|
|
80
|
+
}
|
|
81
|
+
export function detectClientsFromRegistry(registry) {
|
|
82
|
+
const current = osKey();
|
|
83
|
+
const out = [];
|
|
84
|
+
for (const cp of [...registry.mcpClientProfiles].sort((a, b) => a.sortOrder - b.sortOrder || a.code.localeCompare(b.code))) {
|
|
85
|
+
const paths = cp.osPaths?.[current] ?? [];
|
|
86
|
+
for (const p of paths) {
|
|
87
|
+
const configPath = expandPath(p);
|
|
88
|
+
out.push({
|
|
89
|
+
code: cp.code,
|
|
90
|
+
name: cp.name,
|
|
91
|
+
configPath,
|
|
92
|
+
merge: mergeForStrategy(cp.mergeStrategy),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out.filter((c) => existsSync(join(c.configPath, "..")));
|
|
103
97
|
}
|
|
104
98
|
export function writeClientConfig(client, serverBlock) {
|
|
105
99
|
const dir = join(client.configPath, "..");
|
|
@@ -107,9 +101,20 @@ export function writeClientConfig(client, serverBlock) {
|
|
|
107
101
|
const existing = existsSync(client.configPath) ? readFileSync(client.configPath, "utf-8") : "";
|
|
108
102
|
writeFileSync(client.configPath, client.merge(existing, serverBlock), "utf-8");
|
|
109
103
|
}
|
|
110
|
-
export function
|
|
104
|
+
export function nexarchServerBlockFromRegistry(registry) {
|
|
105
|
+
const first = [...registry.mcpClientProfiles].sort((a, b) => a.sortOrder - b.sortOrder || a.code.localeCompare(b.code))[0];
|
|
106
|
+
if (!first) {
|
|
107
|
+
throw new Error("Nexarch integration registry is missing MCP client profiles.");
|
|
108
|
+
}
|
|
111
109
|
return {
|
|
112
|
-
command:
|
|
113
|
-
args:
|
|
110
|
+
command: first.serverCommand,
|
|
111
|
+
args: first.serverArgs,
|
|
114
112
|
};
|
|
115
113
|
}
|
|
114
|
+
export function findClientProfile(registry, code) {
|
|
115
|
+
const normalized = code.trim().toLowerCase();
|
|
116
|
+
if (normalized === "continue") {
|
|
117
|
+
return registry.mcpClientProfiles.find((c) => c.code === "continue-dev") ?? null;
|
|
118
|
+
}
|
|
119
|
+
return registry.mcpClientProfiles.find((c) => c.code === normalized) ?? null;
|
|
120
|
+
}
|
package/dist/lib/mcp.js
CHANGED
|
@@ -62,7 +62,7 @@ export async function mcpInitialize(options = {}) {
|
|
|
62
62
|
return callMcpRpc("initialize", {
|
|
63
63
|
protocolVersion: "2024-11-05",
|
|
64
64
|
capabilities: {},
|
|
65
|
-
clientInfo: { name: "nexarch-cli", version: "0.
|
|
65
|
+
clientInfo: { name: "nexarch-cli", version: "0.4.1" },
|
|
66
66
|
}, options);
|
|
67
67
|
}
|
|
68
68
|
export async function mcpListTools(options = {}) {
|
package/package.json
CHANGED