multicorn-shield 0.13.0 → 1.0.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.
@@ -1,31 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, statSync } from 'fs';
3
- import { mkdir, writeFile, readFile, copyFile, chmod, unlink } from 'fs/promises';
4
- import { join, dirname } from 'path';
3
+ import { readFile, mkdir, writeFile, copyFile, chmod, unlink } from 'fs/promises';
4
+ import { dirname, join } from 'path';
5
5
  import { homedir } from 'os';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { createInterface } from 'readline';
8
- import { spawn } from 'child_process';
9
8
  import { createHash } from 'crypto';
9
+ import { spawn } from 'child_process';
10
10
  import 'stream';
11
11
 
12
- var style = {
13
- violet: (s) => `\x1B[38;2;124;58;237m${s}\x1B[0m`,
14
- violetLight: (s) => `\x1B[38;2;167;139;250m${s}\x1B[0m`,
15
- green: (s) => `\x1B[38;2;34;197;94m${s}\x1B[0m`,
16
- yellow: (s) => `\x1B[38;2;245;158;11m${s}\x1B[0m`,
17
- red: (s) => `\x1B[38;2;239;68;68m${s}\x1B[0m`,
18
- cyan: (s) => `\x1B[38;2;6;182;212m${s}\x1B[0m`,
19
- bold: (s) => `\x1B[1m${s}\x1B[0m`,
20
- dim: (s) => `\x1B[2m${s}\x1B[0m`
12
+ var __defProp = Object.defineProperty;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __esm = (fn, res) => function __init() {
15
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
16
+ };
17
+ var __export = (target, all) => {
18
+ for (var name in all)
19
+ __defProp(target, name, { get: all[name], enumerable: true });
21
20
  };
22
- var BANNER = [
23
- " \u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588\u2588\u2588 \u2588 \u2588\u2588\u2584 ",
24
- " \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588",
25
- " \u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588 \u2588\u2588 \u2588 \u2588 \u2588",
26
- " \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588",
27
- " \u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588\u2580 "
28
- ].map((line) => style.violet(line)).join("\n");
29
21
  function withSpinner(message) {
30
22
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
31
23
  let i = 0;
@@ -43,12 +35,6 @@ function withSpinner(message) {
43
35
  }
44
36
  };
45
37
  }
46
- var NativePluginPrerequisiteMissingError = class extends Error {
47
- constructor() {
48
- super("Native plugin prerequisites not met");
49
- this.name = "NativePluginPrerequisiteMissingError";
50
- }
51
- };
52
38
  function isExistingDirectory(path) {
53
39
  try {
54
40
  if (!existsSync(path)) return false;
@@ -60,10 +46,6 @@ function isExistingDirectory(path) {
60
46
  function nativePluginSkippedSaveNote(wizardCommand, productName) {
61
47
  return "\n" + style.dim("Your agent config has been saved. Run ") + style.cyan(wizardCommand) + style.dim(` again after installing ${productName} to complete hook setup.`) + "\n";
62
48
  }
63
- var CONFIG_DIR = join(homedir(), ".multicorn");
64
- var CONFIG_PATH = join(CONFIG_DIR, "config.json");
65
- var OPENCLAW_CONFIG_PATH = join(homedir(), ".openclaw", "openclaw.json");
66
- var ANSI_PATTERN = new RegExp(String.fromCharCode(27) + "\\[[0-9;]*[a-zA-Z]", "g");
67
49
  function stripAnsi(str) {
68
50
  return str.replace(ANSI_PATTERN, "");
69
51
  }
@@ -207,7 +189,6 @@ async function saveConfig(config) {
207
189
  mode: 384
208
190
  });
209
191
  }
210
- var OPENCLAW_MIN_VERSION = "2026.2.26";
211
192
  async function detectOpenClaw() {
212
193
  let raw;
213
194
  try {
@@ -373,7 +354,8 @@ async function isCursorConnected() {
373
354
  const url = rec["url"];
374
355
  if (typeof url === "string" && url.includes("multicorn")) return true;
375
356
  const args = rec["args"];
376
- if (Array.isArray(args) && args.includes("multicorn-proxy")) return true;
357
+ if (Array.isArray(args) && (args.includes("multicorn-shield") || args.includes("multicorn-proxy")))
358
+ return true;
377
359
  }
378
360
  return false;
379
361
  } catch (err) {
@@ -452,7 +434,7 @@ async function installWindsurfNativeHooks() {
452
434
  "Open Windsurf at least once so this folder exists, or install from:\n " + style.cyan("https://windsurf.com/download") + "\n\n"
453
435
  );
454
436
  process.stderr.write("Then run this wizard again:\n");
455
- process.stderr.write(" " + style.cyan("npx multicorn-proxy init") + "\n");
437
+ process.stderr.write(" " + style.cyan("npx multicorn-shield init") + "\n");
456
438
  throw new NativePluginPrerequisiteMissingError();
457
439
  }
458
440
  const installDir = getWindsurfHooksInstallDir();
@@ -770,35 +752,6 @@ function getClaudeDesktopConfigPath() {
770
752
  );
771
753
  }
772
754
  }
773
- var PLATFORM_LABELS = [
774
- "OpenClaw",
775
- "Claude Code",
776
- "Cursor",
777
- "Windsurf",
778
- "Cline",
779
- "Claude Desktop",
780
- "Gemini CLI",
781
- "Local MCP / Other"
782
- ];
783
- var PLATFORM_BY_SELECTION = {
784
- 1: "openclaw",
785
- 2: "claude-code",
786
- 3: "cursor",
787
- 4: "windsurf",
788
- 5: "cline",
789
- 6: "claude-desktop",
790
- 7: "gemini-cli",
791
- 8: "other-mcp"
792
- };
793
- var DEFAULT_AGENT_NAMES = {
794
- openclaw: "my-openclaw-agent",
795
- "claude-code": "my-claude-code-agent",
796
- cursor: "my-cursor-agent",
797
- windsurf: "my-windsurf-agent",
798
- cline: "my-cline-agent",
799
- "claude-desktop": "my-claude-desktop-agent",
800
- "gemini-cli": "my-gemini-cli-agent"
801
- };
802
755
  async function promptPlatformSelection(ask) {
803
756
  process.stderr.write(
804
757
  "\n" + style.bold(style.violet("Which platform are you connecting?")) + "\n"
@@ -817,7 +770,7 @@ async function promptPlatformSelection(ask) {
817
770
  );
818
771
  }
819
772
  process.stderr.write(
820
- style.dim(" Pick 8 if you want to wrap a local MCP server with multicorn-proxy --wrap.") + "\n"
773
+ style.dim(" Pick 8 if you want to wrap a local MCP server with multicorn-shield --wrap.") + "\n"
821
774
  );
822
775
  let selection = 0;
823
776
  while (selection === 0) {
@@ -1036,7 +989,6 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1036
989
  );
1037
990
  }
1038
991
  }
1039
- var DEFAULT_SHIELD_API_BASE_URL = "https://api.multicorn.ai";
1040
992
  async function runInit(explicitBaseUrl) {
1041
993
  if (!process.stdin.isTTY) {
1042
994
  process.stderr.write(
@@ -1140,7 +1092,7 @@ async function runInit(explicitBaseUrl) {
1140
1092
  );
1141
1093
  process.stderr.write(
1142
1094
  "\n" + style.bold("Try it:") + " " + style.cyan(
1143
- "npx multicorn-proxy --wrap npx @modelcontextprotocol/server-filesystem /tmp"
1095
+ "npx multicorn-shield --wrap npx @modelcontextprotocol/server-filesystem /tmp"
1144
1096
  ) + "\n"
1145
1097
  );
1146
1098
  } catch (error) {
@@ -1190,7 +1142,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1190
1142
  }
1191
1143
  if (detection.status === "not-found") {
1192
1144
  process.stderr.write(
1193
- style.red("\u2717") + " OpenClaw is not installed. Install OpenClaw first, then run npx multicorn-proxy init again.\n"
1145
+ style.red("\u2717") + " OpenClaw is not installed. Install OpenClaw first, then run npx multicorn-shield init again.\n"
1194
1146
  );
1195
1147
  rl.close();
1196
1148
  return null;
@@ -1306,7 +1258,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1306
1258
  } catch (error) {
1307
1259
  if (error instanceof NativePluginPrerequisiteMissingError) {
1308
1260
  postSaveNativeSkipNote = nativePluginSkippedSaveNote(
1309
- "npx multicorn-proxy init",
1261
+ "npx multicorn-shield init",
1310
1262
  "Windsurf"
1311
1263
  );
1312
1264
  configuredAgents.push({
@@ -1689,22 +1641,96 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1689
1641
  }
1690
1642
  return lastConfig;
1691
1643
  }
1644
+ var style, BANNER, NativePluginPrerequisiteMissingError, CONFIG_DIR, CONFIG_PATH, OPENCLAW_CONFIG_PATH, ANSI_PATTERN, OPENCLAW_MIN_VERSION, PLATFORM_LABELS, PLATFORM_BY_SELECTION, DEFAULT_AGENT_NAMES, DEFAULT_SHIELD_API_BASE_URL;
1645
+ var init_config = __esm({
1646
+ "src/proxy/config.ts"() {
1647
+ style = {
1648
+ violet: (s) => `\x1B[38;2;124;58;237m${s}\x1B[0m`,
1649
+ violetLight: (s) => `\x1B[38;2;167;139;250m${s}\x1B[0m`,
1650
+ green: (s) => `\x1B[38;2;34;197;94m${s}\x1B[0m`,
1651
+ yellow: (s) => `\x1B[38;2;245;158;11m${s}\x1B[0m`,
1652
+ red: (s) => `\x1B[38;2;239;68;68m${s}\x1B[0m`,
1653
+ cyan: (s) => `\x1B[38;2;6;182;212m${s}\x1B[0m`,
1654
+ bold: (s) => `\x1B[1m${s}\x1B[0m`,
1655
+ dim: (s) => `\x1B[2m${s}\x1B[0m`
1656
+ };
1657
+ BANNER = [
1658
+ " \u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588\u2588\u2588 \u2588 \u2588\u2588\u2584 ",
1659
+ " \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588",
1660
+ " \u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588 \u2588\u2588 \u2588 \u2588 \u2588",
1661
+ " \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588",
1662
+ " \u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588\u2580 "
1663
+ ].map((line) => style.violet(line)).join("\n");
1664
+ NativePluginPrerequisiteMissingError = class extends Error {
1665
+ constructor() {
1666
+ super("Native plugin prerequisites not met");
1667
+ this.name = "NativePluginPrerequisiteMissingError";
1668
+ }
1669
+ };
1670
+ CONFIG_DIR = join(homedir(), ".multicorn");
1671
+ CONFIG_PATH = join(CONFIG_DIR, "config.json");
1672
+ OPENCLAW_CONFIG_PATH = join(homedir(), ".openclaw", "openclaw.json");
1673
+ ANSI_PATTERN = new RegExp(String.fromCharCode(27) + "\\[[0-9;]*[a-zA-Z]", "g");
1674
+ OPENCLAW_MIN_VERSION = "2026.2.26";
1675
+ PLATFORM_LABELS = [
1676
+ "OpenClaw",
1677
+ "Claude Code",
1678
+ "Cursor",
1679
+ "Windsurf",
1680
+ "Cline",
1681
+ "Claude Desktop",
1682
+ "Gemini CLI",
1683
+ "Local MCP / Other"
1684
+ ];
1685
+ PLATFORM_BY_SELECTION = {
1686
+ 1: "openclaw",
1687
+ 2: "claude-code",
1688
+ 3: "cursor",
1689
+ 4: "windsurf",
1690
+ 5: "cline",
1691
+ 6: "claude-desktop",
1692
+ 7: "gemini-cli",
1693
+ 8: "other-mcp"
1694
+ };
1695
+ DEFAULT_AGENT_NAMES = {
1696
+ openclaw: "my-openclaw-agent",
1697
+ "claude-code": "my-claude-code-agent",
1698
+ cursor: "my-cursor-agent",
1699
+ windsurf: "my-windsurf-agent",
1700
+ cline: "my-cline-agent",
1701
+ "claude-desktop": "my-claude-desktop-agent",
1702
+ "gemini-cli": "my-gemini-cli-agent"
1703
+ };
1704
+ DEFAULT_SHIELD_API_BASE_URL = "https://api.multicorn.ai";
1705
+ }
1706
+ });
1692
1707
 
1693
1708
  // src/types/index.ts
1694
- var PERMISSION_LEVELS = {
1695
- Read: "read",
1696
- Write: "write",
1697
- Execute: "execute",
1698
- Publish: "publish",
1699
- Create: "create"
1700
- };
1709
+ var PERMISSION_LEVELS;
1710
+ var init_types = __esm({
1711
+ "src/types/index.ts"() {
1712
+ PERMISSION_LEVELS = {
1713
+ Read: "read",
1714
+ Write: "write",
1715
+ Execute: "execute",
1716
+ Publish: "publish",
1717
+ Create: "create"
1718
+ };
1719
+ }
1720
+ });
1701
1721
 
1702
1722
  // src/scopes/scope-parser.ts
1703
- var VALID_PERMISSION_LEVELS = new Set(Object.values(PERMISSION_LEVELS));
1704
- [...VALID_PERMISSION_LEVELS].join(", ");
1705
1723
  function formatScope(scope) {
1706
1724
  return `${scope.permissionLevel}:${scope.service}`;
1707
1725
  }
1726
+ var VALID_PERMISSION_LEVELS;
1727
+ var init_scope_parser = __esm({
1728
+ "src/scopes/scope-parser.ts"() {
1729
+ init_types();
1730
+ VALID_PERMISSION_LEVELS = new Set(Object.values(PERMISSION_LEVELS));
1731
+ [...VALID_PERMISSION_LEVELS].join(", ");
1732
+ }
1733
+ });
1708
1734
 
1709
1735
  // src/scopes/scope-validator.ts
1710
1736
  function validateScopeAccess(grantedScopes, requested) {
@@ -1732,6 +1758,11 @@ function hasScope(grantedScopes, requested) {
1732
1758
  (granted) => granted.service === requested.service && granted.permissionLevel === requested.permissionLevel
1733
1759
  );
1734
1760
  }
1761
+ var init_scope_validator = __esm({
1762
+ "src/scopes/scope-validator.ts"() {
1763
+ init_scope_parser();
1764
+ }
1765
+ });
1735
1766
 
1736
1767
  // src/logger/action-logger.ts
1737
1768
  function createActionLogger(config) {
@@ -1905,6 +1936,10 @@ function createActionLogger(config) {
1905
1936
  function sleep(ms) {
1906
1937
  return new Promise((resolve) => setTimeout(resolve, ms));
1907
1938
  }
1939
+ var init_action_logger = __esm({
1940
+ "src/logger/action-logger.ts"() {
1941
+ }
1942
+ });
1908
1943
 
1909
1944
  // src/spending/spending-checker.ts
1910
1945
  function createSpendingChecker(config) {
@@ -2037,13 +2072,12 @@ function validateLimits(limits) {
2037
2072
  function dollarsToCents(dollars) {
2038
2073
  return Math.round(dollars * 100);
2039
2074
  }
2075
+ var init_spending_checker = __esm({
2076
+ "src/spending/spending-checker.ts"() {
2077
+ }
2078
+ });
2040
2079
 
2041
2080
  // src/proxy/interceptor.ts
2042
- var BLOCKED_ERROR_CODE = -32e3;
2043
- var SPENDING_BLOCKED_ERROR_CODE = -32001;
2044
- var INTERNAL_ERROR_CODE = -32002;
2045
- var SERVICE_UNREACHABLE_ERROR_CODE = -32003;
2046
- var AUTH_ERROR_CODE = -32004;
2047
2081
  function parseJsonRpcLine(line) {
2048
2082
  const trimmed = line.trim();
2049
2083
  if (trimmed.length === 0) return null;
@@ -2111,7 +2145,7 @@ function buildServiceUnreachableResponse(id, dashboardUrl) {
2111
2145
  };
2112
2146
  }
2113
2147
  function buildAuthErrorResponse(id) {
2114
- const message = "Action blocked: Shield API key is invalid or has been revoked. Run npx multicorn-proxy init to reconfigure.";
2148
+ const message = "Action blocked: Shield API key is invalid or has been revoked. Run npx multicorn-shield init to reconfigure.";
2115
2149
  return {
2116
2150
  jsonrpc: "2.0",
2117
2151
  id,
@@ -2143,9 +2177,16 @@ function capitalize(str) {
2143
2177
  const first = str[0];
2144
2178
  return first !== void 0 ? first.toUpperCase() + str.slice(1) : str;
2145
2179
  }
2146
- var MULTICORN_DIR = join(homedir(), ".multicorn");
2147
- var SCOPES_PATH = join(MULTICORN_DIR, "scopes.json");
2148
- var CACHE_META_PATH = join(MULTICORN_DIR, "cache-meta.json");
2180
+ var BLOCKED_ERROR_CODE, SPENDING_BLOCKED_ERROR_CODE, INTERNAL_ERROR_CODE, SERVICE_UNREACHABLE_ERROR_CODE, AUTH_ERROR_CODE;
2181
+ var init_interceptor = __esm({
2182
+ "src/proxy/interceptor.ts"() {
2183
+ BLOCKED_ERROR_CODE = -32e3;
2184
+ SPENDING_BLOCKED_ERROR_CODE = -32001;
2185
+ INTERNAL_ERROR_CODE = -32002;
2186
+ SERVICE_UNREACHABLE_ERROR_CODE = -32003;
2187
+ AUTH_ERROR_CODE = -32004;
2188
+ }
2189
+ });
2149
2190
  function cacheKey(agentName, apiKey) {
2150
2191
  return createHash("sha256").update(`${agentName}:${apiKey}`).digest("hex").slice(0, 16);
2151
2192
  }
@@ -2216,10 +2257,14 @@ async function saveCachedScopes(agentName, agentId, scopes, apiKey) {
2216
2257
  function isScopesCacheFile(value) {
2217
2258
  return typeof value === "object" && value !== null;
2218
2259
  }
2219
-
2220
- // src/proxy/consent.ts
2221
- var CONSENT_POLL_INTERVAL_MS = 3e3;
2222
- var CONSENT_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
2260
+ var MULTICORN_DIR, SCOPES_PATH, CACHE_META_PATH;
2261
+ var init_scope_cache = __esm({
2262
+ "src/openclaw/scope-cache.ts"() {
2263
+ MULTICORN_DIR = join(homedir(), ".multicorn");
2264
+ SCOPES_PATH = join(MULTICORN_DIR, "scopes.json");
2265
+ CACHE_META_PATH = join(MULTICORN_DIR, "cache-meta.json");
2266
+ }
2267
+ });
2223
2268
  function deriveDashboardUrl(baseUrl) {
2224
2269
  try {
2225
2270
  const url = new URL(baseUrl);
@@ -2244,13 +2289,6 @@ function deriveDashboardUrl(baseUrl) {
2244
2289
  return "https://app.multicorn.ai";
2245
2290
  }
2246
2291
  }
2247
- var ShieldAuthError = class _ShieldAuthError extends Error {
2248
- constructor(message) {
2249
- super(message);
2250
- this.name = "ShieldAuthError";
2251
- Object.setPrototypeOf(this, _ShieldAuthError.prototype);
2252
- }
2253
- };
2254
2292
  async function findAgentByName(agentName, apiKey, baseUrl) {
2255
2293
  let response;
2256
2294
  try {
@@ -2443,13 +2481,25 @@ function isPermissionShape(value) {
2443
2481
  const obj = value;
2444
2482
  return typeof obj["service"] === "string" && typeof obj["read"] === "boolean" && typeof obj["write"] === "boolean" && typeof obj["execute"] === "boolean" && (obj["revoked_at"] === null || obj["revoked_at"] === void 0 || typeof obj["revoked_at"] === "string");
2445
2483
  }
2446
-
2447
- // src/proxy/index.ts
2448
- var DEFAULT_SCOPE_REFRESH_INTERVAL_MS = 6e4;
2484
+ var CONSENT_POLL_INTERVAL_MS, CONSENT_POLL_TIMEOUT_MS, ShieldAuthError;
2485
+ var init_consent = __esm({
2486
+ "src/proxy/consent.ts"() {
2487
+ init_scope_cache();
2488
+ CONSENT_POLL_INTERVAL_MS = 3e3;
2489
+ CONSENT_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
2490
+ ShieldAuthError = class _ShieldAuthError extends Error {
2491
+ constructor(message) {
2492
+ super(message);
2493
+ this.name = "ShieldAuthError";
2494
+ Object.setPrototypeOf(this, _ShieldAuthError.prototype);
2495
+ }
2496
+ };
2497
+ }
2498
+ });
2449
2499
  function createProxyServer(config) {
2450
2500
  if (!config.baseUrl.startsWith("https://") && !config.baseUrl.startsWith("http://localhost") && !config.baseUrl.startsWith("http://127.0.0.1")) {
2451
2501
  throw new Error(
2452
- `[multicorn-proxy] Base URL must use HTTPS. Received: "${config.baseUrl}". Use https:// or http://localhost for local development.`
2502
+ `[multicorn-shield] Base URL must use HTTPS. Received: "${config.baseUrl}". Use https:// or http://localhost for local development.`
2453
2503
  );
2454
2504
  }
2455
2505
  let child = null;
@@ -2546,7 +2596,7 @@ function createProxyServer(config) {
2546
2596
  if (actionLogger !== null) {
2547
2597
  if (!config.agentName || config.agentName.trim().length === 0) {
2548
2598
  process.stderr.write(
2549
- "[multicorn-proxy] Cannot log action: agent name not resolved\n"
2599
+ "[multicorn-shield] Cannot log action: agent name not resolved\n"
2550
2600
  );
2551
2601
  } else {
2552
2602
  config.logger.debug("Logging blocked action (post-consent).", {
@@ -2576,7 +2626,7 @@ function createProxyServer(config) {
2576
2626
  if (actionLogger !== null) {
2577
2627
  if (!config.agentName || config.agentName.trim().length === 0) {
2578
2628
  process.stderr.write(
2579
- "[multicorn-proxy] Cannot log action: agent name not resolved\n"
2629
+ "[multicorn-shield] Cannot log action: agent name not resolved\n"
2580
2630
  );
2581
2631
  } else {
2582
2632
  config.logger.debug("Logging blocked action (spending).", {
@@ -2605,7 +2655,7 @@ function createProxyServer(config) {
2605
2655
  }
2606
2656
  if (actionLogger !== null) {
2607
2657
  if (!config.agentName || config.agentName.trim().length === 0) {
2608
- process.stderr.write("[multicorn-proxy] Cannot log action: agent name not resolved\n");
2658
+ process.stderr.write("[multicorn-shield] Cannot log action: agent name not resolved\n");
2609
2659
  } else {
2610
2660
  config.logger.debug("Logging approved action.", {
2611
2661
  agent: config.agentName,
@@ -2687,7 +2737,7 @@ function createProxyServer(config) {
2687
2737
  agent: config.agentName
2688
2738
  });
2689
2739
  process.stderr.write(
2690
- "\nError: API key was rejected by the Multicorn service.\nCheck your key at https://app.multicorn.ai/settings#api-keys or run `npx multicorn-proxy init` to reconfigure.\n\n"
2740
+ "\nError: API key was rejected by the Multicorn service.\nCheck your key at https://app.multicorn.ai/settings#api-keys or run `npx multicorn-shield init` to reconfigure.\n\n"
2691
2741
  );
2692
2742
  throw new Error("API key was rejected by the Multicorn service.");
2693
2743
  }
@@ -2760,12 +2810,17 @@ function extractCostCents(args) {
2760
2810
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return 0;
2761
2811
  return dollarsToCents(amount);
2762
2812
  }
2763
- var LOG_LEVELS = {
2764
- debug: 0,
2765
- info: 1,
2766
- warn: 2,
2767
- error: 3
2768
- };
2813
+ var DEFAULT_SCOPE_REFRESH_INTERVAL_MS;
2814
+ var init_proxy = __esm({
2815
+ "src/proxy/index.ts"() {
2816
+ init_scope_validator();
2817
+ init_action_logger();
2818
+ init_spending_checker();
2819
+ init_interceptor();
2820
+ init_consent();
2821
+ DEFAULT_SCOPE_REFRESH_INTERVAL_MS = 6e4;
2822
+ }
2823
+ });
2769
2824
  function createLogger(level, output = process.stderr) {
2770
2825
  const minLevel = LOG_LEVELS[level];
2771
2826
  function write(logLevel, msg, data) {
@@ -2796,8 +2851,93 @@ function createLogger(level, output = process.stderr) {
2796
2851
  function isValidLogLevel(value) {
2797
2852
  return typeof value === "string" && Object.hasOwn(LOG_LEVELS, value);
2798
2853
  }
2854
+ var LOG_LEVELS;
2855
+ var init_logger = __esm({
2856
+ "src/proxy/logger.ts"() {
2857
+ LOG_LEVELS = {
2858
+ debug: 0,
2859
+ info: 1,
2860
+ warn: 2,
2861
+ error: 3
2862
+ };
2863
+ }
2864
+ });
2865
+ function getExtensionBackupPath() {
2866
+ return join(homedir(), ".multicorn", EXTENSION_BACKUP_FILENAME);
2867
+ }
2868
+ function isRecord(value) {
2869
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2870
+ }
2871
+ async function readExtensionBackup() {
2872
+ try {
2873
+ const raw = await readFile(getExtensionBackupPath(), "utf8");
2874
+ const parsed = JSON.parse(raw);
2875
+ if (!isRecord(parsed)) return null;
2876
+ if (parsed["version"] !== 1) return null;
2877
+ if (typeof parsed["createdAt"] !== "string") return null;
2878
+ if (typeof parsed["claudeDesktopConfigPath"] !== "string") return null;
2879
+ const mcpServers = parsed["mcpServers"];
2880
+ if (!isRecord(mcpServers)) return null;
2881
+ return {
2882
+ version: 1,
2883
+ createdAt: parsed["createdAt"],
2884
+ claudeDesktopConfigPath: parsed["claudeDesktopConfigPath"],
2885
+ mcpServers
2886
+ };
2887
+ } catch {
2888
+ return null;
2889
+ }
2890
+ }
2891
+ var EXTENSION_BACKUP_FILENAME;
2892
+ var init_config_reader = __esm({
2893
+ "src/extension/config-reader.ts"() {
2894
+ EXTENSION_BACKUP_FILENAME = "extension-backup.json";
2895
+ }
2896
+ });
2897
+ function isErrnoException2(e) {
2898
+ return typeof e === "object" && e !== null && "code" in e;
2899
+ }
2900
+ function isRecord2(value) {
2901
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2902
+ }
2903
+ async function restoreClaudeDesktopMcpFromBackup() {
2904
+ const backup = await readExtensionBackup();
2905
+ if (backup === null) {
2906
+ throw new Error(
2907
+ "No Shield extension backup found. Expected ~/.multicorn/extension-backup.json from a previous Shield Desktop Extension session."
2908
+ );
2909
+ }
2910
+ const configPath = getClaudeDesktopConfigPath();
2911
+ let root = {};
2912
+ try {
2913
+ const raw = await readFile(configPath, "utf8");
2914
+ const parsed = JSON.parse(raw);
2915
+ if (isRecord2(parsed)) {
2916
+ root = parsed;
2917
+ }
2918
+ } catch (error) {
2919
+ if (!isErrnoException2(error) || error.code !== "ENOENT") {
2920
+ throw error;
2921
+ }
2922
+ }
2923
+ root["mcpServers"] = backup.mcpServers;
2924
+ await mkdir(dirname(configPath), { recursive: true });
2925
+ await writeFile(configPath, JSON.stringify(root, null, 2) + "\n", { encoding: "utf8" });
2926
+ }
2927
+ var init_restore = __esm({
2928
+ "src/extension/restore.ts"() {
2929
+ init_config();
2930
+ init_config_reader();
2931
+ }
2932
+ });
2799
2933
 
2800
- // bin/multicorn-proxy.ts
2934
+ // bin/multicorn-shield.ts
2935
+ var multicorn_shield_exports = {};
2936
+ __export(multicorn_shield_exports, {
2937
+ parseArgs: () => parseArgs,
2938
+ resolveWrapConfig: () => resolveWrapConfig,
2939
+ runCli: () => runCli
2940
+ });
2801
2941
  function parseArgs(argv) {
2802
2942
  const args = argv.slice(2);
2803
2943
  let subcommand = "help";
@@ -2820,7 +2960,7 @@ function parseArgs(argv) {
2820
2960
  const name = args[i + 1];
2821
2961
  if (name === void 0 || name.startsWith("-")) {
2822
2962
  process.stderr.write("Error: delete-agent requires an agent name.\n");
2823
- process.stderr.write("Example: npx multicorn-proxy delete-agent my-agent\n");
2963
+ process.stderr.write("Example: npx multicorn-shield delete-agent my-agent\n");
2824
2964
  process.exit(1);
2825
2965
  }
2826
2966
  deleteAgentName = name;
@@ -2872,7 +3012,7 @@ function parseArgs(argv) {
2872
3012
  }
2873
3013
  if (remaining.length === 0) {
2874
3014
  process.stderr.write("Error: --wrap requires a command to run.\n");
2875
- process.stderr.write("Example: npx multicorn-proxy --wrap my-mcp-server\n");
3015
+ process.stderr.write("Example: npx multicorn-shield --wrap my-mcp-server\n");
2876
3016
  process.exit(1);
2877
3017
  }
2878
3018
  wrapCommand = remaining[0] ?? "";
@@ -2925,19 +3065,22 @@ function parseArgs(argv) {
2925
3065
  function printHelp() {
2926
3066
  process.stderr.write(
2927
3067
  [
2928
- "multicorn-proxy: MCP permission proxy",
3068
+ "multicorn-shield: MCP permission proxy and Shield setup",
2929
3069
  "",
2930
3070
  "Usage:",
2931
- " npx multicorn-proxy init",
3071
+ " npx multicorn-shield init",
2932
3072
  " Interactive setup. Saves API key to ~/.multicorn/config.json.",
2933
3073
  "",
2934
- " npx multicorn-proxy agents",
3074
+ " npx multicorn-shield restore",
3075
+ " Restore MCP servers in claude_desktop_config.json from the Shield extension backup.",
3076
+ "",
3077
+ " npx multicorn-shield agents",
2935
3078
  " List configured agents and show which is the default.",
2936
3079
  "",
2937
- " npx multicorn-proxy delete-agent <name>",
3080
+ " npx multicorn-shield delete-agent <name>",
2938
3081
  " Remove a saved agent.",
2939
3082
  "",
2940
- " npx multicorn-proxy --wrap <command> [args...]",
3083
+ " npx multicorn-shield --wrap <command> [args...]",
2941
3084
  " Start <command> as an MCP server and proxy all tool calls through",
2942
3085
  " Shield's permission layer.",
2943
3086
  "",
@@ -2949,14 +3092,22 @@ function printHelp() {
2949
3092
  " --agent-name <name> Override agent name derived from the wrapped command",
2950
3093
  "",
2951
3094
  "Examples:",
2952
- " npx multicorn-proxy init",
2953
- " npx multicorn-proxy --wrap npx @modelcontextprotocol/server-filesystem /tmp",
2954
- " npx multicorn-proxy --wrap my-mcp-server --log-level debug",
3095
+ " npx multicorn-shield init",
3096
+ " npx multicorn-shield --wrap npx @modelcontextprotocol/server-filesystem /tmp",
3097
+ " npx multicorn-shield --wrap my-mcp-server --log-level debug",
2955
3098
  ""
2956
3099
  ].join("\n")
2957
3100
  );
2958
3101
  }
2959
- async function main() {
3102
+ async function runCli() {
3103
+ const first = process.argv[2];
3104
+ if (first === "restore") {
3105
+ await restoreClaudeDesktopMcpFromBackup();
3106
+ process.stderr.write(
3107
+ "Restored MCP server entries from ~/.multicorn/extension-backup.json into Claude Desktop config.\nRestart Claude Desktop to apply changes.\n"
3108
+ );
3109
+ return;
3110
+ }
2960
3111
  const cli = parseArgs(process.argv);
2961
3112
  const logger = createLogger(cli.logLevel);
2962
3113
  if (cli.subcommand === "help") {
@@ -2971,13 +3122,13 @@ async function main() {
2971
3122
  const config2 = await loadConfig();
2972
3123
  if (config2 === null) {
2973
3124
  process.stderr.write(
2974
- "No config found. Run `npx multicorn-proxy init` to set up your API key.\n"
3125
+ "No config found. Run `npx multicorn-shield init` to set up your API key.\n"
2975
3126
  );
2976
3127
  process.exit(1);
2977
3128
  }
2978
3129
  const agents = collectAgentsFromConfig(config2);
2979
3130
  if (agents.length === 0) {
2980
- process.stderr.write("No agents configured. Run `npx multicorn-proxy init` to add one.\n");
3131
+ process.stderr.write("No agents configured. Run `npx multicorn-shield init` to add one.\n");
2981
3132
  process.exit(0);
2982
3133
  }
2983
3134
  const def = config2.defaultAgent;
@@ -3067,7 +3218,7 @@ async function resolveWrapConfig(cli, logger) {
3067
3218
  return config;
3068
3219
  }
3069
3220
  process.stderr.write(
3070
- "No API key found. Provide one via the --api-key flag, the MULTICORN_API_KEY environment variable, or run `npx multicorn-proxy init` to set up a config file.\n"
3221
+ "No API key found. Provide one via the --api-key flag, the MULTICORN_API_KEY environment variable, or run `npx multicorn-shield init` to set up a config file.\n"
3071
3222
  );
3072
3223
  process.exit(1);
3073
3224
  }
@@ -3095,14 +3246,32 @@ function deriveAgentName(command) {
3095
3246
  const base = command.split("/").pop() ?? command;
3096
3247
  return base.replace(/\.[cm]?[jt]s$/, "");
3097
3248
  }
3098
- var isDirectRun = process.argv[1] !== void 0 && (import.meta.url.endsWith(process.argv[1]) || import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith("/multicorn-proxy.js") || import.meta.url.endsWith("/multicorn-proxy.ts"));
3099
- if (isDirectRun && process.env["VITEST"] === void 0) {
3100
- main().catch((error) => {
3101
- const message = error instanceof Error ? error.message : String(error);
3102
- process.stderr.write(`Fatal error: ${message}
3249
+ var isDirectRun;
3250
+ var init_multicorn_shield = __esm({
3251
+ "bin/multicorn-shield.ts"() {
3252
+ init_config();
3253
+ init_proxy();
3254
+ init_logger();
3255
+ init_consent();
3256
+ init_restore();
3257
+ isDirectRun = process.argv[1] !== void 0 && (import.meta.url.endsWith(process.argv[1]) || import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith("/multicorn-shield.js") || import.meta.url.endsWith("/multicorn-shield.ts"));
3258
+ if (isDirectRun && process.env["VITEST"] === void 0) {
3259
+ runCli().catch((error) => {
3260
+ const message = error instanceof Error ? error.message : String(error);
3261
+ process.stderr.write(`Fatal error: ${message}
3103
3262
  `);
3104
- process.exit(1);
3105
- });
3106
- }
3263
+ process.exit(1);
3264
+ });
3265
+ }
3266
+ }
3267
+ });
3107
3268
 
3108
- export { parseArgs, resolveWrapConfig };
3269
+ // bin/multicorn-proxy.ts
3270
+ process.stderr.write("Warning: multicorn-proxy is deprecated. Use multicorn-shield instead.\n");
3271
+ var { runCli: runCli2 } = await Promise.resolve().then(() => (init_multicorn_shield(), multicorn_shield_exports));
3272
+ void runCli2().catch((error) => {
3273
+ const message = error instanceof Error ? error.message : String(error);
3274
+ process.stderr.write(`Fatal error: ${message}
3275
+ `);
3276
+ process.exit(1);
3277
+ });