nexarch 0.1.8 → 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 CHANGED
@@ -13,9 +13,9 @@ 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 |
@@ -23,6 +23,13 @@ npx nexarch setup
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
24
  | `nexarch init-agent` (default behavior) | Also upserts technology component entities (host, OS, Node.js runtime) and links them to the agent |
25
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.
32
+
26
33
  ## Requirements
27
34
 
28
35
  - Node.js 18 or later
@@ -2,7 +2,7 @@ import { arch, hostname, platform, release, type as osType, userInfo } from "os"
2
2
  import process from "process";
3
3
  import { requireCredentials } from "../lib/credentials.js";
4
4
  import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
5
- const CLI_VERSION = "0.1.8";
5
+ const CLI_VERSION = "0.1.11";
6
6
  const AGENT_ENTITY_TYPE = "agent";
7
7
  const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
8
8
  function parseFlag(args, flag) {
@@ -80,7 +80,7 @@ async function emitInitObservation(params) {
80
80
  waivers: [],
81
81
  },
82
82
  },
83
- });
83
+ }, { companyId: params.companyId });
84
84
  }
85
85
  catch {
86
86
  // non-fatal telemetry path
@@ -105,13 +105,14 @@ export async function initAgent(args) {
105
105
  };
106
106
  const checks = [];
107
107
  const creds = requireCredentials();
108
- const init = await mcpInitialize();
108
+ const selectedCompanyId = creds.companyId;
109
+ const init = await mcpInitialize({ companyId: selectedCompanyId });
109
110
  checks.push({
110
111
  name: "mcp.initialize",
111
112
  ok: true,
112
113
  detail: `${init.serverInfo?.name ?? "unknown"}@${init.serverInfo?.version ?? "unknown"}`,
113
114
  });
114
- const tools = await mcpListTools();
115
+ const tools = await mcpListTools({ companyId: selectedCompanyId });
115
116
  const toolNames = new Set(tools.map((t) => t.name));
116
117
  const required = [
117
118
  "nexarch_get_applied_policies",
@@ -126,7 +127,7 @@ export async function initAgent(args) {
126
127
  ok: missing.length === 0,
127
128
  detail: missing.length ? `missing: ${missing.join(", ")}` : `required tools present (${required.length})`,
128
129
  });
129
- const policiesRaw = await callMcpTool("nexarch_get_applied_policies", {});
130
+ const policiesRaw = await callMcpTool("nexarch_get_applied_policies", {}, { companyId: selectedCompanyId });
130
131
  const policies = parseToolText(policiesRaw);
131
132
  checks.push({
132
133
  name: "governance.bootstrap",
@@ -135,7 +136,7 @@ export async function initAgent(args) {
135
136
  ? `policyBundleHash=${policies.policyBundleHash.slice(0, 12)}… policyCount=${policies.policyCount ?? 0}`
136
137
  : "missing policyBundleHash",
137
138
  });
138
- const contractRaw = await callMcpTool("nexarch_get_ingest_contract", {});
139
+ const contractRaw = await callMcpTool("nexarch_get_ingest_contract", {}, { companyId: selectedCompanyId });
139
140
  const contract = parseToolText(contractRaw);
140
141
  checks.push({
141
142
  name: "ingest.contract",
@@ -243,7 +244,7 @@ export async function initAgent(args) {
243
244
  },
244
245
  },
245
246
  dryRun: false,
246
- });
247
+ }, { companyId: selectedCompanyId });
247
248
  const upsert = parseToolText(upsertRaw);
248
249
  const firstResult = upsert.results?.[0];
249
250
  const failed = Number(upsert.summary?.failed ?? 0) > 0;
@@ -259,6 +260,7 @@ export async function initAgent(args) {
259
260
  if (!registration.ok) {
260
261
  await emitInitObservation({
261
262
  agentId,
263
+ companyId: selectedCompanyId,
262
264
  policyBundleHash: policies.policyBundleHash,
263
265
  message: `init-agent registration failed: ${registration.detail}`,
264
266
  confidence: 1,
@@ -274,6 +276,7 @@ export async function initAgent(args) {
274
276
  {
275
277
  externalKey: hostExternalKey,
276
278
  entityTypeCode: TECH_COMPONENT_ENTITY_TYPE,
279
+ entitySubtypeCode: "tech_compute",
277
280
  name: `Host ${runtime.hostname}`,
278
281
  description: "Execution host for registered agent runtimes",
279
282
  confidence: 0.95,
@@ -288,6 +291,7 @@ export async function initAgent(args) {
288
291
  {
289
292
  externalKey: osExternalKey,
290
293
  entityTypeCode: TECH_COMPONENT_ENTITY_TYPE,
294
+ entitySubtypeCode: "tech_operating_system",
291
295
  name: `${runtime.osPlatform} ${runtime.osRelease}`,
292
296
  description: "Operating system platform used by registered agents",
293
297
  confidence: 0.95,
@@ -302,6 +306,7 @@ export async function initAgent(args) {
302
306
  {
303
307
  externalKey: nodeExternalKey,
304
308
  entityTypeCode: TECH_COMPONENT_ENTITY_TYPE,
309
+ entitySubtypeCode: "tech_runtime",
305
310
  name: `Node.js ${runtime.nodeVersion}`,
306
311
  description: "Node.js runtime used by nexarch-cli agent",
307
312
  confidence: 0.95,
@@ -330,7 +335,7 @@ export async function initAgent(args) {
330
335
  },
331
336
  },
332
337
  dryRun: false,
333
- });
338
+ }, { companyId: selectedCompanyId });
334
339
  const techUpsert = parseToolText(techUpsertRaw);
335
340
  const techFailed = Number(techUpsert.summary?.failed ?? 0) > 0;
336
341
  techComponents = {
@@ -345,6 +350,7 @@ export async function initAgent(args) {
345
350
  if (!techComponents.ok) {
346
351
  await emitInitObservation({
347
352
  agentId,
353
+ companyId: selectedCompanyId,
348
354
  policyBundleHash: policies.policyBundleHash,
349
355
  message: `init-agent technology component upsert failed: ${techComponents.detail}`,
350
356
  confidence: 1,
@@ -394,7 +400,7 @@ export async function initAgent(args) {
394
400
  },
395
401
  },
396
402
  dryRun: false,
397
- });
403
+ }, { companyId: selectedCompanyId });
398
404
  const relUpsert = parseToolText(techRelRaw);
399
405
  const relFailed = Number(relUpsert.summary?.failed ?? 0) > 0;
400
406
  techRelationships = {
@@ -409,6 +415,7 @@ export async function initAgent(args) {
409
415
  if (!techRelationships.ok) {
410
416
  await emitInitObservation({
411
417
  agentId,
418
+ companyId: selectedCompanyId,
412
419
  policyBundleHash: policies.policyBundleHash,
413
420
  message: `init-agent technology relationship upsert failed: ${techRelationships.detail}`,
414
421
  confidence: 1,
@@ -449,7 +456,7 @@ export async function initAgent(args) {
449
456
  },
450
457
  },
451
458
  dryRun: false,
452
- });
459
+ }, { companyId: selectedCompanyId });
453
460
  const rel = parseToolText(relRaw);
454
461
  const relResult = rel.results?.[0];
455
462
  const relFailed = Number(rel.summary?.failed ?? 0) > 0;
@@ -466,6 +473,7 @@ export async function initAgent(args) {
466
473
  if (!binding.ok) {
467
474
  await emitInitObservation({
468
475
  agentId,
476
+ companyId: selectedCompanyId,
469
477
  policyBundleHash: policies.policyBundleHash,
470
478
  message: `init-agent binding failed: ${binding.detail}`,
471
479
  confidence: 1,
@@ -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
- export async function login(_args) {
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 authUrl = `${NEXARCH_URL}/auth/cli?port=${port}&state=${state}`;
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({ token, email: "", company: "", companyId, expiresAt: expiresAt.toISOString() });
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
  }
@@ -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 the browser and store credentials
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.)
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": creds.companyId,
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.8" },
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.1.8",
3
+ "version": "0.1.11",
4
4
  "description": "Connect AI coding tools to your Nexarch architecture workspace",
5
5
  "keywords": [
6
6
  "nexarch",