@agentvalet/register 0.4.7 → 0.4.8

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.
Files changed (2) hide show
  1. package/dist/index.js +243 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command4 } from "commander";
5
- import chalk11 from "chalk";
4
+ import { Command as Command5 } from "commander";
5
+ import chalk12 from "chalk";
6
6
 
7
7
  // src/wizard.ts
8
8
  import prompts from "prompts";
@@ -166,7 +166,7 @@ function parseEvent(data) {
166
166
  return null;
167
167
  }
168
168
  function sleep(ms) {
169
- return new Promise((resolve4) => setTimeout(resolve4, ms));
169
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
170
170
  }
171
171
 
172
172
  // src/save.ts
@@ -1454,12 +1454,12 @@ function revokeCommand() {
1454
1454
  process.exit(1);
1455
1455
  }
1456
1456
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1457
- const confirmed = await new Promise((resolve4) => {
1457
+ const confirmed = await new Promise((resolve5) => {
1458
1458
  rl.question(
1459
1459
  chalk9.yellow(`Revoke ${opts.agentId}? This cascades to all child agents and cannot be undone. (y/N) `),
1460
1460
  (ans) => {
1461
1461
  rl.close();
1462
- resolve4(ans.trim().toLowerCase() === "y");
1462
+ resolve5(ans.trim().toLowerCase() === "y");
1463
1463
  }
1464
1464
  );
1465
1465
  });
@@ -1567,12 +1567,229 @@ function injectCommand() {
1567
1567
  });
1568
1568
  }
1569
1569
 
1570
+ // src/commands/uninstall.ts
1571
+ import { Command as Command4 } from "commander";
1572
+ import chalk11 from "chalk";
1573
+ import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync3, rmSync } from "fs";
1574
+ import { resolve as resolve4, join as join3 } from "path";
1575
+ import { homedir as homedir2 } from "os";
1576
+ import { createSign as createSign2, createPrivateKey as createPrivateKey2 } from "crypto";
1577
+ var CLAUDE_START2 = "<!-- agentvalet:start -->";
1578
+ var CLAUDE_END2 = "<!-- agentvalet:end -->";
1579
+ var HOOK_MARKER2 = "agentvalet/register refresh";
1580
+ function signJwt2(agentId, ownerId, privateKeyPem) {
1581
+ const now = Math.floor(Date.now() / 1e3);
1582
+ const b64 = (s) => Buffer.from(s).toString("base64url");
1583
+ const header = b64(JSON.stringify({ alg: "RS256", typ: "JWT" }));
1584
+ const payload = b64(JSON.stringify({ agent_id: agentId, owner_id: ownerId, iat: now, exp: now + 300 }));
1585
+ const signingInput = `${header}.${payload}`;
1586
+ const sign = createSign2("RSA-SHA256");
1587
+ sign.update(signingInput);
1588
+ return `${signingInput}.${sign.sign(createPrivateKey2(privateKeyPem), "base64url")}`;
1589
+ }
1590
+ function stripClaudeMd() {
1591
+ const path7 = resolve4("CLAUDE.md");
1592
+ if (!existsSync7(path7)) return;
1593
+ let content = readFileSync4(path7, "utf-8");
1594
+ content = content.replace(new RegExp(`${CLAUDE_START2}[\\s\\S]*?${CLAUDE_END2}`, "m"), "");
1595
+ content = content.replace(/^@\.claude\/agentvalet-permissions\.md\s*$/m, "");
1596
+ content = content.replace(/(\r?\n){3,}/g, "\n\n").trim();
1597
+ writeFileSync3(path7, content ? content + "\n" : "", "utf-8");
1598
+ }
1599
+ function stripSettingsJson() {
1600
+ const path7 = resolve4(".claude/settings.local.json");
1601
+ if (!existsSync7(path7)) return;
1602
+ try {
1603
+ const settings = JSON.parse(readFileSync4(path7, "utf-8"));
1604
+ const hooks = settings.hooks ?? {};
1605
+ const ups = hooks.UserPromptSubmit ?? [];
1606
+ hooks.UserPromptSubmit = ups.filter(
1607
+ (e) => !e.hooks?.some((h) => h.command?.includes(HOOK_MARKER2))
1608
+ );
1609
+ settings.hooks = hooks;
1610
+ const perms = settings.permissions ?? {};
1611
+ const allow = perms.allow ?? [];
1612
+ perms.allow = allow.filter((e) => !e.startsWith("mcp__agentvalet__"));
1613
+ settings.permissions = perms;
1614
+ writeFileSync3(path7, JSON.stringify(settings, null, 2) + "\n", "utf-8");
1615
+ } catch {
1616
+ }
1617
+ }
1618
+ function stripMcpJson() {
1619
+ const path7 = resolve4(".mcp.json");
1620
+ if (!existsSync7(path7)) return;
1621
+ try {
1622
+ const config = JSON.parse(readFileSync4(path7, "utf-8"));
1623
+ const servers = config.mcpServers ?? {};
1624
+ delete servers["agentvalet"];
1625
+ config.mcpServers = servers;
1626
+ writeFileSync3(path7, JSON.stringify(config, null, 2) + "\n", "utf-8");
1627
+ } catch {
1628
+ }
1629
+ }
1630
+ function deleteIfExists(p) {
1631
+ const abs = resolve4(p);
1632
+ if (existsSync7(abs)) {
1633
+ rmSync(abs, { recursive: true, force: true });
1634
+ return abs;
1635
+ }
1636
+ return null;
1637
+ }
1638
+ function cleanLocally() {
1639
+ const removed = [];
1640
+ const r = (p) => {
1641
+ const f = deleteIfExists(p);
1642
+ if (f) removed.push(f);
1643
+ };
1644
+ r(".env.agentvalet");
1645
+ r(".agentvalet");
1646
+ r("agentvalet.key");
1647
+ r(".claude/agentvalet-permissions.md");
1648
+ stripClaudeMd();
1649
+ stripSettingsJson();
1650
+ stripMcpJson();
1651
+ try {
1652
+ saveConfig({ lastAgentId: "", lastKeyPath: "" });
1653
+ } catch {
1654
+ }
1655
+ const oldConfig = join3(homedir2(), ".agentvalet", "config.json");
1656
+ if (existsSync7(oldConfig)) {
1657
+ rmSync(oldConfig, { force: true });
1658
+ removed.push(oldConfig);
1659
+ }
1660
+ if (removed.length > 0) {
1661
+ console.log(chalk11.green(" \u2713 Removed local files:"));
1662
+ for (const f of removed) console.log(chalk11.dim(` ${f}`));
1663
+ }
1664
+ console.log(chalk11.green(" \u2713 Cleaned CLAUDE.md, .mcp.json, .claude/settings.local.json"));
1665
+ }
1666
+ async function pollForApproval(apiUrl, revocationToken, timeoutMs) {
1667
+ const streamUrl = `${apiUrl}/v1/revoke-request/stream/${revocationToken}`;
1668
+ const deadline = Date.now() + timeoutMs;
1669
+ return new Promise((resolve5) => {
1670
+ let settled = false;
1671
+ function done(result) {
1672
+ if (!settled) {
1673
+ settled = true;
1674
+ resolve5(result);
1675
+ }
1676
+ }
1677
+ function connect() {
1678
+ if (Date.now() > deadline) {
1679
+ done("timeout");
1680
+ return;
1681
+ }
1682
+ fetch(streamUrl, { headers: { Accept: "text/event-stream" } }).then(async (res) => {
1683
+ if (!res.ok || !res.body) {
1684
+ done("timeout");
1685
+ return;
1686
+ }
1687
+ const reader = res.body.getReader();
1688
+ const decoder = new TextDecoder();
1689
+ let buf = "";
1690
+ while (true) {
1691
+ const { done: streamDone, value } = await reader.read();
1692
+ if (streamDone) break;
1693
+ buf += decoder.decode(value, { stream: true });
1694
+ const lines = buf.split("\n");
1695
+ buf = lines.pop() ?? "";
1696
+ for (const line of lines) {
1697
+ if (!line.startsWith("data: ")) continue;
1698
+ try {
1699
+ const evt = JSON.parse(line.slice(6));
1700
+ if (evt.status === "approved") {
1701
+ done("approved");
1702
+ return;
1703
+ }
1704
+ if (evt.status === "rejected") {
1705
+ done("rejected");
1706
+ return;
1707
+ }
1708
+ if (evt.status === "timeout") {
1709
+ done("timeout");
1710
+ return;
1711
+ }
1712
+ } catch {
1713
+ }
1714
+ }
1715
+ }
1716
+ if (!settled) setTimeout(connect, 1e3);
1717
+ }).catch(() => {
1718
+ if (!settled) setTimeout(connect, 2e3);
1719
+ });
1720
+ }
1721
+ connect();
1722
+ setTimeout(() => done("timeout"), timeoutMs);
1723
+ });
1724
+ }
1725
+ function uninstallCommand() {
1726
+ return new Command4("uninstall").description("Request agent revocation (requires owner approval) and remove all local files").option("--local-only", "Skip the server-side revoke request \u2014 just clean local files").option("--timeout <seconds>", "Seconds to wait for owner approval", "300").action(async (opts) => {
1727
+ const timeoutMs = parseInt(opts.timeout, 10) * 1e3;
1728
+ const creds = tryResolveAgent();
1729
+ if (opts.localOnly || !creds) {
1730
+ if (!creds) console.log(chalk11.dim(" No agent credentials found \u2014 cleaning local files only."));
1731
+ cleanLocally();
1732
+ console.log(chalk11.dim("\n To register a new agent: npx @agentvalet/register"));
1733
+ return;
1734
+ }
1735
+ let privateKeyPem;
1736
+ try {
1737
+ privateKeyPem = readFileSync4(creds.keyPath, "utf-8").trim();
1738
+ } catch {
1739
+ }
1740
+ if (!privateKeyPem) {
1741
+ console.log(chalk11.yellow(" \u26A0 Private key not found \u2014 cannot submit revocation request."));
1742
+ console.log(chalk11.dim(" Use --local-only to clean local files without revoking."));
1743
+ process.exit(1);
1744
+ }
1745
+ console.log(chalk11.dim(`
1746
+ Submitting revocation request for ${creds.agentId}...`));
1747
+ const jwt = signJwt2(creds.agentId, creds.ownerId, privateKeyPem);
1748
+ let revocationToken;
1749
+ try {
1750
+ const res = await fetch(`${creds.apiUrl}/v1/revoke-request`, {
1751
+ method: "POST",
1752
+ headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
1753
+ body: JSON.stringify({ reason: "CLI uninstall" })
1754
+ });
1755
+ const body = await res.json();
1756
+ if (!res.ok || !body.revocation_token) {
1757
+ throw new Error(body.error ?? `HTTP ${res.status}`);
1758
+ }
1759
+ revocationToken = body.revocation_token;
1760
+ } catch (err) {
1761
+ console.error(chalk11.red(` \u2717 Failed to submit revocation request: ${err instanceof Error ? err.message : err}`));
1762
+ process.exit(1);
1763
+ }
1764
+ console.log(chalk11.green(" \u2713 Revocation request submitted"));
1765
+ console.log("");
1766
+ console.log(chalk11.yellow(" ACTION REQUIRED:"));
1767
+ console.log(chalk11.yellow(` Open ${creds.apiUrl.replace("api.", "app.")}/agents and approve the uninstall request.`));
1768
+ console.log(chalk11.dim(` Waiting up to ${opts.timeout}s for approval...
1769
+ `));
1770
+ const result = await pollForApproval(creds.apiUrl, revocationToken, timeoutMs);
1771
+ if (result === "rejected") {
1772
+ console.log(chalk11.red("\n \u2717 Owner rejected the uninstall request. Agent remains active."));
1773
+ process.exit(1);
1774
+ }
1775
+ if (result === "timeout") {
1776
+ console.log(chalk11.yellow("\n \u26A0 Timed out waiting for owner approval."));
1777
+ console.log(chalk11.dim(" The request remains pending in your dashboard. Run again to resume."));
1778
+ process.exit(1);
1779
+ }
1780
+ console.log(chalk11.green("\n \u2713 Approved \u2014 cleaning local files...\n"));
1781
+ cleanLocally();
1782
+ console.log("");
1783
+ console.log(chalk11.dim(" Agent revoked. To register a new agent: npx @agentvalet/register"));
1784
+ });
1785
+ }
1786
+
1570
1787
  // src/index.ts
1571
1788
  function collectScope(value, previous) {
1572
1789
  return [...previous, value];
1573
1790
  }
1574
- var program = new Command4();
1575
- program.name("agentvalet-register").description("Self-register an AI agent with AgentValet").version("0.4.7").option("--owner <uuid>", "Owner ID (or set AGENTVALET_OWNER env var)").option("--name <name>", "Agent name").option("--description <desc>", "Agent description").option("--scope <platform:scope>", "Requested scope, repeatable", collectScope, []).option("--api-url <url>", "AgentValet API base URL", "https://api.agentvalet.ai").option("--output <path>", "Path to write .env file", "./.env.agentvalet").option("--key-path <path>", "Path to write private key", "./agentvalet.key").option("--json", "Machine-readable JSON output").option("--no-wait", "Register only, do not wait for approval").option("--timeout <seconds>", "Approval wait timeout in seconds", "300").action(async (opts) => {
1791
+ var program = new Command5();
1792
+ program.name("agentvalet-register").description("Self-register an AI agent with AgentValet").version("0.4.8").option("--owner <uuid>", "Owner ID (or set AGENTVALET_OWNER env var)").option("--name <name>", "Agent name").option("--description <desc>", "Agent description").option("--scope <platform:scope>", "Requested scope, repeatable", collectScope, []).option("--api-url <url>", "AgentValet API base URL", "https://api.agentvalet.ai").option("--output <path>", "Path to write .env file", "./.env.agentvalet").option("--key-path <path>", "Path to write private key", "./agentvalet.key").option("--json", "Machine-readable JSON output").option("--no-wait", "Register only, do not wait for approval").option("--timeout <seconds>", "Approval wait timeout in seconds", "300").action(async (opts) => {
1576
1793
  const isJson = !!opts.json;
1577
1794
  let ownerId = opts.owner ?? process.env.AGENTVALET_OWNER ?? "";
1578
1795
  let agentName = opts.name ?? "";
@@ -1598,7 +1815,7 @@ program.name("agentvalet-register").description("Self-register an AI agent with
1598
1815
  console.error(JSON.stringify({ error: "--owner or AGENTVALET_OWNER is required" }));
1599
1816
  process.exit(3);
1600
1817
  }
1601
- console.error(chalk11.red("Error: --owner or AGENTVALET_OWNER environment variable is required"));
1818
+ console.error(chalk12.red("Error: --owner or AGENTVALET_OWNER environment variable is required"));
1602
1819
  process.exit(3);
1603
1820
  }
1604
1821
  const scopeMap = /* @__PURE__ */ new Map();
@@ -1606,7 +1823,7 @@ program.name("agentvalet-register").description("Self-register an AI agent with
1606
1823
  const colon = s.indexOf(":");
1607
1824
  if (colon === -1) {
1608
1825
  console.error(
1609
- isJson ? JSON.stringify({ error: `Invalid scope format: ${s} (expected platform:scope)` }) : chalk11.red(`Invalid scope format: ${s} (expected platform:scope)`)
1826
+ isJson ? JSON.stringify({ error: `Invalid scope format: ${s} (expected platform:scope)` }) : chalk12.red(`Invalid scope format: ${s} (expected platform:scope)`)
1610
1827
  );
1611
1828
  process.exit(3);
1612
1829
  }
@@ -1629,11 +1846,11 @@ program.name("agentvalet-register").description("Self-register an AI agent with
1629
1846
  });
1630
1847
  } catch (err) {
1631
1848
  const msg = err instanceof Error ? err.message : String(err);
1632
- console.error(isJson ? JSON.stringify({ error: msg }) : chalk11.red(`Registration failed: ${msg}`));
1849
+ console.error(isJson ? JSON.stringify({ error: msg }) : chalk12.red(`Registration failed: ${msg}`));
1633
1850
  process.exit(3);
1634
1851
  }
1635
1852
  if (!isJson) {
1636
- console.log(chalk11.cyan(`
1853
+ console.log(chalk12.cyan(`
1637
1854
  Approve at: https://app.agentvalet.ai/agents
1638
1855
  `));
1639
1856
  }
@@ -1641,7 +1858,7 @@ Approve at: https://app.agentvalet.ai/agents
1641
1858
  if (isJson) {
1642
1859
  console.log(JSON.stringify({ status: "pending", token }));
1643
1860
  } else {
1644
- console.log(chalk11.yellow("Registration submitted (--no-wait). Token:"), token);
1861
+ console.log(chalk12.yellow("Registration submitted (--no-wait). Token:"), token);
1645
1862
  }
1646
1863
  process.exit(0);
1647
1864
  }
@@ -1652,14 +1869,14 @@ Approve at: https://app.agentvalet.ai/agents
1652
1869
  });
1653
1870
  } catch (err) {
1654
1871
  const msg = err instanceof Error ? err.message : String(err);
1655
- console.error(isJson ? JSON.stringify({ error: msg }) : chalk11.red(`Wait failed: ${msg}`));
1872
+ console.error(isJson ? JSON.stringify({ error: msg }) : chalk12.red(`Wait failed: ${msg}`));
1656
1873
  process.exit(3);
1657
1874
  }
1658
1875
  if (result.status === "rejected") {
1659
1876
  if (isJson) {
1660
1877
  console.log(JSON.stringify({ status: "rejected" }));
1661
1878
  } else {
1662
- console.log(chalk11.red("\nRegistration denied by owner."));
1879
+ console.log(chalk12.red("\nRegistration denied by owner."));
1663
1880
  }
1664
1881
  process.exit(1);
1665
1882
  }
@@ -1667,7 +1884,7 @@ Approve at: https://app.agentvalet.ai/agents
1667
1884
  if (isJson) {
1668
1885
  console.log(JSON.stringify({ status: "timeout" }));
1669
1886
  } else {
1670
- console.log(chalk11.yellow("\nTimed out waiting for approval."));
1887
+ console.log(chalk12.yellow("\nTimed out waiting for approval."));
1671
1888
  }
1672
1889
  process.exit(2);
1673
1890
  }
@@ -1686,7 +1903,7 @@ Approve at: https://app.agentvalet.ai/agents
1686
1903
  }));
1687
1904
  } catch (err) {
1688
1905
  const msg = err instanceof Error ? err.message : String(err);
1689
- console.error(isJson ? JSON.stringify({ error: msg }) : chalk11.red(`Failed to save output: ${msg}`));
1906
+ console.error(isJson ? JSON.stringify({ error: msg }) : chalk12.red(`Failed to save output: ${msg}`));
1690
1907
  process.exit(3);
1691
1908
  }
1692
1909
  const keyWritten = !!privateKey;
@@ -1696,23 +1913,23 @@ Approve at: https://app.agentvalet.ai/agents
1696
1913
  if (isJson) {
1697
1914
  console.log(JSON.stringify({ status: "approved", agentId, keyPath: keyWritten ? keyPath : null, envPath }));
1698
1915
  } else {
1699
- console.log(chalk11.green(`
1916
+ console.log(chalk12.green(`
1700
1917
  Approved! Agent ID: ${agentId}`));
1701
1918
  if (keyWritten) {
1702
- console.log(chalk11.gray(`Key: ${keyPath}`));
1919
+ console.log(chalk12.gray(`Key: ${keyPath}`));
1703
1920
  } else {
1704
- console.log(chalk11.yellow(`Key: not written \u2014 copy it from the dashboard and paste into ${keyPath}`));
1921
+ console.log(chalk12.yellow(`Key: not written \u2014 copy it from the dashboard and paste into ${keyPath}`));
1705
1922
  }
1706
- console.log(chalk11.gray(`Env file: ${envPath}`));
1923
+ console.log(chalk12.gray(`Env file: ${envPath}`));
1707
1924
  console.log(`
1708
1925
  Source the env file before calling any tool:`);
1709
- console.log(chalk11.cyan(` source ${envPath}`));
1926
+ console.log(chalk12.cyan(` source ${envPath}`));
1710
1927
  }
1711
1928
  if (keyWritten) {
1712
1929
  try {
1713
1930
  if (grantedPlatforms && grantedPlatforms.length > 0) {
1714
1931
  writePermissionsFromEvent(agentId, resolvedOwnerId, grantedPlatforms);
1715
- if (!isJson) console.log(chalk11.gray(`Permissions: .claude/agentvalet-permissions.md`));
1932
+ if (!isJson) console.log(chalk12.gray(`Permissions: .claude/agentvalet-permissions.md`));
1716
1933
  } else {
1717
1934
  await runRefresh({
1718
1935
  envFile: opts.output,
@@ -1720,10 +1937,10 @@ Source the env file before calling any tool:`);
1720
1937
  ifStale: false,
1721
1938
  quiet: isJson
1722
1939
  });
1723
- if (!isJson) console.log(chalk11.gray(`Permissions: .claude/agentvalet-permissions.md`));
1940
+ if (!isJson) console.log(chalk12.gray(`Permissions: .claude/agentvalet-permissions.md`));
1724
1941
  }
1725
1942
  } catch {
1726
- if (!isJson) console.log(chalk11.yellow(`Permissions: run 'agentvalet-register refresh' to generate`));
1943
+ if (!isJson) console.log(chalk12.yellow(`Permissions: run 'agentvalet-register refresh' to generate`));
1727
1944
  }
1728
1945
  }
1729
1946
  process.exit(0);
@@ -1738,7 +1955,7 @@ program.command("refresh").description("Refresh approved permissions and update
1738
1955
  });
1739
1956
  } catch (err) {
1740
1957
  const msg = err instanceof Error ? err.message : String(err);
1741
- console.error(chalk11.red(`Refresh failed: ${msg}`));
1958
+ console.error(chalk12.red(`Refresh failed: ${msg}`));
1742
1959
  process.exit(3);
1743
1960
  }
1744
1961
  });
@@ -1748,6 +1965,7 @@ program.command("mcp-server").description("Start the AgentValet MCP server (stdi
1748
1965
  program.addCommand(listCommand());
1749
1966
  program.addCommand(revokeCommand());
1750
1967
  program.addCommand(injectCommand());
1968
+ program.addCommand(uninstallCommand());
1751
1969
  program.parseAsync().catch((err) => {
1752
1970
  console.error(err);
1753
1971
  process.exit(3);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentvalet/register",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Self-register AI agents with AgentValet",
5
5
  "type": "module",
6
6
  "bin": {