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.
@@ -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" : "application_not_found_for_company");
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.claimable) {
105
- console.log(` Claim cmd : npx nexarch command-claim --id "${cmd.id}"`);
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
+ }
@@ -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 "register with Nexarch".`);
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('\nSetup complete. Once you have configured an MCP client (see above), open a new session and ask your agent to "register with Nexarch".');
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",