@agentwares/agentguard 0.1.1 → 0.1.3

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.d.ts CHANGED
@@ -482,7 +482,7 @@ interface DemoOptions {
482
482
  /** Drive a connected client through the scripted scenario. Returns every step's outcome. */
483
483
  declare function runDemoAgent(client: Client, opts?: DemoOptions): Promise<DemoStep[]>;
484
484
 
485
- declare const HELP = "agentguard \u2014 MCP policy proxy for agents that touch production\n\n npx @agentwares/agentguard init read your MCP config, write agentguard.yaml (dry-run), route every server through the proxy\n agentguard report [--run id|--all] what this run did / would have destroyed / spent, where it was halted\n agentguard diff [--run id] record-by-record mutation diff from a dry run\n agentguard verify [audit.jsonl] prove the hash-chained audit log was not edited\n\n agentguard proxy [--http --port 8788] [--agent name] [--run-id id] [--mode dry-run|enforce]\n agentguard tools [--json] the tools your agent will see, with class and why\n agentguard status [--run id] counters vs caps, kill state, pending approvals\n\n agentguard kill [reason] halt every run now (file + running HTTP proxy); agentguard resume\n agentguard approvals [--all] pending approvals; agentguard approve <id> | deny <id> [--note text]\n agentguard key create <agent> [--allow 'crm_get_*']... [--deny p] [--writes n] [--spend n] [--mode m]\n agentguard key list | revoke <agent>\n agentguard permission-diff [--base ref] [--head ref] [--paths a,b] [--format markdown|json] [--fail-on-widen]\n\nOptions: --config <agentguard.yaml> (or AGENTGUARD_CONFIG), --json, --help, --version\nDocs: https://github.com/agentwares/agentguard/tree/main/apps/agentguard-cli";
485
+ declare const HELP = "agentguard \u2014 MCP policy proxy for agents that touch production\n\n npx @agentwares/agentguard init read your MCP config, write agentguard.yaml (dry-run), route every server through the proxy\n agentguard report [--run id|--all] what this run did / would have destroyed / spent, where it was halted\n agentguard diff [--run id] record-by-record mutation diff from a dry run\n agentguard verify [audit.jsonl] prove the hash-chained audit log was not edited\n agentguard connect <key> [--write] point this machine at your hosted proxy (paid tiers)\n\n agentguard proxy [--http --port 8788] [--agent name] [--run-id id] [--mode dry-run|enforce]\n agentguard tools [--json] the tools your agent will see, with class and why\n agentguard status [--run id] counters vs caps, kill state, pending approvals\n\n agentguard kill [reason] halt every run now (file + running HTTP proxy); agentguard resume\n agentguard approvals [--all] pending approvals; agentguard approve <id> | deny <id> [--note text]\n agentguard key create <agent> [--allow 'crm_get_*']... [--deny p] [--writes n] [--spend n] [--mode m]\n agentguard key list | revoke <agent>\n agentguard permission-diff [--base ref] [--head ref] [--paths a,b] [--format markdown|json] [--fail-on-widen]\n\nOptions: --config <agentguard.yaml> (or AGENTGUARD_CONFIG), --json, --help, --version\nDocs: https://github.com/agentwares/agentguard/tree/main/apps/agentguard-cli";
486
486
  declare function main(argv: readonly string[], io?: Io): Promise<number>;
487
487
 
488
488
  export { type CallContext, type ClientConfigFile, type CrmState, DEFAULT_PERMISSION_PATHS, type DemoOptions, type DemoStep, type DownstreamOptions, type DownstreamSession, type ExposedTool, type FileGuard, HELP, type HttpProxy, type HttpProxyOptions, type HttpRegistration, type Io, type PermissionDiffResult, type ProbeResult, type ProbedTool, ProxyRuntime, type RuntimeOptions, type ServerEntry, type UpstreamStatus, approvalSlackMessage, candidateConfigs, createAlerter, createCrmMcpServer, createDownstreamServer, createFileGuard, crmTools, detectClientConfigs, haltSlackMessage, loadPolicyFor, main, probeUpstreams, proxyEntry, readRegistration, readServers, registrationPath, renderStarterPolicy, restoreClientConfig, rewriteClientConfig, runDemoAgent, runIdFor, runPermissionDiff, seedState, serversToUpstreams, startHttpProxy, toCallToolResult };
package/dist/index.js CHANGED
@@ -806,6 +806,21 @@ var require_dist = __commonJS({
806
806
 
807
807
  // src/proxy/runtime.ts
808
808
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
809
+
810
+ // src/version.ts
811
+ import { readFileSync } from "fs";
812
+ var cached;
813
+ function version() {
814
+ if (cached !== void 0) return cached;
815
+ try {
816
+ cached = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
817
+ } catch {
818
+ cached = "0.0.0";
819
+ }
820
+ return cached;
821
+ }
822
+
823
+ // src/proxy/runtime.ts
809
824
  import {
810
825
  getDefaultEnvironment,
811
826
  StdioClientTransport
@@ -886,7 +901,7 @@ var ProxyRuntime = class {
886
901
  }
887
902
  async connect(upstream) {
888
903
  const client = new Client(
889
- { name: "agentguard", version: "0.1.0" },
904
+ { name: "agentguard", version: version() },
890
905
  { capabilities: { sampling: {}, elicitation: {} } }
891
906
  );
892
907
  const conn = {
@@ -1331,7 +1346,7 @@ import {
1331
1346
  keyFromHeaders,
1332
1347
  resolveAgent
1333
1348
  } from "@agentwares/agentguard-core";
1334
- var PROXY_VERSION = "0.1.0";
1349
+ var PROXY_VERSION = version();
1335
1350
  function headerValue(extra, name) {
1336
1351
  const raw = extra.requestInfo?.headers?.[name] ?? extra.requestInfo?.headers?.[name.toLowerCase()];
1337
1352
  if (Array.isArray(raw)) return raw[0];
@@ -1559,7 +1574,7 @@ function createDownstreamServer(runtime, opts = {}) {
1559
1574
  return server;
1560
1575
  }
1561
1576
 
1562
- // ../../packages/mcp-kit/dist/chunk-AO7AV7MI.js
1577
+ // ../../packages/mcp-kit/dist/chunk-NF4BQXVM.js
1563
1578
  import { completable } from "@modelcontextprotocol/sdk/server/completable.js";
1564
1579
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
1565
1580
  import {
@@ -1571,6 +1586,7 @@ import {
1571
1586
  import { z } from "zod";
1572
1587
 
1573
1588
  // ../../packages/x402/dist/index.js
1589
+ var DEFAULT_X402_NETWORK = "eip155:8453";
1574
1590
  var X402_HEADER = "X-Payment-Requirements";
1575
1591
  var X402_REQUIRED_HEADER = "PAYMENT-REQUIRED";
1576
1592
  function toAmount(priceUsd) {
@@ -1580,42 +1596,51 @@ function toAmount(priceUsd) {
1580
1596
  }
1581
1597
  function buildPaymentRequired(opts) {
1582
1598
  const amountUsd = toAmount(opts.priceUsd);
1583
- const accepts = [
1584
- {
1599
+ const creditsPayTo = opts.creditsPayTo ?? opts.selfServe?.keysUrl;
1600
+ const accepts = [];
1601
+ if (opts.payTo) {
1602
+ accepts.push({
1585
1603
  rail: "x402",
1586
1604
  amountUsd,
1587
1605
  resource: opts.resource,
1588
1606
  description: opts.description,
1589
- network: "eip155:8453",
1607
+ network: opts.network ?? DEFAULT_X402_NETWORK,
1590
1608
  asset: "USDC",
1591
1609
  payTo: opts.payTo,
1592
1610
  maxTimeoutSeconds: 300
1593
- },
1594
- {
1595
- rail: "mpp",
1611
+ });
1612
+ }
1613
+ if (creditsPayTo) {
1614
+ accepts.push({
1615
+ rail: "prepaid_credits",
1596
1616
  amountUsd,
1597
1617
  resource: opts.resource,
1598
1618
  description: opts.description,
1599
1619
  currency: "usd",
1600
- payTo: opts.payTo,
1601
- maxTimeoutSeconds: 300
1602
- }
1603
- ];
1620
+ payTo: creditsPayTo,
1621
+ maxTimeoutSeconds: 300,
1622
+ // funding a balance ends at a hosted Stripe Checkout page, which an agent cannot fill in
1623
+ humanRequired: true
1624
+ });
1625
+ }
1604
1626
  const live = opts.live === true;
1627
+ const selfServe = opts.selfServe;
1628
+ const liveFix = selfServe ? `Free first: retry with sample=true \u2014 no key, no payment. To pay: POST ${selfServe.keysUrl} (no auth, no signup) returns a key and a Stripe Checkout URL. Funding that key needs a person to complete the Checkout page in a browser; if you are running unattended, surface the URL to your operator rather than retrying. Once funded, send Authorization: Bearer <key>, plus Idempotency-Key so a repeated call is charged once.` : "Retry with a PAYMENT header satisfying one of `accepts`, or send an API key with credit balance.";
1605
1629
  const body = {
1606
1630
  code: "PAYMENT_REQUIRED",
1607
1631
  cause: live ? `This call costs $${amountUsd}. No valid payment or credit was presented.` : `This call costs $${amountUsd}. Pay-per-call (x402 / Stripe MPP) is coming soon; use an API key with prepaid credits.`,
1608
- fix: live ? "Retry with a PAYMENT header satisfying one of `accepts`, or send an API key with credit balance." : `Create an API key${opts.signupUrl ? ` at ${opts.signupUrl}` : ""} and retry with Authorization: Bearer <key>. Try the free sample mode first.`,
1632
+ fix: live ? liveFix : `Create an API key${opts.signupUrl ? ` at ${opts.signupUrl}` : ""} and retry with Authorization: Bearer <key>. Try the free sample mode first.`,
1609
1633
  retryable: true,
1610
1634
  priceUsd: opts.priceUsd,
1611
1635
  accepts,
1612
1636
  comingSoon: !live,
1613
- alternatives: ["api_key_credits", "sample_mode"]
1637
+ alternatives: ["api_key_credits", "sample_mode"],
1638
+ ...selfServe ? { selfServe } : {}
1614
1639
  };
1615
1640
  const encoded = JSON.stringify({ x402Version: 2, accepts, resource: opts.resource });
1616
1641
  const headers = {
1617
1642
  "Content-Type": "application/json",
1618
- "WWW-Authenticate": `Payment realm="agentwares", methods="x402 mpp", price="${amountUsd} USD"`,
1643
+ "WWW-Authenticate": `Payment realm="agentwares", methods="x402 prepaid_credits", price="${amountUsd} USD"`,
1619
1644
  [X402_HEADER]: encoded,
1620
1645
  [X402_REQUIRED_HEADER]: btoa(encoded),
1621
1646
  "Cache-Control": "no-store"
@@ -1633,7 +1658,7 @@ var StubVerifier = class {
1633
1658
  }
1634
1659
  };
1635
1660
 
1636
- // ../../packages/mcp-kit/dist/chunk-AO7AV7MI.js
1661
+ // ../../packages/mcp-kit/dist/chunk-NF4BQXVM.js
1637
1662
  import { z as z2 } from "zod";
1638
1663
  import { z as z3 } from "zod";
1639
1664
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -1815,13 +1840,13 @@ function defineTool(opts) {
1815
1840
  annotations,
1816
1841
  handler,
1817
1842
  async invoke(rawInput, ctx) {
1818
- const fail = (err) => {
1843
+ const fail2 = (err) => {
1819
1844
  const result = errorResult(err, { requestId: ctx.requestId });
1820
1845
  if (output) delete result.structuredContent;
1821
1846
  return result;
1822
1847
  };
1823
1848
  const parsed = input.safeParse(rawInput ?? {});
1824
- if (!parsed.success) return fail(invalidInputError(parsed.error.issues));
1849
+ if (!parsed.success) return fail2(invalidInputError(parsed.error.issues));
1825
1850
  try {
1826
1851
  const value = await handler(parsed.data, ctx);
1827
1852
  if (output && !isCallToolResult(value)) {
@@ -1839,7 +1864,7 @@ function defineTool(opts) {
1839
1864
  }
1840
1865
  return toCallToolResult2(value);
1841
1866
  } catch (err) {
1842
- return fail(err);
1867
+ return fail2(err);
1843
1868
  }
1844
1869
  }
1845
1870
  };
@@ -2080,6 +2105,7 @@ function withPayment(tool, opts) {
2080
2105
  );
2081
2106
  }
2082
2107
  const verifier = opts.verifier ?? new StubVerifier();
2108
+ const liveRail = opts.verifier !== void 0;
2083
2109
  const input = tool.input.extend({
2084
2110
  [sampleField]: z2.boolean().default(false).describe(
2085
2111
  "Set true to return an example response at no charge (sample mode). Default false."
@@ -2110,17 +2136,35 @@ function withPayment(tool, opts) {
2110
2136
  live: opts.live,
2111
2137
  description: opts.description,
2112
2138
  payTo: opts.payTo,
2113
- signupUrl: opts.signupUrl
2139
+ creditsPayTo: opts.creditsPayTo,
2140
+ signupUrl: opts.signupUrl,
2141
+ selfServe: opts.selfServe
2114
2142
  });
2115
- const requirement = required.body.accepts[0];
2116
- if (requirement) {
2117
- const verified = await verifier.verify(
2118
- new Request(`https://mcp.local/${tool.name}`, { headers: ctx.headers }),
2119
- requirement
2120
- );
2121
- if (verified.ok) return tool.handler(record, ctx);
2122
- reason = reason ?? verified.reason;
2143
+ const advertised = required.body.accepts.find((a) => a.rail === "prepaid_credits") ?? required.body.accepts[0];
2144
+ const requirement = advertised ?? {
2145
+ rail: "prepaid_credits",
2146
+ amountUsd: String(required.body.priceUsd),
2147
+ resource: tool.name
2148
+ };
2149
+ const verified = await verifier.verify(
2150
+ new Request(`https://mcp.local/${tool.name}`, { headers: ctx.headers }),
2151
+ requirement
2152
+ );
2153
+ if (verified.ok) {
2154
+ let result;
2155
+ try {
2156
+ result = await tool.handler(record, ctx);
2157
+ } catch (error) {
2158
+ await opts.onPaidFailure?.(ctx, verified, error);
2159
+ throw error;
2160
+ }
2161
+ const settled = await verifier.settle?.(verified);
2162
+ if (settled && !settled.ok) {
2163
+ opts.onSettlementFailure?.(ctx, verified, settled.reason);
2164
+ }
2165
+ return result;
2123
2166
  }
2167
+ reason = liveRail ? verified.reason ?? reason : reason ?? verified.reason;
2124
2168
  throw paymentRequiredError(required.body, reason);
2125
2169
  }
2126
2170
  });
@@ -2809,7 +2853,7 @@ import {
2809
2853
  FileStateStore,
2810
2854
  loadPolicyFile
2811
2855
  } from "@agentwares/agentguard-core/node";
2812
- import { existsSync as exists, readFileSync } from "fs";
2856
+ import { existsSync as exists, readFileSync as readFileSync2 } from "fs";
2813
2857
 
2814
2858
  // ../../node_modules/.pnpm/postal-mime@2.7.5/node_modules/postal-mime/src/decode-strings.js
2815
2859
  var textEncoder = new TextEncoder();
@@ -2842,8 +2886,8 @@ var MAX_HEADERS_SIZE = 2 * 1024 * 1024;
2842
2886
 
2843
2887
  // ../../node_modules/.pnpm/resend@6.25.0/node_modules/resend/dist/index.mjs
2844
2888
  var import_standardwebhooks = __toESM(require_dist(), 1);
2845
- var version = "6.25.0";
2846
- var defaultUserAgent = `resend-node:${version}`;
2889
+ var version2 = "6.25.0";
2890
+ var defaultUserAgent = `resend-node:${version2}`;
2847
2891
 
2848
2892
  // ../../packages/notify/dist/index.js
2849
2893
  var palette = {
@@ -3193,7 +3237,7 @@ function readRegistration(loaded) {
3193
3237
  const file = registrationPath(loaded.stateDir);
3194
3238
  if (!exists(file)) return void 0;
3195
3239
  try {
3196
- const reg = JSON.parse(readFileSync(file, "utf8"));
3240
+ const reg = JSON.parse(readFileSync2(file, "utf8"));
3197
3241
  return reg;
3198
3242
  } catch {
3199
3243
  return void 0;
@@ -3209,7 +3253,7 @@ function describeError(err) {
3209
3253
  }
3210
3254
 
3211
3255
  // src/configs.ts
3212
- import { copyFileSync, existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
3256
+ import { copyFileSync, existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
3213
3257
  import { homedir as homedir2, platform } from "os";
3214
3258
  import { join as join2, resolve as resolve2 } from "path";
3215
3259
  function candidateConfigs(cwd = process.cwd(), home = homedir2(), env = process.env) {
@@ -3272,7 +3316,7 @@ function describeConfigPath(path2) {
3272
3316
  if (known) return known;
3273
3317
  let serversKey = "mcpServers";
3274
3318
  try {
3275
- const parsed = JSON.parse(readFileSync2(abs, "utf8"));
3319
+ const parsed = JSON.parse(readFileSync3(abs, "utf8"));
3276
3320
  if (parsed.servers && !parsed.mcpServers) serversKey = "servers";
3277
3321
  } catch {
3278
3322
  }
@@ -3284,7 +3328,7 @@ function describeConfigPath(path2) {
3284
3328
  };
3285
3329
  }
3286
3330
  function readServers(file) {
3287
- const raw = JSON.parse(readFileSync2(file.path, "utf8"));
3331
+ const raw = JSON.parse(readFileSync3(file.path, "utf8"));
3288
3332
  const servers = raw[file.serversKey];
3289
3333
  if (!servers || typeof servers !== "object") return {};
3290
3334
  return servers;
@@ -3340,7 +3384,7 @@ function proxyEntry(file, policyPath, opts = {}) {
3340
3384
  return entry;
3341
3385
  }
3342
3386
  function rewriteClientConfig(file, policyPath, opts = {}) {
3343
- const raw = JSON.parse(readFileSync2(file.path, "utf8"));
3387
+ const raw = JSON.parse(readFileSync3(file.path, "utf8"));
3344
3388
  const servers = raw[file.serversKey] ?? {};
3345
3389
  const replaced = Object.keys(servers).filter((n) => !isProxyEntry(servers[n]));
3346
3390
  const backup = backupPath(file);
@@ -3355,6 +3399,16 @@ function restoreClientConfig(file) {
3355
3399
  copyFileSync(backup, file.path);
3356
3400
  return true;
3357
3401
  }
3402
+ function mergeServerIntoConfig(file, name, entry) {
3403
+ const raw = JSON.parse(readFileSync3(file.path, "utf8"));
3404
+ const servers = raw[file.serversKey] ?? {};
3405
+ const replaced = name in servers;
3406
+ const backup = backupPath(file);
3407
+ if (!existsSync3(backup)) copyFileSync(file.path, backup);
3408
+ raw[file.serversKey] = { ...servers, [name]: entry };
3409
+ writeFileSync2(file.path, JSON.stringify(raw, null, 2) + "\n");
3410
+ return { backup, replaced };
3411
+ }
3358
3412
 
3359
3413
  // src/commands/init.ts
3360
3414
  import { existsSync as existsSync4, writeFileSync as writeFileSync3 } from "fs";
@@ -4083,8 +4137,111 @@ ${steps.length} calls; halts: ${halts.join(", ") || "none"}. Now run: agentguard
4083
4137
  }
4084
4138
  }
4085
4139
 
4086
- // src/cli.ts
4087
- import { readFileSync as readFileSync4 } from "fs";
4140
+ // src/commands/connect.ts
4141
+ import { existsSync as existsSync5 } from "fs";
4142
+ import { relative as relative2 } from "path";
4143
+ var DEFAULT_PROXY_URL = "https://agentwares-agentguard-proxy.vercel.app";
4144
+ function fail(e) {
4145
+ throw e;
4146
+ }
4147
+ async function connectCommand(args, io, fetchImpl = fetch) {
4148
+ const key = args.positionals[0] ?? io.env.AGENTGUARD_KEY;
4149
+ if (!key) {
4150
+ io.err("usage: agentguard connect <key> [--write] [--client <path>] [--url <proxy-url>]");
4151
+ io.err(" the key is on your dashboard; it starts with agk_");
4152
+ return 2;
4153
+ }
4154
+ const base = (flagString(args, "url") ?? io.env.AGENTGUARD_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\/+$/, "");
4155
+ let res;
4156
+ try {
4157
+ res = await fetchImpl(`${base}/connect`, {
4158
+ method: "POST",
4159
+ headers: { "content-type": "application/json" },
4160
+ body: JSON.stringify({ key })
4161
+ });
4162
+ } catch (err) {
4163
+ fail({
4164
+ code: "PROXY_UNREACHABLE",
4165
+ cause: `could not reach ${base}: ${err instanceof Error ? err.message : String(err)}`,
4166
+ fix: "check the network, or pass --url if you run your own proxy",
4167
+ retryable: true
4168
+ });
4169
+ }
4170
+ const text = await res.text();
4171
+ let body;
4172
+ try {
4173
+ body = JSON.parse(text);
4174
+ } catch {
4175
+ fail({
4176
+ code: "PROXY_BAD_RESPONSE",
4177
+ cause: `${base}/connect returned ${res.status} that was not JSON: ${text.slice(0, 200)}`,
4178
+ fix: "check --url points at an agentguard proxy, not at the dashboard",
4179
+ retryable: false
4180
+ });
4181
+ }
4182
+ if (!res.ok) {
4183
+ const e = body;
4184
+ fail({
4185
+ code: e.code ?? "CONNECT_REFUSED",
4186
+ cause: e.cause ?? `${base}/connect returned ${res.status}`,
4187
+ fix: e.fix ?? "check the key on your dashboard; revoked keys stop working immediately",
4188
+ retryable: e.retryable ?? false
4189
+ });
4190
+ }
4191
+ const conn = body;
4192
+ if (flagBool(args, "json")) {
4193
+ io.out(JSON.stringify(conn, null, 2));
4194
+ return 0;
4195
+ }
4196
+ const rel = (p) => p.startsWith(io.cwd + "/") ? relative2(io.cwd, p) : p;
4197
+ const entry = conn.mcpServers[PROXY_SERVER_NAME] ?? {
4198
+ type: "http",
4199
+ url: conn.mcpUrl,
4200
+ headers: conn.headers
4201
+ };
4202
+ if (!flagBool(args, "write")) {
4203
+ io.out(JSON.stringify({ mcpServers: { [PROXY_SERVER_NAME]: entry } }, null, 2));
4204
+ } else {
4205
+ const explicit = flagString(args, "client");
4206
+ const configs = explicit ? [describeConfigPath(explicit)] : detectClientConfigs(io.cwd, homeFrom(io), io.env);
4207
+ const projectLevel = configs.filter((c) => c.path.startsWith(io.cwd + "/"));
4208
+ let chosen;
4209
+ if (explicit || flagBool(args, "all")) chosen = configs;
4210
+ else chosen = projectLevel.slice(0, 1);
4211
+ if (chosen.length === 0) {
4212
+ io.err(
4213
+ "no MCP config found here (.mcp.json, .cursor/mcp.json, .vscode/mcp.json). Create one, or paste this into your client:"
4214
+ );
4215
+ io.out(JSON.stringify({ mcpServers: { [PROXY_SERVER_NAME]: entry } }, null, 2));
4216
+ return 1;
4217
+ }
4218
+ for (const c of chosen) {
4219
+ if (!existsSync5(c.path)) {
4220
+ io.err(`no MCP config at ${c.path}`);
4221
+ return 1;
4222
+ }
4223
+ const { backup, replaced } = mergeServerIntoConfig(c, PROXY_SERVER_NAME, entry);
4224
+ io.out(
4225
+ `${replaced ? "updated" : "wrote "} ${rel(c.path)} (${c.label}) \u2192 ${PROXY_SERVER_NAME} = ${conn.mcpUrl}`
4226
+ );
4227
+ io.out(`backup ${rel(backup)}`);
4228
+ }
4229
+ for (const c of configs.filter((c2) => !chosen.includes(c2)))
4230
+ io.out(
4231
+ `found ${c.path} (${c.label}, user-level) \u2014 not touched; pass --client ${JSON.stringify(c.path)} to write it too`
4232
+ );
4233
+ }
4234
+ const band = conn.band ? ` band=${conn.band.tier} (${conn.band.quantity} calls/mo)` : "";
4235
+ io.out(`
4236
+ proxy ${conn.slug} (${conn.proxyId}) mode=${conn.mode}${band}`);
4237
+ if (conn.upstreams?.length) io.out(`upstreams: ${conn.upstreams.join(", ")}`);
4238
+ if (conn.dashboardUrl) io.out(`dashboard: ${conn.dashboardUrl}`);
4239
+ if (conn.claudeCode) io.out(`claude code: ${conn.claudeCode}`);
4240
+ if (!flagBool(args, "write"))
4241
+ io.out(`
4242
+ run again with --write to add this to your MCP client config`);
4243
+ return 0;
4244
+ }
4088
4245
 
4089
4246
  // src/commands/control.ts
4090
4247
  async function pokeHttp(io, reg, path2, body) {
@@ -4178,13 +4335,13 @@ async function approvalsCommand(args, io) {
4178
4335
  }
4179
4336
 
4180
4337
  // src/commands/keys.ts
4181
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
4338
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
4182
4339
  import YAML2, { isMap, isSeq, YAMLSeq } from "yaml";
4183
4340
  import { generateAgentKey, hashAgentKey } from "@agentwares/agentguard-core";
4184
4341
  async function keyCommand(args, io) {
4185
4342
  const [sub, name] = args.positionals;
4186
4343
  const path2 = policyPathFrom(args, io);
4187
- const doc = YAML2.parseDocument(readFileSync3(path2, "utf8"));
4344
+ const doc = YAML2.parseDocument(readFileSync4(path2, "utf8"));
4188
4345
  let agents = doc.get("agents");
4189
4346
  if (!isSeq(agents)) {
4190
4347
  agents = new YAMLSeq();
@@ -4461,19 +4618,13 @@ async function statusCommand(args, io) {
4461
4618
  }
4462
4619
 
4463
4620
  // src/cli.ts
4464
- function version2() {
4465
- try {
4466
- return JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8")).version;
4467
- } catch {
4468
- return "0.0.0";
4469
- }
4470
- }
4471
4621
  var HELP = `agentguard \u2014 MCP policy proxy for agents that touch production
4472
4622
 
4473
4623
  npx @agentwares/agentguard init read your MCP config, write agentguard.yaml (dry-run), route every server through the proxy
4474
4624
  agentguard report [--run id|--all] what this run did / would have destroyed / spent, where it was halted
4475
4625
  agentguard diff [--run id] record-by-record mutation diff from a dry run
4476
4626
  agentguard verify [audit.jsonl] prove the hash-chained audit log was not edited
4627
+ agentguard connect <key> [--write] point this machine at your hosted proxy (paid tiers)
4477
4628
 
4478
4629
  agentguard proxy [--http --port 8788] [--agent name] [--run-id id] [--mode dry-run|enforce]
4479
4630
  agentguard tools [--json] the tools your agent will see, with class and why
@@ -4490,7 +4641,7 @@ Docs: https://github.com/agentwares/agentguard/tree/main/apps/agentguard-cli`;
4490
4641
  async function main(argv, io = defaultIo()) {
4491
4642
  const args = parseArgs(argv);
4492
4643
  if (args.flags.version) {
4493
- io.out(version2());
4644
+ io.out(version());
4494
4645
  return 0;
4495
4646
  }
4496
4647
  if (args.command === void 0 && !args.flags.help && io.tty === false) {
@@ -4518,6 +4669,8 @@ async function main(argv, io = defaultIo()) {
4518
4669
  return await diffCommand(args, io);
4519
4670
  case "verify":
4520
4671
  return await verifyCommand(args, io);
4672
+ case "connect":
4673
+ return await connectCommand(args, io);
4521
4674
  case "status":
4522
4675
  return await statusCommand(args, io);
4523
4676
  case "tools":