@agentvalet/register 1.3.0 → 1.4.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 +21 -1
- package/dist/index.js +178 -102
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,9 +15,29 @@ No installation required. Authenticate with GitHub, and your agent is registered
|
|
|
15
15
|
1. Opens your browser to authenticate with GitHub
|
|
16
16
|
2. Creates your AgentValet account (or finds your existing one)
|
|
17
17
|
3. Registers this machine as an agent
|
|
18
|
-
4. Writes config to
|
|
18
|
+
4. Writes config and your private key to `~/.agentvalet/` in your **home**
|
|
19
|
+
directory (not the current directory)
|
|
19
20
|
5. Outputs the environment variables you need
|
|
20
21
|
|
|
22
|
+
## Already have an agent on this machine?
|
|
23
|
+
|
|
24
|
+
An agent's private key exists only on your machine — the proxy never sees it,
|
|
25
|
+
and there is no rotation endpoint. Overwriting it destroys that identity, along
|
|
26
|
+
with its grants and audit continuity.
|
|
27
|
+
|
|
28
|
+
So registering while `~/.agentvalet/agent.key` already belongs to a **different**
|
|
29
|
+
agent stops with an error rather than overwriting it. You have two options:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Keep both — store the new agent under ~/.agentvalet/<label>/
|
|
33
|
+
npx @agentvalet/register claim <bind_secret> --label work-laptop
|
|
34
|
+
|
|
35
|
+
# Replace the existing one — a timestamped backup is written first
|
|
36
|
+
npx @agentvalet/register --force
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Re-registering the *same* agent over its own key is always allowed.
|
|
40
|
+
|
|
21
41
|
## Requirements
|
|
22
42
|
|
|
23
43
|
- Node.js 18+
|
package/dist/index.js
CHANGED
|
@@ -180,8 +180,72 @@ function sleep(ms) {
|
|
|
180
180
|
|
|
181
181
|
// src/save.ts
|
|
182
182
|
import { writeFile, mkdir } from "fs/promises";
|
|
183
|
+
|
|
184
|
+
// src/config/key-guard.ts
|
|
185
|
+
import { existsSync, copyFileSync, readFileSync } from "fs";
|
|
186
|
+
import { dirname, join } from "path";
|
|
187
|
+
var KeyClobberError = class extends Error {
|
|
188
|
+
constructor(keyPath, existingAgentId, message) {
|
|
189
|
+
super(message);
|
|
190
|
+
this.keyPath = keyPath;
|
|
191
|
+
this.existingAgentId = existingAgentId;
|
|
192
|
+
this.name = "KeyClobberError";
|
|
193
|
+
}
|
|
194
|
+
keyPath;
|
|
195
|
+
existingAgentId;
|
|
196
|
+
};
|
|
197
|
+
function existingKeyOwner(keyPath) {
|
|
198
|
+
if (!existsSync(keyPath)) return null;
|
|
199
|
+
const configPath = join(dirname(keyPath), "config.json");
|
|
200
|
+
if (!existsSync(configPath)) return null;
|
|
201
|
+
try {
|
|
202
|
+
const parsed = JSON.parse(readFileSync(configPath, "utf8"));
|
|
203
|
+
const id = parsed.agentId ?? parsed.lastAgentId;
|
|
204
|
+
return typeof id === "string" && id ? id : null;
|
|
205
|
+
} catch {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function backupExistingKey(keyPath) {
|
|
210
|
+
if (!existsSync(keyPath)) return null;
|
|
211
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
212
|
+
let backup = `${keyPath}.bak.${stamp}`;
|
|
213
|
+
let n = 1;
|
|
214
|
+
while (existsSync(backup)) backup = `${keyPath}.bak.${stamp}-${n++}`;
|
|
215
|
+
copyFileSync(keyPath, backup);
|
|
216
|
+
return backup;
|
|
217
|
+
}
|
|
218
|
+
function assertSafeToWriteKey(keyPath, incomingAgentId, opts = {}) {
|
|
219
|
+
if (!existsSync(keyPath)) return;
|
|
220
|
+
const owner = existingKeyOwner(keyPath);
|
|
221
|
+
if (owner && owner === incomingAgentId) return;
|
|
222
|
+
if (opts.force) {
|
|
223
|
+
backupExistingKey(keyPath);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const whose = owner ? `agent ${owner}` : "another agent (owner unknown)";
|
|
227
|
+
throw new KeyClobberError(
|
|
228
|
+
keyPath,
|
|
229
|
+
owner,
|
|
230
|
+
[
|
|
231
|
+
`${keyPath} already holds the private key for ${whose}.`,
|
|
232
|
+
"",
|
|
233
|
+
"Overwriting it would destroy that identity permanently: the key exists",
|
|
234
|
+
"only on this machine and there is no way to recover or rotate it.",
|
|
235
|
+
"",
|
|
236
|
+
"Options:",
|
|
237
|
+
` \u2022 Keep both \u2014 move ${keyPath} aside first, or use 'claim --label <name>'`,
|
|
238
|
+
" to store an additional agent under ~/.agentvalet/<name>/.",
|
|
239
|
+
" \u2022 Replace it \u2014 re-run with --force. The existing key is copied to",
|
|
240
|
+
` ${keyPath}.bak.<timestamp> before being replaced.`
|
|
241
|
+
].join("\n")
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/save.ts
|
|
183
246
|
async function saveOutput(opts) {
|
|
184
247
|
await mkdir(AGENT_DIR, { recursive: true });
|
|
248
|
+
assertSafeToWriteKey(AGENT_KEY_PATH, opts.agentId, { force: opts.force });
|
|
185
249
|
await writeFile(AGENT_KEY_PATH, opts.privateKey, { mode: 384 });
|
|
186
250
|
saveConfig({
|
|
187
251
|
agentId: opts.agentId,
|
|
@@ -196,8 +260,8 @@ async function saveOutput(opts) {
|
|
|
196
260
|
}
|
|
197
261
|
|
|
198
262
|
// src/commands/refresh.ts
|
|
199
|
-
import { readFileSync as
|
|
200
|
-
import { dirname, join, resolve as resolve2 } from "path";
|
|
263
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync2, statSync } from "fs";
|
|
264
|
+
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
201
265
|
|
|
202
266
|
// src/lib/jwt.ts
|
|
203
267
|
import { createSign, createPrivateKey } from "crypto";
|
|
@@ -215,13 +279,13 @@ function signJwt(agentId, ownerId, privateKeyPem) {
|
|
|
215
279
|
}
|
|
216
280
|
|
|
217
281
|
// src/lib/skill-writer.ts
|
|
218
|
-
import { existsSync, writeFileSync, mkdirSync } from "fs";
|
|
282
|
+
import { existsSync as existsSync2, writeFileSync, mkdirSync } from "fs";
|
|
219
283
|
import { execSync } from "child_process";
|
|
220
284
|
import path from "path";
|
|
221
285
|
import chalk2 from "chalk";
|
|
222
286
|
function writeAgentsMd() {
|
|
223
287
|
const dest = path.join(process.cwd(), "AGENTS.md");
|
|
224
|
-
if (
|
|
288
|
+
if (existsSync2(dest)) {
|
|
225
289
|
console.log(chalk2.dim(" AGENTS.md already exists \u2014 skipping"));
|
|
226
290
|
return;
|
|
227
291
|
}
|
|
@@ -264,7 +328,7 @@ Do not call platform APIs directly. Tell the user:
|
|
|
264
328
|
}
|
|
265
329
|
function writeSkillMd() {
|
|
266
330
|
const dest = path.join(process.cwd(), "SKILL.md");
|
|
267
|
-
if (
|
|
331
|
+
if (existsSync2(dest)) {
|
|
268
332
|
console.log(chalk2.dim(" SKILL.md already exists \u2014 skipping"));
|
|
269
333
|
return;
|
|
270
334
|
}
|
|
@@ -302,7 +366,7 @@ Dashboard: https://app.agentvalet.ai
|
|
|
302
366
|
}
|
|
303
367
|
function writeQuickstartDoc(opts) {
|
|
304
368
|
const dest = path.join(process.cwd(), "AGENTVALET.md");
|
|
305
|
-
if (
|
|
369
|
+
if (existsSync2(dest)) {
|
|
306
370
|
console.log(chalk2.dim(" AGENTVALET.md already exists \u2014 skipping"));
|
|
307
371
|
return;
|
|
308
372
|
}
|
|
@@ -385,9 +449,9 @@ AGENT_PRIVATE_KEY_PATH=${keyPath}
|
|
|
385
449
|
}
|
|
386
450
|
function writeFactoryDroidDroid() {
|
|
387
451
|
const droidsDir = path.join(process.cwd(), ".factory", "droids");
|
|
388
|
-
if (!
|
|
452
|
+
if (!existsSync2(droidsDir)) mkdirSync(droidsDir, { recursive: true });
|
|
389
453
|
const dest = path.join(droidsDir, "agentvalet.yaml");
|
|
390
|
-
if (
|
|
454
|
+
if (existsSync2(dest)) {
|
|
391
455
|
console.log(chalk2.dim(" Factory Droid: .factory/droids/agentvalet.yaml already exists"));
|
|
392
456
|
return;
|
|
393
457
|
}
|
|
@@ -457,9 +521,9 @@ instructions: |
|
|
|
457
521
|
}
|
|
458
522
|
function writeFactoryDroidCommand() {
|
|
459
523
|
const commandsDir = path.join(process.cwd(), ".factory", "commands");
|
|
460
|
-
if (!
|
|
524
|
+
if (!existsSync2(commandsDir)) mkdirSync(commandsDir, { recursive: true });
|
|
461
525
|
const statusDest = path.join(commandsDir, "av-status.md");
|
|
462
|
-
if (!
|
|
526
|
+
if (!existsSync2(statusDest)) {
|
|
463
527
|
const statusContent = `# /av-status
|
|
464
528
|
|
|
465
529
|
Check your AgentValet agent status and approved platform connections.
|
|
@@ -482,7 +546,7 @@ Check your AgentValet agent status and approved platform connections.
|
|
|
482
546
|
console.log(chalk2.dim(" Factory Droid: .factory/commands/av-status.md already exists"));
|
|
483
547
|
}
|
|
484
548
|
const registerDest = path.join(commandsDir, "av-register.md");
|
|
485
|
-
if (!
|
|
549
|
+
if (!existsSync2(registerDest)) {
|
|
486
550
|
const registerContent = `# /av-register
|
|
487
551
|
|
|
488
552
|
Register or re-register this project with AgentValet.
|
|
@@ -509,9 +573,9 @@ After running, approve the agent at: https://app.agentvalet.ai/agents
|
|
|
509
573
|
}
|
|
510
574
|
function writeFactoryDroidReliabilityDroid() {
|
|
511
575
|
const droidsDir = path.join(process.cwd(), ".factory", "droids");
|
|
512
|
-
if (!
|
|
576
|
+
if (!existsSync2(droidsDir)) mkdirSync(droidsDir, { recursive: true });
|
|
513
577
|
const dest = path.join(droidsDir, "agentvalet-reliability.yaml");
|
|
514
|
-
if (
|
|
578
|
+
if (existsSync2(dest)) return;
|
|
515
579
|
const content = `# AgentValet Reliability Droid
|
|
516
580
|
# Monitors agent call patterns and surfaces governance issues
|
|
517
581
|
# Commit this file to your repo
|
|
@@ -677,7 +741,7 @@ To refresh this file: \`npx @agentvalet/register refresh\`
|
|
|
677
741
|
}
|
|
678
742
|
|
|
679
743
|
// src/lib/require-agent.ts
|
|
680
|
-
import { existsSync as
|
|
744
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
681
745
|
import { resolve } from "path";
|
|
682
746
|
import chalk3 from "chalk";
|
|
683
747
|
var NO_AGENT_MSG = chalk3.red("\n \u2717 No agent registered.\n") + chalk3.dim(
|
|
@@ -710,9 +774,9 @@ function tryResolveAgent(envFile = ".env.agentvalet") {
|
|
|
710
774
|
if (!keyPath) keyPath = AGENT_KEY_PATH;
|
|
711
775
|
if (!agentId || !ownerId) {
|
|
712
776
|
const envPath = resolve(envFile);
|
|
713
|
-
if (
|
|
777
|
+
if (existsSync3(envPath)) {
|
|
714
778
|
try {
|
|
715
|
-
const vars = parseEnvFile(
|
|
779
|
+
const vars = parseEnvFile(readFileSync3(envPath, "utf-8"));
|
|
716
780
|
agentId = agentId ?? vars["AGENTVALET_AGENT_ID"];
|
|
717
781
|
ownerId = ownerId ?? vars["AGENTVALET_OWNER_ID"];
|
|
718
782
|
apiUrl = vars["AGENTVALET_API_URL"] ?? apiUrl;
|
|
@@ -726,12 +790,12 @@ function tryResolveAgent(envFile = ".env.agentvalet") {
|
|
|
726
790
|
if (process.env["AGENTVALET_API_URL"]) apiUrl = process.env["AGENTVALET_API_URL"];
|
|
727
791
|
if (process.env["AGENTVALET_PRIVATE_KEY_PATH"]) keyPath = process.env["AGENTVALET_PRIVATE_KEY_PATH"];
|
|
728
792
|
if (!agentId || !ownerId || !keyPath) return null;
|
|
729
|
-
if (!
|
|
793
|
+
if (!existsSync3(resolve(keyPath))) return null;
|
|
730
794
|
return { agentId, ownerId, keyPath, apiUrl };
|
|
731
795
|
}
|
|
732
796
|
|
|
733
797
|
// src/config/frameworks.ts
|
|
734
|
-
import { existsSync as
|
|
798
|
+
import { existsSync as existsSync4 } from "fs";
|
|
735
799
|
import { execSync as execSync2 } from "child_process";
|
|
736
800
|
import os from "os";
|
|
737
801
|
import path2 from "path";
|
|
@@ -749,27 +813,27 @@ function codexDesktopInstalled() {
|
|
|
749
813
|
const home = os.homedir();
|
|
750
814
|
const platform = process.platform;
|
|
751
815
|
if (platform === "darwin") {
|
|
752
|
-
return
|
|
816
|
+
return existsSync4("/Applications/Codex.app") || existsSync4(path2.join(home, "Applications", "Codex.app"));
|
|
753
817
|
}
|
|
754
818
|
if (platform === "win32") {
|
|
755
819
|
const localAppData = process.env.LOCALAPPDATA ?? path2.join(home, "AppData", "Local");
|
|
756
820
|
const programFiles = process.env["ProgramFiles"] ?? "C:\\Program Files";
|
|
757
|
-
return
|
|
821
|
+
return existsSync4(path2.join(localAppData, "Programs", "codex")) || existsSync4(path2.join(localAppData, "Programs", "Codex")) || existsSync4(path2.join(programFiles, "Codex"));
|
|
758
822
|
}
|
|
759
|
-
return
|
|
823
|
+
return existsSync4("/usr/share/applications/codex.desktop") || existsSync4(path2.join(home, ".local", "share", "applications", "codex.desktop"));
|
|
760
824
|
}
|
|
761
825
|
function claudeDesktopInstalled() {
|
|
762
826
|
const home = os.homedir();
|
|
763
827
|
const platform = process.platform;
|
|
764
828
|
if (platform === "darwin") {
|
|
765
|
-
return
|
|
829
|
+
return existsSync4("/Applications/Claude.app") || existsSync4(path2.join(home, "Library", "Application Support", "Claude"));
|
|
766
830
|
}
|
|
767
831
|
if (platform === "win32") {
|
|
768
832
|
const appData = process.env.APPDATA ?? path2.join(home, "AppData", "Roaming");
|
|
769
833
|
const localAppData = process.env.LOCALAPPDATA ?? path2.join(home, "AppData", "Local");
|
|
770
|
-
return
|
|
834
|
+
return existsSync4(path2.join(appData, "Claude")) || existsSync4(path2.join(localAppData, "AnthropicClaude")) || existsSync4(path2.join(localAppData, "Programs", "claude"));
|
|
771
835
|
}
|
|
772
|
-
return
|
|
836
|
+
return existsSync4(path2.join(home, ".config", "Claude"));
|
|
773
837
|
}
|
|
774
838
|
function cursorInstalled() {
|
|
775
839
|
const home = os.homedir();
|
|
@@ -777,24 +841,24 @@ function cursorInstalled() {
|
|
|
777
841
|
if (platform === "win32") {
|
|
778
842
|
const appData = process.env.APPDATA ?? path2.join(home, "AppData", "Roaming");
|
|
779
843
|
const localAppData = process.env.LOCALAPPDATA ?? path2.join(home, "AppData", "Local");
|
|
780
|
-
return
|
|
844
|
+
return existsSync4(path2.join(appData, "Cursor")) || existsSync4(path2.join(localAppData, "Programs", "cursor"));
|
|
781
845
|
}
|
|
782
846
|
if (platform === "darwin") {
|
|
783
|
-
return
|
|
847
|
+
return existsSync4(path2.join(home, "Library", "Application Support", "Cursor")) || existsSync4("/Applications/Cursor.app");
|
|
784
848
|
}
|
|
785
|
-
return
|
|
849
|
+
return existsSync4(path2.join(home, ".config", "Cursor")) || existsSync4("/usr/share/applications/cursor.desktop");
|
|
786
850
|
}
|
|
787
851
|
function detectFrameworks() {
|
|
788
852
|
const home = os.homedir();
|
|
789
853
|
return {
|
|
790
|
-
codex:
|
|
854
|
+
codex: existsSync4(path2.join(home, ".codex")) || tryBinary("codex"),
|
|
791
855
|
codexDesktop: codexDesktopInstalled(),
|
|
792
|
-
claudeCode:
|
|
856
|
+
claudeCode: existsSync4(path2.join(home, ".claude")) || tryBinary("claude"),
|
|
793
857
|
claudeDesktop: claudeDesktopInstalled(),
|
|
794
|
-
cursor:
|
|
858
|
+
cursor: existsSync4(path2.join(process.cwd(), ".cursor")) || cursorInstalled(),
|
|
795
859
|
cursorCli: tryBinary("cursor-agent"),
|
|
796
|
-
openclaw:
|
|
797
|
-
factoryDroid:
|
|
860
|
+
openclaw: existsSync4(path2.join(home, ".openclaw")),
|
|
861
|
+
factoryDroid: existsSync4(path2.join(home, ".factory")) || existsSync4(path2.join(process.cwd(), ".factory"))
|
|
798
862
|
};
|
|
799
863
|
}
|
|
800
864
|
function resolveAgentType(detected) {
|
|
@@ -862,13 +926,13 @@ var CLAUDE_END = "<!-- agentvalet:end -->";
|
|
|
862
926
|
var HOOK_COMMAND = "npx @agentvalet/register refresh --if-stale --quiet 2>/dev/null || true";
|
|
863
927
|
var HOOK_MARKER = "agentvalet/register refresh";
|
|
864
928
|
function projectRootFor(outputPath) {
|
|
865
|
-
return
|
|
929
|
+
return dirname2(dirname2(resolve2(outputPath)));
|
|
866
930
|
}
|
|
867
931
|
function writePermissionsFromEvent(agentId, ownerId, platforms, outputPath = "./.claude/agentvalet-permissions.md") {
|
|
868
932
|
const content = generatePermissionsFile(agentId, ownerId, platforms);
|
|
869
933
|
const resolvedPath = resolve2(outputPath);
|
|
870
934
|
const projectRoot = projectRootFor(resolvedPath);
|
|
871
|
-
mkdirSync2(
|
|
935
|
+
mkdirSync2(dirname2(resolvedPath), { recursive: true });
|
|
872
936
|
writeFileSync2(resolvedPath, content, "utf-8");
|
|
873
937
|
embedInClaudeMd(content, projectRoot);
|
|
874
938
|
ensureHook(projectRoot);
|
|
@@ -908,8 +972,8 @@ async function runRefresh(options) {
|
|
|
908
972
|
try {
|
|
909
973
|
const stat = statSync(outputPath);
|
|
910
974
|
const fileIsFresh = Date.now() - stat.mtimeMs < 36e5;
|
|
911
|
-
const claudeMdPath =
|
|
912
|
-
const claudeMd =
|
|
975
|
+
const claudeMdPath = join2(projectRoot, "CLAUDE.md");
|
|
976
|
+
const claudeMd = existsSync5(claudeMdPath) ? readFileSync4(claudeMdPath, "utf-8") : "";
|
|
913
977
|
const blockStart = claudeMd.indexOf(CLAUDE_START);
|
|
914
978
|
const blockEnd = claudeMd.indexOf(CLAUDE_END);
|
|
915
979
|
const blockContent = blockStart !== -1 && blockEnd !== -1 ? claudeMd.slice(blockStart, blockEnd) : "";
|
|
@@ -919,14 +983,14 @@ async function runRefresh(options) {
|
|
|
919
983
|
}
|
|
920
984
|
}
|
|
921
985
|
const creds = tryResolveAgent(options.envFile);
|
|
922
|
-
if (!creds || !
|
|
986
|
+
if (!creds || !existsSync5(resolve2(creds.keyPath))) {
|
|
923
987
|
console.error(" \u2717 No agent registered.\n Run the following command first to register an agent in this directory:\n\n npx @agentvalet/register\n");
|
|
924
988
|
process.exit(1);
|
|
925
989
|
}
|
|
926
990
|
const { agentId, ownerId, keyPath, apiUrl } = creds;
|
|
927
991
|
let privateKeyPem;
|
|
928
992
|
try {
|
|
929
|
-
privateKeyPem =
|
|
993
|
+
privateKeyPem = readFileSync4(keyPath, "utf-8");
|
|
930
994
|
} catch {
|
|
931
995
|
throw new Error(`Cannot read private key: ${keyPath}`);
|
|
932
996
|
}
|
|
@@ -944,17 +1008,17 @@ async function runRefresh(options) {
|
|
|
944
1008
|
const json = await res.json();
|
|
945
1009
|
const platforms = json.data?.platforms ?? [];
|
|
946
1010
|
const content = generatePermissionsFile(agentId, ownerId, platforms);
|
|
947
|
-
mkdirSync2(
|
|
1011
|
+
mkdirSync2(dirname2(outputPath), { recursive: true });
|
|
948
1012
|
writeFileSync2(outputPath, content, "utf-8");
|
|
949
1013
|
embedInClaudeMd(content, projectRoot);
|
|
950
1014
|
ensureHook(projectRoot);
|
|
951
1015
|
const detected = detectFrameworks();
|
|
952
1016
|
const refreshed = [];
|
|
953
1017
|
if (detected.codex || detected.codexDesktop) {
|
|
954
|
-
if (embedPermissionsBlock(
|
|
1018
|
+
if (embedPermissionsBlock(join2(projectRoot, "AGENTS.md"), content)) refreshed.push("AGENTS.md");
|
|
955
1019
|
}
|
|
956
1020
|
if (detected.openclaw) {
|
|
957
|
-
if (embedPermissionsBlock(
|
|
1021
|
+
if (embedPermissionsBlock(join2(projectRoot, "SKILL.md"), content)) refreshed.push("SKILL.md");
|
|
958
1022
|
}
|
|
959
1023
|
if (!options.quiet) {
|
|
960
1024
|
const extras = refreshed.length > 0 ? ` (also: ${refreshed.join(", ")})` : "";
|
|
@@ -962,9 +1026,9 @@ async function runRefresh(options) {
|
|
|
962
1026
|
}
|
|
963
1027
|
}
|
|
964
1028
|
function embedPermissionsBlock(filePath, permissionsContent) {
|
|
965
|
-
if (!
|
|
1029
|
+
if (!existsSync5(filePath)) return false;
|
|
966
1030
|
try {
|
|
967
|
-
const existing =
|
|
1031
|
+
const existing = readFileSync4(filePath, "utf-8");
|
|
968
1032
|
const block = `${PERMISSIONS_START}
|
|
969
1033
|
${permissionsContent.trimEnd()}
|
|
970
1034
|
${PERMISSIONS_END}`;
|
|
@@ -987,18 +1051,18 @@ ${block}
|
|
|
987
1051
|
}
|
|
988
1052
|
}
|
|
989
1053
|
function embedInClaudeMd(permissionsContent, projectRoot) {
|
|
990
|
-
const claudeMdPath =
|
|
1054
|
+
const claudeMdPath = join2(projectRoot, "CLAUDE.md");
|
|
991
1055
|
const block = `${CLAUDE_START}
|
|
992
1056
|
${permissionsContent.trimEnd()}
|
|
993
1057
|
${CLAUDE_END}`;
|
|
994
|
-
if (!
|
|
1058
|
+
if (!existsSync5(claudeMdPath)) {
|
|
995
1059
|
writeFileSync2(claudeMdPath, `# Agent Instructions
|
|
996
1060
|
|
|
997
1061
|
${block}
|
|
998
1062
|
`, "utf-8");
|
|
999
1063
|
return;
|
|
1000
1064
|
}
|
|
1001
|
-
const existing =
|
|
1065
|
+
const existing = readFileSync4(claudeMdPath, "utf-8");
|
|
1002
1066
|
if (existing.includes(CLAUDE_START)) {
|
|
1003
1067
|
const replaced = existing.replace(
|
|
1004
1068
|
new RegExp(`${CLAUDE_START}[\\s\\S]*?${CLAUDE_END}`, "m"),
|
|
@@ -1014,11 +1078,11 @@ ${block}
|
|
|
1014
1078
|
}
|
|
1015
1079
|
}
|
|
1016
1080
|
function ensureHook(projectRoot) {
|
|
1017
|
-
const settingsPath =
|
|
1081
|
+
const settingsPath = join2(projectRoot, ".claude", "settings.local.json");
|
|
1018
1082
|
let settings = {};
|
|
1019
|
-
if (
|
|
1083
|
+
if (existsSync5(settingsPath)) {
|
|
1020
1084
|
try {
|
|
1021
|
-
settings = JSON.parse(
|
|
1085
|
+
settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
1022
1086
|
} catch {
|
|
1023
1087
|
return;
|
|
1024
1088
|
}
|
|
@@ -1046,16 +1110,16 @@ function ensureHook(projectRoot) {
|
|
|
1046
1110
|
perms.allow = [...allow, ...missing];
|
|
1047
1111
|
settings.permissions = perms;
|
|
1048
1112
|
}
|
|
1049
|
-
mkdirSync2(
|
|
1113
|
+
mkdirSync2(dirname2(settingsPath), { recursive: true });
|
|
1050
1114
|
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
1051
1115
|
ensureMcpJson(projectRoot);
|
|
1052
1116
|
}
|
|
1053
1117
|
function ensureMcpJson(projectRoot) {
|
|
1054
|
-
const mcpJsonPath =
|
|
1118
|
+
const mcpJsonPath = join2(projectRoot, ".mcp.json");
|
|
1055
1119
|
let mcpConfig = {};
|
|
1056
|
-
if (
|
|
1120
|
+
if (existsSync5(mcpJsonPath)) {
|
|
1057
1121
|
try {
|
|
1058
|
-
mcpConfig = JSON.parse(
|
|
1122
|
+
mcpConfig = JSON.parse(readFileSync4(mcpJsonPath, "utf-8"));
|
|
1059
1123
|
} catch {
|
|
1060
1124
|
return;
|
|
1061
1125
|
}
|
|
@@ -1254,7 +1318,7 @@ async function writeLocalConfig(config) {
|
|
|
1254
1318
|
// src/output/env.ts
|
|
1255
1319
|
import chalk6 from "chalk";
|
|
1256
1320
|
import { appendFile as appendFile2, writeFile as writeFile3 } from "fs/promises";
|
|
1257
|
-
import { existsSync as
|
|
1321
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1258
1322
|
import path4 from "path";
|
|
1259
1323
|
function formatEnvBlock(vars) {
|
|
1260
1324
|
return Object.entries(vars).map(([key, value]) => {
|
|
@@ -1265,7 +1329,7 @@ function formatEnvBlock(vars) {
|
|
|
1265
1329
|
|
|
1266
1330
|
// src/config/ide.ts
|
|
1267
1331
|
import { mkdir as mkdir3, writeFile as writeFile4, readFile as readFile2, appendFile as appendFile3, copyFile } from "fs/promises";
|
|
1268
|
-
import { existsSync as
|
|
1332
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1269
1333
|
import path5 from "path";
|
|
1270
1334
|
import os2 from "os";
|
|
1271
1335
|
function buildMcpEntry(params) {
|
|
@@ -1302,7 +1366,7 @@ function claudeDesktopFriendly() {
|
|
|
1302
1366
|
}
|
|
1303
1367
|
async function mergeJsonFile(filePath, key, value) {
|
|
1304
1368
|
let existing = {};
|
|
1305
|
-
if (
|
|
1369
|
+
if (existsSync7(filePath)) {
|
|
1306
1370
|
try {
|
|
1307
1371
|
const text = await readFile2(filePath, "utf8");
|
|
1308
1372
|
existing = JSON.parse(text);
|
|
@@ -1324,8 +1388,8 @@ env = { AGENT_ID = "${params.agentId}", OWNER_ID = "${params.ownerId}", PROXY_UR
|
|
|
1324
1388
|
}
|
|
1325
1389
|
async function appendCodexConfig(configPath) {
|
|
1326
1390
|
const dir = path5.dirname(configPath);
|
|
1327
|
-
if (!
|
|
1328
|
-
const existing =
|
|
1391
|
+
if (!existsSync7(dir)) await mkdir3(dir, { recursive: true });
|
|
1392
|
+
const existing = existsSync7(configPath) ? await readFile2(configPath, "utf8") : "";
|
|
1329
1393
|
if (existing.includes("[mcp_servers.agentvalet]")) return false;
|
|
1330
1394
|
return true;
|
|
1331
1395
|
}
|
|
@@ -1334,7 +1398,7 @@ async function writeGlobalFrameworkConfigs(params) {
|
|
|
1334
1398
|
const codexDir = path5.join(os2.homedir(), ".codex");
|
|
1335
1399
|
const codexConfigPath = path5.join(codexDir, "config.toml");
|
|
1336
1400
|
try {
|
|
1337
|
-
if (!
|
|
1401
|
+
if (!existsSync7(codexDir)) await mkdir3(codexDir, { recursive: true });
|
|
1338
1402
|
if (await appendCodexConfig(codexConfigPath)) {
|
|
1339
1403
|
await appendFile3(codexConfigPath, buildCodexTomlBlock(params), "utf8");
|
|
1340
1404
|
written.push("~/.codex/config.toml");
|
|
@@ -1359,7 +1423,7 @@ async function writeCodexProjectConfig(params) {
|
|
|
1359
1423
|
return written;
|
|
1360
1424
|
}
|
|
1361
1425
|
async function stripAgentvaletFromMcpJson(filePath) {
|
|
1362
|
-
if (!
|
|
1426
|
+
if (!existsSync7(filePath)) return false;
|
|
1363
1427
|
try {
|
|
1364
1428
|
const text = await readFile2(filePath, "utf8");
|
|
1365
1429
|
const config = JSON.parse(text);
|
|
@@ -1383,7 +1447,7 @@ async function cleanProjectScopedEntries() {
|
|
|
1383
1447
|
cleaned.push(".cursor/mcp.json (project entry removed)");
|
|
1384
1448
|
}
|
|
1385
1449
|
const factoryProjectMcp = path5.join(cwd, ".factory/mcp.json");
|
|
1386
|
-
if (
|
|
1450
|
+
if (existsSync7(factoryProjectMcp)) {
|
|
1387
1451
|
try {
|
|
1388
1452
|
const text = await readFile2(factoryProjectMcp, "utf8");
|
|
1389
1453
|
const config = JSON.parse(text);
|
|
@@ -1474,7 +1538,7 @@ async function writeIdeConfigs(params) {
|
|
|
1474
1538
|
}
|
|
1475
1539
|
async function mergeFactoryDroidMcpJson(filePath, key, value) {
|
|
1476
1540
|
let existing = {};
|
|
1477
|
-
if (
|
|
1541
|
+
if (existsSync7(filePath)) {
|
|
1478
1542
|
try {
|
|
1479
1543
|
const text = await readFile2(filePath, "utf8");
|
|
1480
1544
|
existing = JSON.parse(text);
|
|
@@ -1488,7 +1552,7 @@ async function mergeFactoryDroidMcpJson(filePath, key, value) {
|
|
|
1488
1552
|
}
|
|
1489
1553
|
async function updateGitignoreForFactoryDroid() {
|
|
1490
1554
|
const gitignorePath = path5.join(process.cwd(), ".gitignore");
|
|
1491
|
-
if (!
|
|
1555
|
+
if (!existsSync7(gitignorePath)) return;
|
|
1492
1556
|
const existing = await readFile2(gitignorePath, "utf8");
|
|
1493
1557
|
if (existing.includes(".factory/mcp.json")) return;
|
|
1494
1558
|
const additions = [
|
|
@@ -1870,8 +1934,8 @@ import chalk10 from "chalk";
|
|
|
1870
1934
|
// src/commands/uninstall.ts
|
|
1871
1935
|
import { Command as Command2 } from "commander";
|
|
1872
1936
|
import chalk9 from "chalk";
|
|
1873
|
-
import { existsSync as
|
|
1874
|
-
import { resolve as resolve3, join as
|
|
1937
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync3, rmSync } from "fs";
|
|
1938
|
+
import { resolve as resolve3, join as join3 } from "path";
|
|
1875
1939
|
import { homedir } from "os";
|
|
1876
1940
|
var CLAUDE_START2 = "<!-- agentvalet:start -->";
|
|
1877
1941
|
var CLAUDE_END2 = "<!-- agentvalet:end -->";
|
|
@@ -1879,9 +1943,9 @@ var PERMISSIONS_START2 = "<!-- agentvalet:permissions:start -->";
|
|
|
1879
1943
|
var PERMISSIONS_END2 = "<!-- agentvalet:permissions:end -->";
|
|
1880
1944
|
var HOOK_MARKER2 = "agentvalet/register refresh";
|
|
1881
1945
|
function stripPermissionsBlock(filePath) {
|
|
1882
|
-
if (!
|
|
1946
|
+
if (!existsSync8(filePath)) return false;
|
|
1883
1947
|
try {
|
|
1884
|
-
const content =
|
|
1948
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1885
1949
|
if (!content.includes(PERMISSIONS_START2)) return false;
|
|
1886
1950
|
const stripped = content.replace(new RegExp(`${PERMISSIONS_START2}[\\s\\S]*?${PERMISSIONS_END2}`, "m"), "").replace(/(\r?\n){3,}/g, "\n\n").trimEnd();
|
|
1887
1951
|
writeFileSync3(filePath, stripped + "\n", "utf-8");
|
|
@@ -1892,8 +1956,8 @@ function stripPermissionsBlock(filePath) {
|
|
|
1892
1956
|
}
|
|
1893
1957
|
function stripClaudeMd() {
|
|
1894
1958
|
const path8 = resolve3("CLAUDE.md");
|
|
1895
|
-
if (!
|
|
1896
|
-
let content =
|
|
1959
|
+
if (!existsSync8(path8)) return;
|
|
1960
|
+
let content = readFileSync5(path8, "utf-8");
|
|
1897
1961
|
content = content.replace(new RegExp(`${CLAUDE_START2}[\\s\\S]*?${CLAUDE_END2}`, "m"), "");
|
|
1898
1962
|
content = content.replace(/^@\.claude\/agentvalet-permissions\.md\s*$/m, "");
|
|
1899
1963
|
content = content.replace(/(\r?\n){3,}/g, "\n\n").trim();
|
|
@@ -1901,9 +1965,9 @@ function stripClaudeMd() {
|
|
|
1901
1965
|
}
|
|
1902
1966
|
function stripSettingsJson() {
|
|
1903
1967
|
const path8 = resolve3(".claude/settings.local.json");
|
|
1904
|
-
if (!
|
|
1968
|
+
if (!existsSync8(path8)) return;
|
|
1905
1969
|
try {
|
|
1906
|
-
const settings = JSON.parse(
|
|
1970
|
+
const settings = JSON.parse(readFileSync5(path8, "utf-8"));
|
|
1907
1971
|
const hooks = settings.hooks ?? {};
|
|
1908
1972
|
const ups = hooks.UserPromptSubmit ?? [];
|
|
1909
1973
|
hooks.UserPromptSubmit = ups.filter(
|
|
@@ -1919,9 +1983,9 @@ function stripSettingsJson() {
|
|
|
1919
1983
|
}
|
|
1920
1984
|
}
|
|
1921
1985
|
function stripMcpJson(filePath, options = {}) {
|
|
1922
|
-
if (!
|
|
1986
|
+
if (!existsSync8(filePath)) return false;
|
|
1923
1987
|
try {
|
|
1924
|
-
const config = JSON.parse(
|
|
1988
|
+
const config = JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
1925
1989
|
let removed = false;
|
|
1926
1990
|
if (options.wrapped === false) {
|
|
1927
1991
|
if (config["agentvalet"] !== void 0) {
|
|
@@ -1945,9 +2009,9 @@ function stripMcpJson(filePath, options = {}) {
|
|
|
1945
2009
|
}
|
|
1946
2010
|
}
|
|
1947
2011
|
function stripCodexTomlAt(filePath) {
|
|
1948
|
-
if (!
|
|
2012
|
+
if (!existsSync8(filePath)) return false;
|
|
1949
2013
|
try {
|
|
1950
|
-
const content =
|
|
2014
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1951
2015
|
if (!content.includes("[mcp_servers.agentvalet]")) return false;
|
|
1952
2016
|
const stripped = content.replace(
|
|
1953
2017
|
/\r?\n?\[mcp_servers\.agentvalet\][\s\S]*?(?=\r?\n\[|\r?\n*$)/,
|
|
@@ -1960,9 +2024,9 @@ function stripCodexTomlAt(filePath) {
|
|
|
1960
2024
|
}
|
|
1961
2025
|
}
|
|
1962
2026
|
function isAgentValetGeneratedDoc(filePath, markers) {
|
|
1963
|
-
if (!
|
|
2027
|
+
if (!existsSync8(filePath)) return false;
|
|
1964
2028
|
try {
|
|
1965
|
-
const content =
|
|
2029
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1966
2030
|
const firstLine = content.split(/\r?\n/).find((l) => l.trim().length > 0) ?? "";
|
|
1967
2031
|
return markers.some((m) => firstLine.startsWith(m));
|
|
1968
2032
|
} catch {
|
|
@@ -1974,13 +2038,13 @@ function cleanLocally() {
|
|
|
1974
2038
|
const stripped = [];
|
|
1975
2039
|
const r = (p) => {
|
|
1976
2040
|
const abs = resolve3(p);
|
|
1977
|
-
if (
|
|
2041
|
+
if (existsSync8(abs)) {
|
|
1978
2042
|
rmSync(abs, { recursive: true, force: true });
|
|
1979
2043
|
removed.push(abs);
|
|
1980
2044
|
}
|
|
1981
2045
|
};
|
|
1982
2046
|
const rAbs = (abs) => {
|
|
1983
|
-
if (
|
|
2047
|
+
if (existsSync8(abs)) {
|
|
1984
2048
|
rmSync(abs, { recursive: true, force: true });
|
|
1985
2049
|
removed.push(abs);
|
|
1986
2050
|
}
|
|
@@ -2003,12 +2067,12 @@ function cleanLocally() {
|
|
|
2003
2067
|
if (stripMcpJson(resolve3(".cursor/mcp.json"))) stripped.push(".cursor/mcp.json");
|
|
2004
2068
|
if (stripMcpJson(resolve3(".factory/mcp.json"), { wrapped: false })) stripped.push(".factory/mcp.json");
|
|
2005
2069
|
const home = homedir();
|
|
2006
|
-
if (stripMcpJson(
|
|
2007
|
-
const claudeDesktopConfig = process.platform === "darwin" ?
|
|
2070
|
+
if (stripMcpJson(join3(home, ".claude.json"))) stripped.push("~/.claude.json");
|
|
2071
|
+
const claudeDesktopConfig = process.platform === "darwin" ? join3(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : process.platform === "win32" ? join3(process.env.APPDATA ?? join3(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json") : join3(home, ".config", "Claude", "claude_desktop_config.json");
|
|
2008
2072
|
if (stripMcpJson(claudeDesktopConfig)) stripped.push("claude_desktop_config.json");
|
|
2009
|
-
if (stripMcpJson(
|
|
2010
|
-
if (stripMcpJson(
|
|
2011
|
-
if (stripCodexTomlAt(
|
|
2073
|
+
if (stripMcpJson(join3(home, ".cursor", "mcp.json"))) stripped.push("~/.cursor/mcp.json");
|
|
2074
|
+
if (stripMcpJson(join3(home, ".factory", "mcp.json"), { wrapped: false })) stripped.push("~/.factory/mcp.json");
|
|
2075
|
+
if (stripCodexTomlAt(join3(home, ".codex", "config.toml"))) stripped.push("~/.codex/config.toml");
|
|
2012
2076
|
if (stripCodexTomlAt(resolve3(".codex/config.toml"))) stripped.push(".codex/config.toml");
|
|
2013
2077
|
stripClaudeMd();
|
|
2014
2078
|
stripSettingsJson();
|
|
@@ -2113,7 +2177,7 @@ function uninstallCommand() {
|
|
|
2113
2177
|
}
|
|
2114
2178
|
let privateKeyPem;
|
|
2115
2179
|
try {
|
|
2116
|
-
privateKeyPem =
|
|
2180
|
+
privateKeyPem = readFileSync5(creds.keyPath, "utf-8").trim();
|
|
2117
2181
|
} catch {
|
|
2118
2182
|
}
|
|
2119
2183
|
if (!privateKeyPem) {
|
|
@@ -2230,13 +2294,13 @@ function revokeCommand() {
|
|
|
2230
2294
|
import { Command as Command4 } from "commander";
|
|
2231
2295
|
import chalk11 from "chalk";
|
|
2232
2296
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile8 } from "fs/promises";
|
|
2233
|
-
import { dirname as
|
|
2297
|
+
import { dirname as dirname3 } from "path";
|
|
2234
2298
|
|
|
2235
2299
|
// src/lib/claude-config.ts
|
|
2236
2300
|
import { homedir as homedir2 } from "os";
|
|
2237
|
-
import { join as
|
|
2301
|
+
import { join as join4 } from "path";
|
|
2238
2302
|
import { readFile as readFile3, writeFile as writeFile6, mkdir as mkdir4, access } from "fs/promises";
|
|
2239
|
-
var CLAUDE_CONFIG_PATH =
|
|
2303
|
+
var CLAUDE_CONFIG_PATH = join4(homedir2(), ".claude", "config");
|
|
2240
2304
|
async function writeKeyToClaudeConfig(agentId, privateKeyPem) {
|
|
2241
2305
|
let config = {};
|
|
2242
2306
|
try {
|
|
@@ -2247,7 +2311,7 @@ async function writeKeyToClaudeConfig(agentId, privateKeyPem) {
|
|
|
2247
2311
|
}
|
|
2248
2312
|
config.agent_keys = config.agent_keys ?? {};
|
|
2249
2313
|
config.agent_keys[agentId] = Buffer.from(privateKeyPem).toString("base64");
|
|
2250
|
-
await mkdir4(
|
|
2314
|
+
await mkdir4(join4(homedir2(), ".claude"), { recursive: true });
|
|
2251
2315
|
await writeFile6(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
2252
2316
|
}
|
|
2253
2317
|
|
|
@@ -2282,7 +2346,7 @@ function injectCommand() {
|
|
|
2282
2346
|
"--target <target>",
|
|
2283
2347
|
"Where to write the key: agentvalet (default \u2014 modern store + MCP configs), env-local (.env.local), claude-legacy (~/.claude/config, deprecated)",
|
|
2284
2348
|
"agentvalet"
|
|
2285
|
-
).option("--no-frameworks", "Skip IDE/framework configuration prompts (only with --target agentvalet)").option("--project", "Write project-scoped framework configs instead of user-scoped").action(async (opts) => {
|
|
2349
|
+
).option("--no-frameworks", "Skip IDE/framework configuration prompts (only with --target agentvalet)").option("--project", "Write project-scoped framework configs instead of user-scoped").option("--force", "Replace an existing agent key (a timestamped backup is written first)").action(async (opts) => {
|
|
2286
2350
|
let privateKeyPem;
|
|
2287
2351
|
try {
|
|
2288
2352
|
privateKeyPem = await readFile5(opts.keyFile, "utf-8");
|
|
@@ -2302,7 +2366,8 @@ function injectCommand() {
|
|
|
2302
2366
|
console.error(chalk11.dim(" Find it at https://app.agentvalet.ai or in your existing ~/.agentvalet/config.json"));
|
|
2303
2367
|
process.exit(1);
|
|
2304
2368
|
}
|
|
2305
|
-
await mkdir5(
|
|
2369
|
+
await mkdir5(dirname3(AGENT_KEY_PATH), { recursive: true });
|
|
2370
|
+
assertSafeToWriteKey(AGENT_KEY_PATH, opts.agentId, { force: opts.force });
|
|
2306
2371
|
await writeFile8(AGENT_KEY_PATH, privateKeyPem, { mode: 384 });
|
|
2307
2372
|
saveConfig({
|
|
2308
2373
|
agentId: opts.agentId,
|
|
@@ -2357,7 +2422,7 @@ function injectCommand() {
|
|
|
2357
2422
|
process.exit(1);
|
|
2358
2423
|
}
|
|
2359
2424
|
} catch (err) {
|
|
2360
|
-
console.error(chalk11.red(String(err)));
|
|
2425
|
+
console.error(chalk11.red(err instanceof KeyClobberError ? err.message : String(err)));
|
|
2361
2426
|
process.exit(1);
|
|
2362
2427
|
}
|
|
2363
2428
|
console.log(chalk11.green("\n Done. Your agent is ready to use."));
|
|
@@ -2400,7 +2465,7 @@ async function bindInstallToken(proxyUrl, bootstrapToken) {
|
|
|
2400
2465
|
return { ...body, privateKeyPem: privateKey };
|
|
2401
2466
|
}
|
|
2402
2467
|
function installCommand() {
|
|
2403
|
-
return new Command5("install").description("Install an agent on this machine using a one-time install token (generates the keypair locally and binds it)").argument("<token>", "install token (itok_...) from the dashboard").option("--no-frameworks", "skip IDE/framework configuration prompts").option("--project", "write project-scoped configs (.mcp.json, .cursor/mcp.json) instead of user-scoped (~/.claude.json, ~/.cursor/mcp.json)").action(async (token, opts) => {
|
|
2468
|
+
return new Command5("install").description("Install an agent on this machine using a one-time install token (generates the keypair locally and binds it)").argument("<token>", "install token (itok_...) from the dashboard").option("--no-frameworks", "skip IDE/framework configuration prompts").option("--project", "write project-scoped configs (.mcp.json, .cursor/mcp.json) instead of user-scoped (~/.claude.json, ~/.cursor/mcp.json)").option("--force", "replace an existing agent key (a timestamped backup is written first)").action(async (token, opts) => {
|
|
2404
2469
|
const scope = opts.project ? "project" : "user";
|
|
2405
2470
|
if (!token.startsWith("itok_")) {
|
|
2406
2471
|
console.error(chalk12.red('Invalid install token. Tokens start with "itok_".'));
|
|
@@ -2426,8 +2491,14 @@ function installCommand() {
|
|
|
2426
2491
|
const agent_name = claim.agent_id;
|
|
2427
2492
|
try {
|
|
2428
2493
|
await mkdir6(AGENT_DIR, { recursive: true });
|
|
2494
|
+
assertSafeToWriteKey(AGENT_KEY_PATH, agent_id, { force: opts.force });
|
|
2429
2495
|
await writeFile9(AGENT_KEY_PATH, private_key_pem, { mode: 384 });
|
|
2430
2496
|
} catch (err) {
|
|
2497
|
+
if (err instanceof KeyClobberError) {
|
|
2498
|
+
console.error(chalk12.red(`
|
|
2499
|
+
\u2717 ${err.message}`));
|
|
2500
|
+
process.exit(3);
|
|
2501
|
+
}
|
|
2431
2502
|
console.error(chalk12.red(`
|
|
2432
2503
|
\u2717 Failed to write key: ${err instanceof Error ? err.message : err}`));
|
|
2433
2504
|
process.exit(3);
|
|
@@ -2563,14 +2634,14 @@ function installCommand() {
|
|
|
2563
2634
|
// src/commands/claim.ts
|
|
2564
2635
|
import { Command as Command6 } from "commander";
|
|
2565
2636
|
import { generateKeyPairSync as generateKeyPairSync2 } from "crypto";
|
|
2566
|
-
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, existsSync as
|
|
2637
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, existsSync as existsSync9 } from "fs";
|
|
2567
2638
|
import { homedir as homedir3 } from "os";
|
|
2568
|
-
import { join as
|
|
2639
|
+
import { join as join5 } from "path";
|
|
2569
2640
|
import chalk13 from "chalk";
|
|
2570
2641
|
var DEFAULT_PROXY_URL = "https://api.agentvalet.ai";
|
|
2571
2642
|
function resolveAgentDir(label) {
|
|
2572
|
-
const root =
|
|
2573
|
-
return label ?
|
|
2643
|
+
const root = join5(homedir3(), ".agentvalet");
|
|
2644
|
+
return label ? join5(root, label) : root;
|
|
2574
2645
|
}
|
|
2575
2646
|
function sanitiseLabel(label) {
|
|
2576
2647
|
return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -2614,9 +2685,9 @@ function claimCommand() {
|
|
|
2614
2685
|
process.exit(3);
|
|
2615
2686
|
}
|
|
2616
2687
|
const agentDir = resolveAgentDir(label);
|
|
2617
|
-
const keyPath =
|
|
2618
|
-
const identityPath =
|
|
2619
|
-
if (
|
|
2688
|
+
const keyPath = join5(agentDir, "agent.key");
|
|
2689
|
+
const identityPath = join5(agentDir, "agent.json");
|
|
2690
|
+
if (existsSync9(keyPath)) {
|
|
2620
2691
|
const msg = label ? `An agent already exists at ${keyPath}. Pick a different --label or remove the directory first.` : `A default agent already exists at ${keyPath}. Pass --label <name> to claim alongside it without overwriting.`;
|
|
2621
2692
|
console.error(isJson ? JSON.stringify({ error: msg }) : chalk13.red(msg));
|
|
2622
2693
|
process.exit(3);
|
|
@@ -2708,7 +2779,7 @@ function collectScope(value, previous) {
|
|
|
2708
2779
|
return [...previous, value];
|
|
2709
2780
|
}
|
|
2710
2781
|
var program = new Command7();
|
|
2711
|
-
program.name("agentvalet-register").description("Self-register an AI agent with AgentValet").version(version).option("--owner <uuid>", "Owner ID (or set AGENTVALET_OWNER env var)").option("--name <name>", "Agent name").option("--description <desc>", "Agent description").option("--scope <platform:scope>", "Requested scope, repeatable", collectScope, []).option("--api-url <url>", "AgentValet API base URL", "https://api.agentvalet.ai").option("--json", "Machine-readable JSON output").option("--no-wait", "Register only, do not wait for approval").option("--timeout <seconds>", "Approval wait timeout in seconds", "300").option("--project", "Write project-scoped MCP configs (.mcp.json, .cursor/mcp.json) instead of user-scoped (~/.claude.json, ~/.cursor/mcp.json)").action(async (opts) => {
|
|
2782
|
+
program.name("agentvalet-register").description("Self-register an AI agent with AgentValet").version(version).option("--owner <uuid>", "Owner ID (or set AGENTVALET_OWNER env var)").option("--name <name>", "Agent name").option("--description <desc>", "Agent description").option("--scope <platform:scope>", "Requested scope, repeatable", collectScope, []).option("--api-url <url>", "AgentValet API base URL", "https://api.agentvalet.ai").option("--json", "Machine-readable JSON output").option("--no-wait", "Register only, do not wait for approval").option("--timeout <seconds>", "Approval wait timeout in seconds", "300").option("--project", "Write project-scoped MCP configs (.mcp.json, .cursor/mcp.json) instead of user-scoped (~/.claude.json, ~/.cursor/mcp.json)").option("--force", "Replace an existing agent's key (a timestamped backup is written first)").action(async (opts) => {
|
|
2712
2783
|
const isJson = !!opts.json;
|
|
2713
2784
|
let ownerId = opts.owner ?? process.env.AGENTVALET_OWNER ?? "";
|
|
2714
2785
|
let agentName = opts.name ?? "";
|
|
@@ -2824,10 +2895,15 @@ Approve at: https://app.agentvalet.ai/agents
|
|
|
2824
2895
|
agentId,
|
|
2825
2896
|
ownerId: resolvedOwnerId,
|
|
2826
2897
|
privateKey,
|
|
2827
|
-
apiUrl: opts.apiUrl
|
|
2898
|
+
apiUrl: opts.apiUrl,
|
|
2899
|
+
force: !!opts.force
|
|
2828
2900
|
}));
|
|
2829
2901
|
} catch (err) {
|
|
2830
2902
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2903
|
+
if (err instanceof KeyClobberError) {
|
|
2904
|
+
console.error(isJson ? JSON.stringify({ error: msg, existing_agent_id: err.existingAgentId }) : chalk14.red(msg));
|
|
2905
|
+
process.exit(3);
|
|
2906
|
+
}
|
|
2831
2907
|
console.error(isJson ? JSON.stringify({ error: msg }) : chalk14.red(`Failed to save output: ${msg}`));
|
|
2832
2908
|
process.exit(3);
|
|
2833
2909
|
}
|