nexarch 0.8.15 → 0.8.17

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.
@@ -8,7 +8,7 @@ import { fetchAgentRegistryOrThrow } from "../lib/agent-registry.js";
8
8
  import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
9
9
  import { buildVersionAttributes } from "../lib/version-normalization.js";
10
10
  import { requestTrustAttestation } from "../lib/trust.js";
11
- const CLI_VERSION = "0.8.12";
11
+ const CLI_VERSION = "0.8.17";
12
12
  const AGENT_ENTITY_TYPE = "agent";
13
13
  const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
14
14
  function parseFlag(args, flag) {
@@ -287,13 +287,39 @@ After running, confirm \`"ok": true\` in the JSON output. No further action is n
287
287
  writeFileSync(filePath, content, "utf8");
288
288
  return filePath;
289
289
  }
290
+ function managedMarkers(key) {
291
+ return {
292
+ start: `<!-- nexarch:${key}:start -->`,
293
+ end: `<!-- nexarch:${key}:end -->`,
294
+ };
295
+ }
296
+ function wrapManagedSection(key, body) {
297
+ const markers = managedMarkers(key);
298
+ return `${markers.start}\n${body.trim()}\n${markers.end}`;
299
+ }
300
+ function replaceManagedSection(existing, key, body) {
301
+ const markers = managedMarkers(key);
302
+ const escapedStart = markers.start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
303
+ const escapedEnd = markers.end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
304
+ const managedRegex = new RegExp(`${escapedStart}[\\s\\S]*?${escapedEnd}`, "gm");
305
+ if (!managedRegex.test(existing))
306
+ return existing;
307
+ const canonicalBlock = wrapManagedSection(key, body);
308
+ let replaced = existing.replace(managedRegex, canonicalBlock);
309
+ // collapse accidental duplicates if multiple managed blocks exist
310
+ replaced = replaced.replace(new RegExp(`(${escapedStart}[\\s\\S]*?${escapedEnd})(?:\\s*${escapedStart}[\\s\\S]*?${escapedEnd})+`, "gm"), "$1");
311
+ return replaced.endsWith("\n") ? replaced : `${replaced}\n`;
312
+ }
290
313
  function replaceInjectedSection(existing, sectionHeading, sectionBody) {
291
- const headingRegex = new RegExp(`^${sectionHeading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?(?=^##\\s|\\Z)`, "m");
292
- if (headingRegex.test(existing)) {
293
- const replaced = existing.replace(headingRegex, sectionBody.trim());
294
- return replaced.endsWith("\n") ? replaced : `${replaced}\n`;
295
- }
296
- return existing;
314
+ const escapedHeading = sectionHeading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
315
+ const blockRegex = new RegExp(`^${escapedHeading}[\\t ]*\\n[\\s\\S]*?(?=^##\\s|$)`, "gm");
316
+ const matches = existing.match(blockRegex);
317
+ if (!matches || matches.length === 0)
318
+ return existing;
319
+ const canonicalBlock = `${sectionBody.trim()}\n`;
320
+ let replaced = existing.replace(blockRegex, "");
321
+ replaced = replaced.replace(/\n{3,}/g, "\n\n").trimEnd();
322
+ return `${replaced}${replaced ? "\n\n" : ""}${canonicalBlock}`;
297
323
  }
298
324
  function injectAgentConfigs(registry) {
299
325
  const templateByCode = new Map(registry.instructionTemplates.map((t) => [t.code, t]));
@@ -312,30 +338,37 @@ function injectAgentConfigs(registry) {
312
338
  const sectionBody = template.body.trim();
313
339
  const sectionHeading = target.sectionHeading ?? "## Nexarch Agent Registration";
314
340
  const sectionMarker = target.sectionMarker ?? sectionHeading;
341
+ const managedBody = wrapManagedSection("agent-registration", sectionBody);
315
342
  if (target.insertionMode === "replace_section") {
316
- let replaced = replaceInjectedSection(existing, sectionHeading, sectionBody);
343
+ let replaced = replaceManagedSection(existing, "agent-registration", sectionBody);
344
+ if (replaced === existing) {
345
+ replaced = replaceInjectedSection(existing, sectionHeading, managedBody);
346
+ }
317
347
  if (replaced === existing && existing.includes(sectionMarker)) {
318
348
  const markerIndex = existing.indexOf(sectionMarker);
319
349
  const before = existing.slice(0, markerIndex).replace(/\s*$/, "\n\n");
320
- replaced = `${before}${sectionBody}\n`;
350
+ replaced = `${before}${managedBody}\n`;
321
351
  }
322
352
  if (replaced !== existing) {
323
353
  writeFileSync(filePath, replaced, "utf8");
324
354
  results.push({ path: filePath, status: "updated" });
325
355
  }
356
+ else if (existing.includes(managedBody) || existing.includes(sectionBody)) {
357
+ results.push({ path: filePath, status: "already_present" });
358
+ }
326
359
  else {
327
360
  const separator = existing.endsWith("\n") ? "" : "\n";
328
- writeFileSync(filePath, existing + separator + sectionBody + "\n", "utf8");
361
+ writeFileSync(filePath, existing + separator + managedBody + "\n", "utf8");
329
362
  results.push({ path: filePath, status: "injected" });
330
363
  }
331
364
  continue;
332
365
  }
333
- const separator = existing.endsWith("\n") ? "" : "\n";
334
- const next = existing + separator + sectionBody + "\n";
335
- if (next === existing) {
366
+ if (existing.includes(managedBody) || existing.includes(sectionBody)) {
336
367
  results.push({ path: filePath, status: "already_present" });
337
368
  }
338
369
  else {
370
+ const separator = existing.endsWith("\n") ? "" : "\n";
371
+ const next = existing + separator + managedBody + "\n";
339
372
  writeFileSync(filePath, next, "utf8");
340
373
  results.push({ path: filePath, status: "injected" });
341
374
  }
@@ -360,11 +393,33 @@ function injectTrustAttestationBlock(path, attestation) {
360
393
  `agent_id: ${attestation.payload.agent_id}`,
361
394
  `expires_at: ${expiresAt}`,
362
395
  `verify_url: ${verifyUrl}`,
396
+ `token: ${attestation.token}`,
397
+ "",
398
+ ].join("\n");
399
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
400
+ const managed = wrapManagedSection("trust-attestation", section.trim());
401
+ let replaced = replaceManagedSection(existing, "trust-attestation", section.trim());
402
+ if (replaced === existing) {
403
+ replaced = replaceInjectedSection(existing, "## Nexarch Trust Attestation", managed);
404
+ }
405
+ writeFileSync(path, replaced !== existing ? replaced : `${existing}${existing.endsWith("\n") ? "" : "\n"}${managed}\n`, "utf8");
406
+ }
407
+ function injectTrustAttestationUnavailableBlock(path, reason) {
408
+ const section = [
409
+ "## Nexarch Trust Attestation",
410
+ "",
411
+ "Trust attestation was requested but is currently unavailable.",
412
+ `status: unavailable (${reason})`,
413
+ "action: check MCP gateway TRUST_ATTESTATION_SECRET and restart gateway",
363
414
  "",
364
415
  ].join("\n");
365
416
  const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
366
- const replaced = replaceInjectedSection(existing, "## Nexarch Trust Attestation", section.trim());
367
- writeFileSync(path, replaced !== existing ? replaced : `${existing}${existing.endsWith("\n") ? "" : "\n"}${section}`, "utf8");
417
+ const managed = wrapManagedSection("trust-attestation", section.trim());
418
+ let replaced = replaceManagedSection(existing, "trust-attestation", section.trim());
419
+ if (replaced === existing) {
420
+ replaced = replaceInjectedSection(existing, "## Nexarch Trust Attestation", managed);
421
+ }
422
+ writeFileSync(path, replaced !== existing ? replaced : `${existing}${existing.endsWith("\n") ? "" : "\n"}${managed}\n`, "utf8");
368
423
  }
369
424
  function injectGenericAgentConfig(registry) {
370
425
  const templateByCode = new Map(registry.instructionTemplates.map((t) => [t.code, t]));
@@ -378,20 +433,30 @@ function injectGenericAgentConfig(registry) {
378
433
  const filePath = join(process.cwd(), target.filePathPattern);
379
434
  const sectionBody = template.body.trim();
380
435
  const sectionHeading = target.sectionHeading ?? "## Nexarch Agent Registration";
436
+ const managedBody = wrapManagedSection("agent-registration", sectionBody);
381
437
  if (!existsSync(filePath)) {
382
- writeFileSync(filePath, `${sectionBody}\n`, "utf8");
438
+ writeFileSync(filePath, `${managedBody}\n`, "utf8");
383
439
  return [{ path: filePath, status: "injected" }];
384
440
  }
385
441
  const existing = readFileSync(filePath, "utf8");
386
442
  if (target.insertionMode === "replace_section") {
387
- const replaced = replaceInjectedSection(existing, sectionHeading, sectionBody);
443
+ let replaced = replaceManagedSection(existing, "agent-registration", sectionBody);
444
+ if (replaced === existing) {
445
+ replaced = replaceInjectedSection(existing, sectionHeading, managedBody);
446
+ }
388
447
  if (replaced !== existing) {
389
448
  writeFileSync(filePath, replaced, "utf8");
390
449
  return [{ path: filePath, status: "updated" }];
391
450
  }
451
+ if (existing.includes(managedBody) || existing.includes(sectionBody)) {
452
+ return [{ path: filePath, status: "already_present" }];
453
+ }
454
+ }
455
+ if (existing.includes(managedBody) || existing.includes(sectionBody)) {
456
+ return [{ path: filePath, status: "already_present" }];
392
457
  }
393
458
  const separator = existing.endsWith("\n") ? "" : "\n";
394
- writeFileSync(filePath, existing + separator + sectionBody + "\n", "utf8");
459
+ writeFileSync(filePath, existing + separator + managedBody + "\n", "utf8");
395
460
  return [{ path: filePath, status: "injected" }];
396
461
  }
397
462
  const fallbackTemplate = templateByCode.get("nexarch_agent_registration_v1") ?? registry.instructionTemplates[0];
@@ -399,19 +464,26 @@ function injectGenericAgentConfig(registry) {
399
464
  return [];
400
465
  const fallbackPath = join(process.cwd(), "AGENTS.md");
401
466
  const sectionBody = fallbackTemplate.body.trim();
467
+ const managedBody = wrapManagedSection("agent-registration", sectionBody);
402
468
  if (!existsSync(fallbackPath)) {
403
- writeFileSync(fallbackPath, `${sectionBody}\n`, "utf8");
469
+ writeFileSync(fallbackPath, `${managedBody}\n`, "utf8");
404
470
  return [{ path: fallbackPath, status: "injected" }];
405
471
  }
406
472
  const existing = readFileSync(fallbackPath, "utf8");
407
473
  const sectionHeading = "## Nexarch Agent Registration";
408
- const replaced = replaceInjectedSection(existing, sectionHeading, sectionBody);
474
+ let replaced = replaceManagedSection(existing, "agent-registration", sectionBody);
475
+ if (replaced === existing) {
476
+ replaced = replaceInjectedSection(existing, sectionHeading, managedBody);
477
+ }
409
478
  if (replaced !== existing) {
410
479
  writeFileSync(fallbackPath, replaced, "utf8");
411
480
  return [{ path: fallbackPath, status: "updated" }];
412
481
  }
482
+ if (existing.includes(managedBody) || existing.includes(sectionBody)) {
483
+ return [{ path: fallbackPath, status: "already_present" }];
484
+ }
413
485
  const separator = existing.endsWith("\n") ? "" : "\n";
414
- writeFileSync(fallbackPath, existing + separator + sectionBody + "\n", "utf8");
486
+ writeFileSync(fallbackPath, existing + separator + managedBody + "\n", "utf8");
415
487
  return [{ path: fallbackPath, status: "injected" }];
416
488
  }
417
489
  export async function initAgent(args) {
@@ -973,6 +1045,7 @@ export async function initAgent(args) {
973
1045
  let agentConfigResults = [];
974
1046
  let instructionsWriteAllowed = false;
975
1047
  let trustAttestation = null;
1048
+ let trustAttestationAttempted = false;
976
1049
  if (registration.ok) {
977
1050
  try {
978
1051
  // Save identity so check-in can find the agent key
@@ -998,16 +1071,20 @@ export async function initAgent(args) {
998
1071
  agentConfigResults = injectGenericAgentConfig(registry);
999
1072
  }
1000
1073
  if (agentConfigResults.length > 0) {
1074
+ trustAttestationAttempted = true;
1001
1075
  trustAttestation = await requestTrustAttestation(agentId);
1002
- if (trustAttestation?.ok) {
1003
- for (const r of agentConfigResults) {
1004
- try {
1076
+ for (const r of agentConfigResults) {
1077
+ try {
1078
+ if (trustAttestation.ok) {
1005
1079
  injectTrustAttestationBlock(r.path, trustAttestation);
1006
1080
  }
1007
- catch {
1008
- // non-fatal
1081
+ else {
1082
+ injectTrustAttestationUnavailableBlock(r.path, trustAttestation.reason ?? "unknown");
1009
1083
  }
1010
1084
  }
1085
+ catch {
1086
+ // non-fatal
1087
+ }
1011
1088
  }
1012
1089
  }
1013
1090
  }
@@ -1038,6 +1115,19 @@ export async function initAgent(args) {
1038
1115
  ? `updated ${agentConfigResults.length} instruction target file(s)`
1039
1116
  : "no runtime instruction target matched this repository (non-fatal; create AGENTS.md/CLAUDE.md or configure a generic target)",
1040
1117
  });
1118
+ checks.push({
1119
+ name: "agent.trust.attestation",
1120
+ ok: !registration.ok || !instructionsWriteAllowed || !trustAttestationAttempted || Boolean(trustAttestation?.ok),
1121
+ detail: !registration.ok
1122
+ ? "skipped (registration failed)"
1123
+ : !instructionsWriteAllowed
1124
+ ? "skipped (consent not granted)"
1125
+ : !trustAttestationAttempted
1126
+ ? "skipped (no instruction target written)"
1127
+ : trustAttestation?.ok
1128
+ ? "minted and injected into instruction file(s)"
1129
+ : `unavailable (${trustAttestation?.reason ?? "unknown"})`,
1130
+ });
1041
1131
  checks.push({
1042
1132
  name: "technology.components",
1043
1133
  ok: techComponents.ok,
@@ -1152,6 +1242,9 @@ export async function initAgent(args) {
1152
1242
  : `https://mcp.nexarch.ai${trustAttestation.verifyUrl}`;
1153
1243
  console.log(` Trust attestation verify URL: ${verifyUrl}`);
1154
1244
  }
1245
+ else if (trustAttestationAttempted) {
1246
+ console.log(` Trust attestation unavailable (${trustAttestation?.reason ?? "unknown"}).`);
1247
+ }
1155
1248
  }
1156
1249
  if (needsIdentityInput) {
1157
1250
  console.log("\nℹ Additional identity details are still needed to complete agent profile enrichment.");
package/dist/lib/trust.js CHANGED
@@ -26,20 +26,22 @@ export async function requestTrustAttestation(agentId) {
26
26
  res.on("data", (c) => chunks.push(c));
27
27
  res.on("end", () => {
28
28
  try {
29
- const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
30
- if (!res.statusCode || res.statusCode >= 400)
31
- return resolve(null);
29
+ const raw = Buffer.concat(chunks).toString("utf8");
30
+ const parsed = JSON.parse(raw);
31
+ if (!res.statusCode || res.statusCode >= 400) {
32
+ return resolve({ ok: false, reason: parsed.error?.code ?? parsed.error?.message ?? `http_${res.statusCode ?? 0}` });
33
+ }
32
34
  resolve(parsed);
33
35
  }
34
36
  catch {
35
- resolve(null);
37
+ resolve({ ok: false, reason: "parse_error" });
36
38
  }
37
39
  });
38
40
  });
39
- req.on("error", () => resolve(null));
41
+ req.on("error", () => resolve({ ok: false, reason: "network_error" }));
40
42
  req.on("timeout", () => {
41
43
  req.destroy();
42
- resolve(null);
44
+ resolve({ ok: false, reason: "timeout" });
43
45
  });
44
46
  req.write(body);
45
47
  req.end();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.8.15",
3
+ "version": "0.8.17",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",