@behalfid/cli 0.1.0

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.
Files changed (43) hide show
  1. package/dist/commands/agents.d.ts +2 -0
  2. package/dist/commands/agents.js +159 -0
  3. package/dist/commands/config.d.ts +2 -0
  4. package/dist/commands/config.js +67 -0
  5. package/dist/commands/health.d.ts +2 -0
  6. package/dist/commands/health.js +25 -0
  7. package/dist/commands/init.d.ts +2 -0
  8. package/dist/commands/init.js +71 -0
  9. package/dist/commands/login.d.ts +2 -0
  10. package/dist/commands/login.js +45 -0
  11. package/dist/commands/logout.d.ts +2 -0
  12. package/dist/commands/logout.js +33 -0
  13. package/dist/commands/logs.d.ts +2 -0
  14. package/dist/commands/logs.js +32 -0
  15. package/dist/commands/mcp.d.ts +2 -0
  16. package/dist/commands/mcp.js +150 -0
  17. package/dist/commands/passport.d.ts +2 -0
  18. package/dist/commands/passport.js +41 -0
  19. package/dist/commands/permissions.d.ts +2 -0
  20. package/dist/commands/permissions.js +74 -0
  21. package/dist/commands/run.d.ts +4 -0
  22. package/dist/commands/run.js +106 -0
  23. package/dist/commands/verify.d.ts +2 -0
  24. package/dist/commands/verify.js +37 -0
  25. package/dist/commands/whoami.d.ts +2 -0
  26. package/dist/commands/whoami.js +49 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +68 -0
  29. package/dist/lib/client.d.ts +13 -0
  30. package/dist/lib/client.js +62 -0
  31. package/dist/lib/config.d.ts +13 -0
  32. package/dist/lib/config.js +48 -0
  33. package/dist/lib/context-generator.d.ts +3 -0
  34. package/dist/lib/context-generator.js +70 -0
  35. package/dist/lib/mcp-server.d.ts +5 -0
  36. package/dist/lib/mcp-server.js +140 -0
  37. package/dist/lib/output.d.ts +10 -0
  38. package/dist/lib/output.js +48 -0
  39. package/dist/lib/passport-cache.d.ts +31 -0
  40. package/dist/lib/passport-cache.js +43 -0
  41. package/dist/lib/prompt.d.ts +3 -0
  42. package/dist/lib/prompt.js +53 -0
  43. package/package.json +33 -0
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function agentsCommand(): Command;
@@ -0,0 +1,159 @@
1
+ import { Command } from "commander";
2
+ import { apiRequest, resolveBaseUrl } from "../lib/client.js";
3
+ import { isJsonMode, printJson, printKv, printSuccess, printTable, runAction } from "../lib/output.js";
4
+ import { patchConfig, readSession } from "../lib/config.js";
5
+ function requireSession() {
6
+ if (!readSession()) {
7
+ throw new Error("This command requires you to be logged in. Run `behalf login`.");
8
+ }
9
+ }
10
+ export function agentsCommand() {
11
+ const cmd = new Command("agents").description("manage agents");
12
+ cmd
13
+ .command("list")
14
+ .description("list all agents in your account")
15
+ .action(runAction(async () => {
16
+ requireSession();
17
+ const baseUrl = resolveBaseUrl();
18
+ const data = await apiRequest("/api/dashboard/agents", { baseUrl });
19
+ if (isJsonMode()) {
20
+ printJson(data);
21
+ return;
22
+ }
23
+ if (!data.agents.length) {
24
+ console.log("No agents yet. Run `behalf agents create --name <name>` to create one.");
25
+ return;
26
+ }
27
+ printTable(data.agents.map(a => ({
28
+ agentId: a.agentId,
29
+ name: a.name,
30
+ status: a.status,
31
+ type: a.agentType ?? "native",
32
+ provider: a.provider ?? "",
33
+ created: a.createdAt.slice(0, 10),
34
+ })));
35
+ }));
36
+ cmd
37
+ .command("create")
38
+ .description("create a new agent")
39
+ .requiredOption("-n, --name <name>", "agent name")
40
+ .option("--type <type>", "agent type: native or connected (default: native)")
41
+ .option("--provider <provider>", "provider (for connected agents): ollie, chatgpt, claude, etc.")
42
+ .option("--description <desc>", "agent description")
43
+ .option("--external-id <id>", "external agent ID (for connected agents)")
44
+ .option("--external-label <label>", "external agent label (for connected agents)")
45
+ .option("--save", "save the new agent ID and API key to ~/.behalf/config.json")
46
+ .action(runAction(async (opts) => {
47
+ requireSession();
48
+ const baseUrl = resolveBaseUrl();
49
+ const body = { name: opts.name };
50
+ if (opts.type)
51
+ body.agentType = opts.type;
52
+ if (opts.provider)
53
+ body.provider = opts.provider;
54
+ if (opts.description)
55
+ body.description = opts.description;
56
+ if (opts.externalId)
57
+ body.externalAgentId = opts.externalId;
58
+ if (opts.externalLabel)
59
+ body.externalAgentLabel = opts.externalLabel;
60
+ const data = await apiRequest("/api/dashboard/agents", { method: "POST", body, baseUrl });
61
+ if (opts.save) {
62
+ patchConfig({ agentId: data.agent.agentId, apiKey: data.apiKey });
63
+ }
64
+ if (isJsonMode()) {
65
+ printJson(data);
66
+ return;
67
+ }
68
+ console.log(`\nAgent created: ${data.agent.agentId}`);
69
+ console.log(`\nAPI Key (shown once — save it now):\n`);
70
+ console.log(` ${data.apiKey}\n`);
71
+ if (opts.save) {
72
+ console.log(`Saved agent ID and API key to config.`);
73
+ }
74
+ else {
75
+ console.log(`To save to config, run:`);
76
+ console.log(` behalf config set agent-id ${data.agent.agentId}`);
77
+ console.log(` behalf config set api-key ${data.apiKey}`);
78
+ }
79
+ }));
80
+ cmd
81
+ .command("show <agentId>")
82
+ .description("show agent details and permissions")
83
+ .action(runAction(async (agentId) => {
84
+ requireSession();
85
+ const baseUrl = resolveBaseUrl();
86
+ const data = await apiRequest(`/api/dashboard/agents/${encodeURIComponent(agentId)}`, { baseUrl });
87
+ if (isJsonMode()) {
88
+ printJson(data);
89
+ return;
90
+ }
91
+ const a = data.agent;
92
+ printKv({
93
+ agentId: a.agentId,
94
+ name: a.name,
95
+ status: a.status,
96
+ type: a.agentType ?? "native",
97
+ provider: a.provider ?? "",
98
+ description: a.description ?? "",
99
+ "last used": a.lastUsedAt ?? "never",
100
+ created: a.createdAt,
101
+ });
102
+ if (Array.isArray(data.permissions) && data.permissions.length) {
103
+ console.log("\nPermissions:");
104
+ printTable(data.permissions.map(p => ({
105
+ permissionId: String(p.permissionId ?? ""),
106
+ action: String(p.action ?? ""),
107
+ resource: String(p.resource ?? ""),
108
+ status: String(p.status ?? ""),
109
+ expires: p.expiresAt ? String(p.expiresAt).slice(0, 10) : "never",
110
+ })));
111
+ }
112
+ else {
113
+ console.log("\nNo permissions.");
114
+ }
115
+ }));
116
+ cmd
117
+ .command("disable <agentId>")
118
+ .description("disable an agent")
119
+ .action(runAction(async (agentId) => {
120
+ requireSession();
121
+ const baseUrl = resolveBaseUrl();
122
+ await apiRequest(`/api/dashboard/agents/${encodeURIComponent(agentId)}/disable`, { method: "POST", baseUrl });
123
+ if (isJsonMode())
124
+ printJson({ disabled: true, agentId });
125
+ else
126
+ printSuccess(`Agent ${agentId} disabled.`);
127
+ }));
128
+ cmd
129
+ .command("enable <agentId>")
130
+ .description("enable a disabled agent")
131
+ .action(runAction(async (agentId) => {
132
+ requireSession();
133
+ const baseUrl = resolveBaseUrl();
134
+ await apiRequest(`/api/dashboard/agents/${encodeURIComponent(agentId)}/enable`, { method: "POST", baseUrl });
135
+ if (isJsonMode())
136
+ printJson({ enabled: true, agentId });
137
+ else
138
+ printSuccess(`Agent ${agentId} enabled.`);
139
+ }));
140
+ cmd
141
+ .command("rotate-key <agentId>")
142
+ .description("rotate an agent's API key")
143
+ .option("-k, --api-key <key>", "current agent API key (overrides config)")
144
+ .action(runAction(async (agentId, opts) => {
145
+ const baseUrl = resolveBaseUrl();
146
+ const session = readSession();
147
+ const data = await apiRequest(session
148
+ ? `/api/dashboard/agents/${encodeURIComponent(agentId)}/rotate-key`
149
+ : `/api/agents/${encodeURIComponent(agentId)}/rotate-key`, { method: "POST", apiKey: opts.apiKey, baseUrl });
150
+ if (isJsonMode()) {
151
+ printJson(data);
152
+ return;
153
+ }
154
+ console.log(`\nNew API Key for ${agentId} (shown once — save it now):\n`);
155
+ console.log(` ${data.apiKey}\n`);
156
+ console.log(`Run: behalf config set api-key ${data.apiKey}`);
157
+ }));
158
+ return cmd;
159
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function configCommand(): Command;
@@ -0,0 +1,67 @@
1
+ import { Command } from "commander";
2
+ import { CONFIG_FILE_PATH, patchConfig, readConfig, writeConfig } from "../lib/config.js";
3
+ import { isJsonMode, printJson, printTable, runAction } from "../lib/output.js";
4
+ const VALID_KEYS = ["api-key", "agent-id", "base-url"];
5
+ const KEY_MAP = {
6
+ "api-key": "apiKey",
7
+ "agent-id": "agentId",
8
+ "base-url": "baseUrl",
9
+ };
10
+ function assertKey(key) {
11
+ if (!VALID_KEYS.includes(key)) {
12
+ throw new Error(`Unknown key "${key}". Valid keys: ${VALID_KEYS.join(", ")}`);
13
+ }
14
+ return key;
15
+ }
16
+ export function configCommand() {
17
+ const cmd = new Command("config").description("manage local CLI config");
18
+ cmd
19
+ .command("set <key> <value>")
20
+ .description("set a config value (keys: api-key, agent-id, base-url)")
21
+ .action(runAction(async (key, value) => {
22
+ const k = assertKey(key);
23
+ patchConfig({ [KEY_MAP[k]]: value });
24
+ if (!isJsonMode())
25
+ console.log(`Set ${key}.`);
26
+ else
27
+ printJson({ key, value });
28
+ }));
29
+ cmd
30
+ .command("get <key>")
31
+ .description("get a config value")
32
+ .action(runAction(async (key) => {
33
+ const k = assertKey(key);
34
+ const val = readConfig()[KEY_MAP[k]];
35
+ if (isJsonMode())
36
+ printJson({ [key]: val ?? null });
37
+ else
38
+ console.log(val ?? "(not set)");
39
+ }));
40
+ cmd
41
+ .command("list")
42
+ .description("list all config values")
43
+ .action(runAction(async () => {
44
+ const cfg = readConfig();
45
+ const rows = {
46
+ "api-key": cfg.apiKey ? `${cfg.apiKey.slice(0, 15)}…` : "(not set)",
47
+ "agent-id": cfg.agentId ?? "(not set)",
48
+ "base-url": cfg.baseUrl ?? "(not set)",
49
+ "config file": CONFIG_FILE_PATH,
50
+ };
51
+ if (isJsonMode())
52
+ printJson(rows);
53
+ else
54
+ printTable([rows]);
55
+ }));
56
+ cmd
57
+ .command("clear")
58
+ .description("clear all config values")
59
+ .action(runAction(async () => {
60
+ writeConfig({});
61
+ if (!isJsonMode())
62
+ console.log("Config cleared.");
63
+ else
64
+ printJson({ cleared: true });
65
+ }));
66
+ return cmd;
67
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function healthCommand(): Command;
@@ -0,0 +1,25 @@
1
+ import { Command } from "commander";
2
+ import { apiRequest, resolveBaseUrl } from "../lib/client.js";
3
+ import { isJsonMode, printJson, printKv, runAction } from "../lib/output.js";
4
+ export function healthCommand() {
5
+ return new Command("health")
6
+ .description("check API health")
7
+ .option("--db", "also check database connectivity (requires setup token or console auth)")
8
+ .action(runAction(async (opts) => {
9
+ const baseUrl = resolveBaseUrl();
10
+ const data = await apiRequest("/api/health", { baseUrl });
11
+ if (opts.db) {
12
+ const dbData = await apiRequest("/api/health/db", { baseUrl });
13
+ const merged = { ...data, ...dbData };
14
+ if (isJsonMode())
15
+ printJson(merged);
16
+ else
17
+ printKv(merged);
18
+ return;
19
+ }
20
+ if (isJsonMode())
21
+ printJson(data);
22
+ else
23
+ printKv(data);
24
+ }));
25
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function initCommand(): Command;
@@ -0,0 +1,71 @@
1
+ import { Command } from "commander";
2
+ import { patchConfig, readConfig, readSession, writeSession } from "../lib/config.js";
3
+ import { DEFAULT_BASE_URL, originOf } from "../lib/client.js";
4
+ import { ask, askPassword } from "../lib/prompt.js";
5
+ import { runAction } from "../lib/output.js";
6
+ export function initCommand() {
7
+ return new Command("init")
8
+ .description("interactive setup wizard")
9
+ .action(runAction(async function () {
10
+ const config = readConfig();
11
+ const hasSession = !!readSession();
12
+ console.log("\nWelcome to BehalfID CLI\n");
13
+ if (config.baseUrl || config.apiKey || hasSession) {
14
+ console.log("Current config:");
15
+ if (config.baseUrl)
16
+ console.log(` base url: ${config.baseUrl}`);
17
+ if (config.apiKey)
18
+ console.log(` api key: ${config.apiKey.slice(0, 15)}…`);
19
+ if (hasSession)
20
+ console.log(` session: active`);
21
+ console.log("");
22
+ }
23
+ // Base URL
24
+ const baseUrlInput = await ask("Base URL", config.baseUrl ?? DEFAULT_BASE_URL);
25
+ const baseUrl = baseUrlInput.replace(/\/+$/, "");
26
+ if (baseUrl !== config.baseUrl)
27
+ patchConfig({ baseUrl });
28
+ // Auth
29
+ console.log("\nHow would you like to authenticate?");
30
+ console.log(" 1. Log in with email and password");
31
+ console.log(" 2. Enter an agent API key");
32
+ console.log(" 3. Skip");
33
+ const choice = await ask("Choice", "1");
34
+ if (choice === "1") {
35
+ const email = await ask("Email");
36
+ const password = await askPassword("Password");
37
+ const response = await fetch(`${baseUrl}/api/auth/login`, {
38
+ method: "POST",
39
+ headers: {
40
+ "Content-Type": "application/json",
41
+ Accept: "application/json",
42
+ Origin: originOf(baseUrl),
43
+ },
44
+ body: JSON.stringify({ email, password }),
45
+ });
46
+ const setCookie = response.headers.get("set-cookie");
47
+ const match = setCookie?.match(/behalfid_developer=([^;]+)/);
48
+ const sessionCookie = match ? `behalfid_developer=${match[1]}` : null;
49
+ const body = (await response.json().catch(() => null));
50
+ if (!response.ok || !sessionCookie) {
51
+ const msg = typeof body === "object" &&
52
+ body !== null &&
53
+ "error" in body &&
54
+ typeof body.error === "string"
55
+ ? body.error
56
+ : "Login failed.";
57
+ throw new Error(msg);
58
+ }
59
+ writeSession(sessionCookie);
60
+ console.log(`\nLogged in as ${body?.user.email}.`);
61
+ }
62
+ else if (choice === "2") {
63
+ const apiKey = await ask("Agent API key (bhf_sk_...)");
64
+ if (apiKey) {
65
+ patchConfig({ apiKey });
66
+ console.log("API key saved.");
67
+ }
68
+ }
69
+ console.log("\nSetup complete. Run `behalf --help` to see available commands.\n");
70
+ }));
71
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function loginCommand(): Command;
@@ -0,0 +1,45 @@
1
+ import { Command } from "commander";
2
+ import { readSession, writeSession } from "../lib/config.js";
3
+ import { originOf, resolveBaseUrl } from "../lib/client.js";
4
+ import { isJsonMode, printJson, printSuccess, runAction } from "../lib/output.js";
5
+ import { ask, askPassword, confirm } from "../lib/prompt.js";
6
+ export function loginCommand() {
7
+ return new Command("login")
8
+ .description("log in to your BehalfID developer account")
9
+ .option("-e, --email <email>", "developer account email")
10
+ .option("-p, --password <password>", "developer account password")
11
+ .action(runAction(async (opts) => {
12
+ if (readSession()) {
13
+ const ok = await confirm("You are already logged in. Log in again?");
14
+ if (!ok)
15
+ return;
16
+ }
17
+ const baseUrl = resolveBaseUrl();
18
+ const email = opts.email ?? (await ask("Email"));
19
+ const password = opts.password ?? (await askPassword("Password"));
20
+ if (!email || !password)
21
+ throw new Error("Email and password are required.");
22
+ const response = await fetch(`${baseUrl}/api/auth/login`, {
23
+ method: "POST",
24
+ headers: { "Content-Type": "application/json", Accept: "application/json", Origin: originOf(baseUrl) },
25
+ body: JSON.stringify({ email, password }),
26
+ });
27
+ const setCookie = response.headers.get("set-cookie");
28
+ const match = setCookie?.match(/behalfid_developer=([^;]+)/);
29
+ const sessionCookie = match ? `behalfid_developer=${match[1]}` : null;
30
+ const body = (await response.json().catch(() => null));
31
+ if (!response.ok) {
32
+ const msg = typeof body === "object" && body !== null && "error" in body && typeof body.error === "string"
33
+ ? body.error
34
+ : `Login failed with status ${response.status}.`;
35
+ throw new Error(msg);
36
+ }
37
+ if (!sessionCookie)
38
+ throw new Error("Login succeeded but no session cookie was returned.");
39
+ writeSession(sessionCookie);
40
+ if (isJsonMode())
41
+ printJson({ loggedIn: true, email: body?.user.email });
42
+ else
43
+ printSuccess(`Logged in as ${body?.user.email}.`);
44
+ }));
45
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function logoutCommand(): Command;
@@ -0,0 +1,33 @@
1
+ import { Command } from "commander";
2
+ import { clearSession, readSession } from "../lib/config.js";
3
+ import { apiRequest, resolveBaseUrl } from "../lib/client.js";
4
+ import { isJsonMode, printJson, printSuccess, runAction } from "../lib/output.js";
5
+ export function logoutCommand() {
6
+ return new Command("logout")
7
+ .description("log out of your BehalfID developer account")
8
+ .action(runAction(async function () {
9
+ const session = readSession();
10
+ if (!session) {
11
+ if (!isJsonMode())
12
+ console.log("Not logged in.");
13
+ else
14
+ printJson({ loggedOut: false, reason: "not logged in" });
15
+ return;
16
+ }
17
+ const baseUrl = resolveBaseUrl();
18
+ try {
19
+ await apiRequest("/api/auth/logout", {
20
+ method: "POST",
21
+ baseUrl,
22
+ });
23
+ }
24
+ catch {
25
+ // Server-side logout is best-effort; always clear local session.
26
+ }
27
+ clearSession();
28
+ if (isJsonMode())
29
+ printJson({ loggedOut: true });
30
+ else
31
+ printSuccess("Logged out.");
32
+ }));
33
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function logsCommand(): Command;
@@ -0,0 +1,32 @@
1
+ import { Command } from "commander";
2
+ import { apiRequest, resolveApiKey, resolveBaseUrl } from "../lib/client.js";
3
+ import { isJsonMode, printJson, printTable, runAction } from "../lib/output.js";
4
+ export function logsCommand() {
5
+ return new Command("logs")
6
+ .description("show recent verification logs for an agent")
7
+ .argument("<agentId>", "agent ID")
8
+ .option("-k, --api-key <key>", "agent API key (overrides config)")
9
+ .action(runAction(async (agentId, opts) => {
10
+ const apiKey = opts.apiKey ?? resolveApiKey();
11
+ if (!apiKey)
12
+ throw new Error("An agent API key is required. Set it with `behalf config set api-key <key>` or pass --api-key.");
13
+ const baseUrl = resolveBaseUrl();
14
+ const data = await apiRequest(`/api/logs/${encodeURIComponent(agentId)}`, { apiKey, baseUrl });
15
+ if (isJsonMode()) {
16
+ printJson(data);
17
+ return;
18
+ }
19
+ if (!Array.isArray(data) || data.length === 0) {
20
+ console.log("No logs yet.");
21
+ return;
22
+ }
23
+ printTable(data.map(l => ({
24
+ requestId: l.requestId,
25
+ action: l.action,
26
+ vendor: l.vendor ?? "",
27
+ allowed: l.allowed ? "yes" : "no",
28
+ risk: l.risk,
29
+ when: l.createdAt.replace("T", " ").slice(0, 19),
30
+ })));
31
+ }));
32
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function mcpCommand(): Command;
@@ -0,0 +1,150 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { Command } from "commander";
4
+ import { resolveApiKey, resolveBaseUrl } from "../lib/client.js";
5
+ import { readConfig } from "../lib/config.js";
6
+ import { generateContextMd, generateMcpJson } from "../lib/context-generator.js";
7
+ import { fetchAndCacheDetail, readCachedDetail } from "../lib/passport-cache.js";
8
+ import { isJsonMode, printJson, printKv, runAction } from "../lib/output.js";
9
+ import { confirm } from "../lib/prompt.js";
10
+ function readJsonFile(path) {
11
+ if (!existsSync(path))
12
+ return null;
13
+ try {
14
+ return JSON.parse(readFileSync(path, "utf-8"));
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ export function mcpCommand() {
21
+ const cmd = new Command("mcp").description("BehalfID MCP server — real-time agent enforcement");
22
+ cmd
23
+ .command("start")
24
+ .description("start the BehalfID MCP server on stdio (used by .mcp.json)")
25
+ .action(runAction(async () => {
26
+ const config = readConfig();
27
+ const agentId = config.agentId ?? process.env.BEHALFID_AGENT_ID;
28
+ const apiKey = resolveApiKey();
29
+ const baseUrl = resolveBaseUrl();
30
+ if (!agentId) {
31
+ throw new Error("Agent ID not configured. Run `behalf config set agent-id <agentId>` first.");
32
+ }
33
+ if (!apiKey) {
34
+ throw new Error("API key not configured. Run `behalf config set api-key <bhf_sk_xxx>` first.");
35
+ }
36
+ // Start the MCP server — this takes over the process
37
+ const { startMcpServer } = await import("../lib/mcp-server.js");
38
+ await startMcpServer({ agentId, apiKey, baseUrl });
39
+ }));
40
+ cmd
41
+ .command("init")
42
+ .description("set up BehalfID enforcement in the current directory")
43
+ .option("--refresh", "force-refresh the permissions cache from the server")
44
+ .option("--no-inject", "skip patching CLAUDE.md / AGENTS.md")
45
+ .option("--dry-run", "show what would be written without writing anything")
46
+ .action(runAction(async (opts) => {
47
+ const config = readConfig();
48
+ const agentId = config.agentId ?? process.env.BEHALFID_AGENT_ID;
49
+ const baseUrl = resolveBaseUrl();
50
+ if (!agentId) {
51
+ throw new Error("Agent ID not configured. Run `behalf config set agent-id <agentId>` first.");
52
+ }
53
+ if (!isJsonMode())
54
+ console.log(`Initializing BehalfID enforcement for agent ${agentId}…\n`);
55
+ // Fetch permissions
56
+ let detail = opts.refresh ? null : readCachedDetail(agentId);
57
+ if (!detail) {
58
+ if (!isJsonMode())
59
+ process.stdout.write("Fetching permissions from server… ");
60
+ detail = await fetchAndCacheDetail(agentId, baseUrl, opts.refresh ?? false);
61
+ if (!isJsonMode())
62
+ console.log("done.");
63
+ }
64
+ else {
65
+ if (!isJsonMode())
66
+ console.log("Using cached permissions (run with --refresh to update).");
67
+ }
68
+ const cwd = process.cwd();
69
+ const behalfDir = join(cwd, ".behalf");
70
+ const contextFile = join(behalfDir, "context.md");
71
+ const mcpJsonFile = join(cwd, ".mcp.json");
72
+ const contextMd = generateContextMd(detail);
73
+ const existingMcp = readJsonFile(mcpJsonFile);
74
+ const mcpJson = generateMcpJson(existingMcp ?? undefined);
75
+ if (opts.dryRun) {
76
+ if (isJsonMode()) {
77
+ printJson({ wouldWrite: [contextFile, mcpJsonFile] });
78
+ }
79
+ else {
80
+ console.log(`Would write:\n ${contextFile}\n ${mcpJsonFile}`);
81
+ console.log("\n--- .behalf/context.md ---\n");
82
+ console.log(contextMd);
83
+ }
84
+ return;
85
+ }
86
+ // Write .behalf/context.md
87
+ if (!existsSync(behalfDir))
88
+ mkdirSync(behalfDir, { recursive: true });
89
+ writeFileSync(contextFile, contextMd);
90
+ // Write / merge .mcp.json
91
+ writeFileSync(mcpJsonFile, mcpJson);
92
+ // Inject into CLAUDE.md if present or if user confirms
93
+ if (opts.inject !== false) {
94
+ const claudeMdPath = join(cwd, "CLAUDE.md");
95
+ const agentsMdPath = join(cwd, "AGENTS.md");
96
+ for (const [label, path] of [["CLAUDE.md", claudeMdPath], ["AGENTS.md", agentsMdPath]]) {
97
+ if (!existsSync(path))
98
+ continue;
99
+ const content = readFileSync(path, "utf-8");
100
+ const include = "@.behalf/context.md";
101
+ if (content.includes(include))
102
+ continue;
103
+ const ok = opts.dryRun
104
+ ? false
105
+ : await confirm(`Add \`${include}\` to ${label}?`, true);
106
+ if (ok) {
107
+ writeFileSync(path, content + `\n\n${include}\n`);
108
+ if (!isJsonMode())
109
+ console.log(` Patched ${label}.`);
110
+ }
111
+ }
112
+ }
113
+ if (isJsonMode()) {
114
+ printJson({ initialized: true, agentId, contextFile, mcpJsonFile });
115
+ return;
116
+ }
117
+ console.log("");
118
+ printKv({
119
+ "context file": resolve(contextFile),
120
+ "mcp config": resolve(mcpJsonFile),
121
+ permissions: `${detail.permissions.filter(p => p.status === "active").length} active`,
122
+ });
123
+ console.log(`\nBehalfID enforcement is active. Launch your AI tool normally — or run \`behalf claude\` to start Claude Code.\n`);
124
+ }));
125
+ cmd
126
+ .command("status")
127
+ .description("show current MCP config and cached permissions for this directory")
128
+ .action(runAction(async () => {
129
+ const config = readConfig();
130
+ const agentId = config.agentId ?? process.env.BEHALFID_AGENT_ID;
131
+ const cwd = process.cwd();
132
+ const mcpJsonPath = join(cwd, ".mcp.json");
133
+ const contextPath = join(cwd, ".behalf/context.md");
134
+ const mcpJson = readJsonFile(mcpJsonPath);
135
+ const hasMcp = !!mcpJson?.mcpServers?.behalfid;
136
+ const hasContext = existsSync(contextPath);
137
+ const cached = agentId ? readCachedDetail(agentId) : null;
138
+ if (isJsonMode()) {
139
+ printJson({ agentId, hasMcp, hasContext, cachedPermissions: cached?.permissions?.length ?? 0 });
140
+ return;
141
+ }
142
+ printKv({
143
+ "agent id": agentId ?? "(not set)",
144
+ ".mcp.json": hasMcp ? "✓ behalfid server configured" : "✗ not configured",
145
+ "context file": hasContext ? "✓ present" : "✗ missing",
146
+ "cached permissions": cached ? `${cached.permissions.filter(p => p.status === "active").length} active` : "none (run mcp init)",
147
+ });
148
+ }));
149
+ return cmd;
150
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function passportCommand(): Command;
@@ -0,0 +1,41 @@
1
+ import { Command } from "commander";
2
+ import { apiRequest, resolveApiKey, resolveBaseUrl } from "../lib/client.js";
3
+ import { isJsonMode, printJson, printKv, printTable, runAction } from "../lib/output.js";
4
+ export function passportCommand() {
5
+ return new Command("passport")
6
+ .description("show the public passport (active permissions) for an agent")
7
+ .argument("<agentId>", "agent ID")
8
+ .option("-k, --api-key <key>", "agent API key or passport token (overrides config)")
9
+ .action(runAction(async (agentId, opts) => {
10
+ const apiKey = opts.apiKey ?? resolveApiKey();
11
+ if (!apiKey)
12
+ throw new Error("An agent API key or passport token is required. Pass --api-key or set it with `behalf config set api-key <key>`.");
13
+ const baseUrl = resolveBaseUrl();
14
+ const data = await apiRequest(`/api/passport/${encodeURIComponent(agentId)}`, { apiKey, baseUrl });
15
+ if (isJsonMode()) {
16
+ printJson(data);
17
+ return;
18
+ }
19
+ printKv({
20
+ agentId: data.agent.agentId,
21
+ name: data.agent.name,
22
+ type: data.agent.agentType ?? "native",
23
+ provider: data.agent.provider ?? "",
24
+ description: data.agent.description ?? "",
25
+ version: data.passportVersion,
26
+ });
27
+ if (data.permissions.length === 0) {
28
+ console.log("\nNo active permissions.");
29
+ }
30
+ else {
31
+ console.log("\nPermissions:");
32
+ printTable(data.permissions.map(p => ({
33
+ action: p.action,
34
+ resource: p.resource ?? "",
35
+ scope: p.scope ?? "",
36
+ status: p.status,
37
+ expires: p.expiresAt ? p.expiresAt.slice(0, 10) : "never",
38
+ })));
39
+ }
40
+ }));
41
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function permissionsCommand(): Command;