@agentvalet/register 0.4.12 → 0.4.14

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.
@@ -0,0 +1,32 @@
1
+ // src/config/store.ts
2
+ import Conf from "conf";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ var AGENT_DIR = join(homedir(), ".agentvalet");
6
+ var AGENT_KEY_PATH = join(AGENT_DIR, "agent.key");
7
+ var store = new Conf({
8
+ projectName: "agentvalet",
9
+ cwd: AGENT_DIR,
10
+ configName: "config"
11
+ });
12
+ function getStoredConfig() {
13
+ return store.store;
14
+ }
15
+ function saveConfig(config) {
16
+ store.set(config);
17
+ }
18
+ function clearConfig() {
19
+ store.clear();
20
+ }
21
+ function isAuthenticated() {
22
+ return !!store.get("apiKey");
23
+ }
24
+
25
+ export {
26
+ AGENT_DIR,
27
+ AGENT_KEY_PATH,
28
+ getStoredConfig,
29
+ saveConfig,
30
+ clearConfig,
31
+ isAuthenticated
32
+ };
package/dist/index.js CHANGED
@@ -1,4 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ AGENT_DIR,
4
+ AGENT_KEY_PATH,
5
+ clearConfig,
6
+ getStoredConfig,
7
+ isAuthenticated,
8
+ saveConfig
9
+ } from "./chunk-2EFPJNYK.js";
2
10
 
3
11
  // src/index.ts
4
12
  import { createRequire } from "module";
@@ -167,33 +175,29 @@ function parseEvent(data) {
167
175
  return null;
168
176
  }
169
177
  function sleep(ms) {
170
- return new Promise((resolve5) => setTimeout(resolve5, ms));
178
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
171
179
  }
172
180
 
173
181
  // src/save.ts
174
182
  import { writeFile, mkdir } from "fs/promises";
175
- import { dirname, resolve } from "path";
176
183
  async function saveOutput(opts) {
177
- const keyPath = resolve(opts.keyFilePath);
178
- const envPath = resolve(opts.envFilePath);
179
- await mkdir(dirname(keyPath), { recursive: true });
180
- await mkdir(dirname(envPath), { recursive: true });
181
- await writeFile(keyPath, opts.privateKey, { mode: 384 });
182
- const envContent = [
183
- `# AgentValet \u2014 generated by agentvalet-register`,
184
- `AGENTVALET_AGENT_ID=${opts.agentId}`,
185
- `AGENTVALET_OWNER_ID=${opts.ownerId}`,
186
- `AGENTVALET_API_URL=${opts.apiUrl}`,
187
- `AGENTVALET_PRIVATE_KEY_PATH=${keyPath}`,
188
- ""
189
- ].join("\n");
190
- await writeFile(envPath, envContent, "utf-8");
191
- return { keyPath, envPath };
184
+ await mkdir(AGENT_DIR, { recursive: true });
185
+ await writeFile(AGENT_KEY_PATH, opts.privateKey, { mode: 384 });
186
+ saveConfig({
187
+ agentId: opts.agentId,
188
+ ownerId: opts.ownerId,
189
+ apiUrl: opts.apiUrl,
190
+ keyPath: AGENT_KEY_PATH,
191
+ // keep legacy fields in sync
192
+ lastAgentId: opts.agentId,
193
+ lastKeyPath: AGENT_KEY_PATH
194
+ });
195
+ return { keyPath: AGENT_KEY_PATH };
192
196
  }
193
197
 
194
198
  // src/commands/refresh.ts
195
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync, statSync } from "fs";
196
- import { dirname as dirname2, resolve as resolve3 } from "path";
199
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2, statSync } from "fs";
200
+ import { dirname, resolve as resolve2 } from "path";
197
201
 
198
202
  // src/lib/jwt.ts
199
203
  import { createSign, createPrivateKey } from "crypto";
@@ -211,7 +215,7 @@ function signJwt(agentId, ownerId, privateKeyPem) {
211
215
  }
212
216
 
213
217
  // src/lib/skill-writer.ts
214
- import { existsSync, writeFileSync } from "fs";
218
+ import { existsSync, writeFileSync, mkdirSync } from "fs";
215
219
  import { execSync } from "child_process";
216
220
  import path from "path";
217
221
  import chalk2 from "chalk";
@@ -316,6 +320,11 @@ The \`.mcp.json\` in this directory has been configured. Run \`/mcp\` in Claude
316
320
  if (detected.openclaw) {
317
321
  lines.push(`### OpenClaw
318
322
  See \`SKILL.md\` for platform call instructions.`);
323
+ }
324
+ if (detected.factoryDroid) {
325
+ lines.push(`### Factory Droid
326
+ \`~/.factory/mcp.json\` has been updated with the AgentValet MCP server block.
327
+ Type \`@AgentValet\` in Factory Droid to activate governance. Use \`/av-status\` to verify connections.`);
319
328
  }
320
329
  if (lines.length === 0) {
321
330
  lines.push(`Add the MCP server to your IDE manually. See: https://docs.agentvalet.ai/setup`);
@@ -370,6 +379,203 @@ AGENT_PRIVATE_KEY_PATH=${keyPath}
370
379
  console.log(chalk2.green(" \u2713 AGENTVALET.md created \u2014 quick reference for this agent"));
371
380
  console.log(chalk2.dim(" Add to .gitignore if it contains sensitive paths"));
372
381
  }
382
+ function writeFactoryDroidDroid() {
383
+ const droidsDir = path.join(process.cwd(), ".factory", "droids");
384
+ if (!existsSync(droidsDir)) mkdirSync(droidsDir, { recursive: true });
385
+ const dest = path.join(droidsDir, "agentvalet.yaml");
386
+ if (existsSync(dest)) {
387
+ console.log(chalk2.dim(" Factory Droid: .factory/droids/agentvalet.yaml already exists"));
388
+ return;
389
+ }
390
+ const content = `# AgentValet Governance Droid
391
+ # Automatically created by @agentvalet/register
392
+ # Commit this file to your repo
393
+
394
+ name: AgentValet
395
+ description: >
396
+ Routes all platform API calls through AgentValet's credential
397
+ proxy. Enforces per-agent permissions, rate limits, and human
398
+ approval for high-risk operations. Every call is logged.
399
+
400
+ model: inherit
401
+
402
+ mcp_servers:
403
+ - agentvalet
404
+
405
+ autonomy: Low
406
+
407
+ instructions: |
408
+ You are operating under AgentValet credential governance.
409
+
410
+ ## Rules \u2014 read before every platform call
411
+
412
+ 1. NEVER call platform APIs (Slack, Stripe, Gmail, GitHub, etc.) directly.
413
+ Always use the \`use_platform\` MCP tool.
414
+
415
+ 2. Call \`list_platforms\` at the start of any session that involves
416
+ platform calls. Do not assume platforms are available.
417
+
418
+ 3. Call \`authzen_evaluate\` before any destructive operation
419
+ (delete, send, charge, publish). Do not proceed if decision=false.
420
+
421
+ 4. If \`use_platform\` returns \`{ status: "pending_approval" }\`,
422
+ wait for human approval. Do not retry the call.
423
+
424
+ 5. If AgentValet MCP tools are unavailable, stop and tell the user:
425
+ "AgentValet MCP is not connected. Run: npx @agentvalet/register"
426
+ Do not attempt platform calls without governance.
427
+
428
+ ## Making a platform call
429
+
430
+ \`\`\`
431
+ use_platform({
432
+ platform: "slack",
433
+ endpoint: "/api/chat.postMessage",
434
+ method: "POST",
435
+ scope: "slack:write",
436
+ data: { channel: "#general", text: "Hello" }
437
+ })
438
+ \`\`\`
439
+
440
+ ## Scope categories
441
+
442
+ - Read scopes (*.read, *.list) \u2014 lower risk, may auto-approve
443
+ - Write scopes (*.write, *.create) \u2014 check authzen_evaluate first
444
+ - Destructive scopes (*.delete, stripe:charge, mail:send) \u2014 always
445
+ call authzen_evaluate, always inform user before proceeding
446
+
447
+ Dashboard: https://app.agentvalet.ai
448
+ Docs: https://docs.agentvalet.ai
449
+ `;
450
+ writeFileSync(dest, content, "utf8");
451
+ console.log(chalk2.green(" \u2713 Factory Droid: created .factory/droids/agentvalet.yaml"));
452
+ console.log(chalk2.dim(" Commit this file to your repo"));
453
+ }
454
+ function writeFactoryDroidCommand() {
455
+ const commandsDir = path.join(process.cwd(), ".factory", "commands");
456
+ if (!existsSync(commandsDir)) mkdirSync(commandsDir, { recursive: true });
457
+ const statusDest = path.join(commandsDir, "av-status.md");
458
+ if (!existsSync(statusDest)) {
459
+ const statusContent = `# /av-status
460
+
461
+ Check your AgentValet agent status and approved platform connections.
462
+
463
+ ## Instructions for the droid
464
+
465
+ 1. Call \`list_platforms\` using the AgentValet MCP server
466
+ 2. Display the results in a clean table:
467
+ - Platform name
468
+ - Approved scopes
469
+ - Whether any scope requires human approval
470
+ 3. If no platforms are approved yet, tell the user to visit
471
+ https://app.agentvalet.ai/agents to configure permissions
472
+ 4. If AgentValet MCP is not connected, explain how to set it up:
473
+ Run: npx @agentvalet/register
474
+ `;
475
+ writeFileSync(statusDest, statusContent, "utf8");
476
+ console.log(chalk2.green(" \u2713 Factory Droid: created .factory/commands/av-status.md"));
477
+ } else {
478
+ console.log(chalk2.dim(" Factory Droid: .factory/commands/av-status.md already exists"));
479
+ }
480
+ const registerDest = path.join(commandsDir, "av-register.md");
481
+ if (!existsSync(registerDest)) {
482
+ const registerContent = `# /av-register
483
+
484
+ Register or re-register this project with AgentValet.
485
+
486
+ ## Instructions for the droid
487
+
488
+ Tell the user to run the following command in their terminal:
489
+
490
+ \`\`\`bash
491
+ npx @agentvalet/register
492
+ \`\`\`
493
+
494
+ This will:
495
+ 1. Authenticate with GitHub (opens browser)
496
+ 2. Create or find their AgentValet account
497
+ 3. Register this machine as an agent
498
+ 4. Configure Factory Droid MCP integration automatically
499
+
500
+ After running, approve the agent at: https://app.agentvalet.ai/agents
501
+ `;
502
+ writeFileSync(registerDest, registerContent, "utf8");
503
+ console.log(chalk2.green(" \u2713 Factory Droid: created .factory/commands/av-register.md"));
504
+ }
505
+ }
506
+ function writeFactoryDroidReliabilityDroid() {
507
+ const droidsDir = path.join(process.cwd(), ".factory", "droids");
508
+ if (!existsSync(droidsDir)) mkdirSync(droidsDir, { recursive: true });
509
+ const dest = path.join(droidsDir, "agentvalet-reliability.yaml");
510
+ if (existsSync(dest)) return;
511
+ const content = `# AgentValet Reliability Droid
512
+ # Monitors agent call patterns and surfaces governance issues
513
+ # Commit this file to your repo
514
+
515
+ name: AgentValet Reliability
516
+ description: >
517
+ Monitors AI agent behaviour against AgentValet governance rules.
518
+ Surfaces approval-required actions, scope violations, rate limit
519
+ warnings, and audit anomalies directly in Factory Droid.
520
+
521
+ type: reliability
522
+ model: inherit
523
+ mcp_servers:
524
+ - agentvalet
525
+ autonomy: Low
526
+
527
+ triggers:
528
+ - on_agent_action: true
529
+ - on_approval_required: true
530
+ - on_scope_violation: true
531
+
532
+ instructions: |
533
+ You are a governance monitor. You observe what the other droids
534
+ are doing and flag any actions that require human oversight.
535
+
536
+ ## Your responsibilities
537
+
538
+ 1. BEFORE any droid makes a platform API call, check if the scope
539
+ requires approval using \`authzen_evaluate\`:
540
+ - If decision=false: block the call, explain why, suggest alternatives
541
+ - If decision=true: allow, but log it
542
+
543
+ 2. WHEN a droid calls \`use_platform\`, verify:
544
+ - The platform is in the approved list from \`list_platforms\`
545
+ - The scope matches what was approved
546
+ - The call hasn't exceeded rate limits
547
+
548
+ 3. SURFACE approval-required actions clearly:
549
+ "\u26A0 This action requires human approval: [action description]
550
+ Waiting for approval at app.agentvalet.ai/approvals"
551
+
552
+ 4. MONITOR for unusual patterns:
553
+ - Multiple calls to the same destructive scope in quick succession
554
+ - Calls to platforms not in the approved list
555
+ - Call volume spikes beyond normal operation
556
+
557
+ 5. REPORT governance status when asked:
558
+ - Current approved platforms and scopes
559
+ - Pending approvals
560
+ - Recent audit log entries (call list_platforms + agent_status)
561
+
562
+ ## What you do NOT do
563
+
564
+ - You do not make platform API calls yourself
565
+ - You do not approve actions on behalf of the human
566
+ - You do not bypass governance even if another droid requests it
567
+
568
+ ## Escalation
569
+
570
+ If you detect a potential security concern (unexpected scope,
571
+ unusual call pattern), immediately tell the human:
572
+ "\u{1F534} Governance alert: [description]. Recommend reviewing at
573
+ app.agentvalet.ai/audit"
574
+ `;
575
+ writeFileSync(dest, content, "utf8");
576
+ console.log(chalk2.green(" \u2713 Factory Droid: created .factory/droids/agentvalet-reliability.yaml"));
577
+ console.log(chalk2.dim(" Commit this file to your repo"));
578
+ }
373
579
  function ensureMcpServer() {
374
580
  try {
375
581
  execSync("npx --yes @agentvalet/mcp-server --version", {
@@ -427,27 +633,7 @@ To refresh this file: \`npx @agentvalet/register refresh\`
427
633
 
428
634
  // src/lib/require-agent.ts
429
635
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
430
- import { resolve as resolve2, join } from "path";
431
-
432
- // src/config/store.ts
433
- import Conf from "conf";
434
- var store = new Conf({
435
- projectName: "agentvalet"
436
- });
437
- function getStoredConfig() {
438
- return store.store;
439
- }
440
- function saveConfig(config) {
441
- store.set(config);
442
- }
443
- function clearConfig() {
444
- store.clear();
445
- }
446
- function isAuthenticated() {
447
- return !!store.get("apiKey");
448
- }
449
-
450
- // src/lib/require-agent.ts
636
+ import { resolve } from "path";
451
637
  import chalk3 from "chalk";
452
638
  var NO_AGENT_MSG = chalk3.red("\n \u2717 No agent registered.\n") + chalk3.dim(
453
639
  " Run the following command first to register an agent in this directory:\n\n npx @agentvalet/register\n"
@@ -468,45 +654,34 @@ function tryResolveAgent(envFile = ".env.agentvalet") {
468
654
  let ownerId;
469
655
  let apiUrl = "https://api.agentvalet.ai";
470
656
  let keyPath;
471
- const envPath = resolve2(envFile);
472
- if (existsSync2(envPath)) {
473
- try {
474
- const vars = parseEnvFile(readFileSync2(envPath, "utf-8"));
475
- agentId = vars["AGENTVALET_AGENT_ID"];
476
- ownerId = vars["AGENTVALET_OWNER_ID"];
477
- apiUrl = vars["AGENTVALET_API_URL"] ?? apiUrl;
478
- keyPath = vars["AGENTVALET_PRIVATE_KEY_PATH"];
479
- } catch {
480
- }
657
+ try {
658
+ const stored = getStoredConfig();
659
+ agentId = stored.agentId ?? stored.lastAgentId ?? void 0;
660
+ ownerId = stored.ownerId ?? void 0;
661
+ apiUrl = stored.apiUrl ?? stored.proxyUrl ?? apiUrl;
662
+ keyPath = stored.keyPath ?? stored.lastKeyPath ?? void 0;
663
+ } catch {
481
664
  }
482
- if (!agentId || !ownerId || !keyPath) {
483
- const localConfigPath = join(process.cwd(), ".agentvalet", "config.json");
484
- if (existsSync2(localConfigPath)) {
665
+ if (!keyPath) keyPath = AGENT_KEY_PATH;
666
+ if (!agentId || !ownerId) {
667
+ const envPath = resolve(envFile);
668
+ if (existsSync2(envPath)) {
485
669
  try {
486
- const local = JSON.parse(readFileSync2(localConfigPath, "utf-8"));
487
- agentId = agentId ?? local["agent_id"];
488
- apiUrl = local["proxy_url"] ?? apiUrl;
489
- keyPath = keyPath ?? join(process.cwd(), ".agentvalet", "agent.key");
670
+ const vars = parseEnvFile(readFileSync2(envPath, "utf-8"));
671
+ agentId = agentId ?? vars["AGENTVALET_AGENT_ID"];
672
+ ownerId = ownerId ?? vars["AGENTVALET_OWNER_ID"];
673
+ apiUrl = vars["AGENTVALET_API_URL"] ?? apiUrl;
674
+ keyPath = vars["AGENTVALET_PRIVATE_KEY_PATH"] ?? keyPath;
490
675
  } catch {
491
676
  }
492
677
  }
493
- try {
494
- const global = getStoredConfig();
495
- if (!ownerId) ownerId = global.ownerId ?? void 0;
496
- } catch {
497
- }
498
- }
499
- if (!agentId || !ownerId || !keyPath) {
500
- try {
501
- const global = getStoredConfig();
502
- agentId = agentId ?? global.lastAgentId ?? void 0;
503
- ownerId = ownerId ?? global.ownerId ?? void 0;
504
- keyPath = keyPath ?? global.lastKeyPath ?? void 0;
505
- apiUrl = global.proxyUrl ?? apiUrl;
506
- } catch {
507
- }
508
678
  }
679
+ agentId = agentId ?? process.env["AGENTVALET_AGENT_ID"];
680
+ ownerId = ownerId ?? process.env["AGENTVALET_OWNER_ID"];
681
+ if (process.env["AGENTVALET_API_URL"]) apiUrl = process.env["AGENTVALET_API_URL"];
682
+ if (process.env["AGENTVALET_PRIVATE_KEY_PATH"]) keyPath = process.env["AGENTVALET_PRIVATE_KEY_PATH"];
509
683
  if (!agentId || !ownerId || !keyPath) return null;
684
+ if (!existsSync2(resolve(keyPath))) return null;
510
685
  return { agentId, ownerId, keyPath, apiUrl };
511
686
  }
512
687
 
@@ -517,19 +692,19 @@ var HOOK_COMMAND = "npx @agentvalet/register refresh --if-stale --quiet 2>/dev/n
517
692
  var HOOK_MARKER = "agentvalet/register refresh";
518
693
  function writePermissionsFromEvent(agentId, ownerId, platforms, outputPath = "./.claude/agentvalet-permissions.md") {
519
694
  const content = generatePermissionsFile(agentId, ownerId, platforms);
520
- const resolvedPath = resolve3(outputPath);
521
- mkdirSync(dirname2(resolvedPath), { recursive: true });
695
+ const resolvedPath = resolve2(outputPath);
696
+ mkdirSync2(dirname(resolvedPath), { recursive: true });
522
697
  writeFileSync2(resolvedPath, content, "utf-8");
523
698
  embedInClaudeMd(content);
524
699
  ensureHook();
525
700
  }
526
701
  async function runRefresh(options) {
527
- const outputPath = resolve3(options.outputPath);
702
+ const outputPath = resolve2(options.outputPath);
528
703
  if (options.ifStale) {
529
704
  try {
530
705
  const stat = statSync(outputPath);
531
706
  const fileIsFresh = Date.now() - stat.mtimeMs < 36e5;
532
- const claudeMdPath = resolve3("CLAUDE.md");
707
+ const claudeMdPath = resolve2("CLAUDE.md");
533
708
  const claudeMd = existsSync3(claudeMdPath) ? readFileSync3(claudeMdPath, "utf-8") : "";
534
709
  const blockStart = claudeMd.indexOf(CLAUDE_START);
535
710
  const blockEnd = claudeMd.indexOf(CLAUDE_END);
@@ -540,7 +715,7 @@ async function runRefresh(options) {
540
715
  }
541
716
  }
542
717
  const creds = tryResolveAgent(options.envFile);
543
- if (!creds || !existsSync3(resolve3(creds.keyPath))) {
718
+ if (!creds || !existsSync3(resolve2(creds.keyPath))) {
544
719
  console.error(" \u2717 No agent registered.\n Run the following command first to register an agent in this directory:\n\n npx @agentvalet/register\n");
545
720
  process.exit(1);
546
721
  }
@@ -565,7 +740,7 @@ async function runRefresh(options) {
565
740
  const json = await res.json();
566
741
  const platforms = json.data?.platforms ?? [];
567
742
  const content = generatePermissionsFile(agentId, ownerId, platforms);
568
- mkdirSync(dirname2(outputPath), { recursive: true });
743
+ mkdirSync2(dirname(outputPath), { recursive: true });
569
744
  writeFileSync2(outputPath, content, "utf-8");
570
745
  embedInClaudeMd(content);
571
746
  ensureHook();
@@ -574,7 +749,7 @@ async function runRefresh(options) {
574
749
  }
575
750
  }
576
751
  function embedInClaudeMd(permissionsContent) {
577
- const claudeMdPath = resolve3("CLAUDE.md");
752
+ const claudeMdPath = resolve2("CLAUDE.md");
578
753
  const block = `${CLAUDE_START}
579
754
  ${permissionsContent.trimEnd()}
580
755
  ${CLAUDE_END}`;
@@ -601,7 +776,7 @@ ${block}
601
776
  }
602
777
  }
603
778
  function ensureHook() {
604
- const settingsPath = resolve3(".claude/settings.local.json");
779
+ const settingsPath = resolve2(".claude/settings.local.json");
605
780
  let settings = {};
606
781
  if (existsSync3(settingsPath)) {
607
782
  try {
@@ -627,12 +802,12 @@ function ensureHook() {
627
802
  perms.allow = [...allow, ...missing];
628
803
  settings.permissions = perms;
629
804
  }
630
- mkdirSync(dirname2(settingsPath), { recursive: true });
805
+ mkdirSync2(dirname(settingsPath), { recursive: true });
631
806
  writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
632
807
  ensureMcpJson();
633
808
  }
634
809
  function ensureMcpJson() {
635
- const mcpJsonPath = resolve3(".mcp.json");
810
+ const mcpJsonPath = resolve2(".mcp.json");
636
811
  let mcpConfig = {};
637
812
  if (existsSync3(mcpJsonPath)) {
638
813
  try {
@@ -849,7 +1024,7 @@ function formatEnvBlock(vars) {
849
1024
  }
850
1025
 
851
1026
  // src/config/ide.ts
852
- import { mkdir as mkdir3, writeFile as writeFile4, readFile as readFile2, appendFile as appendFile3 } from "fs/promises";
1027
+ import { mkdir as mkdir3, writeFile as writeFile4, readFile as readFile2, appendFile as appendFile3, copyFile } from "fs/promises";
853
1028
  import { existsSync as existsSync5 } from "fs";
854
1029
  import path4 from "path";
855
1030
  import os from "os";
@@ -980,6 +1155,72 @@ async function writeIdeConfigs(params) {
980
1155
  console.warn(` Warning: could not write .cursor/mcp.json: ${err.message}`);
981
1156
  }
982
1157
  }
1158
+ if (detected.factoryDroid) {
1159
+ const fdResults = await writeFactoryDroidConfig(mcpParams);
1160
+ written.push(...fdResults);
1161
+ }
1162
+ return written;
1163
+ }
1164
+ async function mergeFactoryDroidMcpJson(filePath, key, value) {
1165
+ let existing = {};
1166
+ if (existsSync5(filePath)) {
1167
+ try {
1168
+ const text = await readFile2(filePath, "utf8");
1169
+ existing = JSON.parse(text);
1170
+ } catch {
1171
+ await copyFile(filePath, filePath + ".backup");
1172
+ }
1173
+ }
1174
+ if (existing[key]) return;
1175
+ existing[key] = value;
1176
+ await writeFile4(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
1177
+ }
1178
+ async function updateGitignoreForFactoryDroid() {
1179
+ const gitignorePath = path4.join(process.cwd(), ".gitignore");
1180
+ if (!existsSync5(gitignorePath)) return;
1181
+ const existing = await readFile2(gitignorePath, "utf8");
1182
+ if (existing.includes(".factory/mcp.json")) return;
1183
+ const additions = [
1184
+ "",
1185
+ "# Factory Droid \u2014 private key in MCP config",
1186
+ ".factory/mcp.json",
1187
+ "",
1188
+ "# Factory Droid \u2014 commit these:",
1189
+ "# .factory/droids/",
1190
+ "# .factory/commands/"
1191
+ ].join("\n");
1192
+ await appendFile3(gitignorePath, additions + "\n", "utf8");
1193
+ }
1194
+ async function writeFactoryDroidConfig(params) {
1195
+ const home = os.homedir();
1196
+ const written = [];
1197
+ const entry = buildMcpEntry(params);
1198
+ const globalDir = path4.join(home, ".factory");
1199
+ const projectDir = path4.join(process.cwd(), ".factory");
1200
+ const globalDirExists = existsSync5(globalDir);
1201
+ const projectDirExists = existsSync5(projectDir);
1202
+ if (globalDirExists) {
1203
+ try {
1204
+ await mergeFactoryDroidMcpJson(path4.join(globalDir, "mcp.json"), "agentvalet", entry);
1205
+ written.push("~/.factory/mcp.json");
1206
+ } catch (err) {
1207
+ console.warn(` Warning: could not write ~/.factory/mcp.json: ${err.message}`);
1208
+ }
1209
+ }
1210
+ if (projectDirExists && !globalDirExists) {
1211
+ try {
1212
+ await mergeFactoryDroidMcpJson(path4.join(projectDir, "mcp.json"), "agentvalet", entry);
1213
+ written.push(".factory/mcp.json");
1214
+ } catch (err) {
1215
+ console.warn(` Warning: could not write .factory/mcp.json: ${err.message}`);
1216
+ }
1217
+ }
1218
+ if (written.length > 0) {
1219
+ try {
1220
+ await updateGitignoreForFactoryDroid();
1221
+ } catch {
1222
+ }
1223
+ }
983
1224
  return written;
984
1225
  }
985
1226
 
@@ -1017,7 +1258,8 @@ function detectFrameworks() {
1017
1258
  codex: existsSync6(path5.join(home, ".codex")) || tryBinary("codex"),
1018
1259
  claudeCode: existsSync6(path5.join(home, ".claude")) || tryBinary("claude"),
1019
1260
  cursor: existsSync6(path5.join(process.cwd(), ".cursor")) || cursorInstalled(),
1020
- openclaw: existsSync6(path5.join(home, ".openclaw"))
1261
+ openclaw: existsSync6(path5.join(home, ".openclaw")),
1262
+ factoryDroid: existsSync6(path5.join(home, ".factory")) || existsSync6(path5.join(process.cwd(), ".factory"))
1021
1263
  };
1022
1264
  }
1023
1265
  async function frameworkSetupConsent(detected) {
@@ -1036,6 +1278,11 @@ async function frameworkSetupConsent(detected) {
1036
1278
  if (detected.openclaw) {
1037
1279
  items.push("OpenClaw \u2192 SKILL.md (usage rules)");
1038
1280
  }
1281
+ if (detected.factoryDroid) {
1282
+ items.push("Factory Droid \u2192 add MCP server to ~/.factory/mcp.json");
1283
+ items.push(" \u2192 create .factory/droids/agentvalet.yaml");
1284
+ items.push(" \u2192 create .factory/commands/av-status.md");
1285
+ }
1039
1286
  if (items.length === 0) return false;
1040
1287
  console.log("");
1041
1288
  console.log(chalk6.bold(" Detected agent frameworks"));
@@ -1255,6 +1502,16 @@ async function registerAgentFlow(apiKey, ownerId) {
1255
1502
  }
1256
1503
  const detected = detectFrameworks();
1257
1504
  const shouldSetup = await frameworkSetupConsent(detected);
1505
+ let installReliabilityDroid = false;
1506
+ if (detected.factoryDroid && shouldSetup) {
1507
+ const { reliability } = await prompts3({
1508
+ type: "confirm",
1509
+ name: "reliability",
1510
+ message: "Also install AgentValet Reliability Droid for Factory Droid? (monitors agent governance)",
1511
+ initial: false
1512
+ });
1513
+ installReliabilityDroid = reliability ?? false;
1514
+ }
1258
1515
  if (shouldSetup) {
1259
1516
  console.log("");
1260
1517
  console.log(chalk7.bold(" Configuring agent frameworks..."));
@@ -1284,6 +1541,13 @@ async function registerAgentFlow(apiKey, ownerId) {
1284
1541
  if (detected.openclaw) {
1285
1542
  writeSkillMd();
1286
1543
  }
1544
+ if (detected.factoryDroid) {
1545
+ writeFactoryDroidDroid();
1546
+ writeFactoryDroidCommand();
1547
+ if (installReliabilityDroid) {
1548
+ writeFactoryDroidReliabilityDroid();
1549
+ }
1550
+ }
1287
1551
  writeQuickstartDoc({
1288
1552
  agentId: agent.agent_id,
1289
1553
  ownerId,
@@ -1412,12 +1676,12 @@ function revokeCommand() {
1412
1676
  process.exit(1);
1413
1677
  }
1414
1678
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1415
- const confirmed = await new Promise((resolve5) => {
1679
+ const confirmed = await new Promise((resolve4) => {
1416
1680
  rl.question(
1417
1681
  chalk9.yellow(`Revoke ${opts.agentId}? This cascades to all child agents and cannot be undone. (y/N) `),
1418
1682
  (ans) => {
1419
1683
  rl.close();
1420
- resolve5(ans.trim().toLowerCase() === "y");
1684
+ resolve4(ans.trim().toLowerCase() === "y");
1421
1685
  }
1422
1686
  );
1423
1687
  });
@@ -1445,9 +1709,9 @@ import { readFile as readFile5 } from "fs/promises";
1445
1709
 
1446
1710
  // src/lib/claude-config.ts
1447
1711
  import { homedir } from "os";
1448
- import { join as join2 } from "path";
1712
+ import { join } from "path";
1449
1713
  import { readFile as readFile3, writeFile as writeFile6, mkdir as mkdir4, access } from "fs/promises";
1450
- var CLAUDE_CONFIG_PATH = join2(homedir(), ".claude", "config");
1714
+ var CLAUDE_CONFIG_PATH = join(homedir(), ".claude", "config");
1451
1715
  async function writeKeyToClaudeConfig(agentId, privateKeyPem) {
1452
1716
  let config = {};
1453
1717
  try {
@@ -1458,7 +1722,7 @@ async function writeKeyToClaudeConfig(agentId, privateKeyPem) {
1458
1722
  }
1459
1723
  config.agent_keys = config.agent_keys ?? {};
1460
1724
  config.agent_keys[agentId] = Buffer.from(privateKeyPem).toString("base64");
1461
- await mkdir4(join2(homedir(), ".claude"), { recursive: true });
1725
+ await mkdir4(join(homedir(), ".claude"), { recursive: true });
1462
1726
  await writeFile6(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
1463
1727
  }
1464
1728
 
@@ -1529,13 +1793,12 @@ function injectCommand() {
1529
1793
  import { Command as Command4 } from "commander";
1530
1794
  import chalk11 from "chalk";
1531
1795
  import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync3, rmSync } from "fs";
1532
- import { resolve as resolve4, join as join3 } from "path";
1533
- import { homedir as homedir2 } from "os";
1796
+ import { resolve as resolve3 } from "path";
1534
1797
  var CLAUDE_START2 = "<!-- agentvalet:start -->";
1535
1798
  var CLAUDE_END2 = "<!-- agentvalet:end -->";
1536
1799
  var HOOK_MARKER2 = "agentvalet/register refresh";
1537
1800
  function stripClaudeMd() {
1538
- const path7 = resolve4("CLAUDE.md");
1801
+ const path7 = resolve3("CLAUDE.md");
1539
1802
  if (!existsSync7(path7)) return;
1540
1803
  let content = readFileSync4(path7, "utf-8");
1541
1804
  content = content.replace(new RegExp(`${CLAUDE_START2}[\\s\\S]*?${CLAUDE_END2}`, "m"), "");
@@ -1544,7 +1807,7 @@ function stripClaudeMd() {
1544
1807
  writeFileSync3(path7, content ? content + "\n" : "", "utf-8");
1545
1808
  }
1546
1809
  function stripSettingsJson() {
1547
- const path7 = resolve4(".claude/settings.local.json");
1810
+ const path7 = resolve3(".claude/settings.local.json");
1548
1811
  if (!existsSync7(path7)) return;
1549
1812
  try {
1550
1813
  const settings = JSON.parse(readFileSync4(path7, "utf-8"));
@@ -1563,7 +1826,7 @@ function stripSettingsJson() {
1563
1826
  }
1564
1827
  }
1565
1828
  function stripMcpJson() {
1566
- const path7 = resolve4(".mcp.json");
1829
+ const path7 = resolve3(".mcp.json");
1567
1830
  if (!existsSync7(path7)) return;
1568
1831
  try {
1569
1832
  const config = JSON.parse(readFileSync4(path7, "utf-8"));
@@ -1574,20 +1837,23 @@ function stripMcpJson() {
1574
1837
  } catch {
1575
1838
  }
1576
1839
  }
1577
- function deleteIfExists(p) {
1578
- const abs = resolve4(p);
1579
- if (existsSync7(abs)) {
1580
- rmSync(abs, { recursive: true, force: true });
1581
- return abs;
1582
- }
1583
- return null;
1584
- }
1585
1840
  function cleanLocally() {
1586
1841
  const removed = [];
1587
1842
  const r = (p) => {
1588
- const f = deleteIfExists(p);
1589
- if (f) removed.push(f);
1843
+ const abs = resolve3(p);
1844
+ if (existsSync7(abs)) {
1845
+ rmSync(abs, { recursive: true, force: true });
1846
+ removed.push(abs);
1847
+ }
1590
1848
  };
1849
+ if (existsSync7(AGENT_KEY_PATH)) {
1850
+ rmSync(AGENT_KEY_PATH, { force: true });
1851
+ removed.push(AGENT_KEY_PATH);
1852
+ }
1853
+ try {
1854
+ clearConfig();
1855
+ } catch {
1856
+ }
1591
1857
  r(".env.agentvalet");
1592
1858
  r(".agentvalet");
1593
1859
  r("agentvalet.key");
@@ -1595,17 +1861,8 @@ function cleanLocally() {
1595
1861
  stripClaudeMd();
1596
1862
  stripSettingsJson();
1597
1863
  stripMcpJson();
1598
- try {
1599
- saveConfig({ lastAgentId: "", lastKeyPath: "" });
1600
- } catch {
1601
- }
1602
- const oldConfig = join3(homedir2(), ".agentvalet", "config.json");
1603
- if (existsSync7(oldConfig)) {
1604
- rmSync(oldConfig, { force: true });
1605
- removed.push(oldConfig);
1606
- }
1607
1864
  if (removed.length > 0) {
1608
- console.log(chalk11.green(" \u2713 Removed local files:"));
1865
+ console.log(chalk11.green(" \u2713 Removed files:"));
1609
1866
  for (const f of removed) console.log(chalk11.dim(` ${f}`));
1610
1867
  }
1611
1868
  console.log(chalk11.green(" \u2713 Cleaned CLAUDE.md, .mcp.json, .claude/settings.local.json"));
@@ -1614,12 +1871,12 @@ var SSE_READ_TIMEOUT_MS = 25e3;
1614
1871
  async function pollForApproval(apiUrl, revocationToken, timeoutMs) {
1615
1872
  const streamUrl = `${apiUrl}/v1/revoke-request/stream/${revocationToken}`;
1616
1873
  const deadline = Date.now() + timeoutMs;
1617
- return new Promise((resolve5) => {
1874
+ return new Promise((resolve4) => {
1618
1875
  let settled = false;
1619
1876
  function done(result) {
1620
1877
  if (!settled) {
1621
1878
  settled = true;
1622
- resolve5(result);
1879
+ resolve4(result);
1623
1880
  }
1624
1881
  }
1625
1882
  function connect() {
@@ -1762,7 +2019,7 @@ function collectScope(value, previous) {
1762
2019
  return [...previous, value];
1763
2020
  }
1764
2021
  var program = new Command5();
1765
- 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) => {
2022
+ 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("--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) => {
1766
2023
  const isJson = !!opts.json;
1767
2024
  let ownerId = opts.owner ?? process.env.AGENTVALET_OWNER ?? "";
1768
2025
  let agentName = opts.name ?? "";
@@ -1864,39 +2121,34 @@ Approve at: https://app.agentvalet.ai/agents
1864
2121
  const { agentId, privateKey, grantedPlatforms } = result;
1865
2122
  const resolvedOwnerId = result.ownerId || ownerId;
1866
2123
  let keyPath;
1867
- let envPath;
1868
- try {
1869
- ({ keyPath, envPath } = await saveOutput({
1870
- agentId,
1871
- ownerId: resolvedOwnerId,
1872
- privateKey: privateKey ?? "",
1873
- apiUrl: opts.apiUrl,
1874
- keyFilePath: opts.keyPath,
1875
- envFilePath: opts.output
1876
- }));
1877
- } catch (err) {
1878
- const msg = err instanceof Error ? err.message : String(err);
1879
- console.error(isJson ? JSON.stringify({ error: msg }) : chalk12.red(`Failed to save output: ${msg}`));
1880
- process.exit(3);
1881
- }
1882
2124
  const keyWritten = !!privateKey;
1883
2125
  if (keyWritten) {
1884
- saveConfig({ lastAgentId: agentId, lastKeyPath: keyPath, ownerId: resolvedOwnerId });
2126
+ try {
2127
+ ({ keyPath } = await saveOutput({
2128
+ agentId,
2129
+ ownerId: resolvedOwnerId,
2130
+ privateKey,
2131
+ apiUrl: opts.apiUrl
2132
+ }));
2133
+ } catch (err) {
2134
+ const msg = err instanceof Error ? err.message : String(err);
2135
+ console.error(isJson ? JSON.stringify({ error: msg }) : chalk12.red(`Failed to save output: ${msg}`));
2136
+ process.exit(3);
2137
+ }
2138
+ } else {
2139
+ const { AGENT_KEY_PATH: AGENT_KEY_PATH2 } = await import("./store-JYJXDBYX.js");
2140
+ keyPath = AGENT_KEY_PATH2;
1885
2141
  }
1886
2142
  if (isJson) {
1887
- console.log(JSON.stringify({ status: "approved", agentId, keyPath: keyWritten ? keyPath : null, envPath }));
2143
+ console.log(JSON.stringify({ status: "approved", agentId, keyPath: keyWritten ? keyPath : null }));
1888
2144
  } else {
1889
2145
  console.log(chalk12.green(`
1890
2146
  Approved! Agent ID: ${agentId}`));
1891
2147
  if (keyWritten) {
1892
- console.log(chalk12.gray(`Key: ${keyPath}`));
2148
+ console.log(chalk12.gray(`Key: ${keyPath}`));
1893
2149
  } else {
1894
- console.log(chalk12.yellow(`Key: not written \u2014 copy it from the dashboard and paste into ${keyPath}`));
2150
+ console.log(chalk12.yellow(`Key: not written \u2014 copy it from the dashboard and paste into ${keyPath}`));
1895
2151
  }
1896
- console.log(chalk12.gray(`Env file: ${envPath}`));
1897
- console.log(`
1898
- Source the env file before calling any tool:`);
1899
- console.log(chalk12.cyan(` source ${envPath}`));
1900
2152
  }
1901
2153
  if (keyWritten) {
1902
2154
  try {
@@ -1904,12 +2156,7 @@ Source the env file before calling any tool:`);
1904
2156
  writePermissionsFromEvent(agentId, resolvedOwnerId, grantedPlatforms);
1905
2157
  if (!isJson) console.log(chalk12.gray(`Permissions: .claude/agentvalet-permissions.md`));
1906
2158
  } else {
1907
- await runRefresh({
1908
- envFile: opts.output,
1909
- outputPath: ".claude/agentvalet-permissions.md",
1910
- ifStale: false,
1911
- quiet: isJson
1912
- });
2159
+ await runRefresh({ envFile: ".env.agentvalet", outputPath: ".claude/agentvalet-permissions.md", ifStale: false, quiet: isJson });
1913
2160
  if (!isJson) console.log(chalk12.gray(`Permissions: .claude/agentvalet-permissions.md`));
1914
2161
  }
1915
2162
  } catch {
@@ -1918,10 +2165,10 @@ Source the env file before calling any tool:`);
1918
2165
  }
1919
2166
  process.exit(0);
1920
2167
  });
1921
- program.command("refresh").description("Refresh approved permissions and update .claude/agentvalet-permissions.md").option("--env-file <path>", "Path to .env.agentvalet file", "./.env.agentvalet").option("--output <path>", "Path to write permissions file", "./.claude/agentvalet-permissions.md").option("--if-stale", "Skip if file is less than 1 hour old").option("--quiet", "Suppress output").action(async (opts) => {
2168
+ program.command("refresh").description("Refresh approved permissions and update .claude/agentvalet-permissions.md").option("--output <path>", "Path to write permissions file", "./.claude/agentvalet-permissions.md").option("--if-stale", "Skip if file is less than 1 hour old").option("--quiet", "Suppress output").action(async (opts) => {
1922
2169
  try {
1923
2170
  await runRefresh({
1924
- envFile: opts.envFile,
2171
+ envFile: ".env.agentvalet",
1925
2172
  outputPath: opts.output,
1926
2173
  ifStale: !!opts.ifStale,
1927
2174
  quiet: !!opts.quiet
@@ -0,0 +1,16 @@
1
+ import {
2
+ AGENT_DIR,
3
+ AGENT_KEY_PATH,
4
+ clearConfig,
5
+ getStoredConfig,
6
+ isAuthenticated,
7
+ saveConfig
8
+ } from "./chunk-2EFPJNYK.js";
9
+ export {
10
+ AGENT_DIR,
11
+ AGENT_KEY_PATH,
12
+ clearConfig,
13
+ getStoredConfig,
14
+ isAuthenticated,
15
+ saveConfig
16
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentvalet/register",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "description": "Self-register AI agents with AgentValet",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,13 @@
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
+ },
13
20
  "dependencies": {
14
21
  "@agentvalet/mcp-server": "*",
15
22
  "@modelcontextprotocol/sdk": "^1.29.0",
@@ -48,11 +55,5 @@
48
55
  "valet",
49
56
  "agentvalet"
50
57
  ],
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
- }
58
+ "license": "MIT"
59
+ }