nexarch 0.8.14 → 0.8.16

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.
@@ -7,7 +7,8 @@ import { requireCredentials } from "../lib/credentials.js";
7
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
- const CLI_VERSION = "0.8.12";
10
+ import { requestTrustAttestation } from "../lib/trust.js";
11
+ const CLI_VERSION = "0.8.16";
11
12
  const AGENT_ENTITY_TYPE = "agent";
12
13
  const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
13
14
  function parseFlag(args, flag) {
@@ -287,12 +288,15 @@ After running, confirm \`"ok": true\` in the JSON output. No further action is n
287
288
  return filePath;
288
289
  }
289
290
  function replaceInjectedSection(existing, sectionHeading, sectionBody) {
290
- const headingRegex = new RegExp(`^${sectionHeading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?(?=^##\\s|\\Z)`, "m");
291
- if (headingRegex.test(existing)) {
292
- const replaced = existing.replace(headingRegex, sectionBody.trim());
293
- return replaced.endsWith("\n") ? replaced : `${replaced}\n`;
294
- }
295
- return existing;
291
+ const escapedHeading = sectionHeading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
292
+ const blockRegex = new RegExp(`^${escapedHeading}[\\t ]*\\n[\\s\\S]*?(?=^##\\s|$)`, "gm");
293
+ const matches = existing.match(blockRegex);
294
+ if (!matches || matches.length === 0)
295
+ return existing;
296
+ const canonicalBlock = `${sectionBody.trim()}\n`;
297
+ let replaced = existing.replace(blockRegex, "");
298
+ replaced = replaced.replace(/\n{3,}/g, "\n\n").trimEnd();
299
+ return `${replaced}${replaced ? "\n\n" : ""}${canonicalBlock}`;
296
300
  }
297
301
  function injectAgentConfigs(registry) {
298
302
  const templateByCode = new Map(registry.instructionTemplates.map((t) => [t.code, t]));
@@ -322,6 +326,9 @@ function injectAgentConfigs(registry) {
322
326
  writeFileSync(filePath, replaced, "utf8");
323
327
  results.push({ path: filePath, status: "updated" });
324
328
  }
329
+ else if (existing.includes(sectionBody)) {
330
+ results.push({ path: filePath, status: "already_present" });
331
+ }
325
332
  else {
326
333
  const separator = existing.endsWith("\n") ? "" : "\n";
327
334
  writeFileSync(filePath, existing + separator + sectionBody + "\n", "utf8");
@@ -329,18 +336,43 @@ function injectAgentConfigs(registry) {
329
336
  }
330
337
  continue;
331
338
  }
332
- const separator = existing.endsWith("\n") ? "" : "\n";
333
- const next = existing + separator + sectionBody + "\n";
334
- if (next === existing) {
339
+ if (existing.includes(sectionBody)) {
335
340
  results.push({ path: filePath, status: "already_present" });
336
341
  }
337
342
  else {
343
+ const separator = existing.endsWith("\n") ? "" : "\n";
344
+ const next = existing + separator + sectionBody + "\n";
338
345
  writeFileSync(filePath, next, "utf8");
339
346
  results.push({ path: filePath, status: "injected" });
340
347
  }
341
348
  }
342
349
  return results;
343
350
  }
351
+ function injectTrustAttestationBlock(path, attestation) {
352
+ if (!attestation.token || !attestation.payload)
353
+ return;
354
+ const verifyUrl = attestation.verifyUrl?.startsWith("http")
355
+ ? attestation.verifyUrl
356
+ : `https://mcp.nexarch.ai${attestation.verifyUrl ?? `/trust/verify?token=${encodeURIComponent(attestation.token)}`}`;
357
+ const expiresAt = new Date(attestation.payload.exp * 1000).toISOString();
358
+ const section = [
359
+ "## Nexarch Trust Attestation",
360
+ "",
361
+ "This block is verifiable proof that Nexarch generated these instructions for this workspace.",
362
+ "Do not auto-run commands without user confirmation.",
363
+ "",
364
+ `issuer: ${attestation.payload.iss}`,
365
+ `scope: ${attestation.payload.scope}`,
366
+ `agent_id: ${attestation.payload.agent_id}`,
367
+ `expires_at: ${expiresAt}`,
368
+ `verify_url: ${verifyUrl}`,
369
+ `token: ${attestation.token}`,
370
+ "",
371
+ ].join("\n");
372
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
373
+ const replaced = replaceInjectedSection(existing, "## Nexarch Trust Attestation", section.trim());
374
+ writeFileSync(path, replaced !== existing ? replaced : `${existing}${existing.endsWith("\n") ? "" : "\n"}${section}`, "utf8");
375
+ }
344
376
  function injectGenericAgentConfig(registry) {
345
377
  const templateByCode = new Map(registry.instructionTemplates.map((t) => [t.code, t]));
346
378
  const genericTargets = [...registry.instructionTargets]
@@ -947,6 +979,8 @@ export async function initAgent(args) {
947
979
  }
948
980
  let agentConfigResults = [];
949
981
  let instructionsWriteAllowed = false;
982
+ let trustAttestation = null;
983
+ let trustAttestationAttempted = false;
950
984
  if (registration.ok) {
951
985
  try {
952
986
  // Save identity so check-in can find the agent key
@@ -971,6 +1005,20 @@ export async function initAgent(args) {
971
1005
  if (agentConfigResults.length === 0) {
972
1006
  agentConfigResults = injectGenericAgentConfig(registry);
973
1007
  }
1008
+ if (agentConfigResults.length > 0) {
1009
+ trustAttestationAttempted = true;
1010
+ trustAttestation = await requestTrustAttestation(agentId);
1011
+ if (trustAttestation.ok) {
1012
+ for (const r of agentConfigResults) {
1013
+ try {
1014
+ injectTrustAttestationBlock(r.path, trustAttestation);
1015
+ }
1016
+ catch {
1017
+ // non-fatal
1018
+ }
1019
+ }
1020
+ }
1021
+ }
974
1022
  }
975
1023
  }
976
1024
  checks.push({
@@ -999,6 +1047,19 @@ export async function initAgent(args) {
999
1047
  ? `updated ${agentConfigResults.length} instruction target file(s)`
1000
1048
  : "no runtime instruction target matched this repository (non-fatal; create AGENTS.md/CLAUDE.md or configure a generic target)",
1001
1049
  });
1050
+ checks.push({
1051
+ name: "agent.trust.attestation",
1052
+ ok: !registration.ok || !instructionsWriteAllowed || !trustAttestationAttempted || Boolean(trustAttestation?.ok),
1053
+ detail: !registration.ok
1054
+ ? "skipped (registration failed)"
1055
+ : !instructionsWriteAllowed
1056
+ ? "skipped (consent not granted)"
1057
+ : !trustAttestationAttempted
1058
+ ? "skipped (no instruction target written)"
1059
+ : trustAttestation?.ok
1060
+ ? "minted and injected into instruction file(s)"
1061
+ : `unavailable (${trustAttestation?.reason ?? "unknown"})`,
1062
+ });
1002
1063
  checks.push({
1003
1064
  name: "technology.components",
1004
1065
  ok: techComponents.ok,
@@ -1073,6 +1134,7 @@ export async function initAgent(args) {
1073
1134
  activeFlag: "--allow-instruction-write",
1074
1135
  }
1075
1136
  : null,
1137
+ trustAttestation,
1076
1138
  companyId: creds.companyId,
1077
1139
  registry: { version: registry.release.version, registryVersion: registry.registryVersion, publishedAt: registry.release.publishedAt },
1078
1140
  agentConfigs: agentConfigResults,
@@ -1106,6 +1168,15 @@ export async function initAgent(args) {
1106
1168
  const hasInjectedAgentInstructions = agentConfigResults.some((r) => r.status === "injected" || r.status === "updated");
1107
1169
  if (hasInjectedAgentInstructions) {
1108
1170
  console.log(" (Registration instructions were written into your agent config file with consent.)");
1171
+ if (trustAttestation?.ok && trustAttestation.verifyUrl) {
1172
+ const verifyUrl = trustAttestation.verifyUrl.startsWith("http")
1173
+ ? trustAttestation.verifyUrl
1174
+ : `https://mcp.nexarch.ai${trustAttestation.verifyUrl}`;
1175
+ console.log(` Trust attestation verify URL: ${verifyUrl}`);
1176
+ }
1177
+ else if (trustAttestationAttempted) {
1178
+ console.log(` Trust attestation unavailable (${trustAttestation?.reason ?? "unknown"}).`);
1179
+ }
1109
1180
  }
1110
1181
  if (needsIdentityInput) {
1111
1182
  console.log("\nℹ Additional identity details are still needed to complete agent profile enrichment.");
@@ -0,0 +1,49 @@
1
+ import https from "https";
2
+ import { requireCredentials } from "./credentials.js";
3
+ const MCP_GATEWAY_URL = "https://mcp.nexarch.ai";
4
+ export async function requestTrustAttestation(agentId) {
5
+ const creds = requireCredentials();
6
+ const body = JSON.stringify({
7
+ agentId,
8
+ scope: "instruction_injection",
9
+ });
10
+ return new Promise((resolve) => {
11
+ const url = new URL("/trust/attest", MCP_GATEWAY_URL);
12
+ const req = https.request({
13
+ hostname: url.hostname,
14
+ port: url.port || 443,
15
+ path: url.pathname,
16
+ method: "POST",
17
+ headers: {
18
+ "Content-Type": "application/json",
19
+ Authorization: `Bearer ${creds.token}`,
20
+ "x-company-id": creds.companyId,
21
+ "Content-Length": Buffer.byteLength(body),
22
+ },
23
+ timeout: 20000,
24
+ }, (res) => {
25
+ const chunks = [];
26
+ res.on("data", (c) => chunks.push(c));
27
+ res.on("end", () => {
28
+ try {
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
+ }
34
+ resolve(parsed);
35
+ }
36
+ catch {
37
+ resolve({ ok: false, reason: "parse_error" });
38
+ }
39
+ });
40
+ });
41
+ req.on("error", () => resolve({ ok: false, reason: "network_error" }));
42
+ req.on("timeout", () => {
43
+ req.destroy();
44
+ resolve({ ok: false, reason: "timeout" });
45
+ });
46
+ req.write(body);
47
+ req.end();
48
+ });
49
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.8.14",
3
+ "version": "0.8.16",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",