nexarch 0.13.7 → 0.14.0
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 +71 -15
- package/dist/commands/login.js +14 -13
- package/dist/commands/policy-audit-template.js +18 -18
- package/dist/commands/status.js +13 -4
- package/dist/lib/credentials.js +34 -6
- package/package.json +39 -39
|
@@ -344,7 +344,8 @@ export function canonicalTargetKey(filePath) {
|
|
|
344
344
|
* consent from the dry-run result before calling again with `dryRun: false`
|
|
345
345
|
* to actually write — see the ADR-0112 note at the call site.
|
|
346
346
|
*/
|
|
347
|
-
|
|
347
|
+
/** Exported for regression coverage of the empty-return bug documented at its tail. */
|
|
348
|
+
export function injectAgentConfigs(registry, runtimeCodes, dryRun) {
|
|
348
349
|
const templateByCode = new Map(registry.instructionTemplates.map((t) => [t.code, t]));
|
|
349
350
|
const sortedTargets = [...registry.instructionTargets]
|
|
350
351
|
.filter((target) => target.matchMode === "exact")
|
|
@@ -437,7 +438,17 @@ function injectAgentConfigs(registry, runtimeCodes, dryRun) {
|
|
|
437
438
|
if (perRuntimeResults.length > 0)
|
|
438
439
|
return [...existingResults, ...perRuntimeResults.filter((result, index, arr) => arr.findIndex((item) => canonicalTargetKey(item.path) === canonicalTargetKey(result.path)) === index)];
|
|
439
440
|
}
|
|
440
|
-
|
|
441
|
+
// Not `[]`. When every runtime's target was already collected into
|
|
442
|
+
// `existingMatches` — which is the steady state once a repo has been set up
|
|
443
|
+
// once — the per-runtime loop `continue`s on all of them and
|
|
444
|
+
// `perRuntimeResults` comes back empty. Returning [] here threw away the
|
|
445
|
+
// results already computed above, the caller read that as "no instruction
|
|
446
|
+
// files" and fell back to injectGenericAgentConfig, and from then on only
|
|
447
|
+
// the generic target (AGENTS.md) was ever seen. Paul's CLAUDE.md and
|
|
448
|
+
// .cursorrules were invisible to staleness checking and attestation refresh
|
|
449
|
+
// from the second setup run onward, which is how they kept an expired
|
|
450
|
+
// `instruction_injection` block while the handshake reported all green.
|
|
451
|
+
return existingResults;
|
|
441
452
|
}
|
|
442
453
|
function trustAttestationStaleness(path) {
|
|
443
454
|
if (!existsSync(path))
|
|
@@ -507,6 +518,45 @@ export function instructionInjectionDetail(registrationOk, staleAttestationTarge
|
|
|
507
518
|
return `updated ${agentConfigResults.length} instruction target file(s)`;
|
|
508
519
|
return "no runtime instruction target matched this repository (non-fatal; create AGENTS.md/CLAUDE.md or configure a generic target)";
|
|
509
520
|
}
|
|
521
|
+
/**
|
|
522
|
+
* Every instruction file the registry recognises that actually exists in this
|
|
523
|
+
* repo, deduplicated the way the rest of this module keys paths.
|
|
524
|
+
*
|
|
525
|
+
* This is the denominator for the coverage guard below. It is deliberately
|
|
526
|
+
* computed straight from the registry and the filesystem rather than from
|
|
527
|
+
* whatever the injection path happened to return, because the bug it guards
|
|
528
|
+
* against was the injection path silently returning fewer files than exist.
|
|
529
|
+
*/
|
|
530
|
+
export function existingInstructionFilePaths(registry) {
|
|
531
|
+
const seen = new Set();
|
|
532
|
+
const paths = [];
|
|
533
|
+
for (const target of registry.instructionTargets) {
|
|
534
|
+
if (target.matchMode !== "exact")
|
|
535
|
+
continue;
|
|
536
|
+
const filePath = join(process.cwd(), target.filePathPattern);
|
|
537
|
+
const key = canonicalTargetKey(filePath);
|
|
538
|
+
if (seen.has(key))
|
|
539
|
+
continue;
|
|
540
|
+
seen.add(key);
|
|
541
|
+
if (existsSync(filePath))
|
|
542
|
+
paths.push(filePath);
|
|
543
|
+
}
|
|
544
|
+
return paths;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Instruction files that exist on disk but were never evaluated for staleness.
|
|
548
|
+
*
|
|
549
|
+
* "already current (no refresh needed)" is only honest about files somebody
|
|
550
|
+
* looked at. When the injection path under-reports its targets, the attestation
|
|
551
|
+
* check evaluates a subset and reports green for the whole repo — which is
|
|
552
|
+
* exactly how two of Paul's three files kept an expired, wrongly-scoped block
|
|
553
|
+
* through a clean setup run. A non-empty result here fails the check rather
|
|
554
|
+
* than letting it speak for files it never opened.
|
|
555
|
+
*/
|
|
556
|
+
export function unevaluatedInstructionFiles(onDisk, evaluated) {
|
|
557
|
+
const evaluatedKeys = new Set(evaluated.map((result) => canonicalTargetKey(result.path)));
|
|
558
|
+
return onDisk.filter((path) => !evaluatedKeys.has(canonicalTargetKey(path)));
|
|
559
|
+
}
|
|
510
560
|
/**
|
|
511
561
|
* Summarises the per-target attestation writes into the check's ok/detail.
|
|
512
562
|
*
|
|
@@ -1481,6 +1531,9 @@ export async function initAgent(args) {
|
|
|
1481
1531
|
: identityCapture.detail,
|
|
1482
1532
|
});
|
|
1483
1533
|
const instructionsAlreadyConfigured = agentConfigResults.length > 0 && agentConfigResults.every((r) => r.status === "already_present");
|
|
1534
|
+
const uncoveredInstructionFiles = registration.ok
|
|
1535
|
+
? unevaluatedInstructionFiles(existingInstructionFilePaths(registry), agentConfigResults)
|
|
1536
|
+
: [];
|
|
1484
1537
|
const attestationSummary = summariseAttestationOutcomes(attestationOutcomes);
|
|
1485
1538
|
/** Renewed means every attested target verified on disk, not that one mint succeeded. */
|
|
1486
1539
|
const attestationRenewed = attestationOutcomes.length > 0 && attestationSummary.ok;
|
|
@@ -1492,23 +1545,26 @@ export async function initAgent(args) {
|
|
|
1492
1545
|
checks.push({
|
|
1493
1546
|
name: "agent.trust.attestation",
|
|
1494
1547
|
ok: !registration.ok || (unsafeReattestationTargetCount === 0
|
|
1548
|
+
&& uncoveredInstructionFiles.length === 0
|
|
1495
1549
|
&& attestationSummary.ok
|
|
1496
1550
|
&& ((staleAttestationTargetCount === 0 && !trustAttestationAttempted) || Boolean(trustAttestation?.ok))),
|
|
1497
1551
|
detail: !registration.ok
|
|
1498
1552
|
? "skipped (registration failed)"
|
|
1499
|
-
:
|
|
1500
|
-
?
|
|
1501
|
-
:
|
|
1502
|
-
?
|
|
1503
|
-
:
|
|
1504
|
-
? "
|
|
1505
|
-
: !attestationWriteAllowed
|
|
1506
|
-
? "skipped (
|
|
1507
|
-
: !
|
|
1508
|
-
?
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1553
|
+
: uncoveredInstructionFiles.length > 0
|
|
1554
|
+
? `${uncoveredInstructionFiles.length} instruction file(s) on disk were never evaluated (${uncoveredInstructionFiles.map((path) => basename(path)).join(", ")}); their attestations are unverified`
|
|
1555
|
+
: unsafeReattestationTargetCount > 0
|
|
1556
|
+
? "managed section does not match the published template; re-run with --allow-instruction-write"
|
|
1557
|
+
: staleAttestationTargetCount > 0 && !attestationWriteAllowed
|
|
1558
|
+
? `${mismatchedAttestationTargetCount > 0 ? "mismatched" : "stale"}; re-run with --reattest`
|
|
1559
|
+
: instructionsAlreadyConfigured && !attestationWriteAllowed
|
|
1560
|
+
? "skipped (already configured)"
|
|
1561
|
+
: !attestationWriteAllowed
|
|
1562
|
+
? "skipped (consent not granted)"
|
|
1563
|
+
: !trustAttestationAttempted
|
|
1564
|
+
? agentConfigResults.length > 0
|
|
1565
|
+
? "already current (no refresh needed)"
|
|
1566
|
+
: "skipped (no instruction target written)"
|
|
1567
|
+
: attestationSummary.detail,
|
|
1512
1568
|
});
|
|
1513
1569
|
checks.push({
|
|
1514
1570
|
name: "technology.components",
|
package/dist/commands/login.js
CHANGED
|
@@ -24,15 +24,15 @@ async function promptCapabilityProfile() {
|
|
|
24
24
|
return selectFromList("Select a capability profile for this credential:", PROFILE_CHOICES, "worker");
|
|
25
25
|
}
|
|
26
26
|
function printLoginBanner() {
|
|
27
|
-
const logo = String.raw `
|
|
28
|
-
###### ######
|
|
29
|
-
####### ######
|
|
30
|
-
######### ###### ##
|
|
31
|
-
################## #####
|
|
32
|
-
##### ########## ######
|
|
33
|
-
###### ####### ######
|
|
34
|
-
##### #### #############
|
|
35
|
-
###### ## ###############
|
|
27
|
+
const logo = String.raw `
|
|
28
|
+
###### ######
|
|
29
|
+
####### ######
|
|
30
|
+
######### ###### ##
|
|
31
|
+
################## #####
|
|
32
|
+
##### ########## ######
|
|
33
|
+
###### ####### ######
|
|
34
|
+
##### #### #############
|
|
35
|
+
###### ## ###############
|
|
36
36
|
`;
|
|
37
37
|
console.log(logo);
|
|
38
38
|
console.log("NEXARCH CLI login\n");
|
|
@@ -95,10 +95,10 @@ function waitForCallback(port, expectedState) {
|
|
|
95
95
|
const companyName = url.searchParams.get("companyName") ?? undefined;
|
|
96
96
|
const companyCode = url.searchParams.get("companyCode") ?? undefined;
|
|
97
97
|
const error = url.searchParams.get("error");
|
|
98
|
-
const html = (message) => `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Nexarch CLI</title>
|
|
99
|
-
<style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#f8fafc}
|
|
100
|
-
.card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:2rem;max-width:400px;text-align:center}
|
|
101
|
-
h1{font-size:1.25rem;margin:0 0 .5rem;color:#0f172a}p{color:#64748b;font-size:.9rem;margin:.5rem 0}</style></head>
|
|
98
|
+
const html = (message) => `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Nexarch CLI</title>
|
|
99
|
+
<style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#f8fafc}
|
|
100
|
+
.card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:2rem;max-width:400px;text-align:center}
|
|
101
|
+
h1{font-size:1.25rem;margin:0 0 .5rem;color:#0f172a}p{color:#64748b;font-size:.9rem;margin:.5rem 0}</style></head>
|
|
102
102
|
<body><div class="card"><h1>Nexarch CLI</h1><p>${message}</p></div></body></html>`;
|
|
103
103
|
if (state !== expectedState) {
|
|
104
104
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
@@ -247,6 +247,7 @@ export async function login(args) {
|
|
|
247
247
|
companyCode: companyCode ?? undefined,
|
|
248
248
|
companyId,
|
|
249
249
|
expiresAt: expiresAt.toISOString(),
|
|
250
|
+
source: "interactive",
|
|
250
251
|
});
|
|
251
252
|
try {
|
|
252
253
|
const identityDir = join(homedir(), ".nexarch");
|
|
@@ -37,24 +37,24 @@ export async function policyAuditTemplate(args) {
|
|
|
37
37
|
const defaultResult = (parseOptionValue(args, "--default-result") ?? "fail").toLowerCase();
|
|
38
38
|
const selectedControlIds = new Set(parseMultiOptionValues(args, "--control-id"));
|
|
39
39
|
if (parseFlag(args, "--help") || parseFlag(args, "-h")) {
|
|
40
|
-
console.log(`
|
|
41
|
-
Usage:
|
|
42
|
-
nexarch policy-audit-template --entity <application:key> [options]
|
|
43
|
-
|
|
44
|
-
Options:
|
|
45
|
-
--entity <key> Required (e.g. application:bad-driving)
|
|
46
|
-
--control-id <uuid> Optional repeatable filter (selected controls only)
|
|
47
|
-
--default-result <value> pass|partial|fail (default: fail)
|
|
48
|
-
--output, --out <path.json> Write findings array to file
|
|
49
|
-
--json Print JSON to stdout
|
|
50
|
-
|
|
51
|
-
Output shape is ready for:
|
|
52
|
-
nexarch policy-audit-submit --findings-file <path.json>
|
|
53
|
-
|
|
54
|
-
Each entry includes empty severity/effort/exploitability/confidence/fileRefs
|
|
55
|
-
placeholders. Fill them in for every partial/fail finding -- they decide how
|
|
56
|
-
findings get ranked and reported. Leave a field blank/empty rather than
|
|
57
|
-
guess; policy-audit-submit drops anything that isn't a valid value.
|
|
40
|
+
console.log(`
|
|
41
|
+
Usage:
|
|
42
|
+
nexarch policy-audit-template --entity <application:key> [options]
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--entity <key> Required (e.g. application:bad-driving)
|
|
46
|
+
--control-id <uuid> Optional repeatable filter (selected controls only)
|
|
47
|
+
--default-result <value> pass|partial|fail (default: fail)
|
|
48
|
+
--output, --out <path.json> Write findings array to file
|
|
49
|
+
--json Print JSON to stdout
|
|
50
|
+
|
|
51
|
+
Output shape is ready for:
|
|
52
|
+
nexarch policy-audit-submit --findings-file <path.json>
|
|
53
|
+
|
|
54
|
+
Each entry includes empty severity/effort/exploitability/confidence/fileRefs
|
|
55
|
+
placeholders. Fill them in for every partial/fail finding -- they decide how
|
|
56
|
+
findings get ranked and reported. Leave a field blank/empty rather than
|
|
57
|
+
guess; policy-audit-submit drops anything that isn't a valid value.
|
|
58
58
|
`);
|
|
59
59
|
return;
|
|
60
60
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -5,8 +5,8 @@ import { callMcpTool } from "../lib/mcp.js";
|
|
|
5
5
|
export async function status(_args) {
|
|
6
6
|
const creds = requireCredentials();
|
|
7
7
|
const selectedCompanyId = creds.companyId;
|
|
8
|
-
const expiresAt = new Date(creds.expiresAt);
|
|
9
|
-
const daysLeft = Math.ceil((expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
8
|
+
const expiresAt = creds.expiresAt ? new Date(creds.expiresAt) : null;
|
|
9
|
+
const daysLeft = expiresAt ? Math.ceil((expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
|
|
10
10
|
const lease = loadCheckInLease();
|
|
11
11
|
const leaseState = lease && lease.companyId === selectedCompanyId ? getCheckInLeaseState(lease) : { status: "missing", expiresInSeconds: null };
|
|
12
12
|
process.stdout.write("Connecting to mcp.nexarch.ai… ");
|
|
@@ -28,6 +28,15 @@ export async function status(_args) {
|
|
|
28
28
|
if (creds.email) {
|
|
29
29
|
console.log(` Signed in as: ${creds.email}`);
|
|
30
30
|
}
|
|
31
|
+
else if (creds.source === "agent") {
|
|
32
|
+
console.log(" Authentication: Hermes agent enrollment");
|
|
33
|
+
if (creds.agentRef) {
|
|
34
|
+
console.log(` Agent: ${creds.agentRef}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if (creds.source === "service") {
|
|
38
|
+
console.log(" Authentication: service credential");
|
|
39
|
+
}
|
|
31
40
|
if (creds.company) {
|
|
32
41
|
console.log(` Workspace: ${creds.company}`);
|
|
33
42
|
}
|
|
@@ -53,7 +62,7 @@ export async function status(_args) {
|
|
|
53
62
|
else {
|
|
54
63
|
console.log(` Latest snapshot: none published yet`);
|
|
55
64
|
}
|
|
56
|
-
console.log(`\n Token expires: ${expiresAt.toLocaleDateString()} (${daysLeft} days)`);
|
|
65
|
+
console.log(`\n Token expires: ${expiresAt ? `${expiresAt.toLocaleDateString()} (${daysLeft} days)` : "not reported"}`);
|
|
57
66
|
if (leaseState.status === "valid" && lease) {
|
|
58
67
|
console.log(` Check-in lease: ${new Date(lease.leaseExpiresAt).toLocaleString()} (${Math.ceil((leaseState.expiresInSeconds ?? 0) / 3600)}h left)`);
|
|
59
68
|
}
|
|
@@ -63,7 +72,7 @@ export async function status(_args) {
|
|
|
63
72
|
else {
|
|
64
73
|
console.log(" Check-in lease: none stored");
|
|
65
74
|
}
|
|
66
|
-
if (daysLeft <= 14) {
|
|
75
|
+
if (daysLeft !== null && daysLeft <= 14) {
|
|
67
76
|
console.log(`\n ⚠ Your token expires soon. Run \`nexarch login\` to renew.`);
|
|
68
77
|
}
|
|
69
78
|
if (leaseState.status !== "valid") {
|
package/dist/lib/credentials.js
CHANGED
|
@@ -4,6 +4,9 @@ import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from "fs";
|
|
|
4
4
|
function credentialsPath() {
|
|
5
5
|
return join(homedir(), ".nexarch", "credentials.json");
|
|
6
6
|
}
|
|
7
|
+
function agentCredentialPath() {
|
|
8
|
+
return join(homedir(), ".nexarch", "agent-credential.json");
|
|
9
|
+
}
|
|
7
10
|
/**
|
|
8
11
|
* Credentials supplied by the environment, for CI.
|
|
9
12
|
*
|
|
@@ -27,24 +30,49 @@ function credentialsFromEnvironment() {
|
|
|
27
30
|
company: "",
|
|
28
31
|
companyId: process.env.NEXARCH_COMPANY_ID?.trim() ?? "",
|
|
29
32
|
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
|
|
33
|
+
source: "service",
|
|
30
34
|
};
|
|
31
35
|
}
|
|
36
|
+
function credentialsFromAgentManifest() {
|
|
37
|
+
const path = agentCredentialPath();
|
|
38
|
+
if (!existsSync(path))
|
|
39
|
+
return null;
|
|
40
|
+
try {
|
|
41
|
+
const manifest = JSON.parse(readFileSync(path, "utf-8"));
|
|
42
|
+
const token = typeof manifest.credential?.token === "string" ? manifest.credential.token.trim() : "";
|
|
43
|
+
const companyId = typeof manifest.workspace?.id === "string" ? manifest.workspace.id : "";
|
|
44
|
+
if (!token || !companyId)
|
|
45
|
+
return null;
|
|
46
|
+
return {
|
|
47
|
+
token,
|
|
48
|
+
email: "",
|
|
49
|
+
company: typeof manifest.workspace?.name === "string" ? manifest.workspace.name : "",
|
|
50
|
+
companyId,
|
|
51
|
+
expiresAt: typeof manifest.credential?.expiresAt === "string" ? manifest.credential.expiresAt : null,
|
|
52
|
+
source: "agent",
|
|
53
|
+
agentRef: typeof manifest.agent?.ref === "string" ? manifest.agent.ref : undefined,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
32
60
|
export function loadCredentials() {
|
|
33
61
|
const fromEnv = credentialsFromEnvironment();
|
|
34
62
|
if (fromEnv)
|
|
35
63
|
return fromEnv;
|
|
36
64
|
const path = credentialsPath();
|
|
37
65
|
if (!existsSync(path))
|
|
38
|
-
return
|
|
66
|
+
return credentialsFromAgentManifest();
|
|
39
67
|
try {
|
|
40
68
|
const raw = readFileSync(path, "utf-8");
|
|
41
69
|
const creds = JSON.parse(raw);
|
|
42
70
|
if (!creds.token)
|
|
43
|
-
return
|
|
44
|
-
return creds;
|
|
71
|
+
return credentialsFromAgentManifest();
|
|
72
|
+
return { ...creds, source: "interactive" };
|
|
45
73
|
}
|
|
46
74
|
catch {
|
|
47
|
-
return
|
|
75
|
+
return credentialsFromAgentManifest();
|
|
48
76
|
}
|
|
49
77
|
}
|
|
50
78
|
export function saveCredentials(creds) {
|
|
@@ -63,13 +91,13 @@ export function removeCredentials() {
|
|
|
63
91
|
export function requireCredentials() {
|
|
64
92
|
const creds = loadCredentials();
|
|
65
93
|
if (!creds) {
|
|
66
|
-
throw new Error("
|
|
94
|
+
throw new Error("No Nexarch credential found. Run `nexarch login` for interactive CLI access, or `nexarch enroll` for headless agent access.");
|
|
67
95
|
}
|
|
68
96
|
// Environment-supplied service credentials are validated by the gateway, not
|
|
69
97
|
// by this process; only the stored interactive session carries a local expiry.
|
|
70
98
|
if (process.env.NEXARCH_TOKEN?.trim())
|
|
71
99
|
return creds;
|
|
72
|
-
if (new Date(creds.expiresAt) < new Date()) {
|
|
100
|
+
if (creds.expiresAt && new Date(creds.expiresAt) < new Date()) {
|
|
73
101
|
throw new Error("Your session has expired. Run `nexarch login` to re-authenticate.");
|
|
74
102
|
}
|
|
75
103
|
return creds;
|
package/package.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "nexarch",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Your architecture workspace for AI delivery.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"nexarch",
|
|
7
|
-
"mcp",
|
|
8
|
-
"architecture",
|
|
9
|
-
"ai"
|
|
10
|
-
],
|
|
11
|
-
"license": "MIT",
|
|
12
|
-
"author": "Nexarch <hello@nexarch.ai>",
|
|
13
|
-
"homepage": "https://nexarch.ai",
|
|
14
|
-
"engines": {
|
|
15
|
-
"node": ">=18"
|
|
16
|
-
},
|
|
17
|
-
"type": "module",
|
|
18
|
-
"bin": {
|
|
19
|
-
"nexarch": "dist/index.js"
|
|
20
|
-
},
|
|
21
|
-
"files": [
|
|
22
|
-
"dist"
|
|
23
|
-
],
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "tsc",
|
|
26
|
-
"prepublishOnly": "tsc",
|
|
27
|
-
"dev": "tsx src/index.ts",
|
|
28
|
-
"typecheck": "tsc --noEmit",
|
|
29
|
-
"test": "tsx scripts/test-mcp-proxy-response.ts && tsx scripts/test-trust-reattest.ts && tsx scripts/test-similar-applications.ts && tsx scripts/test-iac-tool.ts && tsx scripts/test-ansible.ts && tsx scripts/test-reference-sightings.ts && tsx scripts/test-search-icons.ts && tsx scripts/test-paste-login.ts"
|
|
30
|
-
},
|
|
31
|
-
"devDependencies": {
|
|
32
|
-
"@types/node": "^22",
|
|
33
|
-
"tsx": "^4",
|
|
34
|
-
"typescript": "^5"
|
|
35
|
-
},
|
|
36
|
-
"dependencies": {
|
|
37
|
-
"yaml": "^2.9.0"
|
|
38
|
-
}
|
|
39
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "nexarch",
|
|
3
|
+
"version": "0.14.0",
|
|
4
|
+
"description": "Your architecture workspace for AI delivery.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nexarch",
|
|
7
|
+
"mcp",
|
|
8
|
+
"architecture",
|
|
9
|
+
"ai"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "Nexarch <hello@nexarch.ai>",
|
|
13
|
+
"homepage": "https://nexarch.ai",
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"bin": {
|
|
19
|
+
"nexarch": "dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"prepublishOnly": "tsc",
|
|
27
|
+
"dev": "tsx src/index.ts",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"test": "tsx scripts/test-mcp-proxy-response.ts && tsx scripts/test-trust-reattest.ts && tsx scripts/test-similar-applications.ts && tsx scripts/test-iac-tool.ts && tsx scripts/test-ansible.ts && tsx scripts/test-reference-sightings.ts && tsx scripts/test-search-icons.ts && tsx scripts/test-paste-login.ts && tsx scripts/test-credentials-resolution.ts"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22",
|
|
33
|
+
"tsx": "^4",
|
|
34
|
+
"typescript": "^5"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"yaml": "^2.9.0"
|
|
38
|
+
}
|
|
39
|
+
}
|