@agentvalet/register 0.3.0 → 0.3.2
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 +375 -81
- package/package.json +1 -1
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 chalk7 from "chalk";
|
|
6
6
|
|
|
7
7
|
// src/wizard.ts
|
|
8
8
|
import prompts from "prompts";
|
|
@@ -79,7 +79,7 @@ async function registerAgent(apiUrl, body) {
|
|
|
79
79
|
import { fetch as fetch3 } from "undici";
|
|
80
80
|
async function waitForApproval(apiUrl, token, timeoutMs, onTick) {
|
|
81
81
|
const deadline = Date.now() + timeoutMs;
|
|
82
|
-
const sseResult = await trySSE(apiUrl, token,
|
|
82
|
+
const sseResult = await trySSE(apiUrl, token, timeoutMs, onTick);
|
|
83
83
|
if (sseResult) return enrichWithKey(apiUrl, token, sseResult);
|
|
84
84
|
const pollResult = await poll(apiUrl, token, deadline, onTick);
|
|
85
85
|
return enrichWithKey(apiUrl, token, pollResult);
|
|
@@ -191,11 +191,102 @@ async function saveOutput(opts) {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
// src/commands/refresh.ts
|
|
194
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from "fs";
|
|
194
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync, statSync } from "fs";
|
|
195
195
|
import { createSign, createPrivateKey } from "crypto";
|
|
196
196
|
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
197
197
|
|
|
198
198
|
// src/lib/skill-writer.ts
|
|
199
|
+
import { existsSync, writeFileSync } from "fs";
|
|
200
|
+
import { execSync } from "child_process";
|
|
201
|
+
import path from "path";
|
|
202
|
+
import chalk2 from "chalk";
|
|
203
|
+
function writeAgentsMd() {
|
|
204
|
+
const dest = path.join(process.cwd(), "AGENTS.md");
|
|
205
|
+
if (existsSync(dest)) {
|
|
206
|
+
console.log(chalk2.dim(" AGENTS.md already exists \u2014 skipping"));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const content = `# AgentValet \u2014 Credential Governance
|
|
210
|
+
|
|
211
|
+
All external platform API calls must go through the AgentValet proxy.
|
|
212
|
+
Never call platform APIs (Slack, Stripe, Gmail, GitHub, etc.) directly.
|
|
213
|
+
|
|
214
|
+
## Making a platform call
|
|
215
|
+
|
|
216
|
+
Use the \`use_platform\` MCP tool:
|
|
217
|
+
|
|
218
|
+
\`\`\`
|
|
219
|
+
use_platform({
|
|
220
|
+
platform: "slack",
|
|
221
|
+
endpoint: "/api/chat.postMessage",
|
|
222
|
+
method: "POST",
|
|
223
|
+
scope: "slack:write",
|
|
224
|
+
data: { channel: "#general", text: "Hello" }
|
|
225
|
+
})
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
## Before any platform session
|
|
229
|
+
|
|
230
|
+
1. Call \`list_platforms\` to see approved platforms and scopes
|
|
231
|
+
2. Call \`authzen_evaluate\` before destructive operations
|
|
232
|
+
3. If a call returns \`pending_approval\` \u2014 wait, do not retry
|
|
233
|
+
|
|
234
|
+
## If AgentValet MCP is not available
|
|
235
|
+
|
|
236
|
+
Do not call platform APIs directly. Tell the user:
|
|
237
|
+
"AgentValet MCP is not connected. Run: npx @agentvalet/register"
|
|
238
|
+
`;
|
|
239
|
+
writeFileSync(dest, content, "utf8");
|
|
240
|
+
console.log(chalk2.green(" \u2713 AGENTS.md created in current directory"));
|
|
241
|
+
console.log(chalk2.dim(" Commit this file to your repo"));
|
|
242
|
+
}
|
|
243
|
+
function writeSkillMd() {
|
|
244
|
+
const dest = path.join(process.cwd(), "SKILL.md");
|
|
245
|
+
if (existsSync(dest)) {
|
|
246
|
+
console.log(chalk2.dim(" SKILL.md already exists \u2014 skipping"));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const content = `# AgentValet Skill
|
|
250
|
+
|
|
251
|
+
Routes all platform API calls through AgentValet's credential proxy.
|
|
252
|
+
|
|
253
|
+
## Making platform calls
|
|
254
|
+
|
|
255
|
+
Use the \`use_platform\` tool instead of calling APIs directly:
|
|
256
|
+
|
|
257
|
+
\`\`\`
|
|
258
|
+
use_platform({
|
|
259
|
+
platform: "slack",
|
|
260
|
+
endpoint: "/api/chat.postMessage",
|
|
261
|
+
method: "POST",
|
|
262
|
+
scope: "slack:write",
|
|
263
|
+
data: { channel: "#general", text: "Hello" }
|
|
264
|
+
})
|
|
265
|
+
\`\`\`
|
|
266
|
+
|
|
267
|
+
## Rules
|
|
268
|
+
|
|
269
|
+
1. Never call platform APIs directly
|
|
270
|
+
2. Call \`list_platforms\` before assuming a platform is available
|
|
271
|
+
3. Call \`authzen_evaluate\` before destructive operations
|
|
272
|
+
4. Wait for human approval when required \u2014 do not retry aggressively
|
|
273
|
+
|
|
274
|
+
Dashboard: https://app.agentvalet.ai
|
|
275
|
+
`;
|
|
276
|
+
writeFileSync(dest, content, "utf8");
|
|
277
|
+
console.log(chalk2.green(" \u2713 SKILL.md created in current directory"));
|
|
278
|
+
console.log(chalk2.dim(" Commit this file to your repo"));
|
|
279
|
+
}
|
|
280
|
+
function ensureMcpServer() {
|
|
281
|
+
try {
|
|
282
|
+
execSync("npx --yes @agentvalet/mcp-server --version", {
|
|
283
|
+
stdio: "ignore",
|
|
284
|
+
timeout: 1e4
|
|
285
|
+
});
|
|
286
|
+
} catch {
|
|
287
|
+
console.log(chalk2.dim(" @agentvalet/mcp-server will be fetched on first use via npx"));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
199
290
|
function generatePermissionsFile(agentId, ownerId, permissions) {
|
|
200
291
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
201
292
|
const platformSections = permissions.length === 0 ? "_No platforms approved yet. Visit https://agentvalet.ai to connect platforms._" : permissions.map((p) => {
|
|
@@ -274,7 +365,7 @@ function writePermissionsFromEvent(agentId, ownerId, platforms, outputPath = "./
|
|
|
274
365
|
const content = generatePermissionsFile(agentId, ownerId, platforms);
|
|
275
366
|
const resolvedPath = resolve2(outputPath);
|
|
276
367
|
mkdirSync(dirname2(resolvedPath), { recursive: true });
|
|
277
|
-
|
|
368
|
+
writeFileSync2(resolvedPath, content, "utf-8");
|
|
278
369
|
embedInClaudeMd(content);
|
|
279
370
|
ensureHook();
|
|
280
371
|
}
|
|
@@ -285,7 +376,7 @@ async function runRefresh(options) {
|
|
|
285
376
|
const stat = statSync(outputPath);
|
|
286
377
|
const fileIsFresh = Date.now() - stat.mtimeMs < 36e5;
|
|
287
378
|
const claudeMdPath = resolve2("CLAUDE.md");
|
|
288
|
-
const claudeMd =
|
|
379
|
+
const claudeMd = existsSync2(claudeMdPath) ? readFileSync2(claudeMdPath, "utf-8") : "";
|
|
289
380
|
const blockStart = claudeMd.indexOf(CLAUDE_START);
|
|
290
381
|
const blockEnd = claudeMd.indexOf(CLAUDE_END);
|
|
291
382
|
const blockContent = blockStart !== -1 && blockEnd !== -1 ? claudeMd.slice(blockStart, blockEnd) : "";
|
|
@@ -297,7 +388,7 @@ async function runRefresh(options) {
|
|
|
297
388
|
const envPath = resolve2(options.envFile);
|
|
298
389
|
let envVars;
|
|
299
390
|
try {
|
|
300
|
-
envVars = parseEnvFile(
|
|
391
|
+
envVars = parseEnvFile(readFileSync2(envPath, "utf-8"));
|
|
301
392
|
} catch {
|
|
302
393
|
throw new Error(`Cannot read env file: ${envPath}
|
|
303
394
|
Run 'agentvalet-register' first to create it.`);
|
|
@@ -311,7 +402,7 @@ Run 'agentvalet-register' first to create it.`);
|
|
|
311
402
|
}
|
|
312
403
|
let privateKeyPem;
|
|
313
404
|
try {
|
|
314
|
-
privateKeyPem =
|
|
405
|
+
privateKeyPem = readFileSync2(keyPath, "utf-8");
|
|
315
406
|
} catch {
|
|
316
407
|
throw new Error(`Cannot read private key: ${keyPath}`);
|
|
317
408
|
}
|
|
@@ -330,7 +421,7 @@ Run 'agentvalet-register' first to create it.`);
|
|
|
330
421
|
const platforms = json.data?.platforms ?? [];
|
|
331
422
|
const content = generatePermissionsFile(agentId, ownerId, platforms);
|
|
332
423
|
mkdirSync(dirname2(outputPath), { recursive: true });
|
|
333
|
-
|
|
424
|
+
writeFileSync2(outputPath, content, "utf-8");
|
|
334
425
|
embedInClaudeMd(content);
|
|
335
426
|
ensureHook();
|
|
336
427
|
if (!options.quiet) {
|
|
@@ -342,23 +433,23 @@ function embedInClaudeMd(permissionsContent) {
|
|
|
342
433
|
const block = `${CLAUDE_START}
|
|
343
434
|
${permissionsContent.trimEnd()}
|
|
344
435
|
${CLAUDE_END}`;
|
|
345
|
-
if (!
|
|
346
|
-
|
|
436
|
+
if (!existsSync2(claudeMdPath)) {
|
|
437
|
+
writeFileSync2(claudeMdPath, `# Agent Instructions
|
|
347
438
|
|
|
348
439
|
${block}
|
|
349
440
|
`, "utf-8");
|
|
350
441
|
return;
|
|
351
442
|
}
|
|
352
|
-
const existing =
|
|
443
|
+
const existing = readFileSync2(claudeMdPath, "utf-8");
|
|
353
444
|
if (existing.includes(CLAUDE_START)) {
|
|
354
445
|
const replaced = existing.replace(
|
|
355
446
|
new RegExp(`${CLAUDE_START}[\\s\\S]*?${CLAUDE_END}`, "m"),
|
|
356
447
|
block
|
|
357
448
|
);
|
|
358
|
-
|
|
449
|
+
writeFileSync2(claudeMdPath, replaced, "utf-8");
|
|
359
450
|
} else {
|
|
360
451
|
const cleaned = existing.replace(/^@\.claude\/agentvalet-permissions\.md\s*$/m, "").trimEnd();
|
|
361
|
-
|
|
452
|
+
writeFileSync2(claudeMdPath, `${cleaned}
|
|
362
453
|
|
|
363
454
|
${block}
|
|
364
455
|
`, "utf-8");
|
|
@@ -367,9 +458,9 @@ ${block}
|
|
|
367
458
|
function ensureHook() {
|
|
368
459
|
const settingsPath = resolve2(".claude/settings.local.json");
|
|
369
460
|
let settings = {};
|
|
370
|
-
if (
|
|
461
|
+
if (existsSync2(settingsPath)) {
|
|
371
462
|
try {
|
|
372
|
-
settings = JSON.parse(
|
|
463
|
+
settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
|
|
373
464
|
} catch {
|
|
374
465
|
return;
|
|
375
466
|
}
|
|
@@ -392,15 +483,15 @@ function ensureHook() {
|
|
|
392
483
|
settings.permissions = perms;
|
|
393
484
|
}
|
|
394
485
|
mkdirSync(dirname2(settingsPath), { recursive: true });
|
|
395
|
-
|
|
486
|
+
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
396
487
|
ensureMcpJson();
|
|
397
488
|
}
|
|
398
489
|
function ensureMcpJson() {
|
|
399
490
|
const mcpJsonPath = resolve2(".mcp.json");
|
|
400
491
|
let mcpConfig = {};
|
|
401
|
-
if (
|
|
492
|
+
if (existsSync2(mcpJsonPath)) {
|
|
402
493
|
try {
|
|
403
|
-
mcpConfig = JSON.parse(
|
|
494
|
+
mcpConfig = JSON.parse(readFileSync2(mcpJsonPath, "utf-8"));
|
|
404
495
|
} catch {
|
|
405
496
|
return;
|
|
406
497
|
}
|
|
@@ -408,10 +499,10 @@ function ensureMcpJson() {
|
|
|
408
499
|
const servers = mcpConfig.mcpServers ?? {};
|
|
409
500
|
if (servers["agent-authority"]) return;
|
|
410
501
|
const envPath = resolve2(".env.agentvalet");
|
|
411
|
-
if (!
|
|
502
|
+
if (!existsSync2(envPath)) return;
|
|
412
503
|
let envVars;
|
|
413
504
|
try {
|
|
414
|
-
envVars = parseEnvFile(
|
|
505
|
+
envVars = parseEnvFile(readFileSync2(envPath, "utf-8"));
|
|
415
506
|
} catch {
|
|
416
507
|
return;
|
|
417
508
|
}
|
|
@@ -431,18 +522,18 @@ function ensureMcpJson() {
|
|
|
431
522
|
}
|
|
432
523
|
};
|
|
433
524
|
mcpConfig.mcpServers = servers;
|
|
434
|
-
|
|
525
|
+
writeFileSync2(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
|
|
435
526
|
}
|
|
436
527
|
|
|
437
528
|
// src/commands/onboard.ts
|
|
438
|
-
import
|
|
529
|
+
import chalk6 from "chalk";
|
|
439
530
|
import ora from "ora";
|
|
440
|
-
import
|
|
531
|
+
import prompts3 from "prompts";
|
|
441
532
|
|
|
442
533
|
// src/auth/github-device-flow.ts
|
|
443
534
|
import { createOAuthDeviceAuth } from "@octokit/auth-oauth-device";
|
|
444
535
|
import open from "open";
|
|
445
|
-
import
|
|
536
|
+
import chalk3 from "chalk";
|
|
446
537
|
var GITHUB_CLIENT_ID = process.env.AGENTVALET_GITHUB_CLIENT_ID ?? "Ov23liXXXXXXXXXXXXXX";
|
|
447
538
|
async function authenticateWithGitHub() {
|
|
448
539
|
const auth = createOAuthDeviceAuth({
|
|
@@ -451,15 +542,15 @@ async function authenticateWithGitHub() {
|
|
|
451
542
|
scopes: ["user:email", "read:user"],
|
|
452
543
|
onVerification(verification) {
|
|
453
544
|
console.log("");
|
|
454
|
-
console.log(
|
|
545
|
+
console.log(chalk3.bold(" Authenticate with GitHub"));
|
|
455
546
|
console.log("");
|
|
456
|
-
console.log(` 1. Open: ${
|
|
457
|
-
console.log(` 2. Enter code: ${
|
|
547
|
+
console.log(` 1. Open: ${chalk3.cyan.underline(verification.verification_uri)}`);
|
|
548
|
+
console.log(` 2. Enter code: ${chalk3.bold.yellow(verification.user_code)}`);
|
|
458
549
|
console.log("");
|
|
459
550
|
open(verification.verification_uri).catch(() => {
|
|
460
|
-
console.log(
|
|
551
|
+
console.log(chalk3.dim(" (Could not open browser \u2014 navigate to the URL above manually)"));
|
|
461
552
|
});
|
|
462
|
-
console.log(
|
|
553
|
+
console.log(chalk3.dim(" Waiting for authorisation..."));
|
|
463
554
|
}
|
|
464
555
|
});
|
|
465
556
|
const tokenAuth = await auth({ type: "oauth" });
|
|
@@ -566,12 +657,12 @@ function isAuthenticated() {
|
|
|
566
657
|
|
|
567
658
|
// src/config/local.ts
|
|
568
659
|
import { mkdir as mkdir2, writeFile as writeFile2, appendFile, readFile } from "fs/promises";
|
|
569
|
-
import
|
|
660
|
+
import path2 from "path";
|
|
570
661
|
async function writeLocalConfig(config) {
|
|
571
|
-
const dir =
|
|
662
|
+
const dir = path2.join(process.cwd(), ".agentvalet");
|
|
572
663
|
await mkdir2(dir, { recursive: true });
|
|
573
664
|
await writeFile2(
|
|
574
|
-
|
|
665
|
+
path2.join(dir, "config.json"),
|
|
575
666
|
JSON.stringify({
|
|
576
667
|
agent_id: config.agentId,
|
|
577
668
|
agent_name: config.agentName,
|
|
@@ -581,14 +672,14 @@ async function writeLocalConfig(config) {
|
|
|
581
672
|
{ encoding: "utf8", mode: 384 }
|
|
582
673
|
);
|
|
583
674
|
await writeFile2(
|
|
584
|
-
|
|
675
|
+
path2.join(dir, "agent.key"),
|
|
585
676
|
config.privateKeyPem,
|
|
586
677
|
{ encoding: "utf8", mode: 384 }
|
|
587
678
|
);
|
|
588
679
|
if (process.platform === "win32") {
|
|
589
680
|
console.warn(" Note: file permissions (0o600) are not enforced on Windows. Keep .agentvalet/ in a secure location.");
|
|
590
681
|
}
|
|
591
|
-
const gitignorePath =
|
|
682
|
+
const gitignorePath = path2.join(process.cwd(), ".gitignore");
|
|
592
683
|
const gitignoreContent = await readFile(gitignorePath, "utf8").catch(() => null);
|
|
593
684
|
if (gitignoreContent !== null) {
|
|
594
685
|
const lines = gitignoreContent.split("\n").map((l) => l.trim());
|
|
@@ -599,30 +690,196 @@ async function writeLocalConfig(config) {
|
|
|
599
690
|
}
|
|
600
691
|
|
|
601
692
|
// src/output/env.ts
|
|
602
|
-
import
|
|
693
|
+
import chalk4 from "chalk";
|
|
603
694
|
import { appendFile as appendFile2, writeFile as writeFile3 } from "fs/promises";
|
|
604
|
-
import { existsSync as
|
|
605
|
-
import
|
|
695
|
+
import { existsSync as existsSync3 } from "fs";
|
|
696
|
+
import path3 from "path";
|
|
606
697
|
function formatEnvBlock(vars) {
|
|
607
698
|
return Object.entries(vars).map(([key, value]) => {
|
|
608
699
|
const truncated = value.length > 60 ? value.substring(0, 40) + "..." + value.substring(value.length - 10) : value;
|
|
609
|
-
return ` ${
|
|
700
|
+
return ` ${chalk4.dim(key + "=")}${chalk4.white(truncated)}`;
|
|
610
701
|
}).join("\n");
|
|
611
702
|
}
|
|
612
703
|
|
|
613
|
-
// src/
|
|
704
|
+
// src/config/ide.ts
|
|
705
|
+
import { mkdir as mkdir3, writeFile as writeFile4, readFile as readFile2, appendFile as appendFile3 } from "fs/promises";
|
|
706
|
+
import { existsSync as existsSync4 } from "fs";
|
|
707
|
+
import path4 from "path";
|
|
614
708
|
import os from "os";
|
|
709
|
+
function buildMcpEntry(params) {
|
|
710
|
+
return {
|
|
711
|
+
command: "npx",
|
|
712
|
+
args: ["-y", "@agentvalet/register", "mcp-server"],
|
|
713
|
+
env: {
|
|
714
|
+
AGENT_ID: params.agentId,
|
|
715
|
+
OWNER_ID: params.ownerId,
|
|
716
|
+
PROXY_URL: params.proxyUrl,
|
|
717
|
+
AGENT_PRIVATE_KEY_PATH: params.keyPath
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
async function mergeJsonFile(filePath, key, value) {
|
|
722
|
+
let existing = {};
|
|
723
|
+
if (existsSync4(filePath)) {
|
|
724
|
+
try {
|
|
725
|
+
const text = await readFile2(filePath, "utf8");
|
|
726
|
+
existing = JSON.parse(text);
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const mcpServers = existing.mcpServers ?? {};
|
|
731
|
+
mcpServers[key] = value;
|
|
732
|
+
existing.mcpServers = mcpServers;
|
|
733
|
+
await writeFile4(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
734
|
+
}
|
|
735
|
+
async function writeGlobalFrameworkConfigs(params) {
|
|
736
|
+
const written = [];
|
|
737
|
+
const privateKeyEscaped = params.privateKeyPem.replace(/\n/g, "\\n");
|
|
738
|
+
const codexConfigPath = path4.join(os.homedir(), ".codex", "config.toml");
|
|
739
|
+
if (existsSync4(codexConfigPath)) {
|
|
740
|
+
try {
|
|
741
|
+
const existing = await readFile2(codexConfigPath, "utf8");
|
|
742
|
+
if (!existing.includes("[mcp.servers.agentvalet]")) {
|
|
743
|
+
const addition = `
|
|
744
|
+
[mcp.servers.agentvalet]
|
|
745
|
+
command = "npx"
|
|
746
|
+
args = ["-y", "@agentvalet/mcp-server"]
|
|
747
|
+
|
|
748
|
+
[mcp.servers.agentvalet.env]
|
|
749
|
+
AGENT_ID = "${params.agentId}"
|
|
750
|
+
OWNER_ID = "${params.ownerId}"
|
|
751
|
+
PROXY_URL = "${params.proxyUrl}"
|
|
752
|
+
AGENT_PRIVATE_KEY = "${privateKeyEscaped}"
|
|
753
|
+
`;
|
|
754
|
+
await appendFile3(codexConfigPath, addition, "utf8");
|
|
755
|
+
written.push("~/.codex/config.toml");
|
|
756
|
+
}
|
|
757
|
+
} catch (err) {
|
|
758
|
+
console.warn(` Warning: could not write Codex CLI config: ${err.message}`);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const claudeConfigPath = path4.join(os.homedir(), ".claude", "claude_desktop_config.json");
|
|
762
|
+
if (existsSync4(claudeConfigPath)) {
|
|
763
|
+
try {
|
|
764
|
+
let config = {};
|
|
765
|
+
try {
|
|
766
|
+
const text = await readFile2(claudeConfigPath, "utf8");
|
|
767
|
+
config = JSON.parse(text);
|
|
768
|
+
} catch {
|
|
769
|
+
console.warn(" Warning: ~/.claude/claude_desktop_config.json is malformed \u2014 skipping");
|
|
770
|
+
config = null;
|
|
771
|
+
}
|
|
772
|
+
if (config !== null) {
|
|
773
|
+
if (config.mcpServers?.agentvalet) {
|
|
774
|
+
} else {
|
|
775
|
+
config.mcpServers = config.mcpServers ?? {};
|
|
776
|
+
config.mcpServers.agentvalet = {
|
|
777
|
+
command: "npx",
|
|
778
|
+
args: ["-y", "@agentvalet/mcp-server"],
|
|
779
|
+
env: {
|
|
780
|
+
AGENT_ID: params.agentId,
|
|
781
|
+
OWNER_ID: params.ownerId,
|
|
782
|
+
PROXY_URL: params.proxyUrl,
|
|
783
|
+
AGENT_PRIVATE_KEY: privateKeyEscaped
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
await writeFile4(claudeConfigPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
787
|
+
written.push("~/.claude/claude_desktop_config.json");
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
} catch (err) {
|
|
791
|
+
console.warn(` Warning: could not write Claude Code global config: ${err.message}`);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return written;
|
|
795
|
+
}
|
|
796
|
+
async function writeIdeConfigs(params) {
|
|
797
|
+
const cwd = process.cwd();
|
|
798
|
+
const written = [];
|
|
799
|
+
const entry = buildMcpEntry(params);
|
|
800
|
+
const claudeMcpPath = path4.join(cwd, ".mcp.json");
|
|
801
|
+
if (existsSync4(claudeMcpPath) || existsSync4(path4.join(cwd, "CLAUDE.md"))) {
|
|
802
|
+
try {
|
|
803
|
+
await mergeJsonFile(claudeMcpPath, "agentvalet", entry);
|
|
804
|
+
written.push(".mcp.json");
|
|
805
|
+
} catch (err) {
|
|
806
|
+
console.warn(` Warning: could not write .mcp.json: ${err.message}`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const cursorDir = path4.join(cwd, ".cursor");
|
|
810
|
+
const cursorMcpPath = path4.join(cursorDir, "mcp.json");
|
|
811
|
+
if (existsSync4(cursorDir)) {
|
|
812
|
+
try {
|
|
813
|
+
await mkdir3(cursorDir, { recursive: true });
|
|
814
|
+
await mergeJsonFile(cursorMcpPath, "agentvalet", entry);
|
|
815
|
+
written.push(".cursor/mcp.json");
|
|
816
|
+
} catch (err) {
|
|
817
|
+
console.warn(` Warning: could not write .cursor/mcp.json: ${err.message}`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return written;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// src/config/frameworks.ts
|
|
824
|
+
import { existsSync as existsSync5 } from "fs";
|
|
825
|
+
import os2 from "os";
|
|
826
|
+
import path5 from "path";
|
|
827
|
+
import prompts2 from "prompts";
|
|
828
|
+
import chalk5 from "chalk";
|
|
829
|
+
function detectFrameworks() {
|
|
830
|
+
const home = os2.homedir();
|
|
831
|
+
return {
|
|
832
|
+
codex: existsSync5(path5.join(home, ".codex", "config.toml")),
|
|
833
|
+
claudeCode: existsSync5(path5.join(home, ".claude")),
|
|
834
|
+
cursor: existsSync5(path5.join(process.cwd(), ".cursor")),
|
|
835
|
+
openclaw: existsSync5(path5.join(home, ".openclaw"))
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
async function frameworkSetupConsent(detected) {
|
|
839
|
+
const items = [];
|
|
840
|
+
if (detected.codex) {
|
|
841
|
+
items.push("Codex CLI \u2192 add MCP server to ~/.codex/config.toml");
|
|
842
|
+
items.push(" \u2192 copy AGENTS.md to current directory");
|
|
843
|
+
}
|
|
844
|
+
if (detected.openclaw) {
|
|
845
|
+
items.push("OpenClaw \u2192 copy SKILL.md to current directory");
|
|
846
|
+
}
|
|
847
|
+
if (detected.claudeCode) {
|
|
848
|
+
items.push("Claude Code \u2192 ~/.claude/claude_desktop_config.json");
|
|
849
|
+
}
|
|
850
|
+
if (detected.cursor) {
|
|
851
|
+
items.push("Cursor \u2192 .cursor/mcp.json");
|
|
852
|
+
}
|
|
853
|
+
if (items.length === 0) return false;
|
|
854
|
+
console.log("");
|
|
855
|
+
console.log(chalk5.bold(" Agent framework setup"));
|
|
856
|
+
for (const item of items) {
|
|
857
|
+
console.log(chalk5.dim(` ${item}`));
|
|
858
|
+
}
|
|
859
|
+
console.log("");
|
|
860
|
+
const { confirm } = await prompts2({
|
|
861
|
+
type: "confirm",
|
|
862
|
+
name: "confirm",
|
|
863
|
+
message: "Configure these automatically?",
|
|
864
|
+
initial: true
|
|
865
|
+
});
|
|
866
|
+
return confirm ?? false;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/commands/onboard.ts
|
|
870
|
+
import os3 from "os";
|
|
871
|
+
import path6 from "path";
|
|
615
872
|
async function runOnboard() {
|
|
616
873
|
console.log("");
|
|
617
|
-
console.log(
|
|
618
|
-
console.log(
|
|
874
|
+
console.log(chalk6.bold(" AgentValet \u2014 Agent Registration"));
|
|
875
|
+
console.log(chalk6.dim(" agentvalet.ai"));
|
|
619
876
|
console.log("");
|
|
620
877
|
if (isAuthenticated()) {
|
|
621
878
|
const stored = getStoredConfig();
|
|
622
879
|
console.log(
|
|
623
|
-
|
|
880
|
+
chalk6.green(" \u2713 Already authenticated as") + chalk6.bold(` ${stored.email}`)
|
|
624
881
|
);
|
|
625
|
-
const { action } = await
|
|
882
|
+
const { action } = await prompts3({
|
|
626
883
|
type: "select",
|
|
627
884
|
name: "action",
|
|
628
885
|
message: "What would you like to do?",
|
|
@@ -641,7 +898,7 @@ async function runOnboard() {
|
|
|
641
898
|
}
|
|
642
899
|
if (action === "signout") {
|
|
643
900
|
clearConfig();
|
|
644
|
-
console.log(
|
|
901
|
+
console.log(chalk6.dim(" Signed out. Run again to re-authenticate."));
|
|
645
902
|
return;
|
|
646
903
|
}
|
|
647
904
|
await registerAgentFlow(stored.apiKey, stored.ownerId);
|
|
@@ -653,20 +910,20 @@ async function runOnboard() {
|
|
|
653
910
|
const result = await authenticateWithGitHub();
|
|
654
911
|
githubToken = result.token;
|
|
655
912
|
} catch (err) {
|
|
656
|
-
console.error(
|
|
913
|
+
console.error(chalk6.red(` \u2717 Authentication failed: ${err.message}`));
|
|
657
914
|
process.exit(1);
|
|
658
915
|
}
|
|
659
916
|
spinner = ora(" Fetching your GitHub profile...").start();
|
|
660
917
|
let githubUser;
|
|
661
918
|
try {
|
|
662
919
|
githubUser = await getGitHubUser(githubToken);
|
|
663
|
-
spinner.succeed(` GitHub: ${
|
|
920
|
+
spinner.succeed(` GitHub: ${chalk6.bold("@" + githubUser.login)}`);
|
|
664
921
|
} catch (err) {
|
|
665
922
|
spinner.fail(` Failed to fetch GitHub profile: ${err.message}`);
|
|
666
923
|
process.exit(1);
|
|
667
924
|
}
|
|
668
925
|
if (!githubUser.email) {
|
|
669
|
-
const { email } = await
|
|
926
|
+
const { email } = await prompts3({
|
|
670
927
|
type: "text",
|
|
671
928
|
name: "email",
|
|
672
929
|
message: "Your email address (not visible on GitHub):",
|
|
@@ -707,9 +964,9 @@ async function runOnboard() {
|
|
|
707
964
|
await registerAgentFlow(account.owner_id, account.owner_id);
|
|
708
965
|
}
|
|
709
966
|
async function registerAgentFlow(apiKey, ownerId) {
|
|
710
|
-
const hostname =
|
|
967
|
+
const hostname = os3.hostname().split(".")[0];
|
|
711
968
|
const dirName = process.cwd().split("/").pop() ?? process.cwd().split("\\").pop() ?? "agent";
|
|
712
|
-
const { agentName } = await
|
|
969
|
+
const { agentName } = await prompts3({
|
|
713
970
|
type: "text",
|
|
714
971
|
name: "agentName",
|
|
715
972
|
message: "Name for this agent:",
|
|
@@ -727,7 +984,7 @@ async function registerAgentFlow(apiKey, ownerId) {
|
|
|
727
984
|
name: agentName.trim(),
|
|
728
985
|
source: "cli"
|
|
729
986
|
}, apiKey);
|
|
730
|
-
spinner.succeed(` Agent registered: ${
|
|
987
|
+
spinner.succeed(` Agent registered: ${chalk6.bold(agent.agent_id)}`);
|
|
731
988
|
} catch (err) {
|
|
732
989
|
spinner.fail(` Registration failed: ${err.message}`);
|
|
733
990
|
process.exit(1);
|
|
@@ -740,37 +997,74 @@ async function registerAgentFlow(apiKey, ownerId) {
|
|
|
740
997
|
privateKeyPem: agent.private_key_pem
|
|
741
998
|
});
|
|
742
999
|
} catch (err) {
|
|
743
|
-
console.error(
|
|
744
|
-
console.log(
|
|
1000
|
+
console.error(chalk6.red(` \u2717 Failed to write local config: ${err.message}`));
|
|
1001
|
+
console.log(chalk6.dim(" Agent was registered \u2014 run again to save config locally."));
|
|
745
1002
|
process.exit(1);
|
|
746
1003
|
}
|
|
1004
|
+
const detected = detectFrameworks();
|
|
1005
|
+
const shouldSetup = await frameworkSetupConsent(detected);
|
|
1006
|
+
if (shouldSetup) {
|
|
1007
|
+
console.log("");
|
|
1008
|
+
console.log(chalk6.bold(" Configuring agent frameworks..."));
|
|
1009
|
+
console.log("");
|
|
1010
|
+
const ideResults = await writeIdeConfigs({
|
|
1011
|
+
agentId: agent.agent_id,
|
|
1012
|
+
ownerId,
|
|
1013
|
+
proxyUrl: getStoredConfig().proxyUrl,
|
|
1014
|
+
keyPath: path6.join(process.cwd(), ".agentvalet", "agent.key")
|
|
1015
|
+
});
|
|
1016
|
+
for (const r of ideResults) {
|
|
1017
|
+
console.log(chalk6.green(` \u2713 ${r}`));
|
|
1018
|
+
}
|
|
1019
|
+
const globalResults = await writeGlobalFrameworkConfigs({
|
|
1020
|
+
agentId: agent.agent_id,
|
|
1021
|
+
ownerId,
|
|
1022
|
+
proxyUrl: getStoredConfig().proxyUrl,
|
|
1023
|
+
privateKeyPem: agent.private_key_pem
|
|
1024
|
+
});
|
|
1025
|
+
for (const r of globalResults) {
|
|
1026
|
+
console.log(chalk6.green(` \u2713 ${r}`));
|
|
1027
|
+
}
|
|
1028
|
+
if (detected.codex) {
|
|
1029
|
+
writeAgentsMd();
|
|
1030
|
+
}
|
|
1031
|
+
if (detected.openclaw) {
|
|
1032
|
+
writeSkillMd();
|
|
1033
|
+
}
|
|
1034
|
+
ensureMcpServer();
|
|
1035
|
+
} else if (shouldSetup === false) {
|
|
1036
|
+
console.log("");
|
|
1037
|
+
console.log(chalk6.dim(" To configure agent frameworks manually:"));
|
|
1038
|
+
console.log(chalk6.dim(" https://docs.agentvalet.ai/setup"));
|
|
1039
|
+
}
|
|
747
1040
|
console.log("");
|
|
748
|
-
console.log(
|
|
1041
|
+
console.log(chalk6.bold.green(" \u2713 All done!"));
|
|
749
1042
|
console.log("");
|
|
750
1043
|
console.log(" Set these in your environment:");
|
|
751
1044
|
console.log("");
|
|
752
1045
|
console.log(formatEnvBlock({
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
1046
|
+
AGENT_ID: agent.agent_id,
|
|
1047
|
+
OWNER_ID: ownerId,
|
|
1048
|
+
PROXY_URL: getStoredConfig().proxyUrl,
|
|
1049
|
+
AGENT_PRIVATE_KEY_PATH: path6.join(process.cwd(), ".agentvalet", "agent.key")
|
|
756
1050
|
}));
|
|
757
1051
|
console.log("");
|
|
758
|
-
console.log(
|
|
759
|
-
console.log(
|
|
1052
|
+
console.log(chalk6.dim(" Private key also saved to: .agentvalet/agent.key"));
|
|
1053
|
+
console.log(chalk6.dim(" Add .agentvalet/ to your .gitignore"));
|
|
760
1054
|
console.log("");
|
|
761
1055
|
console.log(
|
|
762
|
-
` Dashboard: ${
|
|
1056
|
+
` Dashboard: ${chalk6.cyan.underline("https://app.agentvalet.ai")}`
|
|
763
1057
|
);
|
|
764
|
-
console.log(
|
|
1058
|
+
console.log(chalk6.dim(" Approve this agent in your dashboard to activate it."));
|
|
765
1059
|
console.log("");
|
|
766
1060
|
}
|
|
767
1061
|
function showCredentials(config) {
|
|
768
1062
|
console.log("");
|
|
769
1063
|
console.log(" Your AgentValet credentials:");
|
|
770
1064
|
console.log("");
|
|
771
|
-
console.log(` Owner ID: ${
|
|
772
|
-
console.log(` Proxy URL: ${
|
|
773
|
-
console.log(` Email: ${
|
|
1065
|
+
console.log(` Owner ID: ${chalk6.bold(config.ownerId)}`);
|
|
1066
|
+
console.log(` Proxy URL: ${chalk6.bold(config.proxyUrl)}`);
|
|
1067
|
+
console.log(` Email: ${chalk6.bold(config.email)}`);
|
|
774
1068
|
console.log("");
|
|
775
1069
|
}
|
|
776
1070
|
|
|
@@ -805,7 +1099,7 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
805
1099
|
console.error(JSON.stringify({ error: "--owner or AGENTVALET_OWNER is required" }));
|
|
806
1100
|
process.exit(3);
|
|
807
1101
|
}
|
|
808
|
-
console.error(
|
|
1102
|
+
console.error(chalk7.red("Error: --owner or AGENTVALET_OWNER environment variable is required"));
|
|
809
1103
|
process.exit(3);
|
|
810
1104
|
}
|
|
811
1105
|
const scopeMap = /* @__PURE__ */ new Map();
|
|
@@ -813,7 +1107,7 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
813
1107
|
const colon = s.indexOf(":");
|
|
814
1108
|
if (colon === -1) {
|
|
815
1109
|
console.error(
|
|
816
|
-
isJson ? JSON.stringify({ error: `Invalid scope format: ${s} (expected platform:scope)` }) :
|
|
1110
|
+
isJson ? JSON.stringify({ error: `Invalid scope format: ${s} (expected platform:scope)` }) : chalk7.red(`Invalid scope format: ${s} (expected platform:scope)`)
|
|
817
1111
|
);
|
|
818
1112
|
process.exit(3);
|
|
819
1113
|
}
|
|
@@ -836,11 +1130,11 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
836
1130
|
});
|
|
837
1131
|
} catch (err) {
|
|
838
1132
|
const msg = err instanceof Error ? err.message : String(err);
|
|
839
|
-
console.error(isJson ? JSON.stringify({ error: msg }) :
|
|
1133
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk7.red(`Registration failed: ${msg}`));
|
|
840
1134
|
process.exit(3);
|
|
841
1135
|
}
|
|
842
1136
|
if (!isJson) {
|
|
843
|
-
console.log(
|
|
1137
|
+
console.log(chalk7.cyan(`
|
|
844
1138
|
Approve at: https://agentvalet.ai/registrations
|
|
845
1139
|
`));
|
|
846
1140
|
}
|
|
@@ -848,7 +1142,7 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
848
1142
|
if (isJson) {
|
|
849
1143
|
console.log(JSON.stringify({ status: "pending", token }));
|
|
850
1144
|
} else {
|
|
851
|
-
console.log(
|
|
1145
|
+
console.log(chalk7.yellow("Registration submitted (--no-wait). Token:"), token);
|
|
852
1146
|
}
|
|
853
1147
|
process.exit(0);
|
|
854
1148
|
}
|
|
@@ -859,14 +1153,14 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
859
1153
|
});
|
|
860
1154
|
} catch (err) {
|
|
861
1155
|
const msg = err instanceof Error ? err.message : String(err);
|
|
862
|
-
console.error(isJson ? JSON.stringify({ error: msg }) :
|
|
1156
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk7.red(`Wait failed: ${msg}`));
|
|
863
1157
|
process.exit(3);
|
|
864
1158
|
}
|
|
865
1159
|
if (result.status === "rejected") {
|
|
866
1160
|
if (isJson) {
|
|
867
1161
|
console.log(JSON.stringify({ status: "rejected" }));
|
|
868
1162
|
} else {
|
|
869
|
-
console.log(
|
|
1163
|
+
console.log(chalk7.red("\nRegistration denied by owner."));
|
|
870
1164
|
}
|
|
871
1165
|
process.exit(1);
|
|
872
1166
|
}
|
|
@@ -874,7 +1168,7 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
874
1168
|
if (isJson) {
|
|
875
1169
|
console.log(JSON.stringify({ status: "timeout" }));
|
|
876
1170
|
} else {
|
|
877
|
-
console.log(
|
|
1171
|
+
console.log(chalk7.yellow("\nTimed out waiting for approval."));
|
|
878
1172
|
}
|
|
879
1173
|
process.exit(2);
|
|
880
1174
|
}
|
|
@@ -893,30 +1187,30 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
893
1187
|
}));
|
|
894
1188
|
} catch (err) {
|
|
895
1189
|
const msg = err instanceof Error ? err.message : String(err);
|
|
896
|
-
console.error(isJson ? JSON.stringify({ error: msg }) :
|
|
1190
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk7.red(`Failed to save output: ${msg}`));
|
|
897
1191
|
process.exit(3);
|
|
898
1192
|
}
|
|
899
1193
|
const keyWritten = !!privateKey;
|
|
900
1194
|
if (isJson) {
|
|
901
1195
|
console.log(JSON.stringify({ status: "approved", agentId, keyPath: keyWritten ? keyPath : null, envPath }));
|
|
902
1196
|
} else {
|
|
903
|
-
console.log(
|
|
1197
|
+
console.log(chalk7.green(`
|
|
904
1198
|
Approved! Agent ID: ${agentId}`));
|
|
905
1199
|
if (keyWritten) {
|
|
906
|
-
console.log(
|
|
1200
|
+
console.log(chalk7.gray(`Key: ${keyPath}`));
|
|
907
1201
|
} else {
|
|
908
|
-
console.log(
|
|
1202
|
+
console.log(chalk7.yellow(`Key: not written \u2014 copy it from the dashboard and paste into ${keyPath}`));
|
|
909
1203
|
}
|
|
910
|
-
console.log(
|
|
1204
|
+
console.log(chalk7.gray(`Env file: ${envPath}`));
|
|
911
1205
|
console.log(`
|
|
912
1206
|
Source the env file before calling any tool:`);
|
|
913
|
-
console.log(
|
|
1207
|
+
console.log(chalk7.cyan(` source ${envPath}`));
|
|
914
1208
|
}
|
|
915
1209
|
if (keyWritten) {
|
|
916
1210
|
try {
|
|
917
1211
|
if (grantedPlatforms && grantedPlatforms.length > 0) {
|
|
918
1212
|
writePermissionsFromEvent(agentId, resolvedOwnerId, grantedPlatforms);
|
|
919
|
-
if (!isJson) console.log(
|
|
1213
|
+
if (!isJson) console.log(chalk7.gray(`Permissions: .claude/agentvalet-permissions.md`));
|
|
920
1214
|
} else {
|
|
921
1215
|
await runRefresh({
|
|
922
1216
|
envFile: opts.output,
|
|
@@ -924,10 +1218,10 @@ Source the env file before calling any tool:`);
|
|
|
924
1218
|
ifStale: false,
|
|
925
1219
|
quiet: isJson
|
|
926
1220
|
});
|
|
927
|
-
if (!isJson) console.log(
|
|
1221
|
+
if (!isJson) console.log(chalk7.gray(`Permissions: .claude/agentvalet-permissions.md`));
|
|
928
1222
|
}
|
|
929
1223
|
} catch {
|
|
930
|
-
if (!isJson) console.log(
|
|
1224
|
+
if (!isJson) console.log(chalk7.yellow(`Permissions: run 'agentvalet-register refresh' to generate`));
|
|
931
1225
|
}
|
|
932
1226
|
}
|
|
933
1227
|
process.exit(0);
|
|
@@ -942,7 +1236,7 @@ program.command("refresh").description("Refresh approved permissions and update
|
|
|
942
1236
|
});
|
|
943
1237
|
} catch (err) {
|
|
944
1238
|
const msg = err instanceof Error ? err.message : String(err);
|
|
945
|
-
console.error(
|
|
1239
|
+
console.error(chalk7.red(`Refresh failed: ${msg}`));
|
|
946
1240
|
process.exit(3);
|
|
947
1241
|
}
|
|
948
1242
|
});
|