multicorn-shield 0.13.0 → 1.1.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,60 +752,73 @@ 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
- };
755
+ function platformMenuLabelForSelection(sel) {
756
+ const slug = PLATFORM_BY_SELECTION[sel];
757
+ if (slug === void 0) return "Unknown";
758
+ const entry = INIT_WIZARD_PLATFORM_REGISTRY.find((e) => e.slug === slug);
759
+ return entry?.displayName ?? slug;
760
+ }
761
+ async function promptHostedProxyInstallPrereq(ask, platformLabel, prereqUrl) {
762
+ process.stderr.write("\n");
763
+ process.stderr.write(
764
+ style.bold("Before continuing, make sure you have ") + platformLabel + style.bold(" installed.") + "\n"
765
+ );
766
+ process.stderr.write(" \u2192 " + style.cyan(prereqUrl) + "\n\n");
767
+ const answer = await ask("Ready to continue? (Y/n) ");
768
+ return answer.trim().toLowerCase() !== "n";
769
+ }
770
+ function isPlatformDetectedForMenu(slug) {
771
+ switch (slug) {
772
+ case "openclaw":
773
+ return isOpenClawConnected();
774
+ case "claude-code":
775
+ return Promise.resolve(isClaudeCodeConnected());
776
+ case "cursor":
777
+ return isCursorConnected();
778
+ case "windsurf":
779
+ return isWindsurfConnected();
780
+ default:
781
+ return Promise.resolve(false);
782
+ }
783
+ }
802
784
  async function promptPlatformSelection(ask) {
803
785
  process.stderr.write(
804
- "\n" + style.bold(style.violet("Which platform are you connecting?")) + "\n"
786
+ "\n" + style.bold(style.violet("Which platform are you connecting?")) + "\n\n"
805
787
  );
806
- const connectedFlags = [
807
- await isOpenClawConnected(),
808
- isClaudeCodeConnected(),
809
- await isCursorConnected(),
810
- await isWindsurfConnected()
811
- ];
812
- for (let i = 0; i < PLATFORM_LABELS.length; i++) {
813
- const marker = i < connectedFlags.length && connectedFlags[i] ? " " + style.dim("\u25CF detected locally") : "";
814
- process.stderr.write(
815
- ` ${style.violet(String(i + 1))}. ${PLATFORM_LABELS[i] ?? ""}${marker}
788
+ const detectionSlugs = INIT_WIZARD_PLATFORM_REGISTRY.filter((e) => e.detectable).map(
789
+ (e) => e.slug
790
+ );
791
+ const connectedFlags = await Promise.all(
792
+ detectionSlugs.map((slug) => isPlatformDetectedForMenu(slug))
793
+ );
794
+ function markerFor(platform) {
795
+ const idx = detectionSlugs.indexOf(platform);
796
+ if (idx === -1) return "";
797
+ if (!connectedFlags[idx]) return "";
798
+ return " " + style.dim("\u25CF detected locally");
799
+ }
800
+ let optionNum = 1;
801
+ for (const section of INIT_WIZARD_MENU_SECTIONS) {
802
+ process.stderr.write(" " + style.dim(section.title) + "\n");
803
+ for (const item of section.items) {
804
+ const indent = optionNum >= 10 ? " " : " ";
805
+ process.stderr.write(
806
+ `${indent}${style.violet(String(optionNum))}. ${item.label}${markerFor(item.platform)}
816
807
  `
817
- );
808
+ );
809
+ optionNum++;
810
+ }
818
811
  }
819
812
  process.stderr.write(
820
- style.dim(" Pick 8 if you want to wrap a local MCP server with multicorn-proxy --wrap.") + "\n"
813
+ "\n" + style.dim(
814
+ ` Pick ${String(INIT_WIZARD_SELECTION_MAX)} to wrap a local MCP server with multicorn-shield --wrap.`
815
+ ) + "\n"
821
816
  );
822
817
  let selection = 0;
823
818
  while (selection === 0) {
824
- const input = await ask("Select (1-8): ");
819
+ const input = await ask(`Select (1-${String(INIT_WIZARD_SELECTION_MAX)}): `);
825
820
  const num = parseInt(input.trim(), 10);
826
- if (num >= 1 && num <= 8) {
821
+ if (num >= 1 && num <= INIT_WIZARD_SELECTION_MAX) {
827
822
  selection = num;
828
823
  }
829
824
  }
@@ -894,12 +889,7 @@ async function promptProxyConfig(ask, agentName) {
894
889
  }
895
890
  targetUrl = input.trim();
896
891
  }
897
- const defaultShortName = normalizeAgentName(agentName) || "shield-mcp";
898
- const shortNameInput = await ask(
899
- `
900
- Short name (a nickname for this connection, used in your proxy URL): ${style.dim(`(${defaultShortName})`)} `
901
- );
902
- const shortName = shortNameInput.trim().length > 0 ? normalizeAgentName(shortNameInput.trim()) || defaultShortName : defaultShortName;
892
+ const shortName = normalizeAgentName(agentName) || "shield-mcp";
903
893
  return { targetUrl, shortName };
904
894
  }
905
895
  async function createProxyConfig(baseUrl, apiKey, agentName, targetUrl, serverName, platform) {
@@ -943,24 +933,84 @@ async function createProxyConfig(baseUrl, apiKey, agentName, targetUrl, serverNa
943
933
  const data = envelope["data"];
944
934
  return typeof data?.["proxy_url"] === "string" ? data["proxy_url"] : "";
945
935
  }
936
+ function gooseHostedProxyYaml(shortName, proxyUrl, bearerHeader) {
937
+ return `extensions:
938
+ ${shortName}:
939
+ type: streamable_http
940
+ url: ${proxyUrl}
941
+ headers:
942
+ Authorization: ${bearerHeader}
943
+ enabled: true
944
+ timeout: 300
945
+ `;
946
+ }
946
947
  function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
947
- const usesInlineKey = platform === "cursor" || platform === "claude-desktop" || platform === "windsurf" || platform === "cline" || platform === "gemini-cli";
948
+ const hostedInlinePlatforms = /* @__PURE__ */ new Set([
949
+ "cursor",
950
+ "claude-desktop",
951
+ "windsurf",
952
+ "cline",
953
+ "gemini-cli",
954
+ "kilo-code",
955
+ "github-copilot",
956
+ "continue-dev",
957
+ "goose"
958
+ ]);
959
+ const usesInlineKey = hostedInlinePlatforms.has(platform);
948
960
  const authHeader = usesInlineKey ? `Bearer ${apiKey}` : "Bearer YOUR_SHIELD_API_KEY";
949
- const urlKey = platform === "windsurf" ? "serverUrl" : platform === "gemini-cli" ? "httpUrl" : "url";
950
- const mcpSnippet = JSON.stringify(
951
- {
952
- mcpServers: {
953
- [shortName]: {
954
- [urlKey]: routingToken,
955
- headers: {
956
- Authorization: authHeader
961
+ let snippetText;
962
+ if (platform === "github-copilot") {
963
+ snippetText = JSON.stringify(
964
+ {
965
+ mcp: {
966
+ servers: {
967
+ [shortName]: {
968
+ type: "http",
969
+ url: routingToken,
970
+ headers: {
971
+ Authorization: authHeader
972
+ }
973
+ }
957
974
  }
958
975
  }
959
- }
960
- },
961
- null,
962
- 2
963
- );
976
+ },
977
+ null,
978
+ 2
979
+ );
980
+ } else if (platform === "goose") {
981
+ snippetText = gooseHostedProxyYaml(shortName, routingToken, authHeader);
982
+ } else if (platform === "gemini-cli") {
983
+ snippetText = JSON.stringify(
984
+ {
985
+ mcpServers: {
986
+ [shortName]: {
987
+ httpUrl: routingToken,
988
+ headers: {
989
+ Authorization: authHeader
990
+ }
991
+ }
992
+ }
993
+ },
994
+ null,
995
+ 2
996
+ );
997
+ } else {
998
+ const urlKey = platform === "windsurf" ? "serverUrl" : "url";
999
+ snippetText = JSON.stringify(
1000
+ {
1001
+ mcpServers: {
1002
+ [shortName]: {
1003
+ [urlKey]: routingToken,
1004
+ headers: {
1005
+ Authorization: authHeader
1006
+ }
1007
+ }
1008
+ }
1009
+ },
1010
+ null,
1011
+ 2
1012
+ );
1013
+ }
964
1014
  if (platform === "openclaw") {
965
1015
  process.stderr.write("\n" + style.dim("Add this to your OpenClaw agent config:") + "\n\n");
966
1016
  } else if (platform === "claude-code") {
@@ -994,10 +1044,28 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
994
1044
  "Add this to ~/.gemini/settings.json (create the file if it does not exist). For project-specific config, use .gemini/settings.json in your project root. Restart Gemini CLI after saving. Run /mcp to verify the server is connected."
995
1045
  ) + "\n\n"
996
1046
  );
1047
+ } else if (platform === "kilo-code") {
1048
+ process.stderr.write(
1049
+ "\n" + style.dim("Add this to .kilocode/mcp.json in your project root.") + "\n\n"
1050
+ );
1051
+ } else if (platform === "github-copilot") {
1052
+ process.stderr.write(
1053
+ "\n" + style.dim(
1054
+ "Open VS Code Settings (JSON) and merge this under the mcp key. If you do not have an mcp section yet, add one. Copilot picks up MCP servers when you use Agent mode."
1055
+ ) + "\n\n"
1056
+ );
1057
+ } else if (platform === "continue-dev") {
1058
+ process.stderr.write(
1059
+ "\n" + style.dim("Save this as .continue/mcpServers/shield.json in your workspace root.") + "\n\n"
1060
+ );
1061
+ } else if (platform === "goose") {
1062
+ process.stderr.write(
1063
+ "\n" + style.dim("Add this to ~/.config/goose/config.yaml under the extensions key.") + "\n\n"
1064
+ );
997
1065
  } else {
998
1066
  process.stderr.write("\n" + style.dim("Add this to ~/.cursor/mcp.json:") + "\n\n");
999
1067
  }
1000
- process.stderr.write(style.cyan(mcpSnippet) + "\n\n");
1068
+ process.stderr.write(style.cyan(snippetText) + "\n\n");
1001
1069
  if (!usesInlineKey) {
1002
1070
  process.stderr.write(
1003
1071
  style.dim(
@@ -1035,8 +1103,15 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1035
1103
  ) + "\n"
1036
1104
  );
1037
1105
  }
1106
+ if (platform === "github-copilot" || platform === "continue-dev") {
1107
+ process.stderr.write(
1108
+ style.dim("Reload the editor window if the MCP server does not appear immediately.") + "\n"
1109
+ );
1110
+ }
1111
+ if (platform === "goose") {
1112
+ process.stderr.write(style.dim("Start a new Goose session after updating config.") + "\n");
1113
+ }
1038
1114
  }
1039
- var DEFAULT_SHIELD_API_BASE_URL = "https://api.multicorn.ai";
1040
1115
  async function runInit(explicitBaseUrl) {
1041
1116
  if (!process.stdin.isTTY) {
1042
1117
  process.stderr.write(
@@ -1124,8 +1199,8 @@ async function runInit(explicitBaseUrl) {
1124
1199
  let postSaveNativeSkipNote = null;
1125
1200
  const selection = await promptPlatformSelection(ask);
1126
1201
  const selectedPlatform = PLATFORM_BY_SELECTION[selection] ?? "cursor";
1127
- const selectedLabel = PLATFORM_LABELS[selection - 1] ?? "Cursor";
1128
- if (selection === 8) {
1202
+ const selectedLabel = platformMenuLabelForSelection(selection);
1203
+ if (selectedPlatform === "other-mcp") {
1129
1204
  const raw = existing !== null ? { ...existing } : {};
1130
1205
  raw["apiKey"] = apiKey;
1131
1206
  raw["baseUrl"] = resolvedBaseUrl;
@@ -1140,7 +1215,7 @@ async function runInit(explicitBaseUrl) {
1140
1215
  );
1141
1216
  process.stderr.write(
1142
1217
  "\n" + style.bold("Try it:") + " " + style.cyan(
1143
- "npx multicorn-proxy --wrap npx @modelcontextprotocol/server-filesystem /tmp"
1218
+ "npx multicorn-shield --wrap npx @modelcontextprotocol/server-filesystem /tmp"
1144
1219
  ) + "\n"
1145
1220
  );
1146
1221
  } catch (error) {
@@ -1175,9 +1250,24 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1175
1250
  continue;
1176
1251
  }
1177
1252
  }
1253
+ const prereqEntry = INIT_WIZARD_PLATFORM_REGISTRY.find((e) => e.slug === selectedPlatform);
1254
+ if (prereqEntry?.prereqUrl !== void 0) {
1255
+ const proceed = await promptHostedProxyInstallPrereq(
1256
+ ask,
1257
+ prereqEntry.displayName,
1258
+ prereqEntry.prereqUrl
1259
+ );
1260
+ if (!proceed) {
1261
+ const another2 = await ask("\nConnect another agent? (Y/n) ");
1262
+ if (another2.trim().toLowerCase() === "n") {
1263
+ configuring = false;
1264
+ }
1265
+ continue;
1266
+ }
1267
+ }
1178
1268
  const agentName = await promptAgentName(ask, selectedPlatform);
1179
1269
  let setupSucceeded = false;
1180
- if (selection === 1) {
1270
+ if (selectedPlatform === "openclaw") {
1181
1271
  let detection;
1182
1272
  try {
1183
1273
  detection = await detectOpenClaw();
@@ -1190,7 +1280,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1190
1280
  }
1191
1281
  if (detection.status === "not-found") {
1192
1282
  process.stderr.write(
1193
- style.red("\u2717") + " OpenClaw is not installed. Install OpenClaw first, then run npx multicorn-proxy init again.\n"
1283
+ style.red("\u2717") + " OpenClaw is not installed. Install OpenClaw first, then run npx multicorn-shield init again.\n"
1194
1284
  );
1195
1285
  rl.close();
1196
1286
  return null;
@@ -1257,7 +1347,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1257
1347
  agentName
1258
1348
  });
1259
1349
  setupSucceeded = true;
1260
- } else if (selection === 2) {
1350
+ } else if (selectedPlatform === "claude-code") {
1261
1351
  process.stderr.write("\nTo connect Claude Code to Shield:\n\n");
1262
1352
  process.stderr.write(
1263
1353
  " " + style.bold("Step 1") + " - Add the Multicorn marketplace:\n " + style.cyan("claude plugin marketplace add Multicorn-AI/multicorn-shield") + "\n\n"
@@ -1275,7 +1365,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1275
1365
  agentName
1276
1366
  });
1277
1367
  setupSucceeded = true;
1278
- } else if (selection === 4) {
1368
+ } else if (selectedPlatform === "windsurf") {
1279
1369
  const windsurfMode = await promptWindsurfIntegrationMode(ask);
1280
1370
  if (windsurfMode === "native") {
1281
1371
  try {
@@ -1306,7 +1396,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1306
1396
  } catch (error) {
1307
1397
  if (error instanceof NativePluginPrerequisiteMissingError) {
1308
1398
  postSaveNativeSkipNote = nativePluginSkippedSaveNote(
1309
- "npx multicorn-proxy init",
1399
+ "npx multicorn-shield init",
1310
1400
  "Windsurf"
1311
1401
  );
1312
1402
  configuredAgents.push({
@@ -1363,7 +1453,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1363
1453
  setupSucceeded = true;
1364
1454
  }
1365
1455
  }
1366
- } else if (selection === 7) {
1456
+ } else if (selectedPlatform === "gemini-cli") {
1367
1457
  const geminiMode = await promptGeminiCliIntegrationMode(ask);
1368
1458
  if (geminiMode === "native") {
1369
1459
  try {
@@ -1448,7 +1538,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1448
1538
  setupSucceeded = true;
1449
1539
  }
1450
1540
  }
1451
- } else if (selection === 5) {
1541
+ } else if (selectedPlatform === "cline") {
1452
1542
  const clineMode = await promptClineIntegrationMode(ask);
1453
1543
  if (clineMode === "native") {
1454
1544
  try {
@@ -1634,6 +1724,26 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1634
1724
  "\n" + style.bold("To complete your Cursor setup:") + "\n 1. If you don't have Cursor yet, download it from " + style.cyan("https://cursor.com/downloads") + "\n 2. Open " + style.cyan("~/.cursor/mcp.json") + " and paste the config snippet shown above\n 3. Restart Cursor (or launch it for the first time) to load the new MCP server\n"
1635
1725
  );
1636
1726
  }
1727
+ if (configuredPlatforms.has("kilo-code")) {
1728
+ blocks.push(
1729
+ "\n" + style.bold("To complete your Kilo Code setup:") + "\n 1. Save the snippet to " + style.cyan(".kilocode/mcp.json") + " in your project root, or under the mcp key in " + style.cyan("kilo.jsonc") + "\n 2. Run your next task in Kilo Code so it picks up the MCP server\n"
1730
+ );
1731
+ }
1732
+ if (configuredPlatforms.has("github-copilot")) {
1733
+ blocks.push(
1734
+ "\n" + style.bold("GitHub Copilot MCP:") + "\n 1. Open VS Code Command Palette: Preferences: Open User Settings (JSON)\n 2. Merge the snippet under the " + style.cyan("mcp") + " key and save\n 3. Use Copilot Agent mode and verify the MCP server connects\n"
1735
+ );
1736
+ }
1737
+ if (configuredPlatforms.has("continue-dev")) {
1738
+ blocks.push(
1739
+ "\n" + style.bold("Continue MCP:") + "\n 1. If you don't have Continue yet, install from " + style.cyan("https://docs.continue.dev/ide-extensions/install") + "\n 2. Save JSON as " + style.cyan(".continue/mcpServers/shield.json") + " in your workspace, or add to " + style.cyan("~/.continue/config.yaml") + "\n 3. Reload VS Code and open Continue agent mode\n"
1740
+ );
1741
+ }
1742
+ if (configuredPlatforms.has("goose")) {
1743
+ blocks.push(
1744
+ "\n" + style.bold("Goose MCP extension:") + "\n 1. Edit " + style.cyan("~/.config/goose/config.yaml") + " (or use goose configure)\n 2. Restart Goose CLI or Desktop\n"
1745
+ );
1746
+ }
1637
1747
  const windsurfNativeConfigured = configuredAgents.some(
1638
1748
  (a) => a.platform === "windsurf" && a.windsurfIntegration === "native"
1639
1749
  );
@@ -1689,22 +1799,144 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
1689
1799
  }
1690
1800
  return lastConfig;
1691
1801
  }
1802
+ var style, BANNER, NativePluginPrerequisiteMissingError, CONFIG_DIR, CONFIG_PATH, OPENCLAW_CONFIG_PATH, ANSI_PATTERN, OPENCLAW_MIN_VERSION, INIT_WIZARD_PLATFORM_REGISTRY, INIT_WIZARD_MENU_SECTIONS, INIT_WIZARD_SELECTION_MAX, PLATFORM_BY_SELECTION, DEFAULT_AGENT_NAMES, DEFAULT_SHIELD_API_BASE_URL;
1803
+ var init_config = __esm({
1804
+ "src/proxy/config.ts"() {
1805
+ style = {
1806
+ violet: (s) => `\x1B[38;2;124;58;237m${s}\x1B[0m`,
1807
+ violetLight: (s) => `\x1B[38;2;167;139;250m${s}\x1B[0m`,
1808
+ green: (s) => `\x1B[38;2;34;197;94m${s}\x1B[0m`,
1809
+ yellow: (s) => `\x1B[38;2;245;158;11m${s}\x1B[0m`,
1810
+ red: (s) => `\x1B[38;2;239;68;68m${s}\x1B[0m`,
1811
+ cyan: (s) => `\x1B[38;2;6;182;212m${s}\x1B[0m`,
1812
+ bold: (s) => `\x1B[1m${s}\x1B[0m`,
1813
+ dim: (s) => `\x1B[2m${s}\x1B[0m`
1814
+ };
1815
+ BANNER = [
1816
+ " \u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588\u2588\u2588 \u2588 \u2588\u2588\u2584 ",
1817
+ " \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588",
1818
+ " \u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588 \u2588\u2588 \u2588 \u2588 \u2588",
1819
+ " \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588",
1820
+ " \u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588\u2580 "
1821
+ ].map((line) => style.violet(line)).join("\n");
1822
+ NativePluginPrerequisiteMissingError = class extends Error {
1823
+ constructor() {
1824
+ super("Native plugin prerequisites not met");
1825
+ this.name = "NativePluginPrerequisiteMissingError";
1826
+ }
1827
+ };
1828
+ CONFIG_DIR = join(homedir(), ".multicorn");
1829
+ CONFIG_PATH = join(CONFIG_DIR, "config.json");
1830
+ OPENCLAW_CONFIG_PATH = join(homedir(), ".openclaw", "openclaw.json");
1831
+ ANSI_PATTERN = new RegExp(String.fromCharCode(27) + "\\[[0-9;]*[a-zA-Z]", "g");
1832
+ OPENCLAW_MIN_VERSION = "2026.2.26";
1833
+ INIT_WIZARD_PLATFORM_REGISTRY = [
1834
+ { slug: "openclaw", displayName: "OpenClaw", section: "native", detectable: true },
1835
+ { slug: "claude-code", displayName: "Claude Code", section: "native", detectable: true },
1836
+ { slug: "windsurf", displayName: "Windsurf", section: "native", detectable: true },
1837
+ { slug: "cline", displayName: "Cline", section: "native", detectable: false },
1838
+ { slug: "gemini-cli", displayName: "Gemini CLI", section: "native", detectable: false },
1839
+ {
1840
+ slug: "cursor",
1841
+ displayName: "Cursor",
1842
+ section: "hosted",
1843
+ prereqUrl: "https://www.cursor.com/downloads",
1844
+ detectable: true
1845
+ },
1846
+ {
1847
+ slug: "claude-desktop",
1848
+ displayName: "Claude Desktop",
1849
+ section: "hosted",
1850
+ prereqUrl: "https://claude.ai/download",
1851
+ detectable: false
1852
+ },
1853
+ {
1854
+ slug: "github-copilot",
1855
+ displayName: "GitHub Copilot",
1856
+ section: "hosted",
1857
+ prereqUrl: "https://docs.github.com/en/copilot/get-started",
1858
+ detectable: false
1859
+ },
1860
+ {
1861
+ slug: "kilo-code",
1862
+ displayName: "Kilo Code",
1863
+ section: "hosted",
1864
+ prereqUrl: "https://kilocode.ai/docs/getting-started",
1865
+ detectable: false
1866
+ },
1867
+ {
1868
+ slug: "continue-dev",
1869
+ displayName: "Continue",
1870
+ section: "hosted",
1871
+ prereqUrl: "https://docs.continue.dev/ide-extensions/install",
1872
+ detectable: false
1873
+ },
1874
+ {
1875
+ slug: "goose",
1876
+ displayName: "Goose",
1877
+ section: "hosted",
1878
+ prereqUrl: "https://goose-docs.ai/docs/quickstart/",
1879
+ detectable: false
1880
+ },
1881
+ { slug: "other-mcp", displayName: "Local MCP / Other", section: "hosted", detectable: false }
1882
+ ];
1883
+ INIT_WIZARD_MENU_SECTIONS = (() => {
1884
+ const itemsFor = (section) => INIT_WIZARD_PLATFORM_REGISTRY.filter((e) => e.section === section).map((e) => ({
1885
+ platform: e.slug,
1886
+ label: e.displayName
1887
+ }));
1888
+ return [
1889
+ { title: "Recommended (native plugin)", items: itemsFor("native") },
1890
+ { title: "Hosted proxy (MCP only)", items: itemsFor("hosted") }
1891
+ ];
1892
+ })();
1893
+ INIT_WIZARD_SELECTION_MAX = INIT_WIZARD_PLATFORM_REGISTRY.length;
1894
+ PLATFORM_BY_SELECTION = Object.fromEntries(
1895
+ INIT_WIZARD_PLATFORM_REGISTRY.map((e, i) => [i + 1, e.slug])
1896
+ );
1897
+ DEFAULT_AGENT_NAMES = {
1898
+ openclaw: "my-openclaw-agent",
1899
+ "claude-code": "my-claude-code-agent",
1900
+ cursor: "my-cursor-agent",
1901
+ windsurf: "my-windsurf-agent",
1902
+ cline: "my-cline-agent",
1903
+ "claude-desktop": "my-claude-desktop-agent",
1904
+ "gemini-cli": "my-gemini-cli-agent",
1905
+ "kilo-code": "my-kilo-code-agent",
1906
+ "github-copilot": "my-github-copilot-agent",
1907
+ "continue-dev": "my-continue-agent",
1908
+ goose: "my-goose-agent"
1909
+ };
1910
+ DEFAULT_SHIELD_API_BASE_URL = "https://api.multicorn.ai";
1911
+ }
1912
+ });
1692
1913
 
1693
1914
  // src/types/index.ts
1694
- var PERMISSION_LEVELS = {
1695
- Read: "read",
1696
- Write: "write",
1697
- Execute: "execute",
1698
- Publish: "publish",
1699
- Create: "create"
1700
- };
1915
+ var PERMISSION_LEVELS;
1916
+ var init_types = __esm({
1917
+ "src/types/index.ts"() {
1918
+ PERMISSION_LEVELS = {
1919
+ Read: "read",
1920
+ Write: "write",
1921
+ Execute: "execute",
1922
+ Publish: "publish",
1923
+ Create: "create"
1924
+ };
1925
+ }
1926
+ });
1701
1927
 
1702
1928
  // src/scopes/scope-parser.ts
1703
- var VALID_PERMISSION_LEVELS = new Set(Object.values(PERMISSION_LEVELS));
1704
- [...VALID_PERMISSION_LEVELS].join(", ");
1705
1929
  function formatScope(scope) {
1706
1930
  return `${scope.permissionLevel}:${scope.service}`;
1707
1931
  }
1932
+ var VALID_PERMISSION_LEVELS;
1933
+ var init_scope_parser = __esm({
1934
+ "src/scopes/scope-parser.ts"() {
1935
+ init_types();
1936
+ VALID_PERMISSION_LEVELS = new Set(Object.values(PERMISSION_LEVELS));
1937
+ [...VALID_PERMISSION_LEVELS].join(", ");
1938
+ }
1939
+ });
1708
1940
 
1709
1941
  // src/scopes/scope-validator.ts
1710
1942
  function validateScopeAccess(grantedScopes, requested) {
@@ -1732,6 +1964,11 @@ function hasScope(grantedScopes, requested) {
1732
1964
  (granted) => granted.service === requested.service && granted.permissionLevel === requested.permissionLevel
1733
1965
  );
1734
1966
  }
1967
+ var init_scope_validator = __esm({
1968
+ "src/scopes/scope-validator.ts"() {
1969
+ init_scope_parser();
1970
+ }
1971
+ });
1735
1972
 
1736
1973
  // src/logger/action-logger.ts
1737
1974
  function createActionLogger(config) {
@@ -1905,6 +2142,10 @@ function createActionLogger(config) {
1905
2142
  function sleep(ms) {
1906
2143
  return new Promise((resolve) => setTimeout(resolve, ms));
1907
2144
  }
2145
+ var init_action_logger = __esm({
2146
+ "src/logger/action-logger.ts"() {
2147
+ }
2148
+ });
1908
2149
 
1909
2150
  // src/spending/spending-checker.ts
1910
2151
  function createSpendingChecker(config) {
@@ -2037,13 +2278,12 @@ function validateLimits(limits) {
2037
2278
  function dollarsToCents(dollars) {
2038
2279
  return Math.round(dollars * 100);
2039
2280
  }
2281
+ var init_spending_checker = __esm({
2282
+ "src/spending/spending-checker.ts"() {
2283
+ }
2284
+ });
2040
2285
 
2041
2286
  // 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
2287
  function parseJsonRpcLine(line) {
2048
2288
  const trimmed = line.trim();
2049
2289
  if (trimmed.length === 0) return null;
@@ -2111,7 +2351,7 @@ function buildServiceUnreachableResponse(id, dashboardUrl) {
2111
2351
  };
2112
2352
  }
2113
2353
  function buildAuthErrorResponse(id) {
2114
- const message = "Action blocked: Shield API key is invalid or has been revoked. Run npx multicorn-proxy init to reconfigure.";
2354
+ const message = "Action blocked: Shield API key is invalid or has been revoked. Run npx multicorn-shield init to reconfigure.";
2115
2355
  return {
2116
2356
  jsonrpc: "2.0",
2117
2357
  id,
@@ -2143,9 +2383,16 @@ function capitalize(str) {
2143
2383
  const first = str[0];
2144
2384
  return first !== void 0 ? first.toUpperCase() + str.slice(1) : str;
2145
2385
  }
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");
2386
+ var BLOCKED_ERROR_CODE, SPENDING_BLOCKED_ERROR_CODE, INTERNAL_ERROR_CODE, SERVICE_UNREACHABLE_ERROR_CODE, AUTH_ERROR_CODE;
2387
+ var init_interceptor = __esm({
2388
+ "src/proxy/interceptor.ts"() {
2389
+ BLOCKED_ERROR_CODE = -32e3;
2390
+ SPENDING_BLOCKED_ERROR_CODE = -32001;
2391
+ INTERNAL_ERROR_CODE = -32002;
2392
+ SERVICE_UNREACHABLE_ERROR_CODE = -32003;
2393
+ AUTH_ERROR_CODE = -32004;
2394
+ }
2395
+ });
2149
2396
  function cacheKey(agentName, apiKey) {
2150
2397
  return createHash("sha256").update(`${agentName}:${apiKey}`).digest("hex").slice(0, 16);
2151
2398
  }
@@ -2216,10 +2463,14 @@ async function saveCachedScopes(agentName, agentId, scopes, apiKey) {
2216
2463
  function isScopesCacheFile(value) {
2217
2464
  return typeof value === "object" && value !== null;
2218
2465
  }
2219
-
2220
- // src/proxy/consent.ts
2221
- var CONSENT_POLL_INTERVAL_MS = 3e3;
2222
- var CONSENT_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
2466
+ var MULTICORN_DIR, SCOPES_PATH, CACHE_META_PATH;
2467
+ var init_scope_cache = __esm({
2468
+ "src/openclaw/scope-cache.ts"() {
2469
+ MULTICORN_DIR = join(homedir(), ".multicorn");
2470
+ SCOPES_PATH = join(MULTICORN_DIR, "scopes.json");
2471
+ CACHE_META_PATH = join(MULTICORN_DIR, "cache-meta.json");
2472
+ }
2473
+ });
2223
2474
  function deriveDashboardUrl(baseUrl) {
2224
2475
  try {
2225
2476
  const url = new URL(baseUrl);
@@ -2244,13 +2495,6 @@ function deriveDashboardUrl(baseUrl) {
2244
2495
  return "https://app.multicorn.ai";
2245
2496
  }
2246
2497
  }
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
2498
  async function findAgentByName(agentName, apiKey, baseUrl) {
2255
2499
  let response;
2256
2500
  try {
@@ -2443,13 +2687,25 @@ function isPermissionShape(value) {
2443
2687
  const obj = value;
2444
2688
  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
2689
  }
2446
-
2447
- // src/proxy/index.ts
2448
- var DEFAULT_SCOPE_REFRESH_INTERVAL_MS = 6e4;
2690
+ var CONSENT_POLL_INTERVAL_MS, CONSENT_POLL_TIMEOUT_MS, ShieldAuthError;
2691
+ var init_consent = __esm({
2692
+ "src/proxy/consent.ts"() {
2693
+ init_scope_cache();
2694
+ CONSENT_POLL_INTERVAL_MS = 3e3;
2695
+ CONSENT_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
2696
+ ShieldAuthError = class _ShieldAuthError extends Error {
2697
+ constructor(message) {
2698
+ super(message);
2699
+ this.name = "ShieldAuthError";
2700
+ Object.setPrototypeOf(this, _ShieldAuthError.prototype);
2701
+ }
2702
+ };
2703
+ }
2704
+ });
2449
2705
  function createProxyServer(config) {
2450
2706
  if (!config.baseUrl.startsWith("https://") && !config.baseUrl.startsWith("http://localhost") && !config.baseUrl.startsWith("http://127.0.0.1")) {
2451
2707
  throw new Error(
2452
- `[multicorn-proxy] Base URL must use HTTPS. Received: "${config.baseUrl}". Use https:// or http://localhost for local development.`
2708
+ `[multicorn-shield] Base URL must use HTTPS. Received: "${config.baseUrl}". Use https:// or http://localhost for local development.`
2453
2709
  );
2454
2710
  }
2455
2711
  let child = null;
@@ -2546,7 +2802,7 @@ function createProxyServer(config) {
2546
2802
  if (actionLogger !== null) {
2547
2803
  if (!config.agentName || config.agentName.trim().length === 0) {
2548
2804
  process.stderr.write(
2549
- "[multicorn-proxy] Cannot log action: agent name not resolved\n"
2805
+ "[multicorn-shield] Cannot log action: agent name not resolved\n"
2550
2806
  );
2551
2807
  } else {
2552
2808
  config.logger.debug("Logging blocked action (post-consent).", {
@@ -2576,7 +2832,7 @@ function createProxyServer(config) {
2576
2832
  if (actionLogger !== null) {
2577
2833
  if (!config.agentName || config.agentName.trim().length === 0) {
2578
2834
  process.stderr.write(
2579
- "[multicorn-proxy] Cannot log action: agent name not resolved\n"
2835
+ "[multicorn-shield] Cannot log action: agent name not resolved\n"
2580
2836
  );
2581
2837
  } else {
2582
2838
  config.logger.debug("Logging blocked action (spending).", {
@@ -2605,7 +2861,7 @@ function createProxyServer(config) {
2605
2861
  }
2606
2862
  if (actionLogger !== null) {
2607
2863
  if (!config.agentName || config.agentName.trim().length === 0) {
2608
- process.stderr.write("[multicorn-proxy] Cannot log action: agent name not resolved\n");
2864
+ process.stderr.write("[multicorn-shield] Cannot log action: agent name not resolved\n");
2609
2865
  } else {
2610
2866
  config.logger.debug("Logging approved action.", {
2611
2867
  agent: config.agentName,
@@ -2687,7 +2943,7 @@ function createProxyServer(config) {
2687
2943
  agent: config.agentName
2688
2944
  });
2689
2945
  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"
2946
+ "\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
2947
  );
2692
2948
  throw new Error("API key was rejected by the Multicorn service.");
2693
2949
  }
@@ -2760,12 +3016,17 @@ function extractCostCents(args) {
2760
3016
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return 0;
2761
3017
  return dollarsToCents(amount);
2762
3018
  }
2763
- var LOG_LEVELS = {
2764
- debug: 0,
2765
- info: 1,
2766
- warn: 2,
2767
- error: 3
2768
- };
3019
+ var DEFAULT_SCOPE_REFRESH_INTERVAL_MS;
3020
+ var init_proxy = __esm({
3021
+ "src/proxy/index.ts"() {
3022
+ init_scope_validator();
3023
+ init_action_logger();
3024
+ init_spending_checker();
3025
+ init_interceptor();
3026
+ init_consent();
3027
+ DEFAULT_SCOPE_REFRESH_INTERVAL_MS = 6e4;
3028
+ }
3029
+ });
2769
3030
  function createLogger(level, output = process.stderr) {
2770
3031
  const minLevel = LOG_LEVELS[level];
2771
3032
  function write(logLevel, msg, data) {
@@ -2796,8 +3057,93 @@ function createLogger(level, output = process.stderr) {
2796
3057
  function isValidLogLevel(value) {
2797
3058
  return typeof value === "string" && Object.hasOwn(LOG_LEVELS, value);
2798
3059
  }
3060
+ var LOG_LEVELS;
3061
+ var init_logger = __esm({
3062
+ "src/proxy/logger.ts"() {
3063
+ LOG_LEVELS = {
3064
+ debug: 0,
3065
+ info: 1,
3066
+ warn: 2,
3067
+ error: 3
3068
+ };
3069
+ }
3070
+ });
3071
+ function getExtensionBackupPath() {
3072
+ return join(homedir(), ".multicorn", EXTENSION_BACKUP_FILENAME);
3073
+ }
3074
+ function isRecord(value) {
3075
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3076
+ }
3077
+ async function readExtensionBackup() {
3078
+ try {
3079
+ const raw = await readFile(getExtensionBackupPath(), "utf8");
3080
+ const parsed = JSON.parse(raw);
3081
+ if (!isRecord(parsed)) return null;
3082
+ if (parsed["version"] !== 1) return null;
3083
+ if (typeof parsed["createdAt"] !== "string") return null;
3084
+ if (typeof parsed["claudeDesktopConfigPath"] !== "string") return null;
3085
+ const mcpServers = parsed["mcpServers"];
3086
+ if (!isRecord(mcpServers)) return null;
3087
+ return {
3088
+ version: 1,
3089
+ createdAt: parsed["createdAt"],
3090
+ claudeDesktopConfigPath: parsed["claudeDesktopConfigPath"],
3091
+ mcpServers
3092
+ };
3093
+ } catch {
3094
+ return null;
3095
+ }
3096
+ }
3097
+ var EXTENSION_BACKUP_FILENAME;
3098
+ var init_config_reader = __esm({
3099
+ "src/extension/config-reader.ts"() {
3100
+ EXTENSION_BACKUP_FILENAME = "extension-backup.json";
3101
+ }
3102
+ });
3103
+ function isErrnoException2(e) {
3104
+ return typeof e === "object" && e !== null && "code" in e;
3105
+ }
3106
+ function isRecord2(value) {
3107
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3108
+ }
3109
+ async function restoreClaudeDesktopMcpFromBackup() {
3110
+ const backup = await readExtensionBackup();
3111
+ if (backup === null) {
3112
+ throw new Error(
3113
+ "No Shield extension backup found. Expected ~/.multicorn/extension-backup.json from a previous Shield Desktop Extension session."
3114
+ );
3115
+ }
3116
+ const configPath = getClaudeDesktopConfigPath();
3117
+ let root = {};
3118
+ try {
3119
+ const raw = await readFile(configPath, "utf8");
3120
+ const parsed = JSON.parse(raw);
3121
+ if (isRecord2(parsed)) {
3122
+ root = parsed;
3123
+ }
3124
+ } catch (error) {
3125
+ if (!isErrnoException2(error) || error.code !== "ENOENT") {
3126
+ throw error;
3127
+ }
3128
+ }
3129
+ root["mcpServers"] = backup.mcpServers;
3130
+ await mkdir(dirname(configPath), { recursive: true });
3131
+ await writeFile(configPath, JSON.stringify(root, null, 2) + "\n", { encoding: "utf8" });
3132
+ }
3133
+ var init_restore = __esm({
3134
+ "src/extension/restore.ts"() {
3135
+ init_config();
3136
+ init_config_reader();
3137
+ }
3138
+ });
2799
3139
 
2800
- // bin/multicorn-proxy.ts
3140
+ // bin/multicorn-shield.ts
3141
+ var multicorn_shield_exports = {};
3142
+ __export(multicorn_shield_exports, {
3143
+ parseArgs: () => parseArgs,
3144
+ resolveWrapConfig: () => resolveWrapConfig,
3145
+ runCli: () => runCli
3146
+ });
2801
3147
  function parseArgs(argv) {
2802
3148
  const args = argv.slice(2);
2803
3149
  let subcommand = "help";
@@ -2820,7 +3166,7 @@ function parseArgs(argv) {
2820
3166
  const name = args[i + 1];
2821
3167
  if (name === void 0 || name.startsWith("-")) {
2822
3168
  process.stderr.write("Error: delete-agent requires an agent name.\n");
2823
- process.stderr.write("Example: npx multicorn-proxy delete-agent my-agent\n");
3169
+ process.stderr.write("Example: npx multicorn-shield delete-agent my-agent\n");
2824
3170
  process.exit(1);
2825
3171
  }
2826
3172
  deleteAgentName = name;
@@ -2872,7 +3218,7 @@ function parseArgs(argv) {
2872
3218
  }
2873
3219
  if (remaining.length === 0) {
2874
3220
  process.stderr.write("Error: --wrap requires a command to run.\n");
2875
- process.stderr.write("Example: npx multicorn-proxy --wrap my-mcp-server\n");
3221
+ process.stderr.write("Example: npx multicorn-shield --wrap my-mcp-server\n");
2876
3222
  process.exit(1);
2877
3223
  }
2878
3224
  wrapCommand = remaining[0] ?? "";
@@ -2925,19 +3271,22 @@ function parseArgs(argv) {
2925
3271
  function printHelp() {
2926
3272
  process.stderr.write(
2927
3273
  [
2928
- "multicorn-proxy: MCP permission proxy",
3274
+ "multicorn-shield: MCP permission proxy and Shield setup",
2929
3275
  "",
2930
3276
  "Usage:",
2931
- " npx multicorn-proxy init",
3277
+ " npx multicorn-shield init",
2932
3278
  " Interactive setup. Saves API key to ~/.multicorn/config.json.",
2933
3279
  "",
2934
- " npx multicorn-proxy agents",
3280
+ " npx multicorn-shield restore",
3281
+ " Restore MCP servers in claude_desktop_config.json from the Shield extension backup.",
3282
+ "",
3283
+ " npx multicorn-shield agents",
2935
3284
  " List configured agents and show which is the default.",
2936
3285
  "",
2937
- " npx multicorn-proxy delete-agent <name>",
3286
+ " npx multicorn-shield delete-agent <name>",
2938
3287
  " Remove a saved agent.",
2939
3288
  "",
2940
- " npx multicorn-proxy --wrap <command> [args...]",
3289
+ " npx multicorn-shield --wrap <command> [args...]",
2941
3290
  " Start <command> as an MCP server and proxy all tool calls through",
2942
3291
  " Shield's permission layer.",
2943
3292
  "",
@@ -2949,14 +3298,22 @@ function printHelp() {
2949
3298
  " --agent-name <name> Override agent name derived from the wrapped command",
2950
3299
  "",
2951
3300
  "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",
3301
+ " npx multicorn-shield init",
3302
+ " npx multicorn-shield --wrap npx @modelcontextprotocol/server-filesystem /tmp",
3303
+ " npx multicorn-shield --wrap my-mcp-server --log-level debug",
2955
3304
  ""
2956
3305
  ].join("\n")
2957
3306
  );
2958
3307
  }
2959
- async function main() {
3308
+ async function runCli() {
3309
+ const first = process.argv[2];
3310
+ if (first === "restore") {
3311
+ await restoreClaudeDesktopMcpFromBackup();
3312
+ process.stderr.write(
3313
+ "Restored MCP server entries from ~/.multicorn/extension-backup.json into Claude Desktop config.\nRestart Claude Desktop to apply changes.\n"
3314
+ );
3315
+ return;
3316
+ }
2960
3317
  const cli = parseArgs(process.argv);
2961
3318
  const logger = createLogger(cli.logLevel);
2962
3319
  if (cli.subcommand === "help") {
@@ -2971,13 +3328,13 @@ async function main() {
2971
3328
  const config2 = await loadConfig();
2972
3329
  if (config2 === null) {
2973
3330
  process.stderr.write(
2974
- "No config found. Run `npx multicorn-proxy init` to set up your API key.\n"
3331
+ "No config found. Run `npx multicorn-shield init` to set up your API key.\n"
2975
3332
  );
2976
3333
  process.exit(1);
2977
3334
  }
2978
3335
  const agents = collectAgentsFromConfig(config2);
2979
3336
  if (agents.length === 0) {
2980
- process.stderr.write("No agents configured. Run `npx multicorn-proxy init` to add one.\n");
3337
+ process.stderr.write("No agents configured. Run `npx multicorn-shield init` to add one.\n");
2981
3338
  process.exit(0);
2982
3339
  }
2983
3340
  const def = config2.defaultAgent;
@@ -3067,7 +3424,7 @@ async function resolveWrapConfig(cli, logger) {
3067
3424
  return config;
3068
3425
  }
3069
3426
  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"
3427
+ "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
3428
  );
3072
3429
  process.exit(1);
3073
3430
  }
@@ -3095,14 +3452,32 @@ function deriveAgentName(command) {
3095
3452
  const base = command.split("/").pop() ?? command;
3096
3453
  return base.replace(/\.[cm]?[jt]s$/, "");
3097
3454
  }
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}
3455
+ var isDirectRun;
3456
+ var init_multicorn_shield = __esm({
3457
+ "bin/multicorn-shield.ts"() {
3458
+ init_config();
3459
+ init_proxy();
3460
+ init_logger();
3461
+ init_consent();
3462
+ init_restore();
3463
+ 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"));
3464
+ if (isDirectRun && process.env["VITEST"] === void 0) {
3465
+ runCli().catch((error) => {
3466
+ const message = error instanceof Error ? error.message : String(error);
3467
+ process.stderr.write(`Fatal error: ${message}
3103
3468
  `);
3104
- process.exit(1);
3105
- });
3106
- }
3469
+ process.exit(1);
3470
+ });
3471
+ }
3472
+ }
3473
+ });
3107
3474
 
3108
- export { parseArgs, resolveWrapConfig };
3475
+ // bin/multicorn-proxy.ts
3476
+ process.stderr.write("Warning: multicorn-proxy is deprecated. Use multicorn-shield instead.\n");
3477
+ var { runCli: runCli2 } = await Promise.resolve().then(() => (init_multicorn_shield(), multicorn_shield_exports));
3478
+ void runCli2().catch((error) => {
3479
+ const message = error instanceof Error ? error.message : String(error);
3480
+ process.stderr.write(`Fatal error: ${message}
3481
+ `);
3482
+ process.exit(1);
3483
+ });