nexarch 0.1.7 → 0.1.11
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 +11 -3
- package/dist/commands/init-agent.js +267 -14
- package/dist/commands/login.js +27 -5
- package/dist/commands/status.js +2 -3
- package/dist/index.js +4 -2
- package/dist/lib/mcp.js +10 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,14 +13,22 @@ npx nexarch setup
|
|
|
13
13
|
|
|
14
14
|
| Command | Description |
|
|
15
15
|
|---|---|
|
|
16
|
-
| `nexarch login` | Authenticate via browser and store credentials |
|
|
16
|
+
| `nexarch login [--company <id>]` | Authenticate via browser and store company-scoped credentials; prompts for company when multiple memberships exist |
|
|
17
17
|
| `nexarch logout` | Remove local credentials |
|
|
18
|
-
| `nexarch status` | Check connection and show architecture summary |
|
|
18
|
+
| `nexarch status` | Check connection and show architecture summary for the company selected at login |
|
|
19
19
|
| `nexarch setup` | Auto-configure detected MCP clients |
|
|
20
20
|
| `nexarch mcp-config [client]` | Print config block for manual setup (`claude-desktop`, `cursor`, `windsurf`) |
|
|
21
21
|
| `nexarch mcp-proxy` | stdio→HTTP bridge used internally by MCP clients |
|
|
22
|
-
| `nexarch init-agent [--json] [--strict] [--agent-id <id>]` | Run onboarding handshake and mandatory `agent` entity registration in graph |
|
|
22
|
+
| `nexarch init-agent [--json] [--strict] [--agent-id <id>] [--redact-hostname]` | Run onboarding handshake and mandatory `agent` entity registration in graph |
|
|
23
23
|
| `nexarch init-agent --bind-to-external-key <key> [--bind-relationship-type <code>]` | Optionally bind the agent node to an existing graph external key |
|
|
24
|
+
| `nexarch init-agent` (default behavior) | Also upserts technology component entities (host, OS, Node.js runtime) and links them to the agent |
|
|
25
|
+
|
|
26
|
+
## Company context behavior
|
|
27
|
+
|
|
28
|
+
- Company is selected during `nexarch login`.
|
|
29
|
+
- If your user has one active company, login auto-selects it.
|
|
30
|
+
- If your user has multiple active companies, login requires explicit selection.
|
|
31
|
+
- To switch company later, run `nexarch login` again and pick another company.
|
|
24
32
|
|
|
25
33
|
## Requirements
|
|
26
34
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { hostname, userInfo } from "os";
|
|
1
|
+
import { arch, hostname, platform, release, type as osType, userInfo } from "os";
|
|
2
|
+
import process from "process";
|
|
2
3
|
import { requireCredentials } from "../lib/credentials.js";
|
|
3
4
|
import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
|
|
4
|
-
const CLI_VERSION = "0.1.
|
|
5
|
+
const CLI_VERSION = "0.1.11";
|
|
6
|
+
const AGENT_ENTITY_TYPE = "agent";
|
|
7
|
+
const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
|
|
5
8
|
function parseFlag(args, flag) {
|
|
6
9
|
return args.includes(flag);
|
|
7
10
|
}
|
|
@@ -23,6 +26,28 @@ function getDefaultAgentId() {
|
|
|
23
26
|
const host = hostname() || "unknown-host";
|
|
24
27
|
return `nexarch-cli:${osUser}@${host}`;
|
|
25
28
|
}
|
|
29
|
+
function getRuntimeMode() {
|
|
30
|
+
if (process.env.CI)
|
|
31
|
+
return "ci";
|
|
32
|
+
return "interactive";
|
|
33
|
+
}
|
|
34
|
+
function getSanitizedHostname(redactHostname) {
|
|
35
|
+
const raw = hostname() || "unknown-host";
|
|
36
|
+
return redactHostname ? "redacted" : raw;
|
|
37
|
+
}
|
|
38
|
+
function hasEntityType(contract, code) {
|
|
39
|
+
return (contract.ontology?.entityCodes ?? []).some((entityCode) => entityCode.toLowerCase() === code.toLowerCase());
|
|
40
|
+
}
|
|
41
|
+
function pickRelationshipType(contract) {
|
|
42
|
+
const available = new Set((contract.ontology?.relationshipCodes ?? []).map((c) => c.toLowerCase()));
|
|
43
|
+
if (available.has("depends_on"))
|
|
44
|
+
return "depends_on";
|
|
45
|
+
if (available.has("uses"))
|
|
46
|
+
return "uses";
|
|
47
|
+
if (available.has("runs_on"))
|
|
48
|
+
return "runs_on";
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
26
51
|
async function emitInitObservation(params) {
|
|
27
52
|
if (!params.policyBundleHash)
|
|
28
53
|
return;
|
|
@@ -55,7 +80,7 @@ async function emitInitObservation(params) {
|
|
|
55
80
|
waivers: [],
|
|
56
81
|
},
|
|
57
82
|
},
|
|
58
|
-
});
|
|
83
|
+
}, { companyId: params.companyId });
|
|
59
84
|
}
|
|
60
85
|
catch {
|
|
61
86
|
// non-fatal telemetry path
|
|
@@ -64,19 +89,30 @@ async function emitInitObservation(params) {
|
|
|
64
89
|
export async function initAgent(args) {
|
|
65
90
|
const asJson = parseFlag(args, "--json");
|
|
66
91
|
const strict = parseFlag(args, "--strict");
|
|
92
|
+
const redactHostname = parseFlag(args, "--redact-hostname");
|
|
67
93
|
const explicitAgentId = parseOptionValue(args, "--agent-id");
|
|
68
94
|
const bindToExternalKey = parseOptionValue(args, "--bind-to-external-key");
|
|
69
95
|
const bindRelationshipType = parseOptionValue(args, "--bind-relationship-type") ?? "depends_on";
|
|
70
96
|
const agentId = explicitAgentId ?? getDefaultAgentId();
|
|
97
|
+
const runtime = {
|
|
98
|
+
osPlatform: platform(),
|
|
99
|
+
osType: osType(),
|
|
100
|
+
osRelease: release(),
|
|
101
|
+
arch: arch(),
|
|
102
|
+
hostname: getSanitizedHostname(redactHostname),
|
|
103
|
+
nodeVersion: process.version,
|
|
104
|
+
mode: getRuntimeMode(),
|
|
105
|
+
};
|
|
71
106
|
const checks = [];
|
|
72
107
|
const creds = requireCredentials();
|
|
73
|
-
const
|
|
108
|
+
const selectedCompanyId = creds.companyId;
|
|
109
|
+
const init = await mcpInitialize({ companyId: selectedCompanyId });
|
|
74
110
|
checks.push({
|
|
75
111
|
name: "mcp.initialize",
|
|
76
112
|
ok: true,
|
|
77
113
|
detail: `${init.serverInfo?.name ?? "unknown"}@${init.serverInfo?.version ?? "unknown"}`,
|
|
78
114
|
});
|
|
79
|
-
const tools = await mcpListTools();
|
|
115
|
+
const tools = await mcpListTools({ companyId: selectedCompanyId });
|
|
80
116
|
const toolNames = new Set(tools.map((t) => t.name));
|
|
81
117
|
const required = [
|
|
82
118
|
"nexarch_get_applied_policies",
|
|
@@ -91,7 +127,7 @@ export async function initAgent(args) {
|
|
|
91
127
|
ok: missing.length === 0,
|
|
92
128
|
detail: missing.length ? `missing: ${missing.join(", ")}` : `required tools present (${required.length})`,
|
|
93
129
|
});
|
|
94
|
-
const policiesRaw = await callMcpTool("nexarch_get_applied_policies", {});
|
|
130
|
+
const policiesRaw = await callMcpTool("nexarch_get_applied_policies", {}, { companyId: selectedCompanyId });
|
|
95
131
|
const policies = parseToolText(policiesRaw);
|
|
96
132
|
checks.push({
|
|
97
133
|
name: "governance.bootstrap",
|
|
@@ -100,14 +136,14 @@ export async function initAgent(args) {
|
|
|
100
136
|
? `policyBundleHash=${policies.policyBundleHash.slice(0, 12)}… policyCount=${policies.policyCount ?? 0}`
|
|
101
137
|
: "missing policyBundleHash",
|
|
102
138
|
});
|
|
103
|
-
const contractRaw = await callMcpTool("nexarch_get_ingest_contract", {});
|
|
139
|
+
const contractRaw = await callMcpTool("nexarch_get_ingest_contract", {}, { companyId: selectedCompanyId });
|
|
104
140
|
const contract = parseToolText(contractRaw);
|
|
105
141
|
checks.push({
|
|
106
142
|
name: "ingest.contract",
|
|
107
143
|
ok: Boolean(contract.contractVersion),
|
|
108
144
|
detail: `version=${contract.contractVersion ?? "unknown"}; entities=${contract.ontology?.entityCodes?.length ?? 0}; relationships=${contract.ontology?.relationshipCodes?.length ?? 0}`,
|
|
109
145
|
});
|
|
110
|
-
const hasAgentType = (contract
|
|
146
|
+
const hasAgentType = hasEntityType(contract, AGENT_ENTITY_TYPE);
|
|
111
147
|
checks.push({
|
|
112
148
|
name: "ontology.agent-type",
|
|
113
149
|
ok: hasAgentType,
|
|
@@ -115,23 +151,51 @@ export async function initAgent(args) {
|
|
|
115
151
|
? "entity type 'agent' is enabled"
|
|
116
152
|
: "entity type 'agent' is not enabled for this workspace ontology",
|
|
117
153
|
});
|
|
154
|
+
const hasTechComponentType = hasEntityType(contract, TECH_COMPONENT_ENTITY_TYPE);
|
|
155
|
+
checks.push({
|
|
156
|
+
name: "ontology.technology-component-type",
|
|
157
|
+
ok: hasTechComponentType,
|
|
158
|
+
detail: hasTechComponentType
|
|
159
|
+
? "entity type 'technology_component' is enabled"
|
|
160
|
+
: "entity type 'technology_component' is not enabled for this workspace ontology",
|
|
161
|
+
});
|
|
162
|
+
const relForTechBinding = pickRelationshipType(contract);
|
|
163
|
+
checks.push({
|
|
164
|
+
name: "ontology.agent-tech-relationship",
|
|
165
|
+
ok: Boolean(relForTechBinding),
|
|
166
|
+
detail: relForTechBinding
|
|
167
|
+
? `using relationship '${relForTechBinding}' for agent->technology_component links`
|
|
168
|
+
: "no supported relationship code found (expected one of depends_on|uses|runs_on)",
|
|
169
|
+
});
|
|
118
170
|
const preflightPassed = checks.every((c) => c.ok);
|
|
119
171
|
let registration = {
|
|
120
172
|
ok: false,
|
|
121
173
|
detail: "registration not attempted",
|
|
122
174
|
};
|
|
175
|
+
let techComponents = {
|
|
176
|
+
attempted: false,
|
|
177
|
+
ok: true,
|
|
178
|
+
detail: "not attempted",
|
|
179
|
+
};
|
|
180
|
+
let techRelationships = {
|
|
181
|
+
attempted: false,
|
|
182
|
+
ok: true,
|
|
183
|
+
detail: "not attempted",
|
|
184
|
+
};
|
|
123
185
|
let binding = {
|
|
124
186
|
attempted: false,
|
|
125
187
|
ok: true,
|
|
126
188
|
detail: "not requested",
|
|
127
189
|
};
|
|
128
|
-
if (preflightPassed && policies.policyBundleHash) {
|
|
190
|
+
if (preflightPassed && policies.policyBundleHash && relForTechBinding) {
|
|
129
191
|
const nowIso = new Date().toISOString();
|
|
192
|
+
const agentRunId = `init-agent-${Date.now()}`;
|
|
193
|
+
const agentExternalKey = `agent:${agentId}`;
|
|
130
194
|
const upsertRaw = await callMcpTool("nexarch_upsert_entities", {
|
|
131
195
|
entities: [
|
|
132
196
|
{
|
|
133
|
-
externalKey:
|
|
134
|
-
entityTypeCode:
|
|
197
|
+
externalKey: agentExternalKey,
|
|
198
|
+
entityTypeCode: AGENT_ENTITY_TYPE,
|
|
135
199
|
name: `Agent ${agentId}`,
|
|
136
200
|
description: "Agent identity registered from nexarch init-agent handshake",
|
|
137
201
|
confidence: 1,
|
|
@@ -140,6 +204,21 @@ export async function initAgent(args) {
|
|
|
140
204
|
client: "nexarch-cli",
|
|
141
205
|
version: CLI_VERSION,
|
|
142
206
|
companyId: creds.companyId,
|
|
207
|
+
governance: {
|
|
208
|
+
policyBundleHash: policies.policyBundleHash,
|
|
209
|
+
contractVersion: contract.contractVersion ?? null,
|
|
210
|
+
policyCount: policies.policyCount ?? null,
|
|
211
|
+
strict,
|
|
212
|
+
},
|
|
213
|
+
runtime: {
|
|
214
|
+
os: runtime.osPlatform,
|
|
215
|
+
osType: runtime.osType,
|
|
216
|
+
osRelease: runtime.osRelease,
|
|
217
|
+
arch: runtime.arch,
|
|
218
|
+
hostname: runtime.hostname,
|
|
219
|
+
nodeVersion: runtime.nodeVersion,
|
|
220
|
+
mode: runtime.mode,
|
|
221
|
+
},
|
|
143
222
|
handshake: {
|
|
144
223
|
completedAt: nowIso,
|
|
145
224
|
source: "init-agent",
|
|
@@ -149,7 +228,7 @@ export async function initAgent(args) {
|
|
|
149
228
|
],
|
|
150
229
|
agentContext: {
|
|
151
230
|
agentId,
|
|
152
|
-
agentRunId
|
|
231
|
+
agentRunId,
|
|
153
232
|
repoRef: "nexarch-cli/init-agent",
|
|
154
233
|
observedAt: nowIso,
|
|
155
234
|
source: "nexarch-cli",
|
|
@@ -165,7 +244,7 @@ export async function initAgent(args) {
|
|
|
165
244
|
},
|
|
166
245
|
},
|
|
167
246
|
dryRun: false,
|
|
168
|
-
});
|
|
247
|
+
}, { companyId: selectedCompanyId });
|
|
169
248
|
const upsert = parseToolText(upsertRaw);
|
|
170
249
|
const firstResult = upsert.results?.[0];
|
|
171
250
|
const failed = Number(upsert.summary?.failed ?? 0) > 0;
|
|
@@ -181,11 +260,169 @@ export async function initAgent(args) {
|
|
|
181
260
|
if (!registration.ok) {
|
|
182
261
|
await emitInitObservation({
|
|
183
262
|
agentId,
|
|
263
|
+
companyId: selectedCompanyId,
|
|
184
264
|
policyBundleHash: policies.policyBundleHash,
|
|
185
265
|
message: `init-agent registration failed: ${registration.detail}`,
|
|
186
266
|
confidence: 1,
|
|
187
267
|
});
|
|
188
268
|
}
|
|
269
|
+
if (registration.ok) {
|
|
270
|
+
techComponents.attempted = true;
|
|
271
|
+
const hostExternalKey = `tech:host:${runtime.hostname}:${runtime.arch}`;
|
|
272
|
+
const osExternalKey = `tech:os:${runtime.osPlatform}:${runtime.osRelease}:${runtime.arch}`;
|
|
273
|
+
const nodeExternalKey = `tech:runtime:nodejs:${runtime.nodeVersion}`;
|
|
274
|
+
const techUpsertRaw = await callMcpTool("nexarch_upsert_entities", {
|
|
275
|
+
entities: [
|
|
276
|
+
{
|
|
277
|
+
externalKey: hostExternalKey,
|
|
278
|
+
entityTypeCode: TECH_COMPONENT_ENTITY_TYPE,
|
|
279
|
+
entitySubtypeCode: "tech_compute",
|
|
280
|
+
name: `Host ${runtime.hostname}`,
|
|
281
|
+
description: "Execution host for registered agent runtimes",
|
|
282
|
+
confidence: 0.95,
|
|
283
|
+
attributes: {
|
|
284
|
+
kind: "host_machine",
|
|
285
|
+
hostname: runtime.hostname,
|
|
286
|
+
arch: runtime.arch,
|
|
287
|
+
os: runtime.osPlatform,
|
|
288
|
+
osRelease: runtime.osRelease,
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
externalKey: osExternalKey,
|
|
293
|
+
entityTypeCode: TECH_COMPONENT_ENTITY_TYPE,
|
|
294
|
+
entitySubtypeCode: "tech_operating_system",
|
|
295
|
+
name: `${runtime.osPlatform} ${runtime.osRelease}`,
|
|
296
|
+
description: "Operating system platform used by registered agents",
|
|
297
|
+
confidence: 0.95,
|
|
298
|
+
attributes: {
|
|
299
|
+
kind: "operating_system",
|
|
300
|
+
os: runtime.osPlatform,
|
|
301
|
+
osType: runtime.osType,
|
|
302
|
+
osRelease: runtime.osRelease,
|
|
303
|
+
arch: runtime.arch,
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
externalKey: nodeExternalKey,
|
|
308
|
+
entityTypeCode: TECH_COMPONENT_ENTITY_TYPE,
|
|
309
|
+
entitySubtypeCode: "tech_runtime",
|
|
310
|
+
name: `Node.js ${runtime.nodeVersion}`,
|
|
311
|
+
description: "Node.js runtime used by nexarch-cli agent",
|
|
312
|
+
confidence: 0.95,
|
|
313
|
+
attributes: {
|
|
314
|
+
kind: "runtime",
|
|
315
|
+
runtime: "nodejs",
|
|
316
|
+
version: runtime.nodeVersion,
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
agentContext: {
|
|
321
|
+
agentId,
|
|
322
|
+
agentRunId: `${agentRunId}-tech`,
|
|
323
|
+
repoRef: "nexarch-cli/init-agent",
|
|
324
|
+
observedAt: nowIso,
|
|
325
|
+
source: "nexarch-cli",
|
|
326
|
+
model: "n/a",
|
|
327
|
+
provider: "n/a",
|
|
328
|
+
},
|
|
329
|
+
policyContext: {
|
|
330
|
+
policyBundleHash: policies.policyBundleHash,
|
|
331
|
+
alignmentSummary: {
|
|
332
|
+
score: 1,
|
|
333
|
+
violations: [],
|
|
334
|
+
waivers: [],
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
dryRun: false,
|
|
338
|
+
}, { companyId: selectedCompanyId });
|
|
339
|
+
const techUpsert = parseToolText(techUpsertRaw);
|
|
340
|
+
const techFailed = Number(techUpsert.summary?.failed ?? 0) > 0;
|
|
341
|
+
techComponents = {
|
|
342
|
+
attempted: true,
|
|
343
|
+
ok: !techFailed,
|
|
344
|
+
detail: techFailed
|
|
345
|
+
? `technology component upsert failed (${techUpsert.errors?.[0]?.error ?? "unknown"})`
|
|
346
|
+
: `upserted ${(techUpsert.summary?.succeeded ?? 0)} technology components`,
|
|
347
|
+
results: techUpsert.results,
|
|
348
|
+
errors: techUpsert.errors,
|
|
349
|
+
};
|
|
350
|
+
if (!techComponents.ok) {
|
|
351
|
+
await emitInitObservation({
|
|
352
|
+
agentId,
|
|
353
|
+
companyId: selectedCompanyId,
|
|
354
|
+
policyBundleHash: policies.policyBundleHash,
|
|
355
|
+
message: `init-agent technology component upsert failed: ${techComponents.detail}`,
|
|
356
|
+
confidence: 1,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
if (techComponents.ok) {
|
|
360
|
+
techRelationships.attempted = true;
|
|
361
|
+
const techRelRaw = await callMcpTool("nexarch_upsert_relationships", {
|
|
362
|
+
relationships: [
|
|
363
|
+
{
|
|
364
|
+
relationshipTypeCode: relForTechBinding,
|
|
365
|
+
fromEntityExternalKey: agentExternalKey,
|
|
366
|
+
toEntityExternalKey: hostExternalKey,
|
|
367
|
+
confidence: 0.95,
|
|
368
|
+
attributes: { source: "nexarch-cli-init-agent", kind: "execution_host", createdAt: nowIso },
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
relationshipTypeCode: relForTechBinding,
|
|
372
|
+
fromEntityExternalKey: agentExternalKey,
|
|
373
|
+
toEntityExternalKey: osExternalKey,
|
|
374
|
+
confidence: 0.95,
|
|
375
|
+
attributes: { source: "nexarch-cli-init-agent", kind: "operating_system", createdAt: nowIso },
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
relationshipTypeCode: relForTechBinding,
|
|
379
|
+
fromEntityExternalKey: agentExternalKey,
|
|
380
|
+
toEntityExternalKey: nodeExternalKey,
|
|
381
|
+
confidence: 0.95,
|
|
382
|
+
attributes: { source: "nexarch-cli-init-agent", kind: "runtime", createdAt: nowIso },
|
|
383
|
+
},
|
|
384
|
+
],
|
|
385
|
+
agentContext: {
|
|
386
|
+
agentId,
|
|
387
|
+
agentRunId: `${agentRunId}-tech-rel`,
|
|
388
|
+
repoRef: "nexarch-cli/init-agent",
|
|
389
|
+
observedAt: nowIso,
|
|
390
|
+
source: "nexarch-cli",
|
|
391
|
+
model: "n/a",
|
|
392
|
+
provider: "n/a",
|
|
393
|
+
},
|
|
394
|
+
policyContext: {
|
|
395
|
+
policyBundleHash: policies.policyBundleHash,
|
|
396
|
+
alignmentSummary: {
|
|
397
|
+
score: 1,
|
|
398
|
+
violations: [],
|
|
399
|
+
waivers: [],
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
dryRun: false,
|
|
403
|
+
}, { companyId: selectedCompanyId });
|
|
404
|
+
const relUpsert = parseToolText(techRelRaw);
|
|
405
|
+
const relFailed = Number(relUpsert.summary?.failed ?? 0) > 0;
|
|
406
|
+
techRelationships = {
|
|
407
|
+
attempted: true,
|
|
408
|
+
ok: !relFailed,
|
|
409
|
+
detail: relFailed
|
|
410
|
+
? `technology relationship upsert failed (${relUpsert.errors?.[0]?.error ?? "unknown"})`
|
|
411
|
+
: `upserted ${(relUpsert.summary?.succeeded ?? 0)} technology relationships`,
|
|
412
|
+
results: relUpsert.results,
|
|
413
|
+
errors: relUpsert.errors,
|
|
414
|
+
};
|
|
415
|
+
if (!techRelationships.ok) {
|
|
416
|
+
await emitInitObservation({
|
|
417
|
+
agentId,
|
|
418
|
+
companyId: selectedCompanyId,
|
|
419
|
+
policyBundleHash: policies.policyBundleHash,
|
|
420
|
+
message: `init-agent technology relationship upsert failed: ${techRelationships.detail}`,
|
|
421
|
+
confidence: 1,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
189
426
|
if (registration.ok && bindToExternalKey) {
|
|
190
427
|
binding.attempted = true;
|
|
191
428
|
const relRaw = await callMcpTool("nexarch_upsert_relationships", {
|
|
@@ -219,7 +456,7 @@ export async function initAgent(args) {
|
|
|
219
456
|
},
|
|
220
457
|
},
|
|
221
458
|
dryRun: false,
|
|
222
|
-
});
|
|
459
|
+
}, { companyId: selectedCompanyId });
|
|
223
460
|
const rel = parseToolText(relRaw);
|
|
224
461
|
const relResult = rel.results?.[0];
|
|
225
462
|
const relFailed = Number(rel.summary?.failed ?? 0) > 0;
|
|
@@ -236,6 +473,7 @@ export async function initAgent(args) {
|
|
|
236
473
|
if (!binding.ok) {
|
|
237
474
|
await emitInitObservation({
|
|
238
475
|
agentId,
|
|
476
|
+
companyId: selectedCompanyId,
|
|
239
477
|
policyBundleHash: policies.policyBundleHash,
|
|
240
478
|
message: `init-agent binding failed: ${binding.detail}`,
|
|
241
479
|
confidence: 1,
|
|
@@ -248,6 +486,16 @@ export async function initAgent(args) {
|
|
|
248
486
|
ok: registration.ok,
|
|
249
487
|
detail: registration.detail,
|
|
250
488
|
});
|
|
489
|
+
checks.push({
|
|
490
|
+
name: "technology.components",
|
|
491
|
+
ok: techComponents.ok,
|
|
492
|
+
detail: techComponents.detail,
|
|
493
|
+
});
|
|
494
|
+
checks.push({
|
|
495
|
+
name: "technology.relationships",
|
|
496
|
+
ok: techRelationships.ok,
|
|
497
|
+
detail: techRelationships.detail,
|
|
498
|
+
});
|
|
251
499
|
if (binding.attempted) {
|
|
252
500
|
checks.push({
|
|
253
501
|
name: "agent.binding",
|
|
@@ -270,7 +518,12 @@ export async function initAgent(args) {
|
|
|
270
518
|
policyBundleHash: policies.policyBundleHash ?? null,
|
|
271
519
|
contractVersion: contract.contractVersion ?? null,
|
|
272
520
|
agentId,
|
|
521
|
+
runtime,
|
|
273
522
|
registration,
|
|
523
|
+
technology: {
|
|
524
|
+
components: techComponents,
|
|
525
|
+
relationships: techRelationships,
|
|
526
|
+
},
|
|
274
527
|
binding,
|
|
275
528
|
companyId: creds.companyId,
|
|
276
529
|
};
|
package/dist/commands/login.js
CHANGED
|
@@ -60,6 +60,8 @@ function waitForCallback(port, expectedState) {
|
|
|
60
60
|
const state = url.searchParams.get("state");
|
|
61
61
|
const token = url.searchParams.get("token");
|
|
62
62
|
const companyId = url.searchParams.get("companyId");
|
|
63
|
+
const companyName = url.searchParams.get("companyName") ?? undefined;
|
|
64
|
+
const companyCode = url.searchParams.get("companyCode") ?? undefined;
|
|
63
65
|
const error = url.searchParams.get("error");
|
|
64
66
|
const html = (message) => `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Nexarch CLI</title>
|
|
65
67
|
<style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#f8fafc}
|
|
@@ -90,7 +92,7 @@ function waitForCallback(port, expectedState) {
|
|
|
90
92
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
91
93
|
res.end(html("Authorization successful! You can close this tab and return to your terminal."));
|
|
92
94
|
server.close();
|
|
93
|
-
settle(() => resolve({ token, companyId }));
|
|
95
|
+
settle(() => resolve({ token, companyId, companyName, companyCode }));
|
|
94
96
|
});
|
|
95
97
|
const timeout = setTimeout(() => {
|
|
96
98
|
server.close();
|
|
@@ -103,18 +105,38 @@ function waitForCallback(port, expectedState) {
|
|
|
103
105
|
});
|
|
104
106
|
});
|
|
105
107
|
}
|
|
106
|
-
|
|
108
|
+
function getArgValue(args, flag) {
|
|
109
|
+
const idx = args.indexOf(flag);
|
|
110
|
+
if (idx === -1)
|
|
111
|
+
return null;
|
|
112
|
+
const v = args[idx + 1];
|
|
113
|
+
if (!v || v.startsWith("--"))
|
|
114
|
+
return null;
|
|
115
|
+
return v;
|
|
116
|
+
}
|
|
117
|
+
export async function login(args) {
|
|
107
118
|
const state = generateState();
|
|
108
119
|
const port = await findFreePort();
|
|
109
|
-
const
|
|
120
|
+
const requestedCompany = getArgValue(args, "--company");
|
|
121
|
+
const qp = new URLSearchParams({ port: String(port), state });
|
|
122
|
+
if (requestedCompany)
|
|
123
|
+
qp.set("company", requestedCompany);
|
|
124
|
+
const authUrl = `${NEXARCH_URL}/auth/cli?${qp.toString()}`;
|
|
110
125
|
console.log("Opening Nexarch in your browser…");
|
|
111
126
|
console.log(`\n ${authUrl}\n`);
|
|
112
127
|
console.log("If the browser did not open, copy the URL above and paste it in manually.\n");
|
|
113
128
|
openBrowser(authUrl);
|
|
114
|
-
const { token, companyId } = await waitForCallback(port, state);
|
|
129
|
+
const { token, companyId, companyName, companyCode } = await waitForCallback(port, state);
|
|
115
130
|
const expiresAt = new Date();
|
|
116
131
|
expiresAt.setDate(expiresAt.getDate() + 90);
|
|
117
|
-
saveCredentials({
|
|
132
|
+
saveCredentials({
|
|
133
|
+
token,
|
|
134
|
+
email: "",
|
|
135
|
+
company: companyName ?? companyCode ?? "",
|
|
136
|
+
companyCode: companyCode ?? undefined,
|
|
137
|
+
companyId,
|
|
138
|
+
expiresAt: expiresAt.toISOString(),
|
|
139
|
+
});
|
|
118
140
|
console.log("✓ Logged in successfully");
|
|
119
141
|
console.log("\nRun `nexarch status` to verify your connection.");
|
|
120
142
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -2,14 +2,13 @@ import { requireCredentials } from "../lib/credentials.js";
|
|
|
2
2
|
import { callMcpTool } from "../lib/mcp.js";
|
|
3
3
|
export async function status(_args) {
|
|
4
4
|
const creds = requireCredentials();
|
|
5
|
+
const selectedCompanyId = creds.companyId;
|
|
5
6
|
const expiresAt = new Date(creds.expiresAt);
|
|
6
7
|
const daysLeft = Math.ceil((expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
7
8
|
process.stdout.write("Connecting to mcp.nexarch.ai… ");
|
|
8
9
|
let governance;
|
|
9
10
|
try {
|
|
10
|
-
const result = await callMcpTool("nexarch_get_governance_summary", {
|
|
11
|
-
companyId: creds.companyId || undefined,
|
|
12
|
-
});
|
|
11
|
+
const result = await callMcpTool("nexarch_get_governance_summary", { companyId: selectedCompanyId || undefined }, { companyId: selectedCompanyId || undefined });
|
|
13
12
|
governance = JSON.parse(result.content[0]?.text ?? "{}");
|
|
14
13
|
}
|
|
15
14
|
catch (err) {
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,8 @@ async function main() {
|
|
|
23
23
|
nexarch — Connect AI coding tools to your architecture workspace
|
|
24
24
|
|
|
25
25
|
Usage:
|
|
26
|
-
nexarch login Authenticate in
|
|
26
|
+
nexarch login Authenticate in browser and store company-scoped credentials
|
|
27
|
+
Option: --company <id>
|
|
27
28
|
nexarch logout Remove stored credentials
|
|
28
29
|
nexarch status Check connection and show architecture summary
|
|
29
30
|
nexarch setup Auto-configure your MCP client (Claude Desktop, Cursor, etc.)
|
|
@@ -31,7 +32,8 @@ Usage:
|
|
|
31
32
|
nexarch mcp-proxy Run as stdio MCP proxy (used by MCP clients)
|
|
32
33
|
nexarch init-agent Run handshake + mandatory agent registration in graph
|
|
33
34
|
Options: --agent-id <id> --bind-to-external-key <key>
|
|
34
|
-
--bind-relationship-type <code> --
|
|
35
|
+
--bind-relationship-type <code> --redact-hostname
|
|
36
|
+
--json --strict
|
|
35
37
|
`);
|
|
36
38
|
process.exit(command ? 1 : 0);
|
|
37
39
|
}
|
package/dist/lib/mcp.js
CHANGED
|
@@ -2,8 +2,9 @@ import https from "https";
|
|
|
2
2
|
import { requireCredentials } from "./credentials.js";
|
|
3
3
|
const MCP_GATEWAY_URL = "https://mcp.nexarch.ai";
|
|
4
4
|
const REQUEST_TIMEOUT_MS = 30 * 1000;
|
|
5
|
-
async function callMcpRpc(method, params = {}) {
|
|
5
|
+
async function callMcpRpc(method, params = {}, options = {}) {
|
|
6
6
|
const creds = requireCredentials();
|
|
7
|
+
const companyId = options.companyId ?? creds.companyId;
|
|
7
8
|
const body = JSON.stringify({
|
|
8
9
|
jsonrpc: "2.0",
|
|
9
10
|
id: 1,
|
|
@@ -20,7 +21,7 @@ async function callMcpRpc(method, params = {}) {
|
|
|
20
21
|
headers: {
|
|
21
22
|
"Content-Type": "application/json",
|
|
22
23
|
Authorization: `Bearer ${creds.token}`,
|
|
23
|
-
"x-company-id":
|
|
24
|
+
"x-company-id": companyId,
|
|
24
25
|
"Content-Length": Buffer.byteLength(body),
|
|
25
26
|
},
|
|
26
27
|
timeout: REQUEST_TIMEOUT_MS,
|
|
@@ -54,18 +55,18 @@ async function callMcpRpc(method, params = {}) {
|
|
|
54
55
|
req.end();
|
|
55
56
|
});
|
|
56
57
|
}
|
|
57
|
-
export async function callMcpTool(toolName, toolArgs = {}) {
|
|
58
|
-
return callMcpRpc("tools/call", { name: toolName, arguments: toolArgs });
|
|
58
|
+
export async function callMcpTool(toolName, toolArgs = {}, options = {}) {
|
|
59
|
+
return callMcpRpc("tools/call", { name: toolName, arguments: toolArgs }, options);
|
|
59
60
|
}
|
|
60
|
-
export async function mcpInitialize() {
|
|
61
|
+
export async function mcpInitialize(options = {}) {
|
|
61
62
|
return callMcpRpc("initialize", {
|
|
62
63
|
protocolVersion: "2024-11-05",
|
|
63
64
|
capabilities: {},
|
|
64
|
-
clientInfo: { name: "nexarch-cli", version: "0.1.
|
|
65
|
-
});
|
|
65
|
+
clientInfo: { name: "nexarch-cli", version: "0.1.11" },
|
|
66
|
+
}, options);
|
|
66
67
|
}
|
|
67
|
-
export async function mcpListTools() {
|
|
68
|
-
const result = await callMcpRpc("tools/list", {});
|
|
68
|
+
export async function mcpListTools(options = {}) {
|
|
69
|
+
const result = await callMcpRpc("tools/list", {}, options);
|
|
69
70
|
return result.tools ?? [];
|
|
70
71
|
}
|
|
71
72
|
export async function forwardToGateway(token, companyId, body) {
|