nexarch 0.1.11 ā 0.1.12
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 +126 -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.12";
|
|
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,83 @@ 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
|
+
`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 "<your tool version if known, otherwise omit>"`,
|
|
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** ā your tool version if available
|
|
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 \`nexarch agent prompt\`. Do so, read the output, and follow the instructions ā it contains a pre-filled \`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 injectClaudeMd() {
|
|
155
|
+
const candidates = ["CLAUDE.md", "claude.md", "Claude.md"];
|
|
156
|
+
const found = candidates.find((name) => existsSync(join(process.cwd(), name)));
|
|
157
|
+
if (!found)
|
|
158
|
+
return null;
|
|
159
|
+
const filePath = join(process.cwd(), found);
|
|
160
|
+
const existing = readFileSync(filePath, "utf8");
|
|
161
|
+
if (existing.includes(CLAUDE_MD_SECTION_MARKER)) {
|
|
162
|
+
return { path: filePath, status: "already_present" };
|
|
163
|
+
}
|
|
164
|
+
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
165
|
+
writeFileSync(filePath, existing + separator + CLAUDE_MD_SECTION, "utf8");
|
|
166
|
+
return { path: filePath, status: "injected" };
|
|
167
|
+
}
|
|
89
168
|
export async function initAgent(args) {
|
|
90
169
|
const asJson = parseFlag(args, "--json");
|
|
91
170
|
const strict = parseFlag(args, "--strict");
|
|
@@ -481,6 +560,29 @@ export async function initAgent(args) {
|
|
|
481
560
|
}
|
|
482
561
|
}
|
|
483
562
|
}
|
|
563
|
+
let bootstrapFilePath = null;
|
|
564
|
+
let claudeMdResult = null;
|
|
565
|
+
if (registration.ok) {
|
|
566
|
+
try {
|
|
567
|
+
bootstrapFilePath = writeAgentBootstrap({
|
|
568
|
+
agentId,
|
|
569
|
+
hostname: runtime.hostname,
|
|
570
|
+
osPlatform: runtime.osPlatform,
|
|
571
|
+
osRelease: runtime.osRelease,
|
|
572
|
+
nodeVersion: runtime.nodeVersion,
|
|
573
|
+
arch: runtime.arch,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
catch {
|
|
577
|
+
// non-fatal
|
|
578
|
+
}
|
|
579
|
+
try {
|
|
580
|
+
claudeMdResult = injectClaudeMd();
|
|
581
|
+
}
|
|
582
|
+
catch {
|
|
583
|
+
// non-fatal
|
|
584
|
+
}
|
|
585
|
+
}
|
|
484
586
|
checks.push({
|
|
485
587
|
name: "agent.registration",
|
|
486
588
|
ok: registration.ok,
|
|
@@ -504,11 +606,17 @@ export async function initAgent(args) {
|
|
|
504
606
|
});
|
|
505
607
|
}
|
|
506
608
|
const allPassed = checks.every((c) => c.ok);
|
|
609
|
+
const identifyPrompt = [
|
|
610
|
+
"Please identify your runtime so the workspace can register richer metadata.",
|
|
611
|
+
"Return ONLY the completed command below with real values (no markdown):",
|
|
612
|
+
`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`,
|
|
613
|
+
].join("\n");
|
|
507
614
|
if (asJson) {
|
|
508
615
|
const output = {
|
|
509
616
|
ok: strict ? allPassed : true,
|
|
510
617
|
passed: allPassed,
|
|
511
618
|
strict,
|
|
619
|
+
status: allPassed ? "needs_agent_identity_followup" : "handshake_completed_with_issues",
|
|
512
620
|
checks,
|
|
513
621
|
summary: {
|
|
514
622
|
total: checks.length,
|
|
@@ -525,7 +633,16 @@ export async function initAgent(args) {
|
|
|
525
633
|
relationships: techRelationships,
|
|
526
634
|
},
|
|
527
635
|
binding,
|
|
636
|
+
followUp: {
|
|
637
|
+
required: allPassed,
|
|
638
|
+
type: "agent_identity",
|
|
639
|
+
detail: "Prompt the coding agent to self-identify and submit via nexarch agent identify",
|
|
640
|
+
prompt: identifyPrompt,
|
|
641
|
+
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`,
|
|
642
|
+
},
|
|
528
643
|
companyId: creds.companyId,
|
|
644
|
+
bootstrapFile: bootstrapFilePath,
|
|
645
|
+
claudeMd: claudeMdResult ?? null,
|
|
529
646
|
};
|
|
530
647
|
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
531
648
|
if (strict && !allPassed)
|
|
@@ -547,6 +664,13 @@ export async function initAgent(args) {
|
|
|
547
664
|
console.log("");
|
|
548
665
|
if (allPassed) {
|
|
549
666
|
console.log("ā
Agent handshake + registration completed.");
|
|
667
|
+
if (bootstrapFilePath) {
|
|
668
|
+
console.log(`\nš Bootstrap instructions written to: ${bootstrapFilePath}`);
|
|
669
|
+
}
|
|
670
|
+
if (claudeMdResult?.status === "injected") {
|
|
671
|
+
console.log(`š Nexarch registration instructions added to: ${claudeMdResult.path}`);
|
|
672
|
+
}
|
|
673
|
+
console.log("\n Tell Claude: \"run nexarch agent prompt and follow the instructions\"");
|
|
550
674
|
}
|
|
551
675
|
else {
|
|
552
676
|
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 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
|
}
|