nexarch 0.12.0 → 0.12.1
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/check-in.js +16 -4
- package/dist/commands/command-claim.js +4 -0
- package/dist/commands/init-project.js +4 -4
- package/dist/commands/register-runtime.js +77 -0
- package/dist/commands/setup.js +2 -2
- package/dist/index.js +11 -0
- package/dist/lib/application-context.js +51 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { homedir } from "os";
|
|
|
5
5
|
import { saveCheckInLease } from "../lib/check-in-lease.js";
|
|
6
6
|
import { requireCredentials } from "../lib/credentials.js";
|
|
7
7
|
import { callMcpTool } from "../lib/mcp.js";
|
|
8
|
+
import { inferAccessibleApplicationRefs } from "../lib/application-context.js";
|
|
8
9
|
function parseFlag(args, flag) {
|
|
9
10
|
return args.includes(flag);
|
|
10
11
|
}
|
|
@@ -36,6 +37,7 @@ function loadIdentity() {
|
|
|
36
37
|
export async function checkIn(args) {
|
|
37
38
|
const asJson = parseFlag(args, "--json");
|
|
38
39
|
const agentKeyArg = parseOptionValue(args, "--agent-ref") ?? parseOptionValue(args, "--agent-key");
|
|
40
|
+
const applicationRefArg = parseOptionValue(args, "--application-ref");
|
|
39
41
|
const creds = requireCredentials();
|
|
40
42
|
const identity = loadIdentity();
|
|
41
43
|
const agentKey = agentKeyArg ?? identity.agentKey;
|
|
@@ -48,9 +50,16 @@ export async function checkIn(args) {
|
|
|
48
50
|
}
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
53
|
+
const applicationContext = await inferAccessibleApplicationRefs({
|
|
54
|
+
companyId: creds.companyId,
|
|
55
|
+
explicitApplicationRefs: applicationRefArg ? [applicationRefArg] : undefined,
|
|
56
|
+
});
|
|
51
57
|
const raw = await callMcpTool("nexarch_check_in", {
|
|
52
58
|
agentRef: agentKey,
|
|
53
59
|
agentKey,
|
|
60
|
+
applicationContext: applicationContext.accessibleApplicationRefs.length > 0
|
|
61
|
+
? { accessibleApplicationRefs: applicationContext.accessibleApplicationRefs }
|
|
62
|
+
: undefined,
|
|
54
63
|
companyId: creds.companyId,
|
|
55
64
|
}, { companyId: creds.companyId });
|
|
56
65
|
const result = parseToolText(raw);
|
|
@@ -74,6 +83,7 @@ export async function checkIn(args) {
|
|
|
74
83
|
if (result.leaseExpiresAt) {
|
|
75
84
|
console.log(`Check-in lease active until ${new Date(result.leaseExpiresAt).toLocaleString()}.`);
|
|
76
85
|
}
|
|
86
|
+
console.log(`Application context: ${applicationContext.detail}`);
|
|
77
87
|
for (const warning of result.warnings ?? []) {
|
|
78
88
|
if (warning.message)
|
|
79
89
|
console.log(`Warning: ${warning.message}`);
|
|
@@ -91,7 +101,7 @@ export async function checkIn(args) {
|
|
|
91
101
|
}
|
|
92
102
|
for (const cmd of commands) {
|
|
93
103
|
const claimable = cmd.claimable ? "yes" : "no";
|
|
94
|
-
const reason = cmd.claimReason ?? (cmd.claimable ? "application_access_confirmed" : "
|
|
104
|
+
const reason = cmd.claimReason ?? (cmd.claimable ? "application_access_confirmed" : "application_context_required_for_claim");
|
|
95
105
|
console.log(`\n- ID : ${cmd.id}`);
|
|
96
106
|
console.log(` Type : ${cmd.command_type}`);
|
|
97
107
|
console.log(` Target : ${cmd.target_entity_key ?? "(none)"}`);
|
|
@@ -101,12 +111,14 @@ export async function checkIn(args) {
|
|
|
101
111
|
console.log(` Type ver : ${cmd.command_type_version}`);
|
|
102
112
|
if (cmd.resolved_runtime_handler)
|
|
103
113
|
console.log(` Runtime : ${cmd.resolved_runtime_handler}`);
|
|
104
|
-
if (cmd.
|
|
105
|
-
console.log(`
|
|
106
|
-
}
|
|
114
|
+
if (cmd.claimGuidance)
|
|
115
|
+
console.log(` Guidance : ${cmd.claimGuidance}`);
|
|
116
|
+
console.log(` Claim cmd : npx nexarch command-claim --id "${cmd.id}"${cmd.target_entity_key ? ` --application-ref "${cmd.target_entity_key}"` : ""}`);
|
|
107
117
|
}
|
|
108
118
|
console.log("\nTo validate an application target is accessible in Nexarch before claiming:");
|
|
109
119
|
console.log(" npx nexarch policy-controls --entity <application:key> --json");
|
|
120
|
+
console.log("If no application context was inferred, re-run with:");
|
|
121
|
+
console.log(" npx nexarch check-in --application-ref <application:key>");
|
|
110
122
|
}
|
|
111
123
|
else {
|
|
112
124
|
console.log("No pending application-target commands found.");
|
|
@@ -37,6 +37,7 @@ export async function commandClaim(args) {
|
|
|
37
37
|
const asJson = parseFlag(args, "--json");
|
|
38
38
|
const id = parseOptionValue(args, "--id");
|
|
39
39
|
const agentKeyArg = parseOptionValue(args, "--agent-ref") ?? parseOptionValue(args, "--agent-key");
|
|
40
|
+
const applicationEntityRef = parseOptionValue(args, "--application-ref");
|
|
40
41
|
if (!id) {
|
|
41
42
|
console.error("error: --id <commandId> is required");
|
|
42
43
|
process.exit(1);
|
|
@@ -54,6 +55,7 @@ export async function commandClaim(args) {
|
|
|
54
55
|
agentRef: agentKey,
|
|
55
56
|
agentKey,
|
|
56
57
|
leaseToken: lease?.leaseToken,
|
|
58
|
+
applicationEntityRef: applicationEntityRef ?? undefined,
|
|
57
59
|
companyId: creds.companyId,
|
|
58
60
|
}, { companyId: creds.companyId });
|
|
59
61
|
const result = parseToolText(raw);
|
|
@@ -72,6 +74,8 @@ export async function commandClaim(args) {
|
|
|
72
74
|
console.log(`Command ${id} was not claimed.`);
|
|
73
75
|
if (result.reason)
|
|
74
76
|
console.log(`Reason: ${result.reason}`);
|
|
77
|
+
if (!applicationEntityRef)
|
|
78
|
+
console.log("Tip: re-run with --application-ref <application:key>.");
|
|
75
79
|
return;
|
|
76
80
|
}
|
|
77
81
|
const cmd = result.command;
|
|
@@ -221,7 +221,7 @@ function choosePreferredRepositoryRef(candidates) {
|
|
|
221
221
|
});
|
|
222
222
|
return scored[0]?.rawRef ?? null;
|
|
223
223
|
}
|
|
224
|
-
function detectSourceRepository(dir) {
|
|
224
|
+
export function detectSourceRepository(dir) {
|
|
225
225
|
let rawRef = null;
|
|
226
226
|
let vcsType = "unknown";
|
|
227
227
|
// Prefer git when present, and prefer hosted remotes over local-only URLs.
|
|
@@ -869,7 +869,7 @@ function structuralRelForSubPackage(sp, projectExternalKey) {
|
|
|
869
869
|
return { type: "part_of", from: sp.externalKey, to: projectExternalKey };
|
|
870
870
|
return null;
|
|
871
871
|
}
|
|
872
|
-
function scanProject(dir) {
|
|
872
|
+
export function scanProject(dir) {
|
|
873
873
|
const names = new Set();
|
|
874
874
|
let projectName = basename(dir);
|
|
875
875
|
const subPackages = [];
|
|
@@ -995,7 +995,7 @@ function pickRelationshipType(toEntityTypeCode, toEntitySubtypeCode, _fromEntity
|
|
|
995
995
|
return "depends_on";
|
|
996
996
|
}
|
|
997
997
|
}
|
|
998
|
-
function normalizeToken(value) {
|
|
998
|
+
export function normalizeToken(value) {
|
|
999
999
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1000
1000
|
}
|
|
1001
1001
|
function extractHost(input) {
|
|
@@ -1008,7 +1008,7 @@ function extractHost(input) {
|
|
|
1008
1008
|
return null;
|
|
1009
1009
|
}
|
|
1010
1010
|
}
|
|
1011
|
-
function scoreApplicationCandidate(app, projectName, repoUrl) {
|
|
1011
|
+
export function scoreApplicationCandidate(app, projectName, repoUrl) {
|
|
1012
1012
|
const entityRef = app.entityRef ?? app.externalKey ?? null;
|
|
1013
1013
|
if (!entityRef)
|
|
1014
1014
|
return null;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import process from "process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { arch, hostname, platform, release, type as osType, homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { requireCredentials } from "../lib/credentials.js";
|
|
6
|
+
import { callMcpTool } from "../lib/mcp.js";
|
|
7
|
+
import { inferAccessibleApplicationRefs } from "../lib/application-context.js";
|
|
8
|
+
function parseFlag(args, flag) {
|
|
9
|
+
return args.includes(flag);
|
|
10
|
+
}
|
|
11
|
+
function parseOptionValue(args, option) {
|
|
12
|
+
const idx = args.indexOf(option);
|
|
13
|
+
if (idx === -1)
|
|
14
|
+
return null;
|
|
15
|
+
const value = args[idx + 1];
|
|
16
|
+
if (!value || value.startsWith("--"))
|
|
17
|
+
return null;
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function parseToolText(result) {
|
|
21
|
+
const text = result.content?.[0]?.text ?? "{}";
|
|
22
|
+
return JSON.parse(text);
|
|
23
|
+
}
|
|
24
|
+
function loadIdentity() {
|
|
25
|
+
const identityPath = join(homedir(), ".nexarch", "identity.json");
|
|
26
|
+
if (!existsSync(identityPath))
|
|
27
|
+
return { agentKey: null };
|
|
28
|
+
try {
|
|
29
|
+
const data = JSON.parse(readFileSync(identityPath, "utf8"));
|
|
30
|
+
return { agentKey: data.agentKey ?? null };
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { agentKey: null };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function registerRuntime(args) {
|
|
37
|
+
const asJson = parseFlag(args, "--json");
|
|
38
|
+
const applicationRefArg = parseOptionValue(args, "--application-ref");
|
|
39
|
+
const client = parseOptionValue(args, "--client") ?? "nexarch-cli";
|
|
40
|
+
const version = parseOptionValue(args, "--version") ?? null;
|
|
41
|
+
const mode = process.env.CI ? "ci" : "interactive";
|
|
42
|
+
const creds = requireCredentials();
|
|
43
|
+
const identity = loadIdentity();
|
|
44
|
+
if (!identity.agentKey)
|
|
45
|
+
throw new Error("No agent key found. Run nexarch init-agent first.");
|
|
46
|
+
const applicationContext = await inferAccessibleApplicationRefs({
|
|
47
|
+
companyId: creds.companyId,
|
|
48
|
+
explicitApplicationRefs: applicationRefArg ? [applicationRefArg] : undefined,
|
|
49
|
+
});
|
|
50
|
+
const raw = await callMcpTool("nexarch_register_agent_runtime", {
|
|
51
|
+
agentRef: identity.agentKey,
|
|
52
|
+
runtime: {
|
|
53
|
+
client,
|
|
54
|
+
...(version ? { version } : {}),
|
|
55
|
+
mode,
|
|
56
|
+
nodeVersion: process.version,
|
|
57
|
+
},
|
|
58
|
+
host: {
|
|
59
|
+
hostname: hostname(),
|
|
60
|
+
osPlatform: platform(),
|
|
61
|
+
osType: osType(),
|
|
62
|
+
osRelease: release(),
|
|
63
|
+
arch: arch(),
|
|
64
|
+
},
|
|
65
|
+
applicationContext: applicationContext.accessibleApplicationRefs.length > 0
|
|
66
|
+
? { accessibleApplicationRefs: applicationContext.accessibleApplicationRefs }
|
|
67
|
+
: undefined,
|
|
68
|
+
companyId: creds.companyId,
|
|
69
|
+
}, { companyId: creds.companyId });
|
|
70
|
+
const result = parseToolText(raw);
|
|
71
|
+
if (asJson) {
|
|
72
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(`Registered runtime for ${identity.agentKey}.`);
|
|
76
|
+
console.log(`Application context: ${applicationContext.detail}`);
|
|
77
|
+
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -67,9 +67,9 @@ export async function setup(args) {
|
|
|
67
67
|
if (clients.length > 0) {
|
|
68
68
|
const names = clients.map((c) => c.name);
|
|
69
69
|
const listed = names.length === 1 ? names[0] : `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
70
|
-
console.log(`\nSetup complete. Open a new ${listed} session and ask your agent to
|
|
70
|
+
console.log(`\nSetup complete. Open a new ${listed} session and ask your agent to register its runtime with Nexarch. Check-in is a separate step when you want to preview work.`);
|
|
71
71
|
}
|
|
72
72
|
else {
|
|
73
|
-
console.log(
|
|
73
|
+
console.log("\nSetup complete. Once you have configured an MCP client (see above), open a new session and ask your agent to register its runtime with Nexarch. Check-in is a separate step when you want to preview work.");
|
|
74
74
|
}
|
|
75
75
|
}
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ import { policyAuditResults } from "./commands/policy-audit-results.js";
|
|
|
25
25
|
import { appliedPolicies } from "./commands/applied-policies.js";
|
|
26
26
|
import { governanceSummary } from "./commands/governance-summary.js";
|
|
27
27
|
import { proposalsStart } from "./commands/proposals-start.js";
|
|
28
|
+
import { registerRuntime } from "./commands/register-runtime.js";
|
|
28
29
|
const [, , command, ...args] = process.argv;
|
|
29
30
|
const commands = {
|
|
30
31
|
login,
|
|
@@ -43,6 +44,7 @@ const commands = {
|
|
|
43
44
|
"resolve-names": resolveNames,
|
|
44
45
|
"list-entities": listEntities,
|
|
45
46
|
"list-relationships": listRelationships,
|
|
47
|
+
"register-runtime": registerRuntime,
|
|
46
48
|
"check-in": checkIn,
|
|
47
49
|
"command-done": commandDone,
|
|
48
50
|
"command-fail": commandFail,
|
|
@@ -180,12 +182,20 @@ Usage:
|
|
|
180
182
|
--to <toExternalKey>
|
|
181
183
|
--limit <1-500>
|
|
182
184
|
--json
|
|
185
|
+
nexarch register-runtime
|
|
186
|
+
Register or refresh runtime + optional application context
|
|
187
|
+
without performing check-in.
|
|
188
|
+
Options: --application-ref <entityRef>
|
|
189
|
+
--client <name>
|
|
190
|
+
--version <semver>
|
|
191
|
+
--json
|
|
183
192
|
nexarch check-in Preview pending application-target commands (no auto-claim)
|
|
184
193
|
and report draft/proposed applications needing review so the
|
|
185
194
|
agent can prompt the user to explore and instantiate them.
|
|
186
195
|
Use command-claim to explicitly claim a specific command.
|
|
187
196
|
Scope is resolved server-side from active company context.
|
|
188
197
|
Options: --agent-key <key> override stored agent key
|
|
198
|
+
--application-ref <entityRef> narrow preview scope
|
|
189
199
|
--json JSON output includes draftApplications[] and proposedApplications[]
|
|
190
200
|
nexarch proposals start
|
|
191
201
|
Start a new application workspace from a proposed NexArch app.
|
|
@@ -205,6 +215,7 @@ Usage:
|
|
|
205
215
|
Explicitly claim a pending command by ID.
|
|
206
216
|
Options: --id <commandId> (required)
|
|
207
217
|
--agent-key <key> override stored agent key
|
|
218
|
+
--application-ref <entityRef> required for application-target commands
|
|
208
219
|
--json
|
|
209
220
|
nexarch command-done
|
|
210
221
|
Mark a claimed command as completed.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { resolve as resolvePath } from "node:path";
|
|
2
|
+
import { callMcpTool } from "./mcp.js";
|
|
3
|
+
import { detectSourceRepository, scanProject, scoreApplicationCandidate } from "../commands/init-project.js";
|
|
4
|
+
function parseToolText(result) {
|
|
5
|
+
const text = result.content?.[0]?.text ?? "{}";
|
|
6
|
+
return JSON.parse(text);
|
|
7
|
+
}
|
|
8
|
+
export async function inferAccessibleApplicationRefs(params) {
|
|
9
|
+
const explicit = Array.from(new Set((params.explicitApplicationRefs ?? []).map((value) => value.trim()).filter(Boolean)));
|
|
10
|
+
if (explicit.length > 0) {
|
|
11
|
+
return {
|
|
12
|
+
accessibleApplicationRefs: explicit,
|
|
13
|
+
source: "explicit",
|
|
14
|
+
detail: `Using explicit application context (${explicit.join(", ")}).`,
|
|
15
|
+
repoUrl: detectSourceRepository(params.dir ?? process.cwd())?.url ?? null,
|
|
16
|
+
projectNames: [],
|
|
17
|
+
candidates: [],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const dir = resolvePath(params.dir ?? process.cwd());
|
|
21
|
+
const repoUrl = detectSourceRepository(dir)?.url ?? null;
|
|
22
|
+
const scan = scanProject(dir);
|
|
23
|
+
const projectNames = Array.from(new Set([scan.projectName, ...scan.subPackages.map((sp) => sp.name)])).filter(Boolean);
|
|
24
|
+
const appsRaw = await callMcpTool("nexarch_list_entities", { entityTypeCode: "application", status: "active", limit: 500, companyId: params.companyId }, { companyId: params.companyId });
|
|
25
|
+
const appsData = parseToolText(appsRaw);
|
|
26
|
+
const apps = (appsData.entities ?? []).filter((entity) => (entity.entityRef ?? entity.externalKey));
|
|
27
|
+
const candidateMap = new Map();
|
|
28
|
+
for (const projectName of projectNames) {
|
|
29
|
+
for (const app of apps) {
|
|
30
|
+
const match = scoreApplicationCandidate(app, projectName, repoUrl);
|
|
31
|
+
if (!match)
|
|
32
|
+
continue;
|
|
33
|
+
const existing = candidateMap.get(match.entityRef);
|
|
34
|
+
if (!existing || match.score > existing.score) {
|
|
35
|
+
candidateMap.set(match.entityRef, match);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const candidates = Array.from(candidateMap.values()).sort((a, b) => b.score - a.score);
|
|
40
|
+
const accessibleApplicationRefs = candidates.filter((candidate) => candidate.score >= 0.85).map((candidate) => candidate.entityRef);
|
|
41
|
+
return {
|
|
42
|
+
accessibleApplicationRefs,
|
|
43
|
+
source: accessibleApplicationRefs.length > 0 ? "inferred" : "none",
|
|
44
|
+
detail: accessibleApplicationRefs.length > 0
|
|
45
|
+
? `Inferred application context from current folder (${accessibleApplicationRefs.join(", ")}).`
|
|
46
|
+
: "No high-confidence application context inferred from current folder.",
|
|
47
|
+
repoUrl,
|
|
48
|
+
projectNames,
|
|
49
|
+
candidates,
|
|
50
|
+
};
|
|
51
|
+
}
|