@agentvalet/register 0.4.11 → 0.4.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/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
+ import { createRequire } from "module";
4
5
  import { Command as Command5 } from "commander";
5
6
  import chalk12 from "chalk";
6
7
 
@@ -191,12 +192,26 @@ async function saveOutput(opts) {
191
192
  }
192
193
 
193
194
  // src/commands/refresh.ts
194
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync, statSync } from "fs";
195
- import { createSign, createPrivateKey } from "crypto";
195
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2, statSync } from "fs";
196
196
  import { dirname as dirname2, resolve as resolve3 } from "path";
197
197
 
198
+ // src/lib/jwt.ts
199
+ import { createSign, createPrivateKey } from "crypto";
200
+ function signJwt(agentId, ownerId, privateKeyPem) {
201
+ const now = Math.floor(Date.now() / 1e3);
202
+ const b64 = (s) => Buffer.from(s).toString("base64url");
203
+ const header = b64(JSON.stringify({ alg: "RS256", typ: "JWT" }));
204
+ const payload = b64(
205
+ JSON.stringify({ agent_id: agentId, owner_id: ownerId, iat: now, exp: now + 300 })
206
+ );
207
+ const signingInput = `${header}.${payload}`;
208
+ const sign = createSign("RSA-SHA256");
209
+ sign.update(signingInput);
210
+ return `${signingInput}.${sign.sign(createPrivateKey(privateKeyPem), "base64url")}`;
211
+ }
212
+
198
213
  // src/lib/skill-writer.ts
199
- import { existsSync, writeFileSync } from "fs";
214
+ import { existsSync, writeFileSync, mkdirSync } from "fs";
200
215
  import { execSync } from "child_process";
201
216
  import path from "path";
202
217
  import chalk2 from "chalk";
@@ -301,6 +316,11 @@ The \`.mcp.json\` in this directory has been configured. Run \`/mcp\` in Claude
301
316
  if (detected.openclaw) {
302
317
  lines.push(`### OpenClaw
303
318
  See \`SKILL.md\` for platform call instructions.`);
319
+ }
320
+ if (detected.factoryDroid) {
321
+ lines.push(`### Factory Droid
322
+ \`~/.factory/mcp.json\` has been updated with the AgentValet MCP server block.
323
+ Type \`@AgentValet\` in Factory Droid to activate governance. Use \`/av-status\` to verify connections.`);
304
324
  }
305
325
  if (lines.length === 0) {
306
326
  lines.push(`Add the MCP server to your IDE manually. See: https://docs.agentvalet.ai/setup`);
@@ -355,6 +375,203 @@ AGENT_PRIVATE_KEY_PATH=${keyPath}
355
375
  console.log(chalk2.green(" \u2713 AGENTVALET.md created \u2014 quick reference for this agent"));
356
376
  console.log(chalk2.dim(" Add to .gitignore if it contains sensitive paths"));
357
377
  }
378
+ function writeFactoryDroidDroid() {
379
+ const droidsDir = path.join(process.cwd(), ".factory", "droids");
380
+ if (!existsSync(droidsDir)) mkdirSync(droidsDir, { recursive: true });
381
+ const dest = path.join(droidsDir, "agentvalet.yaml");
382
+ if (existsSync(dest)) {
383
+ console.log(chalk2.dim(" Factory Droid: .factory/droids/agentvalet.yaml already exists"));
384
+ return;
385
+ }
386
+ const content = `# AgentValet Governance Droid
387
+ # Automatically created by @agentvalet/register
388
+ # Commit this file to your repo
389
+
390
+ name: AgentValet
391
+ description: >
392
+ Routes all platform API calls through AgentValet's credential
393
+ proxy. Enforces per-agent permissions, rate limits, and human
394
+ approval for high-risk operations. Every call is logged.
395
+
396
+ model: inherit
397
+
398
+ mcp_servers:
399
+ - agentvalet
400
+
401
+ autonomy: Low
402
+
403
+ instructions: |
404
+ You are operating under AgentValet credential governance.
405
+
406
+ ## Rules \u2014 read before every platform call
407
+
408
+ 1. NEVER call platform APIs (Slack, Stripe, Gmail, GitHub, etc.) directly.
409
+ Always use the \`use_platform\` MCP tool.
410
+
411
+ 2. Call \`list_platforms\` at the start of any session that involves
412
+ platform calls. Do not assume platforms are available.
413
+
414
+ 3. Call \`authzen_evaluate\` before any destructive operation
415
+ (delete, send, charge, publish). Do not proceed if decision=false.
416
+
417
+ 4. If \`use_platform\` returns \`{ status: "pending_approval" }\`,
418
+ wait for human approval. Do not retry the call.
419
+
420
+ 5. If AgentValet MCP tools are unavailable, stop and tell the user:
421
+ "AgentValet MCP is not connected. Run: npx @agentvalet/register"
422
+ Do not attempt platform calls without governance.
423
+
424
+ ## Making a platform call
425
+
426
+ \`\`\`
427
+ use_platform({
428
+ platform: "slack",
429
+ endpoint: "/api/chat.postMessage",
430
+ method: "POST",
431
+ scope: "slack:write",
432
+ data: { channel: "#general", text: "Hello" }
433
+ })
434
+ \`\`\`
435
+
436
+ ## Scope categories
437
+
438
+ - Read scopes (*.read, *.list) \u2014 lower risk, may auto-approve
439
+ - Write scopes (*.write, *.create) \u2014 check authzen_evaluate first
440
+ - Destructive scopes (*.delete, stripe:charge, mail:send) \u2014 always
441
+ call authzen_evaluate, always inform user before proceeding
442
+
443
+ Dashboard: https://app.agentvalet.ai
444
+ Docs: https://docs.agentvalet.ai
445
+ `;
446
+ writeFileSync(dest, content, "utf8");
447
+ console.log(chalk2.green(" \u2713 Factory Droid: created .factory/droids/agentvalet.yaml"));
448
+ console.log(chalk2.dim(" Commit this file to your repo"));
449
+ }
450
+ function writeFactoryDroidCommand() {
451
+ const commandsDir = path.join(process.cwd(), ".factory", "commands");
452
+ if (!existsSync(commandsDir)) mkdirSync(commandsDir, { recursive: true });
453
+ const statusDest = path.join(commandsDir, "av-status.md");
454
+ if (!existsSync(statusDest)) {
455
+ const statusContent = `# /av-status
456
+
457
+ Check your AgentValet agent status and approved platform connections.
458
+
459
+ ## Instructions for the droid
460
+
461
+ 1. Call \`list_platforms\` using the AgentValet MCP server
462
+ 2. Display the results in a clean table:
463
+ - Platform name
464
+ - Approved scopes
465
+ - Whether any scope requires human approval
466
+ 3. If no platforms are approved yet, tell the user to visit
467
+ https://app.agentvalet.ai/agents to configure permissions
468
+ 4. If AgentValet MCP is not connected, explain how to set it up:
469
+ Run: npx @agentvalet/register
470
+ `;
471
+ writeFileSync(statusDest, statusContent, "utf8");
472
+ console.log(chalk2.green(" \u2713 Factory Droid: created .factory/commands/av-status.md"));
473
+ } else {
474
+ console.log(chalk2.dim(" Factory Droid: .factory/commands/av-status.md already exists"));
475
+ }
476
+ const registerDest = path.join(commandsDir, "av-register.md");
477
+ if (!existsSync(registerDest)) {
478
+ const registerContent = `# /av-register
479
+
480
+ Register or re-register this project with AgentValet.
481
+
482
+ ## Instructions for the droid
483
+
484
+ Tell the user to run the following command in their terminal:
485
+
486
+ \`\`\`bash
487
+ npx @agentvalet/register
488
+ \`\`\`
489
+
490
+ This will:
491
+ 1. Authenticate with GitHub (opens browser)
492
+ 2. Create or find their AgentValet account
493
+ 3. Register this machine as an agent
494
+ 4. Configure Factory Droid MCP integration automatically
495
+
496
+ After running, approve the agent at: https://app.agentvalet.ai/agents
497
+ `;
498
+ writeFileSync(registerDest, registerContent, "utf8");
499
+ console.log(chalk2.green(" \u2713 Factory Droid: created .factory/commands/av-register.md"));
500
+ }
501
+ }
502
+ function writeFactoryDroidReliabilityDroid() {
503
+ const droidsDir = path.join(process.cwd(), ".factory", "droids");
504
+ if (!existsSync(droidsDir)) mkdirSync(droidsDir, { recursive: true });
505
+ const dest = path.join(droidsDir, "agentvalet-reliability.yaml");
506
+ if (existsSync(dest)) return;
507
+ const content = `# AgentValet Reliability Droid
508
+ # Monitors agent call patterns and surfaces governance issues
509
+ # Commit this file to your repo
510
+
511
+ name: AgentValet Reliability
512
+ description: >
513
+ Monitors AI agent behaviour against AgentValet governance rules.
514
+ Surfaces approval-required actions, scope violations, rate limit
515
+ warnings, and audit anomalies directly in Factory Droid.
516
+
517
+ type: reliability
518
+ model: inherit
519
+ mcp_servers:
520
+ - agentvalet
521
+ autonomy: Low
522
+
523
+ triggers:
524
+ - on_agent_action: true
525
+ - on_approval_required: true
526
+ - on_scope_violation: true
527
+
528
+ instructions: |
529
+ You are a governance monitor. You observe what the other droids
530
+ are doing and flag any actions that require human oversight.
531
+
532
+ ## Your responsibilities
533
+
534
+ 1. BEFORE any droid makes a platform API call, check if the scope
535
+ requires approval using \`authzen_evaluate\`:
536
+ - If decision=false: block the call, explain why, suggest alternatives
537
+ - If decision=true: allow, but log it
538
+
539
+ 2. WHEN a droid calls \`use_platform\`, verify:
540
+ - The platform is in the approved list from \`list_platforms\`
541
+ - The scope matches what was approved
542
+ - The call hasn't exceeded rate limits
543
+
544
+ 3. SURFACE approval-required actions clearly:
545
+ "\u26A0 This action requires human approval: [action description]
546
+ Waiting for approval at app.agentvalet.ai/approvals"
547
+
548
+ 4. MONITOR for unusual patterns:
549
+ - Multiple calls to the same destructive scope in quick succession
550
+ - Calls to platforms not in the approved list
551
+ - Call volume spikes beyond normal operation
552
+
553
+ 5. REPORT governance status when asked:
554
+ - Current approved platforms and scopes
555
+ - Pending approvals
556
+ - Recent audit log entries (call list_platforms + agent_status)
557
+
558
+ ## What you do NOT do
559
+
560
+ - You do not make platform API calls yourself
561
+ - You do not approve actions on behalf of the human
562
+ - You do not bypass governance even if another droid requests it
563
+
564
+ ## Escalation
565
+
566
+ If you detect a potential security concern (unexpected scope,
567
+ unusual call pattern), immediately tell the human:
568
+ "\u{1F534} Governance alert: [description]. Recommend reviewing at
569
+ app.agentvalet.ai/audit"
570
+ `;
571
+ writeFileSync(dest, content, "utf8");
572
+ console.log(chalk2.green(" \u2713 Factory Droid: created .factory/droids/agentvalet-reliability.yaml"));
573
+ console.log(chalk2.dim(" Commit this file to your repo"));
574
+ }
358
575
  function ensureMcpServer() {
359
576
  try {
360
577
  execSync("npx --yes @agentvalet/mcp-server --version", {
@@ -500,23 +717,10 @@ var CLAUDE_START = "<!-- agentvalet:start -->";
500
717
  var CLAUDE_END = "<!-- agentvalet:end -->";
501
718
  var HOOK_COMMAND = "npx @agentvalet/register refresh --if-stale --quiet 2>/dev/null || true";
502
719
  var HOOK_MARKER = "agentvalet/register refresh";
503
- function base64url(input) {
504
- return Buffer.from(input).toString("base64url");
505
- }
506
- function signJwt(agentId, ownerId, privateKeyPem) {
507
- const now = Math.floor(Date.now() / 1e3);
508
- const header = base64url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
509
- const payload = base64url(JSON.stringify({ agent_id: agentId, owner_id: ownerId, iat: now, exp: now + 300 }));
510
- const signingInput = `${header}.${payload}`;
511
- const sign = createSign("RSA-SHA256");
512
- sign.update(signingInput);
513
- const signature = sign.sign(createPrivateKey(privateKeyPem), "base64url");
514
- return `${signingInput}.${signature}`;
515
- }
516
720
  function writePermissionsFromEvent(agentId, ownerId, platforms, outputPath = "./.claude/agentvalet-permissions.md") {
517
721
  const content = generatePermissionsFile(agentId, ownerId, platforms);
518
722
  const resolvedPath = resolve3(outputPath);
519
- mkdirSync(dirname2(resolvedPath), { recursive: true });
723
+ mkdirSync2(dirname2(resolvedPath), { recursive: true });
520
724
  writeFileSync2(resolvedPath, content, "utf-8");
521
725
  embedInClaudeMd(content);
522
726
  ensureHook();
@@ -563,7 +767,7 @@ async function runRefresh(options) {
563
767
  const json = await res.json();
564
768
  const platforms = json.data?.platforms ?? [];
565
769
  const content = generatePermissionsFile(agentId, ownerId, platforms);
566
- mkdirSync(dirname2(outputPath), { recursive: true });
770
+ mkdirSync2(dirname2(outputPath), { recursive: true });
567
771
  writeFileSync2(outputPath, content, "utf-8");
568
772
  embedInClaudeMd(content);
569
773
  ensureHook();
@@ -625,7 +829,7 @@ function ensureHook() {
625
829
  perms.allow = [...allow, ...missing];
626
830
  settings.permissions = perms;
627
831
  }
628
- mkdirSync(dirname2(settingsPath), { recursive: true });
832
+ mkdirSync2(dirname2(settingsPath), { recursive: true });
629
833
  writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
630
834
  ensureMcpJson();
631
835
  }
@@ -847,7 +1051,7 @@ function formatEnvBlock(vars) {
847
1051
  }
848
1052
 
849
1053
  // src/config/ide.ts
850
- import { mkdir as mkdir3, writeFile as writeFile4, readFile as readFile2, appendFile as appendFile3 } from "fs/promises";
1054
+ import { mkdir as mkdir3, writeFile as writeFile4, readFile as readFile2, appendFile as appendFile3, copyFile } from "fs/promises";
851
1055
  import { existsSync as existsSync5 } from "fs";
852
1056
  import path4 from "path";
853
1057
  import os from "os";
@@ -978,6 +1182,72 @@ async function writeIdeConfigs(params) {
978
1182
  console.warn(` Warning: could not write .cursor/mcp.json: ${err.message}`);
979
1183
  }
980
1184
  }
1185
+ if (detected.factoryDroid) {
1186
+ const fdResults = await writeFactoryDroidConfig(mcpParams);
1187
+ written.push(...fdResults);
1188
+ }
1189
+ return written;
1190
+ }
1191
+ async function mergeFactoryDroidMcpJson(filePath, key, value) {
1192
+ let existing = {};
1193
+ if (existsSync5(filePath)) {
1194
+ try {
1195
+ const text = await readFile2(filePath, "utf8");
1196
+ existing = JSON.parse(text);
1197
+ } catch {
1198
+ await copyFile(filePath, filePath + ".backup");
1199
+ }
1200
+ }
1201
+ if (existing[key]) return;
1202
+ existing[key] = value;
1203
+ await writeFile4(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
1204
+ }
1205
+ async function updateGitignoreForFactoryDroid() {
1206
+ const gitignorePath = path4.join(process.cwd(), ".gitignore");
1207
+ if (!existsSync5(gitignorePath)) return;
1208
+ const existing = await readFile2(gitignorePath, "utf8");
1209
+ if (existing.includes(".factory/mcp.json")) return;
1210
+ const additions = [
1211
+ "",
1212
+ "# Factory Droid \u2014 private key in MCP config",
1213
+ ".factory/mcp.json",
1214
+ "",
1215
+ "# Factory Droid \u2014 commit these:",
1216
+ "# .factory/droids/",
1217
+ "# .factory/commands/"
1218
+ ].join("\n");
1219
+ await appendFile3(gitignorePath, additions + "\n", "utf8");
1220
+ }
1221
+ async function writeFactoryDroidConfig(params) {
1222
+ const home = os.homedir();
1223
+ const written = [];
1224
+ const entry = buildMcpEntry(params);
1225
+ const globalDir = path4.join(home, ".factory");
1226
+ const projectDir = path4.join(process.cwd(), ".factory");
1227
+ const globalDirExists = existsSync5(globalDir);
1228
+ const projectDirExists = existsSync5(projectDir);
1229
+ if (globalDirExists) {
1230
+ try {
1231
+ await mergeFactoryDroidMcpJson(path4.join(globalDir, "mcp.json"), "agentvalet", entry);
1232
+ written.push("~/.factory/mcp.json");
1233
+ } catch (err) {
1234
+ console.warn(` Warning: could not write ~/.factory/mcp.json: ${err.message}`);
1235
+ }
1236
+ }
1237
+ if (projectDirExists && !globalDirExists) {
1238
+ try {
1239
+ await mergeFactoryDroidMcpJson(path4.join(projectDir, "mcp.json"), "agentvalet", entry);
1240
+ written.push(".factory/mcp.json");
1241
+ } catch (err) {
1242
+ console.warn(` Warning: could not write .factory/mcp.json: ${err.message}`);
1243
+ }
1244
+ }
1245
+ if (written.length > 0) {
1246
+ try {
1247
+ await updateGitignoreForFactoryDroid();
1248
+ } catch {
1249
+ }
1250
+ }
981
1251
  return written;
982
1252
  }
983
1253
 
@@ -1015,7 +1285,8 @@ function detectFrameworks() {
1015
1285
  codex: existsSync6(path5.join(home, ".codex")) || tryBinary("codex"),
1016
1286
  claudeCode: existsSync6(path5.join(home, ".claude")) || tryBinary("claude"),
1017
1287
  cursor: existsSync6(path5.join(process.cwd(), ".cursor")) || cursorInstalled(),
1018
- openclaw: existsSync6(path5.join(home, ".openclaw"))
1288
+ openclaw: existsSync6(path5.join(home, ".openclaw")),
1289
+ factoryDroid: existsSync6(path5.join(home, ".factory")) || existsSync6(path5.join(process.cwd(), ".factory"))
1019
1290
  };
1020
1291
  }
1021
1292
  async function frameworkSetupConsent(detected) {
@@ -1034,6 +1305,11 @@ async function frameworkSetupConsent(detected) {
1034
1305
  if (detected.openclaw) {
1035
1306
  items.push("OpenClaw \u2192 SKILL.md (usage rules)");
1036
1307
  }
1308
+ if (detected.factoryDroid) {
1309
+ items.push("Factory Droid \u2192 add MCP server to ~/.factory/mcp.json");
1310
+ items.push(" \u2192 create .factory/droids/agentvalet.yaml");
1311
+ items.push(" \u2192 create .factory/commands/av-status.md");
1312
+ }
1037
1313
  if (items.length === 0) return false;
1038
1314
  console.log("");
1039
1315
  console.log(chalk6.bold(" Detected agent frameworks"));
@@ -1253,6 +1529,16 @@ async function registerAgentFlow(apiKey, ownerId) {
1253
1529
  }
1254
1530
  const detected = detectFrameworks();
1255
1531
  const shouldSetup = await frameworkSetupConsent(detected);
1532
+ let installReliabilityDroid = false;
1533
+ if (detected.factoryDroid && shouldSetup) {
1534
+ const { reliability } = await prompts3({
1535
+ type: "confirm",
1536
+ name: "reliability",
1537
+ message: "Also install AgentValet Reliability Droid for Factory Droid? (monitors agent governance)",
1538
+ initial: false
1539
+ });
1540
+ installReliabilityDroid = reliability ?? false;
1541
+ }
1256
1542
  if (shouldSetup) {
1257
1543
  console.log("");
1258
1544
  console.log(chalk7.bold(" Configuring agent frameworks..."));
@@ -1282,6 +1568,13 @@ async function registerAgentFlow(apiKey, ownerId) {
1282
1568
  if (detected.openclaw) {
1283
1569
  writeSkillMd();
1284
1570
  }
1571
+ if (detected.factoryDroid) {
1572
+ writeFactoryDroidDroid();
1573
+ writeFactoryDroidCommand();
1574
+ if (installReliabilityDroid) {
1575
+ writeFactoryDroidReliabilityDroid();
1576
+ }
1577
+ }
1285
1578
  writeQuickstartDoc({
1286
1579
  agentId: agent.agent_id,
1287
1580
  ownerId,
@@ -1529,20 +1822,9 @@ import chalk11 from "chalk";
1529
1822
  import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync3, rmSync } from "fs";
1530
1823
  import { resolve as resolve4, join as join3 } from "path";
1531
1824
  import { homedir as homedir2 } from "os";
1532
- import { createSign as createSign2, createPrivateKey as createPrivateKey2 } from "crypto";
1533
1825
  var CLAUDE_START2 = "<!-- agentvalet:start -->";
1534
1826
  var CLAUDE_END2 = "<!-- agentvalet:end -->";
1535
1827
  var HOOK_MARKER2 = "agentvalet/register refresh";
1536
- function signJwt2(agentId, ownerId, privateKeyPem) {
1537
- const now = Math.floor(Date.now() / 1e3);
1538
- const b64 = (s) => Buffer.from(s).toString("base64url");
1539
- const header = b64(JSON.stringify({ alg: "RS256", typ: "JWT" }));
1540
- const payload = b64(JSON.stringify({ agent_id: agentId, owner_id: ownerId, iat: now, exp: now + 300 }));
1541
- const signingInput = `${header}.${payload}`;
1542
- const sign = createSign2("RSA-SHA256");
1543
- sign.update(signingInput);
1544
- return `${signingInput}.${sign.sign(createPrivateKey2(privateKeyPem), "base64url")}`;
1545
- }
1546
1828
  function stripClaudeMd() {
1547
1829
  const path7 = resolve4("CLAUDE.md");
1548
1830
  if (!existsSync7(path7)) return;
@@ -1718,7 +2000,7 @@ function uninstallCommand() {
1718
2000
  }
1719
2001
  console.log(chalk11.dim(`
1720
2002
  Submitting revocation request for ${creds.agentId}...`));
1721
- const jwt = signJwt2(creds.agentId, creds.ownerId, privateKeyPem);
2003
+ const jwt = signJwt(creds.agentId, creds.ownerId, privateKeyPem);
1722
2004
  let revocationToken;
1723
2005
  try {
1724
2006
  const res = await fetch(`${creds.apiUrl}/v1/revoke-request`, {
@@ -1765,11 +2047,13 @@ function uninstallCommand() {
1765
2047
  }
1766
2048
 
1767
2049
  // src/index.ts
2050
+ var require2 = createRequire(import.meta.url);
2051
+ var { version } = require2("../package.json");
1768
2052
  function collectScope(value, previous) {
1769
2053
  return [...previous, value];
1770
2054
  }
1771
2055
  var program = new Command5();
1772
- program.name("agentvalet-register").description("Self-register an AI agent with AgentValet").version("0.4.8").option("--owner <uuid>", "Owner ID (or set AGENTVALET_OWNER env var)").option("--name <name>", "Agent name").option("--description <desc>", "Agent description").option("--scope <platform:scope>", "Requested scope, repeatable", collectScope, []).option("--api-url <url>", "AgentValet API base URL", "https://api.agentvalet.ai").option("--output <path>", "Path to write .env file", "./.env.agentvalet").option("--key-path <path>", "Path to write private key", "./agentvalet.key").option("--json", "Machine-readable JSON output").option("--no-wait", "Register only, do not wait for approval").option("--timeout <seconds>", "Approval wait timeout in seconds", "300").action(async (opts) => {
2056
+ program.name("agentvalet-register").description("Self-register an AI agent with AgentValet").version(version).option("--owner <uuid>", "Owner ID (or set AGENTVALET_OWNER env var)").option("--name <name>", "Agent name").option("--description <desc>", "Agent description").option("--scope <platform:scope>", "Requested scope, repeatable", collectScope, []).option("--api-url <url>", "AgentValet API base URL", "https://api.agentvalet.ai").option("--output <path>", "Path to write .env file", "./.env.agentvalet").option("--key-path <path>", "Path to write private key", "./agentvalet.key").option("--json", "Machine-readable JSON output").option("--no-wait", "Register only, do not wait for approval").option("--timeout <seconds>", "Approval wait timeout in seconds", "300").action(async (opts) => {
1773
2057
  const isJson = !!opts.json;
1774
2058
  let ownerId = opts.owner ?? process.env.AGENTVALET_OWNER ?? "";
1775
2059
  let agentName = opts.name ?? "";
@@ -1940,7 +2224,8 @@ program.command("refresh").description("Refresh approved permissions and update
1940
2224
  }
1941
2225
  });
1942
2226
  program.command("mcp-server").description("Start the AgentValet MCP server (stdio transport)").action(async () => {
1943
- await import("./mcp-server-SBGWYBP7.js");
2227
+ const { mcpServerCommand } = await import("./mcp-server-R3LG4PNM.js");
2228
+ await mcpServerCommand();
1944
2229
  });
1945
2230
  program.addCommand(listCommand());
1946
2231
  program.addCommand(revokeCommand());
@@ -0,0 +1,46 @@
1
+ // src/commands/mcp-server.ts
2
+ import { spawn } from "child_process";
3
+ import { createRequire } from "module";
4
+ function resolveMcpServerBin() {
5
+ try {
6
+ const require2 = createRequire(import.meta.url);
7
+ return require2.resolve("@agentvalet/mcp-server/dist/index.js");
8
+ } catch {
9
+ try {
10
+ const require2 = createRequire(import.meta.url);
11
+ const pkgRoot = require2.resolve("@agentvalet/mcp-server/package.json");
12
+ return pkgRoot.replace("package.json", "dist/index.js");
13
+ } catch {
14
+ throw new Error(
15
+ "@agentvalet/mcp-server is not installed. Run: npx @agentvalet/register"
16
+ );
17
+ }
18
+ }
19
+ }
20
+ async function mcpServerCommand() {
21
+ let binPath;
22
+ try {
23
+ binPath = resolveMcpServerBin();
24
+ } catch (err) {
25
+ process.stderr.write(
26
+ `[agentvalet] ${err instanceof Error ? err.message : String(err)}
27
+ `
28
+ );
29
+ process.exit(1);
30
+ }
31
+ const child = spawn(process.execPath, [binPath], {
32
+ stdio: "inherit",
33
+ env: process.env
34
+ });
35
+ child.on("error", (err) => {
36
+ process.stderr.write(`[agentvalet] Failed to start MCP server: ${err.message}
37
+ `);
38
+ process.exit(1);
39
+ });
40
+ child.on("exit", (code) => {
41
+ process.exit(code ?? 0);
42
+ });
43
+ }
44
+ export {
45
+ mcpServerCommand
46
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentvalet/register",
3
- "version": "0.4.11",
3
+ "version": "0.4.13",
4
4
  "description": "Self-register AI agents with AgentValet",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,14 +10,8 @@
10
10
  "dist",
11
11
  "README.md"
12
12
  ],
13
- "scripts": {
14
- "build": "tsup src/index.ts --format esm --dts --clean --shims",
15
- "dev": "tsx src/index.ts",
16
- "test": "vitest run",
17
- "typecheck": "tsc --noEmit",
18
- "prepublishOnly": "pnpm build"
19
- },
20
13
  "dependencies": {
14
+ "@agentvalet/mcp-server": "*",
21
15
  "@modelcontextprotocol/sdk": "^1.29.0",
22
16
  "@octokit/auth-oauth-device": "^7.0.0",
23
17
  "chalk": "^5.3.0",
@@ -54,5 +48,11 @@
54
48
  "valet",
55
49
  "agentvalet"
56
50
  ],
57
- "license": "MIT"
58
- }
51
+ "license": "MIT",
52
+ "scripts": {
53
+ "build": "tsup src/index.ts --format esm --dts --clean --shims",
54
+ "dev": "tsx src/index.ts",
55
+ "test": "vitest run",
56
+ "typecheck": "tsc --noEmit"
57
+ }
58
+ }
@@ -1,139 +0,0 @@
1
- // src/commands/mcp-server.ts
2
- import { readFileSync } from "fs";
3
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
- import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
6
- import { SignJWT, importPKCS8 } from "jose";
7
- if (!process.env.AGENT_PRIVATE_KEY && process.env.AGENT_PRIVATE_KEY_PATH) {
8
- try {
9
- process.env.AGENT_PRIVATE_KEY = readFileSync(process.env.AGENT_PRIVATE_KEY_PATH, "utf-8").trim();
10
- } catch (err) {
11
- process.stderr.write(`[agentvalet] Cannot read AGENT_PRIVATE_KEY_PATH: ${err instanceof Error ? err.message : err}
12
- `);
13
- process.exit(1);
14
- }
15
- }
16
- var REQUIRED = ["AGENT_PRIVATE_KEY", "AGENT_ID", "OWNER_ID", "PROXY_URL"];
17
- for (const key of REQUIRED) {
18
- if (!process.env[key]) {
19
- process.stderr.write(`[agentvalet] Missing env var: ${key}
20
- `);
21
- process.exit(1);
22
- }
23
- }
24
- var AGENT_PRIVATE_KEY_RAW = process.env.AGENT_PRIVATE_KEY;
25
- var AGENT_ID = process.env.AGENT_ID;
26
- var OWNER_ID = process.env.OWNER_ID;
27
- var PROXY_URL = process.env.PROXY_URL.replace(/\/$/, "");
28
- var _privateKey = null;
29
- var _keyError = null;
30
- async function getPrivateKey() {
31
- if (_keyError) throw new Error(_keyError);
32
- if (_privateKey) return _privateKey;
33
- if (AGENT_PRIVATE_KEY_RAW === "PENDING_FIRST_CALL") return null;
34
- const raw = AGENT_PRIVATE_KEY_RAW.trim();
35
- const pem = raw.startsWith("-----") ? raw.replace(/\\n/g, "\n") : `-----BEGIN PRIVATE KEY-----
36
- ${raw}
37
- -----END PRIVATE KEY-----`;
38
- try {
39
- _privateKey = await importPKCS8(pem, "RS256");
40
- return _privateKey;
41
- } catch (err) {
42
- _keyError = `Invalid private key: ${err instanceof Error ? err.message : err}`;
43
- throw new Error(_keyError);
44
- }
45
- }
46
- async function signJWT() {
47
- const key = await getPrivateKey();
48
- if (!key) throw new Error("Private key not loaded");
49
- return new SignJWT({ agent_id: AGENT_ID, owner_id: OWNER_ID }).setProtectedHeader({ alg: "RS256" }).setIssuedAt().setExpirationTime("60s").sign(key);
50
- }
51
- function fetchWithTimeout(url, init, ms = 15e3) {
52
- const ac = new AbortController();
53
- const t = setTimeout(() => ac.abort(), ms);
54
- return fetch(url, { ...init, signal: ac.signal }).finally(() => clearTimeout(t));
55
- }
56
- async function bearerFetch(url, init) {
57
- const token = await signJWT();
58
- return fetchWithTimeout(url, {
59
- ...init,
60
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...init.headers }
61
- });
62
- }
63
- function errResult(msg) {
64
- return { content: [{ type: "text", text: msg }], isError: true };
65
- }
66
- var ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
67
- var server = new Server(
68
- { name: "agentvalet", version: "1.0.0" },
69
- { capabilities: { tools: {} } }
70
- );
71
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
72
- tools: [
73
- {
74
- name: "list_platforms",
75
- description: "List the platforms and permission scopes this agent has access to.",
76
- inputSchema: { type: "object", properties: {} }
77
- },
78
- {
79
- name: "use_platform",
80
- description: "Call an external platform API through the AgentValet proxy.",
81
- inputSchema: {
82
- type: "object",
83
- properties: {
84
- platform: { type: "string", description: "Platform ID (e.g. slack, github)" },
85
- endpoint: { type: "string", description: "API path on the target platform" },
86
- method: { type: "string", enum: ["GET", "POST", "PUT", "PATCH", "DELETE"] },
87
- scope: { type: "string", description: "Permission scope required" },
88
- data: { type: "object", description: "Optional request body" }
89
- },
90
- required: ["platform", "endpoint", "method", "scope"]
91
- }
92
- }
93
- ]
94
- }));
95
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
96
- const { name, arguments: args } = request.params;
97
- if (name === "list_platforms") {
98
- if (AGENT_PRIVATE_KEY_RAW === "PENDING_FIRST_CALL") {
99
- return errResult("Agent not yet activated \u2014 owner confirmation pending. Retry in 60 seconds.");
100
- }
101
- try {
102
- const res = await bearerFetch(`${PROXY_URL}/v1/agent/permissions`, { method: "GET", headers: {} });
103
- const body = await res.text();
104
- if (!res.ok) return errResult(`Proxy error ${res.status}: ${body}`);
105
- return { content: [{ type: "text", text: body }] };
106
- } catch (e) {
107
- return errResult(`Error: ${e instanceof Error ? e.message : e}`);
108
- }
109
- }
110
- if (name === "use_platform") {
111
- if (AGENT_PRIVATE_KEY_RAW === "PENDING_FIRST_CALL") {
112
- return errResult("Agent not yet activated \u2014 owner confirmation pending. Retry in 60 seconds.");
113
- }
114
- if (!args || typeof args.platform !== "string" || typeof args.endpoint !== "string" || typeof args.method !== "string" || typeof args.scope !== "string") {
115
- return errResult("Missing required args: platform, endpoint, method, scope");
116
- }
117
- if (!ALLOWED_METHODS.includes(args.method)) return errResult(`Invalid method: ${args.method}`);
118
- try {
119
- const res = await bearerFetch(`${PROXY_URL}/v1/actions`, {
120
- method: "POST",
121
- body: JSON.stringify({
122
- platform: args.platform,
123
- endpoint: args.endpoint,
124
- method: args.method,
125
- scope: args.scope,
126
- ...args.data !== void 0 && { data: args.data }
127
- })
128
- });
129
- const body = await res.text();
130
- if (!res.ok) return errResult(`Proxy error ${res.status}: ${body}`);
131
- return { content: [{ type: "text", text: body }] };
132
- } catch (e) {
133
- return errResult(`Error: ${e instanceof Error ? e.message : e}`);
134
- }
135
- }
136
- return errResult(`Unknown tool: ${name}`);
137
- });
138
- var transport = new StdioServerTransport();
139
- await server.connect(transport);