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.
- package/dist/commands/init-agent.js +81 -10
- package/dist/lib/trust.js +49 -0
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
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
|
+
}
|