nexarch 0.1.6 → 0.1.8

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
@@ -19,8 +19,9 @@ npx nexarch setup
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 |
24
25
 
25
26
  ## Requirements
26
27
 
@@ -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.6";
5
+ const CLI_VERSION = "0.1.8";
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;
@@ -64,10 +89,20 @@ 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
108
  const init = await mcpInitialize();
@@ -107,7 +142,7 @@ export async function initAgent(args) {
107
142
  ok: Boolean(contract.contractVersion),
108
143
  detail: `version=${contract.contractVersion ?? "unknown"}; entities=${contract.ontology?.entityCodes?.length ?? 0}; relationships=${contract.ontology?.relationshipCodes?.length ?? 0}`,
109
144
  });
110
- const hasAgentType = (contract.ontology?.entityCodes ?? []).some((code) => code.toLowerCase() === "agent");
145
+ const hasAgentType = hasEntityType(contract, AGENT_ENTITY_TYPE);
111
146
  checks.push({
112
147
  name: "ontology.agent-type",
113
148
  ok: hasAgentType,
@@ -115,23 +150,51 @@ export async function initAgent(args) {
115
150
  ? "entity type 'agent' is enabled"
116
151
  : "entity type 'agent' is not enabled for this workspace ontology",
117
152
  });
153
+ const hasTechComponentType = hasEntityType(contract, TECH_COMPONENT_ENTITY_TYPE);
154
+ checks.push({
155
+ name: "ontology.technology-component-type",
156
+ ok: hasTechComponentType,
157
+ detail: hasTechComponentType
158
+ ? "entity type 'technology_component' is enabled"
159
+ : "entity type 'technology_component' is not enabled for this workspace ontology",
160
+ });
161
+ const relForTechBinding = pickRelationshipType(contract);
162
+ checks.push({
163
+ name: "ontology.agent-tech-relationship",
164
+ ok: Boolean(relForTechBinding),
165
+ detail: relForTechBinding
166
+ ? `using relationship '${relForTechBinding}' for agent->technology_component links`
167
+ : "no supported relationship code found (expected one of depends_on|uses|runs_on)",
168
+ });
118
169
  const preflightPassed = checks.every((c) => c.ok);
119
170
  let registration = {
120
171
  ok: false,
121
172
  detail: "registration not attempted",
122
173
  };
174
+ let techComponents = {
175
+ attempted: false,
176
+ ok: true,
177
+ detail: "not attempted",
178
+ };
179
+ let techRelationships = {
180
+ attempted: false,
181
+ ok: true,
182
+ detail: "not attempted",
183
+ };
123
184
  let binding = {
124
185
  attempted: false,
125
186
  ok: true,
126
187
  detail: "not requested",
127
188
  };
128
- if (preflightPassed && policies.policyBundleHash) {
189
+ if (preflightPassed && policies.policyBundleHash && relForTechBinding) {
129
190
  const nowIso = new Date().toISOString();
191
+ const agentRunId = `init-agent-${Date.now()}`;
192
+ const agentExternalKey = `agent:${agentId}`;
130
193
  const upsertRaw = await callMcpTool("nexarch_upsert_entities", {
131
194
  entities: [
132
195
  {
133
- externalKey: `agent:${agentId}`,
134
- entityTypeCode: "agent",
196
+ externalKey: agentExternalKey,
197
+ entityTypeCode: AGENT_ENTITY_TYPE,
135
198
  name: `Agent ${agentId}`,
136
199
  description: "Agent identity registered from nexarch init-agent handshake",
137
200
  confidence: 1,
@@ -140,6 +203,21 @@ export async function initAgent(args) {
140
203
  client: "nexarch-cli",
141
204
  version: CLI_VERSION,
142
205
  companyId: creds.companyId,
206
+ governance: {
207
+ policyBundleHash: policies.policyBundleHash,
208
+ contractVersion: contract.contractVersion ?? null,
209
+ policyCount: policies.policyCount ?? null,
210
+ strict,
211
+ },
212
+ runtime: {
213
+ os: runtime.osPlatform,
214
+ osType: runtime.osType,
215
+ osRelease: runtime.osRelease,
216
+ arch: runtime.arch,
217
+ hostname: runtime.hostname,
218
+ nodeVersion: runtime.nodeVersion,
219
+ mode: runtime.mode,
220
+ },
143
221
  handshake: {
144
222
  completedAt: nowIso,
145
223
  source: "init-agent",
@@ -149,7 +227,7 @@ export async function initAgent(args) {
149
227
  ],
150
228
  agentContext: {
151
229
  agentId,
152
- agentRunId: `init-agent-${Date.now()}`,
230
+ agentRunId,
153
231
  repoRef: "nexarch-cli/init-agent",
154
232
  observedAt: nowIso,
155
233
  source: "nexarch-cli",
@@ -186,6 +264,158 @@ export async function initAgent(args) {
186
264
  confidence: 1,
187
265
  });
188
266
  }
267
+ if (registration.ok) {
268
+ techComponents.attempted = true;
269
+ const hostExternalKey = `tech:host:${runtime.hostname}:${runtime.arch}`;
270
+ const osExternalKey = `tech:os:${runtime.osPlatform}:${runtime.osRelease}:${runtime.arch}`;
271
+ const nodeExternalKey = `tech:runtime:nodejs:${runtime.nodeVersion}`;
272
+ const techUpsertRaw = await callMcpTool("nexarch_upsert_entities", {
273
+ entities: [
274
+ {
275
+ externalKey: hostExternalKey,
276
+ entityTypeCode: TECH_COMPONENT_ENTITY_TYPE,
277
+ name: `Host ${runtime.hostname}`,
278
+ description: "Execution host for registered agent runtimes",
279
+ confidence: 0.95,
280
+ attributes: {
281
+ kind: "host_machine",
282
+ hostname: runtime.hostname,
283
+ arch: runtime.arch,
284
+ os: runtime.osPlatform,
285
+ osRelease: runtime.osRelease,
286
+ },
287
+ },
288
+ {
289
+ externalKey: osExternalKey,
290
+ entityTypeCode: TECH_COMPONENT_ENTITY_TYPE,
291
+ name: `${runtime.osPlatform} ${runtime.osRelease}`,
292
+ description: "Operating system platform used by registered agents",
293
+ confidence: 0.95,
294
+ attributes: {
295
+ kind: "operating_system",
296
+ os: runtime.osPlatform,
297
+ osType: runtime.osType,
298
+ osRelease: runtime.osRelease,
299
+ arch: runtime.arch,
300
+ },
301
+ },
302
+ {
303
+ externalKey: nodeExternalKey,
304
+ entityTypeCode: TECH_COMPONENT_ENTITY_TYPE,
305
+ name: `Node.js ${runtime.nodeVersion}`,
306
+ description: "Node.js runtime used by nexarch-cli agent",
307
+ confidence: 0.95,
308
+ attributes: {
309
+ kind: "runtime",
310
+ runtime: "nodejs",
311
+ version: runtime.nodeVersion,
312
+ },
313
+ },
314
+ ],
315
+ agentContext: {
316
+ agentId,
317
+ agentRunId: `${agentRunId}-tech`,
318
+ repoRef: "nexarch-cli/init-agent",
319
+ observedAt: nowIso,
320
+ source: "nexarch-cli",
321
+ model: "n/a",
322
+ provider: "n/a",
323
+ },
324
+ policyContext: {
325
+ policyBundleHash: policies.policyBundleHash,
326
+ alignmentSummary: {
327
+ score: 1,
328
+ violations: [],
329
+ waivers: [],
330
+ },
331
+ },
332
+ dryRun: false,
333
+ });
334
+ const techUpsert = parseToolText(techUpsertRaw);
335
+ const techFailed = Number(techUpsert.summary?.failed ?? 0) > 0;
336
+ techComponents = {
337
+ attempted: true,
338
+ ok: !techFailed,
339
+ detail: techFailed
340
+ ? `technology component upsert failed (${techUpsert.errors?.[0]?.error ?? "unknown"})`
341
+ : `upserted ${(techUpsert.summary?.succeeded ?? 0)} technology components`,
342
+ results: techUpsert.results,
343
+ errors: techUpsert.errors,
344
+ };
345
+ if (!techComponents.ok) {
346
+ await emitInitObservation({
347
+ agentId,
348
+ policyBundleHash: policies.policyBundleHash,
349
+ message: `init-agent technology component upsert failed: ${techComponents.detail}`,
350
+ confidence: 1,
351
+ });
352
+ }
353
+ if (techComponents.ok) {
354
+ techRelationships.attempted = true;
355
+ const techRelRaw = await callMcpTool("nexarch_upsert_relationships", {
356
+ relationships: [
357
+ {
358
+ relationshipTypeCode: relForTechBinding,
359
+ fromEntityExternalKey: agentExternalKey,
360
+ toEntityExternalKey: hostExternalKey,
361
+ confidence: 0.95,
362
+ attributes: { source: "nexarch-cli-init-agent", kind: "execution_host", createdAt: nowIso },
363
+ },
364
+ {
365
+ relationshipTypeCode: relForTechBinding,
366
+ fromEntityExternalKey: agentExternalKey,
367
+ toEntityExternalKey: osExternalKey,
368
+ confidence: 0.95,
369
+ attributes: { source: "nexarch-cli-init-agent", kind: "operating_system", createdAt: nowIso },
370
+ },
371
+ {
372
+ relationshipTypeCode: relForTechBinding,
373
+ fromEntityExternalKey: agentExternalKey,
374
+ toEntityExternalKey: nodeExternalKey,
375
+ confidence: 0.95,
376
+ attributes: { source: "nexarch-cli-init-agent", kind: "runtime", createdAt: nowIso },
377
+ },
378
+ ],
379
+ agentContext: {
380
+ agentId,
381
+ agentRunId: `${agentRunId}-tech-rel`,
382
+ repoRef: "nexarch-cli/init-agent",
383
+ observedAt: nowIso,
384
+ source: "nexarch-cli",
385
+ model: "n/a",
386
+ provider: "n/a",
387
+ },
388
+ policyContext: {
389
+ policyBundleHash: policies.policyBundleHash,
390
+ alignmentSummary: {
391
+ score: 1,
392
+ violations: [],
393
+ waivers: [],
394
+ },
395
+ },
396
+ dryRun: false,
397
+ });
398
+ const relUpsert = parseToolText(techRelRaw);
399
+ const relFailed = Number(relUpsert.summary?.failed ?? 0) > 0;
400
+ techRelationships = {
401
+ attempted: true,
402
+ ok: !relFailed,
403
+ detail: relFailed
404
+ ? `technology relationship upsert failed (${relUpsert.errors?.[0]?.error ?? "unknown"})`
405
+ : `upserted ${(relUpsert.summary?.succeeded ?? 0)} technology relationships`,
406
+ results: relUpsert.results,
407
+ errors: relUpsert.errors,
408
+ };
409
+ if (!techRelationships.ok) {
410
+ await emitInitObservation({
411
+ agentId,
412
+ policyBundleHash: policies.policyBundleHash,
413
+ message: `init-agent technology relationship upsert failed: ${techRelationships.detail}`,
414
+ confidence: 1,
415
+ });
416
+ }
417
+ }
418
+ }
189
419
  if (registration.ok && bindToExternalKey) {
190
420
  binding.attempted = true;
191
421
  const relRaw = await callMcpTool("nexarch_upsert_relationships", {
@@ -248,6 +478,16 @@ export async function initAgent(args) {
248
478
  ok: registration.ok,
249
479
  detail: registration.detail,
250
480
  });
481
+ checks.push({
482
+ name: "technology.components",
483
+ ok: techComponents.ok,
484
+ detail: techComponents.detail,
485
+ });
486
+ checks.push({
487
+ name: "technology.relationships",
488
+ ok: techRelationships.ok,
489
+ detail: techRelationships.detail,
490
+ });
251
491
  if (binding.attempted) {
252
492
  checks.push({
253
493
  name: "agent.binding",
@@ -270,7 +510,12 @@ export async function initAgent(args) {
270
510
  policyBundleHash: policies.policyBundleHash ?? null,
271
511
  contractVersion: contract.contractVersion ?? null,
272
512
  agentId,
513
+ runtime,
273
514
  registration,
515
+ technology: {
516
+ components: techComponents,
517
+ relationships: techRelationships,
518
+ },
274
519
  binding,
275
520
  companyId: creds.companyId,
276
521
  };
package/dist/index.js CHANGED
@@ -31,7 +31,8 @@ Usage:
31
31
  nexarch mcp-proxy Run as stdio MCP proxy (used by MCP clients)
32
32
  nexarch init-agent Run handshake + mandatory agent registration in graph
33
33
  Options: --agent-id <id> --bind-to-external-key <key>
34
- --bind-relationship-type <code> --json --strict
34
+ --bind-relationship-type <code> --redact-hostname
35
+ --json --strict
35
36
  `);
36
37
  process.exit(command ? 1 : 0);
37
38
  }
package/dist/lib/mcp.js CHANGED
@@ -61,7 +61,7 @@ export async function mcpInitialize() {
61
61
  return callMcpRpc("initialize", {
62
62
  protocolVersion: "2024-11-05",
63
63
  capabilities: {},
64
- clientInfo: { name: "nexarch-cli", version: "0.1.6" },
64
+ clientInfo: { name: "nexarch-cli", version: "0.1.8" },
65
65
  });
66
66
  }
67
67
  export async function mcpListTools() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Connect AI coding tools to your Nexarch architecture workspace",
5
5
  "keywords": [
6
6
  "nexarch",