nexarch 0.1.30 → 0.1.32
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.
|
@@ -17,6 +17,29 @@ function parseToolText(result) {
|
|
|
17
17
|
const text = result.content?.[0]?.text ?? "{}";
|
|
18
18
|
return JSON.parse(text);
|
|
19
19
|
}
|
|
20
|
+
async function tryCreateStubEntity(externalKey, mcpOpts, agentContext, policyContext) {
|
|
21
|
+
// Only auto-create global reference entities — company entities must be created explicitly
|
|
22
|
+
if (!externalKey.startsWith("global:"))
|
|
23
|
+
return false;
|
|
24
|
+
// Derive a normalised alias from the external key (e.g. "global:platform:vercel" → "vercel")
|
|
25
|
+
const parts = externalKey.split(":");
|
|
26
|
+
const alias = parts[parts.length - 1];
|
|
27
|
+
const resolveRaw = await callMcpTool("nexarch_resolve_reference", { names: [alias], companyId: mcpOpts.companyId }, mcpOpts);
|
|
28
|
+
const resolveResult = parseToolText(resolveRaw);
|
|
29
|
+
const match = resolveResult.results?.find((r) => r.resolved && r.canonicalExternalRef === externalKey);
|
|
30
|
+
if (!match)
|
|
31
|
+
return false;
|
|
32
|
+
const entity = {
|
|
33
|
+
externalKey,
|
|
34
|
+
entityTypeCode: match.entityTypeCode,
|
|
35
|
+
name: match.canonicalName,
|
|
36
|
+
confidence: 1,
|
|
37
|
+
};
|
|
38
|
+
if (match.entitySubtypeCode)
|
|
39
|
+
entity.entitySubtypeCode = match.entitySubtypeCode;
|
|
40
|
+
await callMcpTool("nexarch_upsert_entities", { entities: [entity], agentContext, policyContext }, mcpOpts);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
20
43
|
export async function addRelationship(args) {
|
|
21
44
|
const asJson = parseFlag(args, "--json");
|
|
22
45
|
const fromKey = parseOptionValue(args, "--from");
|
|
@@ -58,8 +81,33 @@ export async function addRelationship(args) {
|
|
|
58
81
|
toEntityExternalKey: toKey,
|
|
59
82
|
confidence: 1,
|
|
60
83
|
};
|
|
61
|
-
|
|
62
|
-
|
|
84
|
+
let raw = await callMcpTool("nexarch_upsert_relationships", { relationships: [relationship], agentContext, policyContext }, mcpOpts);
|
|
85
|
+
let result = parseToolText(raw);
|
|
86
|
+
// Auto-create global reference stubs if endpoints are missing, then retry once
|
|
87
|
+
const missingEndpoint = result.errors?.some((e) => e.error === "RELATIONSHIP_ENDPOINT_NOT_FOUND");
|
|
88
|
+
if (missingEndpoint) {
|
|
89
|
+
const failedFromKeys = result.errors
|
|
90
|
+
?.filter((e) => e.error === "RELATIONSHIP_ENDPOINT_NOT_FOUND" && e.fromEntityExternalKey)
|
|
91
|
+
.map((e) => e.fromEntityExternalKey);
|
|
92
|
+
const failedToKeys = result.errors
|
|
93
|
+
?.filter((e) => e.error === "RELATIONSHIP_ENDPOINT_NOT_FOUND" && e.toEntityExternalKey)
|
|
94
|
+
.map((e) => e.toEntityExternalKey);
|
|
95
|
+
const keysToCreate = [...new Set([...(failedFromKeys ?? []), ...(failedToKeys ?? [])])];
|
|
96
|
+
// If no specific endpoint keys returned, fall back to trying both
|
|
97
|
+
if (keysToCreate.length === 0) {
|
|
98
|
+
keysToCreate.push(fromKey, toKey);
|
|
99
|
+
}
|
|
100
|
+
let createdAny = false;
|
|
101
|
+
for (const key of keysToCreate) {
|
|
102
|
+
const created = await tryCreateStubEntity(key, mcpOpts, agentContext, policyContext);
|
|
103
|
+
if (created)
|
|
104
|
+
createdAny = true;
|
|
105
|
+
}
|
|
106
|
+
if (createdAny) {
|
|
107
|
+
raw = await callMcpTool("nexarch_upsert_relationships", { relationships: [relationship], agentContext, policyContext }, mcpOpts);
|
|
108
|
+
result = parseToolText(raw);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
63
111
|
if (asJson) {
|
|
64
112
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
65
113
|
if (Number(result.summary?.failed ?? 0) > 0)
|
|
@@ -4,7 +4,7 @@ import { join } from "path";
|
|
|
4
4
|
import process from "process";
|
|
5
5
|
import { requireCredentials } from "../lib/credentials.js";
|
|
6
6
|
import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
|
|
7
|
-
const CLI_VERSION = "0.1.
|
|
7
|
+
const CLI_VERSION = "0.1.32";
|
|
8
8
|
const AGENT_ENTITY_TYPE = "agent";
|
|
9
9
|
const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
|
|
10
10
|
function parseFlag(args, flag) {
|
|
@@ -281,18 +281,17 @@ function scanProject(dir) {
|
|
|
281
281
|
return { projectName, packageJsonCount: pkgPaths.length, detectedNames: Array.from(names), rootDepNames, subPackages };
|
|
282
282
|
}
|
|
283
283
|
// ─── Relationship type selection ──────────────────────────────────────────────
|
|
284
|
-
function pickRelationshipType(toEntityTypeCode,
|
|
284
|
+
function pickRelationshipType(toEntityTypeCode, _fromEntityTypeCode = "application") {
|
|
285
285
|
switch (toEntityTypeCode) {
|
|
286
286
|
case "model":
|
|
287
287
|
return "uses_model";
|
|
288
288
|
case "platform":
|
|
289
289
|
case "platform_component":
|
|
290
|
+
// runs_on is the valid ontology relationship from application/tech to platform
|
|
291
|
+
return "runs_on";
|
|
290
292
|
case "skill":
|
|
291
293
|
return "uses";
|
|
292
294
|
case "technology_component":
|
|
293
|
-
// tech→tech depends_on is not allowed by ontology; use "uses" instead
|
|
294
|
-
if (fromEntityTypeCode === "technology_component")
|
|
295
|
-
return "uses";
|
|
296
295
|
return "depends_on";
|
|
297
296
|
default:
|
|
298
297
|
return "depends_on";
|
|
@@ -544,8 +543,8 @@ ${subPackages.map((sp) => ` • ${sp.name} (${sp.relativePath})`).join("\n")
|
|
|
544
543
|
|
|
545
544
|
RELATIONSHIP TYPE RULES:
|
|
546
545
|
application → technology_component : --type "depends_on"
|
|
547
|
-
technology_component → technology_component : --type "
|
|
548
|
-
any → platform / platform_component : --type "uses
|
|
546
|
+
technology_component → technology_component : --type "depends_on"
|
|
547
|
+
any → platform / platform_component : --type "runs_on" (NOT uses or depends_on)
|
|
549
548
|
any → model : --type "uses_model"
|
|
550
549
|
|
|
551
550
|
Pre-resolved dependencies per sub-package (wire these after registering each entity):
|
|
@@ -586,15 +585,22 @@ ${finalStep} — Identify architecturally significant components not auto-detect
|
|
|
586
585
|
For each one you identify:
|
|
587
586
|
|
|
588
587
|
1. Resolve it to a canonical reference:
|
|
589
|
-
npx nexarch resolve-names --names "<platform name>"
|
|
588
|
+
npx nexarch resolve-names --names "<platform name>" --json
|
|
590
589
|
|
|
591
|
-
2. If resolved,
|
|
590
|
+
2. If resolved, instantiate the entity in the graph (REQUIRED before add-relationship):
|
|
591
|
+
npx nexarch update-entity \\
|
|
592
|
+
--key "<resolved-ref>" \\
|
|
593
|
+
--entity-type "<entityTypeCode from resolve-names>" \\
|
|
594
|
+
--name "<canonicalName from resolve-names>"
|
|
595
|
+
|
|
596
|
+
3. Wire the relationship:
|
|
592
597
|
npx nexarch add-relationship \\
|
|
593
598
|
--from "${projectExternalKey}" \\
|
|
594
599
|
--to "<resolved-ref>" \\
|
|
595
|
-
--type "
|
|
600
|
+
--type "runs_on" # for platforms (Vercel, Neon, etc.)
|
|
601
|
+
# use --type "depends_on" for external APIs/SaaS (Stripe, Resend, etc.)
|
|
596
602
|
|
|
597
|
-
|
|
603
|
+
4. If unresolved (not in the reference library), skip it for now — it will appear
|
|
598
604
|
as a reference candidate on the next scan once you register an alias for it.
|
|
599
605
|
`;
|
|
600
606
|
return `
|
|
@@ -40,7 +40,7 @@ export async function resolveNames(args) {
|
|
|
40
40
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
|
-
for (const entry of result.
|
|
43
|
+
for (const entry of result.results ?? []) {
|
|
44
44
|
if (entry.resolved) {
|
|
45
45
|
console.log(`✓ ${entry.input} → ${entry.canonicalExternalRef} (${entry.entityTypeCode}${entry.entitySubtypeCode ? `/${entry.entitySubtypeCode}` : ""}) "${entry.canonicalName}"`);
|
|
46
46
|
}
|
|
@@ -48,5 +48,5 @@ export async function resolveNames(args) {
|
|
|
48
48
|
console.log(`✗ ${entry.input} — not found in reference library`);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
console.log(`\n${result.
|
|
51
|
+
console.log(`\n${result.summary?.resolved ?? 0}/${result.summary?.requested ?? 0} resolved`);
|
|
52
52
|
}
|