@agentvalet/register 0.3.1 → 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 +321 -97
- 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,21 +690,22 @@ 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
704
|
// src/config/ide.ts
|
|
614
|
-
import { mkdir as mkdir3, writeFile as writeFile4, readFile as readFile2 } from "fs/promises";
|
|
615
|
-
import { existsSync as
|
|
616
|
-
import
|
|
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";
|
|
708
|
+
import os from "os";
|
|
617
709
|
function buildMcpEntry(params) {
|
|
618
710
|
return {
|
|
619
711
|
command: "npx",
|
|
@@ -628,7 +720,7 @@ function buildMcpEntry(params) {
|
|
|
628
720
|
}
|
|
629
721
|
async function mergeJsonFile(filePath, key, value) {
|
|
630
722
|
let existing = {};
|
|
631
|
-
if (
|
|
723
|
+
if (existsSync4(filePath)) {
|
|
632
724
|
try {
|
|
633
725
|
const text = await readFile2(filePath, "utf8");
|
|
634
726
|
existing = JSON.parse(text);
|
|
@@ -640,12 +732,73 @@ async function mergeJsonFile(filePath, key, value) {
|
|
|
640
732
|
existing.mcpServers = mcpServers;
|
|
641
733
|
await writeFile4(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
642
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
|
+
}
|
|
643
796
|
async function writeIdeConfigs(params) {
|
|
644
797
|
const cwd = process.cwd();
|
|
645
798
|
const written = [];
|
|
646
799
|
const entry = buildMcpEntry(params);
|
|
647
|
-
const claudeMcpPath =
|
|
648
|
-
if (
|
|
800
|
+
const claudeMcpPath = path4.join(cwd, ".mcp.json");
|
|
801
|
+
if (existsSync4(claudeMcpPath) || existsSync4(path4.join(cwd, "CLAUDE.md"))) {
|
|
649
802
|
try {
|
|
650
803
|
await mergeJsonFile(claudeMcpPath, "agentvalet", entry);
|
|
651
804
|
written.push(".mcp.json");
|
|
@@ -653,9 +806,9 @@ async function writeIdeConfigs(params) {
|
|
|
653
806
|
console.warn(` Warning: could not write .mcp.json: ${err.message}`);
|
|
654
807
|
}
|
|
655
808
|
}
|
|
656
|
-
const cursorDir =
|
|
657
|
-
const cursorMcpPath =
|
|
658
|
-
if (
|
|
809
|
+
const cursorDir = path4.join(cwd, ".cursor");
|
|
810
|
+
const cursorMcpPath = path4.join(cursorDir, "mcp.json");
|
|
811
|
+
if (existsSync4(cursorDir)) {
|
|
659
812
|
try {
|
|
660
813
|
await mkdir3(cursorDir, { recursive: true });
|
|
661
814
|
await mergeJsonFile(cursorMcpPath, "agentvalet", entry);
|
|
@@ -667,20 +820,66 @@ async function writeIdeConfigs(params) {
|
|
|
667
820
|
return written;
|
|
668
821
|
}
|
|
669
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
|
+
|
|
670
869
|
// src/commands/onboard.ts
|
|
671
|
-
import
|
|
672
|
-
import
|
|
870
|
+
import os3 from "os";
|
|
871
|
+
import path6 from "path";
|
|
673
872
|
async function runOnboard() {
|
|
674
873
|
console.log("");
|
|
675
|
-
console.log(
|
|
676
|
-
console.log(
|
|
874
|
+
console.log(chalk6.bold(" AgentValet \u2014 Agent Registration"));
|
|
875
|
+
console.log(chalk6.dim(" agentvalet.ai"));
|
|
677
876
|
console.log("");
|
|
678
877
|
if (isAuthenticated()) {
|
|
679
878
|
const stored = getStoredConfig();
|
|
680
879
|
console.log(
|
|
681
|
-
|
|
880
|
+
chalk6.green(" \u2713 Already authenticated as") + chalk6.bold(` ${stored.email}`)
|
|
682
881
|
);
|
|
683
|
-
const { action } = await
|
|
882
|
+
const { action } = await prompts3({
|
|
684
883
|
type: "select",
|
|
685
884
|
name: "action",
|
|
686
885
|
message: "What would you like to do?",
|
|
@@ -699,7 +898,7 @@ async function runOnboard() {
|
|
|
699
898
|
}
|
|
700
899
|
if (action === "signout") {
|
|
701
900
|
clearConfig();
|
|
702
|
-
console.log(
|
|
901
|
+
console.log(chalk6.dim(" Signed out. Run again to re-authenticate."));
|
|
703
902
|
return;
|
|
704
903
|
}
|
|
705
904
|
await registerAgentFlow(stored.apiKey, stored.ownerId);
|
|
@@ -711,20 +910,20 @@ async function runOnboard() {
|
|
|
711
910
|
const result = await authenticateWithGitHub();
|
|
712
911
|
githubToken = result.token;
|
|
713
912
|
} catch (err) {
|
|
714
|
-
console.error(
|
|
913
|
+
console.error(chalk6.red(` \u2717 Authentication failed: ${err.message}`));
|
|
715
914
|
process.exit(1);
|
|
716
915
|
}
|
|
717
916
|
spinner = ora(" Fetching your GitHub profile...").start();
|
|
718
917
|
let githubUser;
|
|
719
918
|
try {
|
|
720
919
|
githubUser = await getGitHubUser(githubToken);
|
|
721
|
-
spinner.succeed(` GitHub: ${
|
|
920
|
+
spinner.succeed(` GitHub: ${chalk6.bold("@" + githubUser.login)}`);
|
|
722
921
|
} catch (err) {
|
|
723
922
|
spinner.fail(` Failed to fetch GitHub profile: ${err.message}`);
|
|
724
923
|
process.exit(1);
|
|
725
924
|
}
|
|
726
925
|
if (!githubUser.email) {
|
|
727
|
-
const { email } = await
|
|
926
|
+
const { email } = await prompts3({
|
|
728
927
|
type: "text",
|
|
729
928
|
name: "email",
|
|
730
929
|
message: "Your email address (not visible on GitHub):",
|
|
@@ -765,9 +964,9 @@ async function runOnboard() {
|
|
|
765
964
|
await registerAgentFlow(account.owner_id, account.owner_id);
|
|
766
965
|
}
|
|
767
966
|
async function registerAgentFlow(apiKey, ownerId) {
|
|
768
|
-
const hostname =
|
|
967
|
+
const hostname = os3.hostname().split(".")[0];
|
|
769
968
|
const dirName = process.cwd().split("/").pop() ?? process.cwd().split("\\").pop() ?? "agent";
|
|
770
|
-
const { agentName } = await
|
|
969
|
+
const { agentName } = await prompts3({
|
|
771
970
|
type: "text",
|
|
772
971
|
name: "agentName",
|
|
773
972
|
message: "Name for this agent:",
|
|
@@ -785,7 +984,7 @@ async function registerAgentFlow(apiKey, ownerId) {
|
|
|
785
984
|
name: agentName.trim(),
|
|
786
985
|
source: "cli"
|
|
787
986
|
}, apiKey);
|
|
788
|
-
spinner.succeed(` Agent registered: ${
|
|
987
|
+
spinner.succeed(` Agent registered: ${chalk6.bold(agent.agent_id)}`);
|
|
789
988
|
} catch (err) {
|
|
790
989
|
spinner.fail(` Registration failed: ${err.message}`);
|
|
791
990
|
process.exit(1);
|
|
@@ -798,23 +997,48 @@ async function registerAgentFlow(apiKey, ownerId) {
|
|
|
798
997
|
privateKeyPem: agent.private_key_pem
|
|
799
998
|
});
|
|
800
999
|
} catch (err) {
|
|
801
|
-
console.error(
|
|
802
|
-
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."));
|
|
803
1002
|
process.exit(1);
|
|
804
1003
|
}
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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
|
+
});
|
|
812
1016
|
for (const r of ideResults) {
|
|
813
|
-
console.log(
|
|
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();
|
|
814
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"));
|
|
815
1039
|
}
|
|
816
1040
|
console.log("");
|
|
817
|
-
console.log(
|
|
1041
|
+
console.log(chalk6.bold.green(" \u2713 All done!"));
|
|
818
1042
|
console.log("");
|
|
819
1043
|
console.log(" Set these in your environment:");
|
|
820
1044
|
console.log("");
|
|
@@ -822,25 +1046,25 @@ async function registerAgentFlow(apiKey, ownerId) {
|
|
|
822
1046
|
AGENT_ID: agent.agent_id,
|
|
823
1047
|
OWNER_ID: ownerId,
|
|
824
1048
|
PROXY_URL: getStoredConfig().proxyUrl,
|
|
825
|
-
AGENT_PRIVATE_KEY_PATH:
|
|
1049
|
+
AGENT_PRIVATE_KEY_PATH: path6.join(process.cwd(), ".agentvalet", "agent.key")
|
|
826
1050
|
}));
|
|
827
1051
|
console.log("");
|
|
828
|
-
console.log(
|
|
829
|
-
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"));
|
|
830
1054
|
console.log("");
|
|
831
1055
|
console.log(
|
|
832
|
-
` Dashboard: ${
|
|
1056
|
+
` Dashboard: ${chalk6.cyan.underline("https://app.agentvalet.ai")}`
|
|
833
1057
|
);
|
|
834
|
-
console.log(
|
|
1058
|
+
console.log(chalk6.dim(" Approve this agent in your dashboard to activate it."));
|
|
835
1059
|
console.log("");
|
|
836
1060
|
}
|
|
837
1061
|
function showCredentials(config) {
|
|
838
1062
|
console.log("");
|
|
839
1063
|
console.log(" Your AgentValet credentials:");
|
|
840
1064
|
console.log("");
|
|
841
|
-
console.log(` Owner ID: ${
|
|
842
|
-
console.log(` Proxy URL: ${
|
|
843
|
-
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)}`);
|
|
844
1068
|
console.log("");
|
|
845
1069
|
}
|
|
846
1070
|
|
|
@@ -875,7 +1099,7 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
875
1099
|
console.error(JSON.stringify({ error: "--owner or AGENTVALET_OWNER is required" }));
|
|
876
1100
|
process.exit(3);
|
|
877
1101
|
}
|
|
878
|
-
console.error(
|
|
1102
|
+
console.error(chalk7.red("Error: --owner or AGENTVALET_OWNER environment variable is required"));
|
|
879
1103
|
process.exit(3);
|
|
880
1104
|
}
|
|
881
1105
|
const scopeMap = /* @__PURE__ */ new Map();
|
|
@@ -883,7 +1107,7 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
883
1107
|
const colon = s.indexOf(":");
|
|
884
1108
|
if (colon === -1) {
|
|
885
1109
|
console.error(
|
|
886
|
-
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)`)
|
|
887
1111
|
);
|
|
888
1112
|
process.exit(3);
|
|
889
1113
|
}
|
|
@@ -906,11 +1130,11 @@ program.name("agentvalet-register").description("Self-register an AI agent with
|
|
|
906
1130
|
});
|
|
907
1131
|
} catch (err) {
|
|
908
1132
|
const msg = err instanceof Error ? err.message : String(err);
|
|
909
|
-
console.error(isJson ? JSON.stringify({ error: msg }) :
|
|
1133
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk7.red(`Registration failed: ${msg}`));
|
|
910
1134
|
process.exit(3);
|
|
911
1135
|
}
|
|
912
1136
|
if (!isJson) {
|
|
913
|
-
console.log(
|
|
1137
|
+
console.log(chalk7.cyan(`
|
|
914
1138
|
Approve at: https://agentvalet.ai/registrations
|
|
915
1139
|
`));
|
|
916
1140
|
}
|
|
@@ -918,7 +1142,7 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
918
1142
|
if (isJson) {
|
|
919
1143
|
console.log(JSON.stringify({ status: "pending", token }));
|
|
920
1144
|
} else {
|
|
921
|
-
console.log(
|
|
1145
|
+
console.log(chalk7.yellow("Registration submitted (--no-wait). Token:"), token);
|
|
922
1146
|
}
|
|
923
1147
|
process.exit(0);
|
|
924
1148
|
}
|
|
@@ -929,14 +1153,14 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
929
1153
|
});
|
|
930
1154
|
} catch (err) {
|
|
931
1155
|
const msg = err instanceof Error ? err.message : String(err);
|
|
932
|
-
console.error(isJson ? JSON.stringify({ error: msg }) :
|
|
1156
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk7.red(`Wait failed: ${msg}`));
|
|
933
1157
|
process.exit(3);
|
|
934
1158
|
}
|
|
935
1159
|
if (result.status === "rejected") {
|
|
936
1160
|
if (isJson) {
|
|
937
1161
|
console.log(JSON.stringify({ status: "rejected" }));
|
|
938
1162
|
} else {
|
|
939
|
-
console.log(
|
|
1163
|
+
console.log(chalk7.red("\nRegistration denied by owner."));
|
|
940
1164
|
}
|
|
941
1165
|
process.exit(1);
|
|
942
1166
|
}
|
|
@@ -944,7 +1168,7 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
944
1168
|
if (isJson) {
|
|
945
1169
|
console.log(JSON.stringify({ status: "timeout" }));
|
|
946
1170
|
} else {
|
|
947
|
-
console.log(
|
|
1171
|
+
console.log(chalk7.yellow("\nTimed out waiting for approval."));
|
|
948
1172
|
}
|
|
949
1173
|
process.exit(2);
|
|
950
1174
|
}
|
|
@@ -963,30 +1187,30 @@ Approve at: https://agentvalet.ai/registrations
|
|
|
963
1187
|
}));
|
|
964
1188
|
} catch (err) {
|
|
965
1189
|
const msg = err instanceof Error ? err.message : String(err);
|
|
966
|
-
console.error(isJson ? JSON.stringify({ error: msg }) :
|
|
1190
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk7.red(`Failed to save output: ${msg}`));
|
|
967
1191
|
process.exit(3);
|
|
968
1192
|
}
|
|
969
1193
|
const keyWritten = !!privateKey;
|
|
970
1194
|
if (isJson) {
|
|
971
1195
|
console.log(JSON.stringify({ status: "approved", agentId, keyPath: keyWritten ? keyPath : null, envPath }));
|
|
972
1196
|
} else {
|
|
973
|
-
console.log(
|
|
1197
|
+
console.log(chalk7.green(`
|
|
974
1198
|
Approved! Agent ID: ${agentId}`));
|
|
975
1199
|
if (keyWritten) {
|
|
976
|
-
console.log(
|
|
1200
|
+
console.log(chalk7.gray(`Key: ${keyPath}`));
|
|
977
1201
|
} else {
|
|
978
|
-
console.log(
|
|
1202
|
+
console.log(chalk7.yellow(`Key: not written \u2014 copy it from the dashboard and paste into ${keyPath}`));
|
|
979
1203
|
}
|
|
980
|
-
console.log(
|
|
1204
|
+
console.log(chalk7.gray(`Env file: ${envPath}`));
|
|
981
1205
|
console.log(`
|
|
982
1206
|
Source the env file before calling any tool:`);
|
|
983
|
-
console.log(
|
|
1207
|
+
console.log(chalk7.cyan(` source ${envPath}`));
|
|
984
1208
|
}
|
|
985
1209
|
if (keyWritten) {
|
|
986
1210
|
try {
|
|
987
1211
|
if (grantedPlatforms && grantedPlatforms.length > 0) {
|
|
988
1212
|
writePermissionsFromEvent(agentId, resolvedOwnerId, grantedPlatforms);
|
|
989
|
-
if (!isJson) console.log(
|
|
1213
|
+
if (!isJson) console.log(chalk7.gray(`Permissions: .claude/agentvalet-permissions.md`));
|
|
990
1214
|
} else {
|
|
991
1215
|
await runRefresh({
|
|
992
1216
|
envFile: opts.output,
|
|
@@ -994,10 +1218,10 @@ Source the env file before calling any tool:`);
|
|
|
994
1218
|
ifStale: false,
|
|
995
1219
|
quiet: isJson
|
|
996
1220
|
});
|
|
997
|
-
if (!isJson) console.log(
|
|
1221
|
+
if (!isJson) console.log(chalk7.gray(`Permissions: .claude/agentvalet-permissions.md`));
|
|
998
1222
|
}
|
|
999
1223
|
} catch {
|
|
1000
|
-
if (!isJson) console.log(
|
|
1224
|
+
if (!isJson) console.log(chalk7.yellow(`Permissions: run 'agentvalet-register refresh' to generate`));
|
|
1001
1225
|
}
|
|
1002
1226
|
}
|
|
1003
1227
|
process.exit(0);
|
|
@@ -1012,7 +1236,7 @@ program.command("refresh").description("Refresh approved permissions and update
|
|
|
1012
1236
|
});
|
|
1013
1237
|
} catch (err) {
|
|
1014
1238
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1015
|
-
console.error(
|
|
1239
|
+
console.error(chalk7.red(`Refresh failed: ${msg}`));
|
|
1016
1240
|
process.exit(3);
|
|
1017
1241
|
}
|
|
1018
1242
|
});
|