nexarch 0.1.11 ā 0.1.13
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/agent-identify.js +151 -0
- package/dist/commands/init-agent.js +137 -2
- package/dist/index.js +35 -0
- package/package.json +1 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import process from "process";
|
|
2
|
+
import { requireCredentials } from "../lib/credentials.js";
|
|
3
|
+
import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
|
|
4
|
+
function parseFlag(args, flag) {
|
|
5
|
+
return args.includes(flag);
|
|
6
|
+
}
|
|
7
|
+
function parseOptionValue(args, option) {
|
|
8
|
+
const idx = args.indexOf(option);
|
|
9
|
+
if (idx === -1)
|
|
10
|
+
return null;
|
|
11
|
+
const value = args[idx + 1];
|
|
12
|
+
if (!value || value.startsWith("--"))
|
|
13
|
+
return null;
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function parseToolText(result) {
|
|
17
|
+
const text = result.content?.[0]?.text ?? "{}";
|
|
18
|
+
return JSON.parse(text);
|
|
19
|
+
}
|
|
20
|
+
function getDefaultAgentId() {
|
|
21
|
+
const osUser = process.env.USERNAME || process.env.USER || "unknown";
|
|
22
|
+
const host = process.env.HOSTNAME || "unknown-host";
|
|
23
|
+
return `nexarch-cli:${osUser}@${host}`;
|
|
24
|
+
}
|
|
25
|
+
function parseCsv(value) {
|
|
26
|
+
if (!value)
|
|
27
|
+
return [];
|
|
28
|
+
return value
|
|
29
|
+
.split(",")
|
|
30
|
+
.map((v) => v.trim())
|
|
31
|
+
.filter(Boolean);
|
|
32
|
+
}
|
|
33
|
+
export async function agentIdentify(args) {
|
|
34
|
+
const asJson = parseFlag(args, "--json");
|
|
35
|
+
const agentId = parseOptionValue(args, "--agent-id") ?? getDefaultAgentId();
|
|
36
|
+
const provider = parseOptionValue(args, "--provider");
|
|
37
|
+
const model = parseOptionValue(args, "--model");
|
|
38
|
+
const client = parseOptionValue(args, "--client");
|
|
39
|
+
const sessionId = parseOptionValue(args, "--session-id");
|
|
40
|
+
const framework = parseOptionValue(args, "--framework");
|
|
41
|
+
const toolVersion = parseOptionValue(args, "--tool-version");
|
|
42
|
+
const capabilities = parseCsv(parseOptionValue(args, "--capabilities"));
|
|
43
|
+
const notes = parseOptionValue(args, "--notes");
|
|
44
|
+
if (!provider || !model || !client) {
|
|
45
|
+
throw new Error("agent-identify requires --provider, --model, and --client");
|
|
46
|
+
}
|
|
47
|
+
const creds = requireCredentials();
|
|
48
|
+
await mcpInitialize({ companyId: creds.companyId });
|
|
49
|
+
const tools = await mcpListTools({ companyId: creds.companyId });
|
|
50
|
+
const toolNames = new Set(tools.map((t) => t.name));
|
|
51
|
+
const required = ["nexarch_upsert_entities"];
|
|
52
|
+
const missing = required.filter((name) => !toolNames.has(name));
|
|
53
|
+
if (missing.length) {
|
|
54
|
+
throw new Error(`missing required MCP tools: ${missing.join(", ")}`);
|
|
55
|
+
}
|
|
56
|
+
let policyBundleHash = null;
|
|
57
|
+
if (toolNames.has("nexarch_get_applied_policies")) {
|
|
58
|
+
try {
|
|
59
|
+
const policiesRaw = await callMcpTool("nexarch_get_applied_policies", {}, { companyId: creds.companyId });
|
|
60
|
+
const policies = parseToolText(policiesRaw);
|
|
61
|
+
policyBundleHash = policies.policyBundleHash ?? null;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
policyBundleHash = null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const nowIso = new Date().toISOString();
|
|
68
|
+
const upsertRaw = await callMcpTool("nexarch_upsert_entities", {
|
|
69
|
+
entities: [
|
|
70
|
+
{
|
|
71
|
+
externalKey: `agent:${agentId}`,
|
|
72
|
+
entityTypeCode: "agent",
|
|
73
|
+
name: `Agent ${agentId}`,
|
|
74
|
+
confidence: 1,
|
|
75
|
+
attributes: {
|
|
76
|
+
identity: {
|
|
77
|
+
source: "nexarch agent identify",
|
|
78
|
+
capturedAt: nowIso,
|
|
79
|
+
provider,
|
|
80
|
+
model,
|
|
81
|
+
client,
|
|
82
|
+
sessionId,
|
|
83
|
+
framework,
|
|
84
|
+
toolVersion,
|
|
85
|
+
capabilities,
|
|
86
|
+
notes,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
agentContext: {
|
|
92
|
+
agentId,
|
|
93
|
+
agentRunId: `agent-identify-${Date.now()}`,
|
|
94
|
+
repoRef: "nexarch-cli/agent-identify",
|
|
95
|
+
observedAt: nowIso,
|
|
96
|
+
source: "nexarch-cli",
|
|
97
|
+
model,
|
|
98
|
+
provider,
|
|
99
|
+
},
|
|
100
|
+
policyContext: policyBundleHash
|
|
101
|
+
? {
|
|
102
|
+
policyBundleHash,
|
|
103
|
+
alignmentSummary: {
|
|
104
|
+
score: 1,
|
|
105
|
+
violations: [],
|
|
106
|
+
waivers: [],
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
: undefined,
|
|
110
|
+
dryRun: false,
|
|
111
|
+
}, { companyId: creds.companyId });
|
|
112
|
+
const upsert = parseToolText(upsertRaw);
|
|
113
|
+
const failed = Number(upsert.summary?.failed ?? 0) > 0;
|
|
114
|
+
const firstResult = upsert.results?.[0];
|
|
115
|
+
const output = {
|
|
116
|
+
ok: !failed,
|
|
117
|
+
detail: failed ? `failed (${upsert.errors?.[0]?.error ?? "unknown"})` : `agent ${firstResult?.action ?? "updated"}`,
|
|
118
|
+
agentId,
|
|
119
|
+
graphEntityId: firstResult?.graphEntityId ?? null,
|
|
120
|
+
identity: {
|
|
121
|
+
provider,
|
|
122
|
+
model,
|
|
123
|
+
client,
|
|
124
|
+
sessionId,
|
|
125
|
+
framework,
|
|
126
|
+
toolVersion,
|
|
127
|
+
capabilities,
|
|
128
|
+
notes,
|
|
129
|
+
capturedAt: nowIso,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
if (asJson) {
|
|
133
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
134
|
+
if (!output.ok)
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (output.ok) {
|
|
139
|
+
console.log("ā
Agent identity captured.");
|
|
140
|
+
console.log(`Agent: ${agentId}`);
|
|
141
|
+
console.log(`Provider/Model: ${provider} / ${model}`);
|
|
142
|
+
console.log(`Client: ${client}`);
|
|
143
|
+
if (framework)
|
|
144
|
+
console.log(`Framework: ${framework}`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log("ā Agent identity capture failed.");
|
|
148
|
+
console.log(output.detail);
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { arch, hostname, platform, release, type as osType, userInfo } from "os";
|
|
1
|
+
import { arch, homedir, hostname, platform, release, type as osType, userInfo } from "os";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
2
4
|
import process from "process";
|
|
3
5
|
import { requireCredentials } from "../lib/credentials.js";
|
|
4
6
|
import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
|
|
5
|
-
const CLI_VERSION = "0.1.
|
|
7
|
+
const CLI_VERSION = "0.1.13";
|
|
6
8
|
const AGENT_ENTITY_TYPE = "agent";
|
|
7
9
|
const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
|
|
8
10
|
function parseFlag(args, flag) {
|
|
@@ -86,6 +88,92 @@ async function emitInitObservation(params) {
|
|
|
86
88
|
// non-fatal telemetry path
|
|
87
89
|
}
|
|
88
90
|
}
|
|
91
|
+
function writeAgentBootstrap(params) {
|
|
92
|
+
const dir = join(homedir(), ".nexarch");
|
|
93
|
+
mkdirSync(dir, { recursive: true });
|
|
94
|
+
const filePath = join(dir, "agent-bootstrap.md");
|
|
95
|
+
const identifyCmd = [
|
|
96
|
+
`npx nexarch agent identify`,
|
|
97
|
+
`--agent-id "${params.agentId}"`,
|
|
98
|
+
`--provider "<anthropic|openai|google|other>"`,
|
|
99
|
+
`--model "<model-id e.g. claude-sonnet-4-6>"`,
|
|
100
|
+
`--client "<claude-code|cursor|copilot|other>"`,
|
|
101
|
+
`--framework "<openclaw|n8n|m365-agent-framework|other>"`,
|
|
102
|
+
`--session-id "<your current session id if known, otherwise omit>"`,
|
|
103
|
+
`--tool-version "<run claude --version to get this, e.g. 1.2.3>"`,
|
|
104
|
+
`--capabilities "<comma,separated,capabilities>"`,
|
|
105
|
+
`--json`,
|
|
106
|
+
].join(" \\\n ");
|
|
107
|
+
const content = `# Nexarch Agent Bootstrap
|
|
108
|
+
|
|
109
|
+
This file was written by \`nexarch init-agent\` on ${new Date().toISOString()}.
|
|
110
|
+
It contains instructions for a Claude Code agent to complete its Nexarch registration.
|
|
111
|
+
|
|
112
|
+
## What has already been registered
|
|
113
|
+
|
|
114
|
+
The following entities are now in the architecture graph:
|
|
115
|
+
|
|
116
|
+
| Entity | Name | Type |
|
|
117
|
+
|--------|------|------|
|
|
118
|
+
| Agent | \`${params.agentId}\` | agent |
|
|
119
|
+
| Host | \`Host ${params.hostname}\` | tech_compute |
|
|
120
|
+
| OS | \`${params.osPlatform} ${params.osRelease}\` | tech_operating_system |
|
|
121
|
+
| Runtime | \`Node.js ${params.nodeVersion}\` | tech_runtime |
|
|
122
|
+
|
|
123
|
+
## Your task
|
|
124
|
+
|
|
125
|
+
Run the command below, substituting your own values for the placeholders.
|
|
126
|
+
Return ONLY the completed command (no markdown, no explanation) ā it will be executed directly.
|
|
127
|
+
|
|
128
|
+
\`\`\`
|
|
129
|
+
${identifyCmd}
|
|
130
|
+
\`\`\`
|
|
131
|
+
|
|
132
|
+
Fill in:
|
|
133
|
+
- **provider** ā your AI provider (e.g. \`anthropic\`)
|
|
134
|
+
- **model** ā the exact model ID you are running as (e.g. \`claude-sonnet-4-6\`)
|
|
135
|
+
- **client** ā the tool you are running inside (e.g. \`claude-code\`)
|
|
136
|
+
- **framework** ā the agent framework in use (e.g. \`openclaw\`)
|
|
137
|
+
- **session-id** ā your current session ID if available
|
|
138
|
+
- **tool-version** ā the version of the client tool you are running inside; run \`claude --version\` (or equivalent) to get it and use the version number only (e.g. \`1.2.3\`)
|
|
139
|
+
- **capabilities** ā comma-separated list of your capabilities (e.g. \`code,files,bash,web\`)
|
|
140
|
+
|
|
141
|
+
Omit any flag whose value you genuinely do not know rather than guessing.
|
|
142
|
+
|
|
143
|
+
After running, confirm \`"ok": true\` in the JSON output. No further action is needed.
|
|
144
|
+
`;
|
|
145
|
+
writeFileSync(filePath, content, "utf8");
|
|
146
|
+
return filePath;
|
|
147
|
+
}
|
|
148
|
+
const CLAUDE_MD_SECTION_MARKER = "## Nexarch Agent Registration";
|
|
149
|
+
const CLAUDE_MD_SECTION = `
|
|
150
|
+
## Nexarch Agent Registration
|
|
151
|
+
|
|
152
|
+
When a human has run \`nexarch login\` and \`nexarch init-agent\`, they will tell you to run \`npx nexarch agent prompt\`. Do so, read the output, and follow the instructions ā it contains a pre-filled \`npx nexarch agent identify\` command for you to complete with your own provider, model, client, and capability values, then execute. Confirm \`"ok": true\` in the JSON output.
|
|
153
|
+
`;
|
|
154
|
+
function injectAgentConfigs() {
|
|
155
|
+
const candidateNames = [
|
|
156
|
+
"CLAUDE.md", "claude.md", "Claude.md", // Claude Code
|
|
157
|
+
"AGENTS.md", "agents.md", // OpenAI Codex CLI
|
|
158
|
+
".github/copilot-instructions.md", // GitHub Copilot
|
|
159
|
+
".cursorrules", // Cursor (legacy)
|
|
160
|
+
];
|
|
161
|
+
const results = [];
|
|
162
|
+
for (const name of candidateNames) {
|
|
163
|
+
const filePath = join(process.cwd(), name);
|
|
164
|
+
if (!existsSync(filePath))
|
|
165
|
+
continue;
|
|
166
|
+
const existing = readFileSync(filePath, "utf8");
|
|
167
|
+
if (existing.includes(CLAUDE_MD_SECTION_MARKER)) {
|
|
168
|
+
results.push({ path: filePath, status: "already_present" });
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
172
|
+
writeFileSync(filePath, existing + separator + CLAUDE_MD_SECTION, "utf8");
|
|
173
|
+
results.push({ path: filePath, status: "injected" });
|
|
174
|
+
}
|
|
175
|
+
return results;
|
|
176
|
+
}
|
|
89
177
|
export async function initAgent(args) {
|
|
90
178
|
const asJson = parseFlag(args, "--json");
|
|
91
179
|
const strict = parseFlag(args, "--strict");
|
|
@@ -481,6 +569,29 @@ export async function initAgent(args) {
|
|
|
481
569
|
}
|
|
482
570
|
}
|
|
483
571
|
}
|
|
572
|
+
let bootstrapFilePath = null;
|
|
573
|
+
let agentConfigResults = [];
|
|
574
|
+
if (registration.ok) {
|
|
575
|
+
try {
|
|
576
|
+
bootstrapFilePath = writeAgentBootstrap({
|
|
577
|
+
agentId,
|
|
578
|
+
hostname: runtime.hostname,
|
|
579
|
+
osPlatform: runtime.osPlatform,
|
|
580
|
+
osRelease: runtime.osRelease,
|
|
581
|
+
nodeVersion: runtime.nodeVersion,
|
|
582
|
+
arch: runtime.arch,
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
catch {
|
|
586
|
+
// non-fatal
|
|
587
|
+
}
|
|
588
|
+
try {
|
|
589
|
+
agentConfigResults = injectAgentConfigs();
|
|
590
|
+
}
|
|
591
|
+
catch {
|
|
592
|
+
// non-fatal
|
|
593
|
+
}
|
|
594
|
+
}
|
|
484
595
|
checks.push({
|
|
485
596
|
name: "agent.registration",
|
|
486
597
|
ok: registration.ok,
|
|
@@ -504,11 +615,17 @@ export async function initAgent(args) {
|
|
|
504
615
|
});
|
|
505
616
|
}
|
|
506
617
|
const allPassed = checks.every((c) => c.ok);
|
|
618
|
+
const identifyPrompt = [
|
|
619
|
+
"Please identify your runtime so the workspace can register richer metadata.",
|
|
620
|
+
"Return ONLY the completed command below with real values (no markdown):",
|
|
621
|
+
`nexarch agent identify --agent-id \"${agentId}\" --provider \"<provider>\" --model \"<model>\" --client \"<client>\" --framework \"<openclaw|n8n|m365-agent-framework|other>\" --session-id \"<session-id>\" --tool-version \"<tool-version>\" --capabilities \"<capability1,capability2>\" --notes \"<optional notes>\" --json`,
|
|
622
|
+
].join("\n");
|
|
507
623
|
if (asJson) {
|
|
508
624
|
const output = {
|
|
509
625
|
ok: strict ? allPassed : true,
|
|
510
626
|
passed: allPassed,
|
|
511
627
|
strict,
|
|
628
|
+
status: allPassed ? "needs_agent_identity_followup" : "handshake_completed_with_issues",
|
|
512
629
|
checks,
|
|
513
630
|
summary: {
|
|
514
631
|
total: checks.length,
|
|
@@ -525,7 +642,16 @@ export async function initAgent(args) {
|
|
|
525
642
|
relationships: techRelationships,
|
|
526
643
|
},
|
|
527
644
|
binding,
|
|
645
|
+
followUp: {
|
|
646
|
+
required: allPassed,
|
|
647
|
+
type: "agent_identity",
|
|
648
|
+
detail: "Prompt the coding agent to self-identify and submit via nexarch agent identify",
|
|
649
|
+
prompt: identifyPrompt,
|
|
650
|
+
submitCommandTemplate: `nexarch agent identify --agent-id \"${agentId}\" --provider \"<provider>\" --model \"<model>\" --client \"<client>\" --framework \"<openclaw|n8n|m365-agent-framework|other>\" --session-id \"<session-id>\" --tool-version \"<tool-version>\" --capabilities \"<capability1,capability2>\" --notes \"<optional notes>\" --json`,
|
|
651
|
+
},
|
|
528
652
|
companyId: creds.companyId,
|
|
653
|
+
bootstrapFile: bootstrapFilePath,
|
|
654
|
+
agentConfigs: agentConfigResults,
|
|
529
655
|
};
|
|
530
656
|
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
531
657
|
if (strict && !allPassed)
|
|
@@ -547,6 +673,15 @@ export async function initAgent(args) {
|
|
|
547
673
|
console.log("");
|
|
548
674
|
if (allPassed) {
|
|
549
675
|
console.log("ā
Agent handshake + registration completed.");
|
|
676
|
+
if (bootstrapFilePath) {
|
|
677
|
+
console.log(`\nš Bootstrap instructions written to: ${bootstrapFilePath}`);
|
|
678
|
+
}
|
|
679
|
+
for (const r of agentConfigResults) {
|
|
680
|
+
if (r.status === "injected") {
|
|
681
|
+
console.log(`š Nexarch registration instructions added to: ${r.path}`);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
console.log("\n Tell Claude: \"run npx nexarch agent prompt and follow the instructions\"");
|
|
550
685
|
}
|
|
551
686
|
else {
|
|
552
687
|
console.log("ā Handshake/registration completed with issues.");
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { readFileSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
2
5
|
import { login } from "./commands/login.js";
|
|
3
6
|
import { logout } from "./commands/logout.js";
|
|
4
7
|
import { status } from "./commands/status.js";
|
|
@@ -6,6 +9,7 @@ import { setup } from "./commands/setup.js";
|
|
|
6
9
|
import { mcpConfig } from "./commands/mcp-config.js";
|
|
7
10
|
import { mcpProxy } from "./commands/mcp-proxy.js";
|
|
8
11
|
import { initAgent } from "./commands/init-agent.js";
|
|
12
|
+
import { agentIdentify } from "./commands/agent-identify.js";
|
|
9
13
|
const [, , command, ...args] = process.argv;
|
|
10
14
|
const commands = {
|
|
11
15
|
login,
|
|
@@ -15,8 +19,27 @@ const commands = {
|
|
|
15
19
|
"mcp-config": mcpConfig,
|
|
16
20
|
"mcp-proxy": mcpProxy,
|
|
17
21
|
"init-agent": initAgent,
|
|
22
|
+
"agent-identify": agentIdentify,
|
|
18
23
|
};
|
|
19
24
|
async function main() {
|
|
25
|
+
if (command === "agent") {
|
|
26
|
+
const [subcommand, ...subArgs] = args;
|
|
27
|
+
if (subcommand === "identify") {
|
|
28
|
+
await agentIdentify(subArgs);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (subcommand === "prompt") {
|
|
32
|
+
const filePath = join(homedir(), ".nexarch", "agent-bootstrap.md");
|
|
33
|
+
try {
|
|
34
|
+
process.stdout.write(readFileSync(filePath, "utf8") + "\n");
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
console.error("No bootstrap file found. Run 'nexarch init-agent' first.");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
20
43
|
const handler = commands[command ?? ""];
|
|
21
44
|
if (!handler) {
|
|
22
45
|
console.log(`
|
|
@@ -34,6 +57,18 @@ Usage:
|
|
|
34
57
|
Options: --agent-id <id> --bind-to-external-key <key>
|
|
35
58
|
--bind-relationship-type <code> --redact-hostname
|
|
36
59
|
--json --strict
|
|
60
|
+
nexarch agent prompt
|
|
61
|
+
Print bootstrap instructions for a Claude Code agent to complete
|
|
62
|
+
its registration (written by init-agent). Tell Claude:
|
|
63
|
+
"run npx nexarch agent prompt and follow the instructions"
|
|
64
|
+
nexarch agent identify
|
|
65
|
+
Capture richer coding-agent identity metadata
|
|
66
|
+
Options: --agent-id <id> --provider <provider> --model <model>
|
|
67
|
+
--client <name> [--framework <name>] [--session-id <id>]
|
|
68
|
+
[--tool-version <v>] [--capabilities <csv>]
|
|
69
|
+
[--notes <text>] [--json]
|
|
70
|
+
nexarch agent-identify
|
|
71
|
+
Alias of 'nexarch agent identify'
|
|
37
72
|
`);
|
|
38
73
|
process.exit(command ? 1 : 0);
|
|
39
74
|
}
|