@agentvalet/register 0.2.1 → 0.3.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.
- package/README.md +35 -70
- package/dist/index.js +365 -21
- package/dist/{mcp-server-AM5ZYWTB.js → mcp-server-LUNOQPUO.js} +30 -24
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,91 +1,56 @@
|
|
|
1
1
|
# @agentvalet/register
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Register your AI agent with AgentValet in 30 seconds.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Usage
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
9
|
-
npx @agentvalet/register --name "My Agent" --scope slack:chat:write
|
|
10
|
-
|
|
11
|
-
# Or install globally:
|
|
12
|
-
npm install -g @agentvalet/register
|
|
13
|
-
agentvalet-register --name "My Agent" --scope slack:chat:write
|
|
8
|
+
npx @agentvalet/register
|
|
14
9
|
```
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
No installation required. Authenticate with GitHub, and your agent is registered automatically.
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
## What it does
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
1. Opens your browser to authenticate with GitHub
|
|
16
|
+
2. Creates your AgentValet account (or finds your existing one)
|
|
17
|
+
3. Registers this machine as an agent
|
|
18
|
+
4. Writes config to `.agentvalet/` in the current directory
|
|
19
|
+
5. Outputs the environment variables you need
|
|
23
20
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
| Flag | Default | Description |
|
|
27
|
-
|------|---------|-------------|
|
|
28
|
-
| `--owner <uuid>` | `AGENTVALET_OWNER` env | Owner ID from https://agentvalet.ai/settings |
|
|
29
|
-
| `--name <name>` | prompted | Agent name |
|
|
30
|
-
| `--description <desc>` | — | Agent description |
|
|
31
|
-
| `--scope <platform:scope>` | — | Repeatable, e.g. `--scope slack:chat:write --scope gmail:send` |
|
|
32
|
-
| `--api-url <url>` | `https://api.agentvalet.ai` | AgentValet API base URL |
|
|
33
|
-
| `--output <path>` | `./.env.agentvalet` | Path to write env vars |
|
|
34
|
-
| `--key-path <path>` | `./agentvalet.key` | Path to write private key |
|
|
35
|
-
| `--json` | — | Machine-readable JSON output |
|
|
36
|
-
| `--no-wait` | — | Register only, don't wait for approval |
|
|
37
|
-
| `--timeout <seconds>` | `300` | Approval wait timeout |
|
|
38
|
-
|
|
39
|
-
## Exit codes
|
|
40
|
-
|
|
41
|
-
| Code | Meaning |
|
|
42
|
-
|------|---------|
|
|
43
|
-
| `0` | Approved |
|
|
44
|
-
| `1` | Denied |
|
|
45
|
-
| `2` | Timeout |
|
|
46
|
-
| `3` | Network or other error |
|
|
47
|
-
|
|
48
|
-
## Integration examples
|
|
49
|
-
|
|
50
|
-
**Claude Code (`CLAUDE.md`):**
|
|
51
|
-
```markdown
|
|
52
|
-
On startup, register with AgentValet:
|
|
53
|
-
npx @agentvalet/register --owner {uuid} --name "Claude Code Agent" --scope slack:chat:write
|
|
54
|
-
|
|
55
|
-
Then source ./.env.agentvalet before calling any tool.
|
|
56
|
-
```
|
|
21
|
+
## Requirements
|
|
57
22
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
subprocess.run([
|
|
62
|
-
"npx", "@agentvalet/register",
|
|
63
|
-
"--name", "MyAgent",
|
|
64
|
-
"--owner", owner_id,
|
|
65
|
-
"--scope", "slack:chat:write",
|
|
66
|
-
])
|
|
67
|
-
```
|
|
23
|
+
- Node.js 18+
|
|
24
|
+
- A GitHub account
|
|
25
|
+
- An internet connection
|
|
68
26
|
|
|
69
|
-
|
|
27
|
+
## After registration
|
|
70
28
|
|
|
71
|
-
|
|
29
|
+
Approve your agent in the AgentValet dashboard to activate it:
|
|
30
|
+
https://app.agentvalet.ai/agents
|
|
31
|
+
|
|
32
|
+
## Commands
|
|
72
33
|
|
|
73
34
|
```bash
|
|
74
|
-
npx @agentvalet/register
|
|
75
|
-
|
|
35
|
+
npx @agentvalet/register # Register a new agent (GitHub onboarding)
|
|
36
|
+
npx @agentvalet/register refresh # Refresh approved permissions
|
|
76
37
|
```
|
|
77
38
|
|
|
78
|
-
##
|
|
39
|
+
## What gets written
|
|
40
|
+
|
|
41
|
+
- `.agentvalet/config.json` — agent ID, proxy URL, registration timestamp
|
|
42
|
+
- `.agentvalet/agent.key` — private key (mode 0600, never shared)
|
|
43
|
+
|
|
44
|
+
`.agentvalet/` is automatically added to your `.gitignore`.
|
|
79
45
|
|
|
80
|
-
|
|
81
|
-
2. Prints the approval URL — the owner approves in the dashboard
|
|
82
|
-
3. Waits for approval via SSE (with polling fallback)
|
|
83
|
-
4. On approval, writes the private key to `--key-path` and env vars to `--output`
|
|
46
|
+
## Advanced: existing owner ID
|
|
84
47
|
|
|
85
|
-
|
|
48
|
+
If you already have an AgentValet owner ID, pass it directly:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
AGENTVALET_OWNER=<your-owner-id> npx @agentvalet/register
|
|
52
|
+
```
|
|
86
53
|
|
|
87
|
-
##
|
|
54
|
+
## GitHub OAuth App
|
|
88
55
|
|
|
89
|
-
|
|
90
|
-
- Docs: https://agentvalet.ai/docs
|
|
91
|
-
- GitHub: https://github.com/MCSEdwin/agentvalet
|
|
56
|
+
The GitHub Device Flow uses AgentValet's OAuth App (client ID is public — the Device Flow does not use or expose the client secret). No secrets are stored on your machine.
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk5 from "chalk";
|
|
6
6
|
|
|
7
7
|
// src/wizard.ts
|
|
8
8
|
import prompts from "prompts";
|
|
@@ -422,7 +422,7 @@ function ensureMcpJson() {
|
|
|
422
422
|
if (!agentId || !ownerId || !keyPath) return;
|
|
423
423
|
servers["agent-authority"] = {
|
|
424
424
|
command: "npx",
|
|
425
|
-
args: ["-y", "@agentvalet/register", "mcp-server"],
|
|
425
|
+
args: ["-y", "@agentvalet/register@0.2.2", "mcp-server"],
|
|
426
426
|
env: {
|
|
427
427
|
AGENT_ID: agentId,
|
|
428
428
|
OWNER_ID: ownerId,
|
|
@@ -434,6 +434,346 @@ function ensureMcpJson() {
|
|
|
434
434
|
writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
+
// src/commands/onboard.ts
|
|
438
|
+
import chalk4 from "chalk";
|
|
439
|
+
import ora from "ora";
|
|
440
|
+
import prompts2 from "prompts";
|
|
441
|
+
|
|
442
|
+
// src/auth/github-device-flow.ts
|
|
443
|
+
import { createOAuthDeviceAuth } from "@octokit/auth-oauth-device";
|
|
444
|
+
import open from "open";
|
|
445
|
+
import chalk2 from "chalk";
|
|
446
|
+
var GITHUB_CLIENT_ID = process.env.AGENTVALET_GITHUB_CLIENT_ID ?? "Ov23liXXXXXXXXXXXXXX";
|
|
447
|
+
async function authenticateWithGitHub() {
|
|
448
|
+
const auth = createOAuthDeviceAuth({
|
|
449
|
+
clientType: "oauth-app",
|
|
450
|
+
clientId: GITHUB_CLIENT_ID,
|
|
451
|
+
scopes: ["user:email", "read:user"],
|
|
452
|
+
onVerification(verification) {
|
|
453
|
+
console.log("");
|
|
454
|
+
console.log(chalk2.bold(" Authenticate with GitHub"));
|
|
455
|
+
console.log("");
|
|
456
|
+
console.log(` 1. Open: ${chalk2.cyan.underline(verification.verification_uri)}`);
|
|
457
|
+
console.log(` 2. Enter code: ${chalk2.bold.yellow(verification.user_code)}`);
|
|
458
|
+
console.log("");
|
|
459
|
+
open(verification.verification_uri).catch(() => {
|
|
460
|
+
console.log(chalk2.dim(" (Could not open browser \u2014 navigate to the URL above manually)"));
|
|
461
|
+
});
|
|
462
|
+
console.log(chalk2.dim(" Waiting for authorisation..."));
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
const tokenAuth = await auth({ type: "oauth" });
|
|
466
|
+
return {
|
|
467
|
+
token: tokenAuth.token,
|
|
468
|
+
scopes: tokenAuth.scopes ?? []
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
async function getGitHubUser(token) {
|
|
472
|
+
const response = await fetch("https://api.github.com/user", {
|
|
473
|
+
headers: {
|
|
474
|
+
Authorization: `Bearer ${token}`,
|
|
475
|
+
Accept: "application/vnd.github.v3+json"
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
if (!response.ok) {
|
|
479
|
+
throw new Error(`GitHub API error: ${response.status}`);
|
|
480
|
+
}
|
|
481
|
+
const user = await response.json();
|
|
482
|
+
if (!user.email) {
|
|
483
|
+
const emailsResponse = await fetch("https://api.github.com/user/emails", {
|
|
484
|
+
headers: {
|
|
485
|
+
Authorization: `Bearer ${token}`,
|
|
486
|
+
Accept: "application/vnd.github.v3+json"
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
if (emailsResponse.ok) {
|
|
490
|
+
const emails = await emailsResponse.json();
|
|
491
|
+
user.email = emails.find((e) => e.primary && e.verified)?.email ?? emails[0]?.email ?? null;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return user;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/api/agentvalet.ts
|
|
498
|
+
var API_BASE = process.env.AGENTVALET_API_URL ?? "https://api.agentvalet.ai";
|
|
499
|
+
async function createOrFindAccount(params) {
|
|
500
|
+
const controller = new AbortController();
|
|
501
|
+
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
502
|
+
try {
|
|
503
|
+
const response = await fetch(`${API_BASE}/v1/cli/account`, {
|
|
504
|
+
method: "POST",
|
|
505
|
+
headers: { "Content-Type": "application/json" },
|
|
506
|
+
body: JSON.stringify(params),
|
|
507
|
+
signal: controller.signal
|
|
508
|
+
});
|
|
509
|
+
if (!response.ok) {
|
|
510
|
+
const error = await response.json().catch(() => ({}));
|
|
511
|
+
throw new Error(`Account creation failed: ${response.status} ${error.message ?? ""}`);
|
|
512
|
+
}
|
|
513
|
+
return response.json();
|
|
514
|
+
} catch (err) {
|
|
515
|
+
clearTimeout(timeout);
|
|
516
|
+
if (err?.name === "AbortError") throw new Error("Request timed out \u2014 is the AgentValet API reachable?");
|
|
517
|
+
throw err;
|
|
518
|
+
} finally {
|
|
519
|
+
clearTimeout(timeout);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
async function registerAgent2(params, apiKey) {
|
|
523
|
+
const controller = new AbortController();
|
|
524
|
+
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
525
|
+
try {
|
|
526
|
+
const response = await fetch(`${API_BASE}/v1/cli/register-agent`, {
|
|
527
|
+
method: "POST",
|
|
528
|
+
headers: {
|
|
529
|
+
"Content-Type": "application/json",
|
|
530
|
+
"Authorization": `Bearer ${apiKey}`
|
|
531
|
+
},
|
|
532
|
+
body: JSON.stringify(params),
|
|
533
|
+
signal: controller.signal
|
|
534
|
+
});
|
|
535
|
+
if (!response.ok) {
|
|
536
|
+
const error = await response.json().catch(() => ({}));
|
|
537
|
+
throw new Error(`Agent registration failed: ${response.status} ${error.message ?? ""}`);
|
|
538
|
+
}
|
|
539
|
+
return response.json();
|
|
540
|
+
} catch (err) {
|
|
541
|
+
clearTimeout(timeout);
|
|
542
|
+
if (err?.name === "AbortError") throw new Error("Request timed out \u2014 is the AgentValet API reachable?");
|
|
543
|
+
throw err;
|
|
544
|
+
} finally {
|
|
545
|
+
clearTimeout(timeout);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// src/config/store.ts
|
|
550
|
+
import Conf from "conf";
|
|
551
|
+
var store = new Conf({
|
|
552
|
+
projectName: "agentvalet"
|
|
553
|
+
});
|
|
554
|
+
function getStoredConfig() {
|
|
555
|
+
return store.store;
|
|
556
|
+
}
|
|
557
|
+
function saveConfig(config) {
|
|
558
|
+
store.set(config);
|
|
559
|
+
}
|
|
560
|
+
function clearConfig() {
|
|
561
|
+
store.clear();
|
|
562
|
+
}
|
|
563
|
+
function isAuthenticated() {
|
|
564
|
+
return !!store.get("apiKey");
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/config/local.ts
|
|
568
|
+
import { mkdir as mkdir2, writeFile as writeFile2, appendFile, readFile } from "fs/promises";
|
|
569
|
+
import path from "path";
|
|
570
|
+
async function writeLocalConfig(config) {
|
|
571
|
+
const dir = path.join(process.cwd(), ".agentvalet");
|
|
572
|
+
await mkdir2(dir, { recursive: true });
|
|
573
|
+
await writeFile2(
|
|
574
|
+
path.join(dir, "config.json"),
|
|
575
|
+
JSON.stringify({
|
|
576
|
+
agent_id: config.agentId,
|
|
577
|
+
agent_name: config.agentName,
|
|
578
|
+
proxy_url: config.proxyUrl,
|
|
579
|
+
registered_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
580
|
+
}, null, 2),
|
|
581
|
+
{ encoding: "utf8", mode: 384 }
|
|
582
|
+
);
|
|
583
|
+
await writeFile2(
|
|
584
|
+
path.join(dir, "agent.key"),
|
|
585
|
+
config.privateKeyPem,
|
|
586
|
+
{ encoding: "utf8", mode: 384 }
|
|
587
|
+
);
|
|
588
|
+
if (process.platform === "win32") {
|
|
589
|
+
console.warn(" Note: file permissions (0o600) are not enforced on Windows. Keep .agentvalet/ in a secure location.");
|
|
590
|
+
}
|
|
591
|
+
const gitignorePath = path.join(process.cwd(), ".gitignore");
|
|
592
|
+
const gitignoreContent = await readFile(gitignorePath, "utf8").catch(() => null);
|
|
593
|
+
if (gitignoreContent !== null) {
|
|
594
|
+
const lines = gitignoreContent.split("\n").map((l) => l.trim());
|
|
595
|
+
if (!lines.includes(".agentvalet/")) {
|
|
596
|
+
await appendFile(gitignorePath, "\n# AgentValet\n.agentvalet/\n");
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/output/env.ts
|
|
602
|
+
import chalk3 from "chalk";
|
|
603
|
+
import { appendFile as appendFile2, writeFile as writeFile3 } from "fs/promises";
|
|
604
|
+
import { existsSync as existsSync2 } from "fs";
|
|
605
|
+
import path2 from "path";
|
|
606
|
+
function formatEnvBlock(vars) {
|
|
607
|
+
return Object.entries(vars).map(([key, value]) => {
|
|
608
|
+
const truncated = value.length > 60 ? value.substring(0, 40) + "..." + value.substring(value.length - 10) : value;
|
|
609
|
+
return ` ${chalk3.dim(key + "=")}${chalk3.white(truncated)}`;
|
|
610
|
+
}).join("\n");
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// src/commands/onboard.ts
|
|
614
|
+
import os from "os";
|
|
615
|
+
async function runOnboard() {
|
|
616
|
+
console.log("");
|
|
617
|
+
console.log(chalk4.bold(" AgentValet \u2014 Agent Registration"));
|
|
618
|
+
console.log(chalk4.dim(" agentvalet.ai"));
|
|
619
|
+
console.log("");
|
|
620
|
+
if (isAuthenticated()) {
|
|
621
|
+
const stored = getStoredConfig();
|
|
622
|
+
console.log(
|
|
623
|
+
chalk4.green(" \u2713 Already authenticated as") + chalk4.bold(` ${stored.email}`)
|
|
624
|
+
);
|
|
625
|
+
const { action } = await prompts2({
|
|
626
|
+
type: "select",
|
|
627
|
+
name: "action",
|
|
628
|
+
message: "What would you like to do?",
|
|
629
|
+
choices: [
|
|
630
|
+
{ title: "Register a new agent for this directory", value: "register" },
|
|
631
|
+
{ title: "Show my credentials", value: "show" },
|
|
632
|
+
{ title: "Sign out and use a different account", value: "signout" }
|
|
633
|
+
]
|
|
634
|
+
});
|
|
635
|
+
if (action === void 0) {
|
|
636
|
+
process.exit(0);
|
|
637
|
+
}
|
|
638
|
+
if (action === "show") {
|
|
639
|
+
showCredentials(stored);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (action === "signout") {
|
|
643
|
+
clearConfig();
|
|
644
|
+
console.log(chalk4.dim(" Signed out. Run again to re-authenticate."));
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
await registerAgentFlow(stored.apiKey, stored.ownerId);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
let spinner = ora();
|
|
651
|
+
let githubToken;
|
|
652
|
+
try {
|
|
653
|
+
const result = await authenticateWithGitHub();
|
|
654
|
+
githubToken = result.token;
|
|
655
|
+
} catch (err) {
|
|
656
|
+
console.error(chalk4.red(` \u2717 Authentication failed: ${err.message}`));
|
|
657
|
+
process.exit(1);
|
|
658
|
+
}
|
|
659
|
+
spinner = ora(" Fetching your GitHub profile...").start();
|
|
660
|
+
let githubUser;
|
|
661
|
+
try {
|
|
662
|
+
githubUser = await getGitHubUser(githubToken);
|
|
663
|
+
spinner.succeed(` GitHub: ${chalk4.bold("@" + githubUser.login)}`);
|
|
664
|
+
} catch (err) {
|
|
665
|
+
spinner.fail(` Failed to fetch GitHub profile: ${err.message}`);
|
|
666
|
+
process.exit(1);
|
|
667
|
+
}
|
|
668
|
+
if (!githubUser.email) {
|
|
669
|
+
const { email } = await prompts2({
|
|
670
|
+
type: "text",
|
|
671
|
+
name: "email",
|
|
672
|
+
message: "Your email address (not visible on GitHub):",
|
|
673
|
+
validate: (v) => v.includes("@") || "Please enter a valid email"
|
|
674
|
+
});
|
|
675
|
+
if (email === void 0) {
|
|
676
|
+
process.exit(0);
|
|
677
|
+
}
|
|
678
|
+
githubUser.email = email;
|
|
679
|
+
}
|
|
680
|
+
spinner = ora(" Setting up your AgentValet account...").start();
|
|
681
|
+
let account;
|
|
682
|
+
try {
|
|
683
|
+
account = await createOrFindAccount({
|
|
684
|
+
github_token: githubToken,
|
|
685
|
+
github_username: githubUser.login,
|
|
686
|
+
email: githubUser.email,
|
|
687
|
+
display_name: githubUser.name ?? githubUser.login,
|
|
688
|
+
avatar_url: githubUser.avatar_url
|
|
689
|
+
});
|
|
690
|
+
if (account.is_new_account) {
|
|
691
|
+
spinner.succeed(" AgentValet account created");
|
|
692
|
+
} else {
|
|
693
|
+
spinner.succeed(" Existing AgentValet account found");
|
|
694
|
+
}
|
|
695
|
+
} catch (err) {
|
|
696
|
+
spinner.fail(` Account setup failed: ${err.message}`);
|
|
697
|
+
process.exit(1);
|
|
698
|
+
}
|
|
699
|
+
saveConfig({
|
|
700
|
+
apiKey: account.owner_id,
|
|
701
|
+
ownerId: account.owner_id,
|
|
702
|
+
orgId: account.org_id,
|
|
703
|
+
proxyUrl: account.proxy_url,
|
|
704
|
+
githubUsername: githubUser.login,
|
|
705
|
+
email: githubUser.email
|
|
706
|
+
});
|
|
707
|
+
await registerAgentFlow(account.owner_id, account.owner_id);
|
|
708
|
+
}
|
|
709
|
+
async function registerAgentFlow(apiKey, ownerId) {
|
|
710
|
+
const hostname = os.hostname().split(".")[0];
|
|
711
|
+
const dirName = process.cwd().split("/").pop() ?? process.cwd().split("\\").pop() ?? "agent";
|
|
712
|
+
const { agentName } = await prompts2({
|
|
713
|
+
type: "text",
|
|
714
|
+
name: "agentName",
|
|
715
|
+
message: "Name for this agent:",
|
|
716
|
+
initial: `${hostname} \u2014 ${dirName}`,
|
|
717
|
+
validate: (v) => v.trim().length > 0 || "Agent name is required"
|
|
718
|
+
});
|
|
719
|
+
if (agentName === void 0) {
|
|
720
|
+
process.exit(0);
|
|
721
|
+
}
|
|
722
|
+
const spinner = ora(" Registering agent...").start();
|
|
723
|
+
let agent;
|
|
724
|
+
try {
|
|
725
|
+
agent = await registerAgent2({
|
|
726
|
+
owner_id: ownerId,
|
|
727
|
+
name: agentName.trim(),
|
|
728
|
+
source: "cli"
|
|
729
|
+
}, apiKey);
|
|
730
|
+
spinner.succeed(` Agent registered: ${chalk4.bold(agent.agent_id)}`);
|
|
731
|
+
} catch (err) {
|
|
732
|
+
spinner.fail(` Registration failed: ${err.message}`);
|
|
733
|
+
process.exit(1);
|
|
734
|
+
}
|
|
735
|
+
try {
|
|
736
|
+
await writeLocalConfig({
|
|
737
|
+
agentId: agent.agent_id,
|
|
738
|
+
agentName: agent.agent_name,
|
|
739
|
+
proxyUrl: getStoredConfig().proxyUrl,
|
|
740
|
+
privateKeyPem: agent.private_key_pem
|
|
741
|
+
});
|
|
742
|
+
} catch (err) {
|
|
743
|
+
console.error(chalk4.red(` \u2717 Failed to write local config: ${err.message}`));
|
|
744
|
+
console.log(chalk4.dim(" Agent was registered \u2014 run again to save config locally."));
|
|
745
|
+
process.exit(1);
|
|
746
|
+
}
|
|
747
|
+
console.log("");
|
|
748
|
+
console.log(chalk4.bold.green(" \u2713 All done!"));
|
|
749
|
+
console.log("");
|
|
750
|
+
console.log(" Set these in your environment:");
|
|
751
|
+
console.log("");
|
|
752
|
+
console.log(formatEnvBlock({
|
|
753
|
+
AGENTVALET_PROXY_URL: getStoredConfig().proxyUrl,
|
|
754
|
+
AGENTVALET_AGENT_ID: agent.agent_id,
|
|
755
|
+
AGENTVALET_PRIVATE_KEY: agent.private_key_pem.replace(/\n/g, "\\n")
|
|
756
|
+
}));
|
|
757
|
+
console.log("");
|
|
758
|
+
console.log(chalk4.dim(" Private key also saved to: .agentvalet/agent.key"));
|
|
759
|
+
console.log(chalk4.dim(" Add .agentvalet/ to your .gitignore"));
|
|
760
|
+
console.log("");
|
|
761
|
+
console.log(
|
|
762
|
+
` Dashboard: ${chalk4.cyan.underline("https://app.agentvalet.ai")}`
|
|
763
|
+
);
|
|
764
|
+
console.log(chalk4.dim(" Approve this agent in your dashboard to activate it."));
|
|
765
|
+
console.log("");
|
|
766
|
+
}
|
|
767
|
+
function showCredentials(config) {
|
|
768
|
+
console.log("");
|
|
769
|
+
console.log(" Your AgentValet credentials:");
|
|
770
|
+
console.log("");
|
|
771
|
+
console.log(` Owner ID: ${chalk4.bold(config.ownerId)}`);
|
|
772
|
+
console.log(` Proxy URL: ${chalk4.bold(config.proxyUrl)}`);
|
|
773
|
+
console.log(` Email: ${chalk4.bold(config.email)}`);
|
|
774
|
+
console.log("");
|
|
775
|
+
}
|
|
776
|
+
|
|
437
777
|
// src/index.ts
|
|
438
778
|
function collectScope(value, previous) {
|
|
439
779
|
return [...previous, value];
|
|
@@ -445,6 +785,10 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
445
785
|
let agentName = opts.name ?? "";
|
|
446
786
|
let description = opts.description ?? "";
|
|
447
787
|
let scopes = opts.scope;
|
|
788
|
+
if (!ownerId && !isAuthenticated()) {
|
|
789
|
+
await runOnboard();
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
448
792
|
if (!agentName) {
|
|
449
793
|
if (isJson) {
|
|
450
794
|
console.error(JSON.stringify({ error: "--name is required in non-interactive mode" }));
|
|
@@ -461,7 +805,7 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
461
805
|
console.error(JSON.stringify({ error: "--owner or AGENTVALET_OWNER is required" }));
|
|
462
806
|
process.exit(3);
|
|
463
807
|
}
|
|
464
|
-
console.error(
|
|
808
|
+
console.error(chalk5.red("Error: --owner or AGENTVALET_OWNER environment variable is required"));
|
|
465
809
|
process.exit(3);
|
|
466
810
|
}
|
|
467
811
|
const scopeMap = /* @__PURE__ */ new Map();
|
|
@@ -469,7 +813,7 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
469
813
|
const colon = s.indexOf(":");
|
|
470
814
|
if (colon === -1) {
|
|
471
815
|
console.error(
|
|
472
|
-
isJson ? JSON.stringify({ error: `Invalid scope format: ${s} (expected platform:scope)` }) :
|
|
816
|
+
isJson ? JSON.stringify({ error: `Invalid scope format: ${s} (expected platform:scope)` }) : chalk5.red(`Invalid scope format: ${s} (expected platform:scope)`)
|
|
473
817
|
);
|
|
474
818
|
process.exit(3);
|
|
475
819
|
}
|
|
@@ -492,11 +836,11 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
492
836
|
});
|
|
493
837
|
} catch (err) {
|
|
494
838
|
const msg = err instanceof Error ? err.message : String(err);
|
|
495
|
-
console.error(isJson ? JSON.stringify({ error: msg }) :
|
|
839
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk5.red(`Registration failed: ${msg}`));
|
|
496
840
|
process.exit(3);
|
|
497
841
|
}
|
|
498
842
|
if (!isJson) {
|
|
499
|
-
console.log(
|
|
843
|
+
console.log(chalk5.cyan(`
|
|
500
844
|
Approve at: https://agentvalet.ai/registrations
|
|
501
845
|
`));
|
|
502
846
|
}
|
|
@@ -504,7 +848,7 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
504
848
|
if (isJson) {
|
|
505
849
|
console.log(JSON.stringify({ status: "pending", token }));
|
|
506
850
|
} else {
|
|
507
|
-
console.log(
|
|
851
|
+
console.log(chalk5.yellow("Registration submitted (--no-wait). Token:"), token);
|
|
508
852
|
}
|
|
509
853
|
process.exit(0);
|
|
510
854
|
}
|
|
@@ -515,14 +859,14 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
515
859
|
});
|
|
516
860
|
} catch (err) {
|
|
517
861
|
const msg = err instanceof Error ? err.message : String(err);
|
|
518
|
-
console.error(isJson ? JSON.stringify({ error: msg }) :
|
|
862
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk5.red(`Wait failed: ${msg}`));
|
|
519
863
|
process.exit(3);
|
|
520
864
|
}
|
|
521
865
|
if (result.status === "rejected") {
|
|
522
866
|
if (isJson) {
|
|
523
867
|
console.log(JSON.stringify({ status: "rejected" }));
|
|
524
868
|
} else {
|
|
525
|
-
console.log(
|
|
869
|
+
console.log(chalk5.red("\nRegistration denied by owner."));
|
|
526
870
|
}
|
|
527
871
|
process.exit(1);
|
|
528
872
|
}
|
|
@@ -530,7 +874,7 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
530
874
|
if (isJson) {
|
|
531
875
|
console.log(JSON.stringify({ status: "timeout" }));
|
|
532
876
|
} else {
|
|
533
|
-
console.log(
|
|
877
|
+
console.log(chalk5.yellow("\nTimed out waiting for approval."));
|
|
534
878
|
}
|
|
535
879
|
process.exit(2);
|
|
536
880
|
}
|
|
@@ -549,30 +893,30 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
549
893
|
}));
|
|
550
894
|
} catch (err) {
|
|
551
895
|
const msg = err instanceof Error ? err.message : String(err);
|
|
552
|
-
console.error(isJson ? JSON.stringify({ error: msg }) :
|
|
896
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk5.red(`Failed to save output: ${msg}`));
|
|
553
897
|
process.exit(3);
|
|
554
898
|
}
|
|
555
899
|
const keyWritten = !!privateKey;
|
|
556
900
|
if (isJson) {
|
|
557
901
|
console.log(JSON.stringify({ status: "approved", agentId, keyPath: keyWritten ? keyPath : null, envPath }));
|
|
558
902
|
} else {
|
|
559
|
-
console.log(
|
|
903
|
+
console.log(chalk5.green(`
|
|
560
904
|
Approved! Agent ID: ${agentId}`));
|
|
561
905
|
if (keyWritten) {
|
|
562
|
-
console.log(
|
|
906
|
+
console.log(chalk5.gray(`Key: ${keyPath}`));
|
|
563
907
|
} else {
|
|
564
|
-
console.log(
|
|
908
|
+
console.log(chalk5.yellow(`Key: not written \u2014 copy it from the dashboard and paste into ${keyPath}`));
|
|
565
909
|
}
|
|
566
|
-
console.log(
|
|
910
|
+
console.log(chalk5.gray(`Env file: ${envPath}`));
|
|
567
911
|
console.log(`
|
|
568
912
|
Source the env file before calling any tool:`);
|
|
569
|
-
console.log(
|
|
913
|
+
console.log(chalk5.cyan(` source ${envPath}`));
|
|
570
914
|
}
|
|
571
915
|
if (keyWritten) {
|
|
572
916
|
try {
|
|
573
917
|
if (grantedPlatforms && grantedPlatforms.length > 0) {
|
|
574
918
|
writePermissionsFromEvent(agentId, resolvedOwnerId, grantedPlatforms);
|
|
575
|
-
if (!isJson) console.log(
|
|
919
|
+
if (!isJson) console.log(chalk5.gray(`Permissions: .claude/agentvalet-permissions.md`));
|
|
576
920
|
} else {
|
|
577
921
|
await runRefresh({
|
|
578
922
|
envFile: opts.output,
|
|
@@ -580,10 +924,10 @@ Source the env file before calling any tool:`);
|
|
|
580
924
|
ifStale: false,
|
|
581
925
|
quiet: isJson
|
|
582
926
|
});
|
|
583
|
-
if (!isJson) console.log(
|
|
927
|
+
if (!isJson) console.log(chalk5.gray(`Permissions: .claude/agentvalet-permissions.md`));
|
|
584
928
|
}
|
|
585
929
|
} catch {
|
|
586
|
-
if (!isJson) console.log(
|
|
930
|
+
if (!isJson) console.log(chalk5.yellow(`Permissions: run 'agentvalet-register refresh' to generate`));
|
|
587
931
|
}
|
|
588
932
|
}
|
|
589
933
|
process.exit(0);
|
|
@@ -598,12 +942,12 @@ program.command("refresh").description("Refresh approved permissions and update
|
|
|
598
942
|
});
|
|
599
943
|
} catch (err) {
|
|
600
944
|
const msg = err instanceof Error ? err.message : String(err);
|
|
601
|
-
console.error(
|
|
945
|
+
console.error(chalk5.red(`Refresh failed: ${msg}`));
|
|
602
946
|
process.exit(3);
|
|
603
947
|
}
|
|
604
948
|
});
|
|
605
949
|
program.command("mcp-server").description("Start the AgentValet MCP server (stdio transport)").action(async () => {
|
|
606
|
-
await import("./mcp-server-
|
|
950
|
+
await import("./mcp-server-LUNOQPUO.js");
|
|
607
951
|
});
|
|
608
952
|
program.parseAsync().catch((err) => {
|
|
609
953
|
console.error(err);
|
|
@@ -7,8 +7,8 @@ import { SignJWT, importPKCS8 } from "jose";
|
|
|
7
7
|
if (!process.env.AGENT_PRIVATE_KEY && process.env.AGENT_PRIVATE_KEY_PATH) {
|
|
8
8
|
try {
|
|
9
9
|
process.env.AGENT_PRIVATE_KEY = readFileSync(process.env.AGENT_PRIVATE_KEY_PATH, "utf-8").trim();
|
|
10
|
-
} catch (
|
|
11
|
-
process.stderr.write(`[agent-authority] Cannot read AGENT_PRIVATE_KEY_PATH: ${
|
|
10
|
+
} catch (err) {
|
|
11
|
+
process.stderr.write(`[agent-authority] Cannot read AGENT_PRIVATE_KEY_PATH: ${err instanceof Error ? err.message : err}
|
|
12
12
|
`);
|
|
13
13
|
process.exit(1);
|
|
14
14
|
}
|
|
@@ -25,23 +25,28 @@ var AGENT_PRIVATE_KEY_RAW = process.env.AGENT_PRIVATE_KEY;
|
|
|
25
25
|
var AGENT_ID = process.env.AGENT_ID;
|
|
26
26
|
var OWNER_ID = process.env.OWNER_ID;
|
|
27
27
|
var PROXY_URL = process.env.PROXY_URL.replace(/\/$/, "");
|
|
28
|
-
var
|
|
29
|
-
|
|
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;
|
|
30
34
|
const raw = AGENT_PRIVATE_KEY_RAW.trim();
|
|
31
35
|
const pem = raw.startsWith("-----") ? raw.replace(/\\n/g, "\n") : `-----BEGIN PRIVATE KEY-----
|
|
32
36
|
${raw}
|
|
33
37
|
-----END PRIVATE KEY-----`;
|
|
34
38
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
`
|
|
39
|
-
|
|
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);
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
async function signJWT() {
|
|
43
|
-
|
|
44
|
-
|
|
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);
|
|
45
50
|
}
|
|
46
51
|
function fetchWithTimeout(url, init, ms = 15e3) {
|
|
47
52
|
const ac = new AbortController();
|
|
@@ -55,12 +60,9 @@ async function bearerFetch(url, init) {
|
|
|
55
60
|
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...init.headers }
|
|
56
61
|
});
|
|
57
62
|
}
|
|
58
|
-
function
|
|
63
|
+
function errResult(msg) {
|
|
59
64
|
return { content: [{ type: "text", text: msg }], isError: true };
|
|
60
65
|
}
|
|
61
|
-
function pendingResponse() {
|
|
62
|
-
return err("Agent not yet activated \u2014 owner confirmation pending. Retry in 60 seconds.");
|
|
63
|
-
}
|
|
64
66
|
var ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
65
67
|
var server = new Server(
|
|
66
68
|
{ name: "agent-authority", version: "1.0.0" },
|
|
@@ -93,22 +95,26 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
93
95
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
94
96
|
const { name, arguments: args } = request.params;
|
|
95
97
|
if (name === "list_platforms") {
|
|
96
|
-
if (AGENT_PRIVATE_KEY_RAW === "PENDING_FIRST_CALL")
|
|
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
|
+
}
|
|
97
101
|
try {
|
|
98
102
|
const res = await bearerFetch(`${PROXY_URL}/v1/agent/permissions`, { method: "GET", headers: {} });
|
|
99
103
|
const body = await res.text();
|
|
100
|
-
if (!res.ok) return
|
|
104
|
+
if (!res.ok) return errResult(`Proxy error ${res.status}: ${body}`);
|
|
101
105
|
return { content: [{ type: "text", text: body }] };
|
|
102
106
|
} catch (e) {
|
|
103
|
-
return
|
|
107
|
+
return errResult(`Error: ${e instanceof Error ? e.message : e}`);
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
110
|
if (name === "use_platform") {
|
|
107
|
-
if (AGENT_PRIVATE_KEY_RAW === "PENDING_FIRST_CALL")
|
|
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
|
+
}
|
|
108
114
|
if (!args || typeof args.platform !== "string" || typeof args.endpoint !== "string" || typeof args.method !== "string" || typeof args.scope !== "string") {
|
|
109
|
-
return
|
|
115
|
+
return errResult("Missing required args: platform, endpoint, method, scope");
|
|
110
116
|
}
|
|
111
|
-
if (!ALLOWED_METHODS.includes(args.method)) return
|
|
117
|
+
if (!ALLOWED_METHODS.includes(args.method)) return errResult(`Invalid method: ${args.method}`);
|
|
112
118
|
try {
|
|
113
119
|
const res = await bearerFetch(`${PROXY_URL}/v1/actions`, {
|
|
114
120
|
method: "POST",
|
|
@@ -121,13 +127,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
121
127
|
})
|
|
122
128
|
});
|
|
123
129
|
const body = await res.text();
|
|
124
|
-
if (!res.ok) return
|
|
130
|
+
if (!res.ok) return errResult(`Proxy error ${res.status}: ${body}`);
|
|
125
131
|
return { content: [{ type: "text", text: body }] };
|
|
126
132
|
} catch (e) {
|
|
127
|
-
return
|
|
133
|
+
return errResult(`Error: ${e instanceof Error ? e.message : e}`);
|
|
128
134
|
}
|
|
129
135
|
}
|
|
130
|
-
return
|
|
136
|
+
return errResult(`Unknown tool: ${name}`);
|
|
131
137
|
});
|
|
132
138
|
var transport = new StdioServerTransport();
|
|
133
139
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvalet/register",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Self-register AI agents with AgentValet",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"README.md"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
14
|
+
"build": "tsup src/index.ts --format esm --dts --clean --shims",
|
|
15
15
|
"dev": "tsx src/index.ts",
|
|
16
16
|
"test": "vitest run",
|
|
17
17
|
"typecheck": "tsc --noEmit",
|
|
@@ -19,9 +19,12 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
22
|
+
"@octokit/auth-oauth-device": "^7.0.0",
|
|
22
23
|
"chalk": "^5.3.0",
|
|
23
24
|
"commander": "^12.0.0",
|
|
25
|
+
"conf": "^13.0.0",
|
|
24
26
|
"jose": "^6.2.2",
|
|
27
|
+
"open": "^10.0.0",
|
|
25
28
|
"ora": "^8.0.1",
|
|
26
29
|
"prompts": "^2.4.2",
|
|
27
30
|
"undici": "^6.0.0"
|