multicorn-shield 0.12.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.
- package/CHANGELOG.md +29 -12
- package/README.md +5 -5
- package/dist/index.cjs +13 -0
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +13 -1
- package/dist/multicorn-proxy.js +691 -131
- package/dist/multicorn-shield.js +3091 -31
- package/dist/openclaw-plugin/multicorn-shield.js +2 -2
- package/dist/proxy.cjs +1 -1
- package/dist/proxy.js +1 -1
- package/dist/shield-extension.js +1 -1
- package/package.json +4 -3
- package/plugins/gemini-cli/hooks/scripts/after-tool.cjs +110 -0
- package/plugins/gemini-cli/hooks/scripts/before-tool.cjs +197 -0
- package/plugins/gemini-cli/hooks/scripts/shared.cjs +319 -0
- package/plugins/windsurf/README.md +2 -2
package/dist/multicorn-proxy.js
CHANGED
|
@@ -1,31 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync } from 'fs';
|
|
3
|
-
import { mkdir, writeFile,
|
|
4
|
-
import {
|
|
2
|
+
import { existsSync, statSync } from 'fs';
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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,10 +35,17 @@ function withSpinner(message) {
|
|
|
43
35
|
}
|
|
44
36
|
};
|
|
45
37
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
function isExistingDirectory(path) {
|
|
39
|
+
try {
|
|
40
|
+
if (!existsSync(path)) return false;
|
|
41
|
+
return statSync(path).isDirectory();
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function nativePluginSkippedSaveNote(wizardCommand, productName) {
|
|
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";
|
|
48
|
+
}
|
|
50
49
|
function stripAnsi(str) {
|
|
51
50
|
return str.replace(ANSI_PATTERN, "");
|
|
52
51
|
}
|
|
@@ -190,7 +189,6 @@ async function saveConfig(config) {
|
|
|
190
189
|
mode: 384
|
|
191
190
|
});
|
|
192
191
|
}
|
|
193
|
-
var OPENCLAW_MIN_VERSION = "2026.2.26";
|
|
194
192
|
async function detectOpenClaw() {
|
|
195
193
|
let raw;
|
|
196
194
|
try {
|
|
@@ -356,7 +354,8 @@ async function isCursorConnected() {
|
|
|
356
354
|
const url = rec["url"];
|
|
357
355
|
if (typeof url === "string" && url.includes("multicorn")) return true;
|
|
358
356
|
const args = rec["args"];
|
|
359
|
-
if (Array.isArray(args) && args.includes("multicorn-proxy"))
|
|
357
|
+
if (Array.isArray(args) && (args.includes("multicorn-shield") || args.includes("multicorn-proxy")))
|
|
358
|
+
return true;
|
|
360
359
|
}
|
|
361
360
|
return false;
|
|
362
361
|
} catch (err) {
|
|
@@ -426,6 +425,18 @@ async function installWindsurfNativeHooks() {
|
|
|
426
425
|
`Could not find Shield Windsurf hook scripts at ${srcPre}. If you use npm, install the latest multicorn-shield package.`
|
|
427
426
|
);
|
|
428
427
|
}
|
|
428
|
+
const windsurfConfigDir = join(homedir(), ".codeium", "windsurf");
|
|
429
|
+
if (!isExistingDirectory(windsurfConfigDir)) {
|
|
430
|
+
process.stderr.write(
|
|
431
|
+
style.yellow("\u26A0") + " Windsurf does not appear to be installed (~/.codeium/windsurf/ not found).\n\n"
|
|
432
|
+
);
|
|
433
|
+
process.stderr.write(
|
|
434
|
+
"Open Windsurf at least once so this folder exists, or install from:\n " + style.cyan("https://windsurf.com/download") + "\n\n"
|
|
435
|
+
);
|
|
436
|
+
process.stderr.write("Then run this wizard again:\n");
|
|
437
|
+
process.stderr.write(" " + style.cyan("npx multicorn-shield init") + "\n");
|
|
438
|
+
throw new NativePluginPrerequisiteMissingError();
|
|
439
|
+
}
|
|
429
440
|
const installDir = getWindsurfHooksInstallDir();
|
|
430
441
|
await mkdir(installDir, { recursive: true });
|
|
431
442
|
const destPre = join(installDir, "pre-action.cjs");
|
|
@@ -489,6 +500,19 @@ async function installClineNativeHooks() {
|
|
|
489
500
|
`Could not find Shield Cline hook scripts at ${srcPre}. If you use npm, install the latest multicorn-shield package.`
|
|
490
501
|
);
|
|
491
502
|
}
|
|
503
|
+
const clineDocsDir = join(homedir(), "Documents", "Cline");
|
|
504
|
+
if (!isExistingDirectory(clineDocsDir)) {
|
|
505
|
+
process.stderr.write(
|
|
506
|
+
style.yellow("\u26A0") + " Cline does not appear to be installed (~/Documents/Cline/ not found).\n\n"
|
|
507
|
+
);
|
|
508
|
+
process.stderr.write("Install the Cline VS Code extension first. See:\n");
|
|
509
|
+
process.stderr.write(
|
|
510
|
+
" " + style.cyan("https://docs.cline.bot/getting-started/installing-cline") + "\n\n"
|
|
511
|
+
);
|
|
512
|
+
process.stderr.write("Then run this wizard again:\n");
|
|
513
|
+
process.stderr.write(" " + style.cyan("npx multicorn-shield init") + "\n");
|
|
514
|
+
throw new NativePluginPrerequisiteMissingError();
|
|
515
|
+
}
|
|
492
516
|
const installDir = getClineHooksInstallDir();
|
|
493
517
|
await mkdir(installDir, { recursive: true });
|
|
494
518
|
const destPre = join(installDir, "pre-tool-use.cjs");
|
|
@@ -531,29 +555,203 @@ async function promptClineIntegrationMode(ask) {
|
|
|
531
555
|
}
|
|
532
556
|
return choice === 1 ? "native" : "hosted";
|
|
533
557
|
}
|
|
534
|
-
|
|
535
|
-
"
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
"
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
558
|
+
function getGeminiCliHooksInstallDir() {
|
|
559
|
+
return join(homedir(), ".multicorn", "gemini-cli-hooks");
|
|
560
|
+
}
|
|
561
|
+
function getGeminiCliSettingsPath() {
|
|
562
|
+
return join(homedir(), ".gemini", "settings.json");
|
|
563
|
+
}
|
|
564
|
+
function geminiInnerHooksReferenceShield(inner, multicornName) {
|
|
565
|
+
if (!Array.isArray(inner)) return false;
|
|
566
|
+
for (const h of inner) {
|
|
567
|
+
if (typeof h !== "object" || h === null) continue;
|
|
568
|
+
const rec = h;
|
|
569
|
+
if (rec["name"] === multicornName) return true;
|
|
570
|
+
const cmd = rec["command"];
|
|
571
|
+
if (typeof cmd === "string" && cmd.includes("gemini-cli-hooks")) return true;
|
|
572
|
+
}
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
function geminiHookEventsReferenceShield(arr) {
|
|
576
|
+
if (!Array.isArray(arr)) return false;
|
|
577
|
+
for (const entry of arr) {
|
|
578
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
579
|
+
const hooks = entry["hooks"];
|
|
580
|
+
if (geminiInnerHooksReferenceShield(hooks, "multicorn-shield") || geminiInnerHooksReferenceShield(hooks, "multicorn-shield-log")) {
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
function geminiSettingsHasMulticornHooks(hooks) {
|
|
587
|
+
if (hooks === null || typeof hooks !== "object" || Array.isArray(hooks)) return false;
|
|
588
|
+
const h = hooks;
|
|
589
|
+
return geminiHookEventsReferenceShield(h["BeforeTool"]) || geminiHookEventsReferenceShield(h["AfterTool"]);
|
|
590
|
+
}
|
|
591
|
+
function geminiFilterInnerHooks(inner) {
|
|
592
|
+
if (!Array.isArray(inner)) return [];
|
|
593
|
+
return inner.filter((h) => {
|
|
594
|
+
if (typeof h !== "object" || h === null) return true;
|
|
595
|
+
const rec = h;
|
|
596
|
+
if (rec["name"] === "multicorn-shield" || rec["name"] === "multicorn-shield-log") return false;
|
|
597
|
+
const cmd = rec["command"];
|
|
598
|
+
if (typeof cmd === "string" && cmd.includes("gemini-cli-hooks")) return false;
|
|
599
|
+
return true;
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
function geminiStripMatcherGroups(arr) {
|
|
603
|
+
if (!Array.isArray(arr)) return [];
|
|
604
|
+
const out = [];
|
|
605
|
+
for (const entry of arr) {
|
|
606
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
607
|
+
const e = entry;
|
|
608
|
+
const filtered = geminiFilterInnerHooks(e["hooks"]);
|
|
609
|
+
if (filtered.length > 0) {
|
|
610
|
+
out.push({ ...e, hooks: filtered });
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return out;
|
|
614
|
+
}
|
|
615
|
+
function geminiStripMulticornHookEntries(hooks) {
|
|
616
|
+
const out = { ...hooks };
|
|
617
|
+
out["BeforeTool"] = geminiStripMatcherGroups(out["BeforeTool"]);
|
|
618
|
+
out["AfterTool"] = geminiStripMatcherGroups(out["AfterTool"]);
|
|
619
|
+
return out;
|
|
620
|
+
}
|
|
621
|
+
async function installGeminiCliNativeHooks(ask) {
|
|
622
|
+
const root = multicornShieldPackageRoot();
|
|
623
|
+
const srcBefore = join(root, "plugins", "gemini-cli", "hooks", "scripts", "before-tool.cjs");
|
|
624
|
+
const srcAfter = join(root, "plugins", "gemini-cli", "hooks", "scripts", "after-tool.cjs");
|
|
625
|
+
const srcShared = join(root, "plugins", "gemini-cli", "hooks", "scripts", "shared.cjs");
|
|
626
|
+
if (!existsSync(srcBefore) || !existsSync(srcAfter) || !existsSync(srcShared)) {
|
|
627
|
+
throw new Error(
|
|
628
|
+
`Could not find Shield Gemini CLI hook scripts at ${srcBefore}. If you use npm, install the latest multicorn-shield package.`
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
const geminiConfigDir = join(homedir(), ".gemini");
|
|
632
|
+
if (!isExistingDirectory(geminiConfigDir)) {
|
|
633
|
+
process.stderr.write(
|
|
634
|
+
style.yellow("\u26A0") + " Gemini CLI does not appear to be installed (~/.gemini/ not found).\n\n"
|
|
635
|
+
);
|
|
636
|
+
process.stderr.write("Install Gemini CLI first:\n");
|
|
637
|
+
process.stderr.write(" " + style.cyan("npm install -g @google/gemini-cli") + "\n\n");
|
|
638
|
+
process.stderr.write("Then run this wizard again:\n");
|
|
639
|
+
process.stderr.write(" " + style.cyan("npx multicorn-shield init") + "\n");
|
|
640
|
+
throw new NativePluginPrerequisiteMissingError();
|
|
641
|
+
}
|
|
642
|
+
const installDir = getGeminiCliHooksInstallDir();
|
|
643
|
+
await mkdir(installDir, { recursive: true });
|
|
644
|
+
const destBefore = join(installDir, "before-tool.cjs");
|
|
645
|
+
const destAfter = join(installDir, "after-tool.cjs");
|
|
646
|
+
const destShared = join(installDir, "shared.cjs");
|
|
647
|
+
await copyFile(srcBefore, destBefore);
|
|
648
|
+
await copyFile(srcAfter, destAfter);
|
|
649
|
+
await copyFile(srcShared, destShared);
|
|
650
|
+
const mode = 493;
|
|
651
|
+
await chmod(destBefore, mode);
|
|
652
|
+
await chmod(destAfter, mode);
|
|
653
|
+
await chmod(destShared, mode);
|
|
654
|
+
const settingsPath = getGeminiCliSettingsPath();
|
|
655
|
+
let existing = {};
|
|
656
|
+
try {
|
|
657
|
+
const rawText = await readFile(settingsPath, "utf8");
|
|
658
|
+
const parsed = JSON.parse(rawText);
|
|
659
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
660
|
+
existing = parsed;
|
|
661
|
+
}
|
|
662
|
+
} catch (err) {
|
|
663
|
+
if (isErrnoException(err) && err.code === "ENOENT") {
|
|
664
|
+
existing = {};
|
|
665
|
+
} else {
|
|
666
|
+
process.stderr.write(
|
|
667
|
+
style.yellow("\u26A0") + ` Could not parse ${settingsPath}. Create valid JSON or remove the file, then run init again.
|
|
668
|
+
`
|
|
669
|
+
);
|
|
670
|
+
throw new Error(`Invalid Gemini CLI settings at ${settingsPath}`);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const hooksRaw = existing["hooks"];
|
|
674
|
+
const hooksObj = typeof hooksRaw === "object" && hooksRaw !== null && !Array.isArray(hooksRaw) ? hooksRaw : {};
|
|
675
|
+
if (geminiSettingsHasMulticornHooks(hooksObj)) {
|
|
676
|
+
const answer = await ask(
|
|
677
|
+
"Existing Multicorn Shield hooks were found in ~/.gemini/settings.json. Overwrite? (Y/n) "
|
|
678
|
+
);
|
|
679
|
+
if (answer.trim().toLowerCase() === "n") {
|
|
680
|
+
throw new Error("Installation cancelled: existing Shield hooks left unchanged.");
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
const cleaned = geminiStripMulticornHookEntries({ ...hooksObj });
|
|
684
|
+
const beforeArr = Array.isArray(cleaned["BeforeTool"]) ? [...cleaned["BeforeTool"]] : [];
|
|
685
|
+
const afterArr = Array.isArray(cleaned["AfterTool"]) ? [...cleaned["AfterTool"]] : [];
|
|
686
|
+
const beforeCmd = `node ${destBefore}`;
|
|
687
|
+
const afterCmd = `node ${destAfter}`;
|
|
688
|
+
beforeArr.push({
|
|
689
|
+
matcher: ".*",
|
|
690
|
+
hooks: [
|
|
691
|
+
{
|
|
692
|
+
type: "command",
|
|
693
|
+
name: "multicorn-shield",
|
|
694
|
+
command: beforeCmd,
|
|
695
|
+
timeout: 6e4
|
|
696
|
+
}
|
|
697
|
+
]
|
|
698
|
+
});
|
|
699
|
+
afterArr.push({
|
|
700
|
+
matcher: ".*",
|
|
701
|
+
hooks: [
|
|
702
|
+
{
|
|
703
|
+
type: "command",
|
|
704
|
+
name: "multicorn-shield-log",
|
|
705
|
+
command: afterCmd,
|
|
706
|
+
timeout: 1e4
|
|
707
|
+
}
|
|
708
|
+
]
|
|
709
|
+
});
|
|
710
|
+
existing["hooks"] = {
|
|
711
|
+
...cleaned,
|
|
712
|
+
BeforeTool: beforeArr,
|
|
713
|
+
AfterTool: afterArr
|
|
714
|
+
};
|
|
715
|
+
await mkdir(dirname(settingsPath), { recursive: true });
|
|
716
|
+
await writeFile(settingsPath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
717
|
+
}
|
|
718
|
+
async function promptGeminiCliIntegrationMode(ask) {
|
|
719
|
+
process.stderr.write("\n" + style.bold("Gemini CLI integration") + "\n");
|
|
720
|
+
process.stderr.write(
|
|
721
|
+
" " + style.violet("1") + ". Native plugin (recommended) - Gemini CLI Hooks see every file, terminal, web, and MCP action\n"
|
|
722
|
+
);
|
|
723
|
+
process.stderr.write(
|
|
724
|
+
" " + style.violet("2") + ". Hosted proxy - govern MCP traffic only (paste proxy URL into Gemini CLI settings)\n"
|
|
725
|
+
);
|
|
726
|
+
let choice = 0;
|
|
727
|
+
while (choice === 0) {
|
|
728
|
+
const input = await ask("Choose integration (1-2): ");
|
|
729
|
+
const num = parseInt(input.trim(), 10);
|
|
730
|
+
if (num === 1) choice = 1;
|
|
731
|
+
if (num === 2) choice = 2;
|
|
732
|
+
}
|
|
733
|
+
return choice === 1 ? "native" : "hosted";
|
|
734
|
+
}
|
|
735
|
+
function getClaudeDesktopConfigPath() {
|
|
736
|
+
switch (process.platform) {
|
|
737
|
+
case "win32":
|
|
738
|
+
return join(
|
|
739
|
+
process.env["APPDATA"] ?? join(homedir(), "AppData", "Roaming"),
|
|
740
|
+
"Claude",
|
|
741
|
+
"claude_desktop_config.json"
|
|
742
|
+
);
|
|
743
|
+
case "linux":
|
|
744
|
+
return join(homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
745
|
+
default:
|
|
746
|
+
return join(
|
|
747
|
+
homedir(),
|
|
748
|
+
"Library",
|
|
749
|
+
"Application Support",
|
|
750
|
+
"Claude",
|
|
751
|
+
"claude_desktop_config.json"
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
557
755
|
async function promptPlatformSelection(ask) {
|
|
558
756
|
process.stderr.write(
|
|
559
757
|
"\n" + style.bold(style.violet("Which platform are you connecting?")) + "\n"
|
|
@@ -572,13 +770,13 @@ async function promptPlatformSelection(ask) {
|
|
|
572
770
|
);
|
|
573
771
|
}
|
|
574
772
|
process.stderr.write(
|
|
575
|
-
style.dim(" Pick
|
|
773
|
+
style.dim(" Pick 8 if you want to wrap a local MCP server with multicorn-shield --wrap.") + "\n"
|
|
576
774
|
);
|
|
577
775
|
let selection = 0;
|
|
578
776
|
while (selection === 0) {
|
|
579
|
-
const input = await ask("Select (1-
|
|
777
|
+
const input = await ask("Select (1-8): ");
|
|
580
778
|
const num = parseInt(input.trim(), 10);
|
|
581
|
-
if (num >= 1 && num <=
|
|
779
|
+
if (num >= 1 && num <= 8) {
|
|
582
780
|
selection = num;
|
|
583
781
|
}
|
|
584
782
|
}
|
|
@@ -587,10 +785,10 @@ async function promptPlatformSelection(ask) {
|
|
|
587
785
|
async function promptWindsurfIntegrationMode(ask) {
|
|
588
786
|
process.stderr.write("\n" + style.bold("Windsurf integration") + "\n");
|
|
589
787
|
process.stderr.write(
|
|
590
|
-
" " + style.violet("1") + ". Native plugin (recommended)
|
|
788
|
+
" " + style.violet("1") + ". Native plugin (recommended) - Cascade Hooks see every file, terminal, and MCP action\n"
|
|
591
789
|
);
|
|
592
790
|
process.stderr.write(
|
|
593
|
-
" " + style.violet("2") + ". Hosted proxy
|
|
791
|
+
" " + style.violet("2") + ". Hosted proxy - govern MCP traffic only (paste proxy URL into mcp_config)\n"
|
|
594
792
|
);
|
|
595
793
|
let choice = 0;
|
|
596
794
|
while (choice === 0) {
|
|
@@ -699,9 +897,9 @@ async function createProxyConfig(baseUrl, apiKey, agentName, targetUrl, serverNa
|
|
|
699
897
|
return typeof data?.["proxy_url"] === "string" ? data["proxy_url"] : "";
|
|
700
898
|
}
|
|
701
899
|
function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
|
|
702
|
-
const usesInlineKey = platform === "cursor" || platform === "windsurf" || platform === "cline";
|
|
900
|
+
const usesInlineKey = platform === "cursor" || platform === "claude-desktop" || platform === "windsurf" || platform === "cline" || platform === "gemini-cli";
|
|
703
901
|
const authHeader = usesInlineKey ? `Bearer ${apiKey}` : "Bearer YOUR_SHIELD_API_KEY";
|
|
704
|
-
const urlKey = platform === "windsurf" ? "serverUrl" : "url";
|
|
902
|
+
const urlKey = platform === "windsurf" ? "serverUrl" : platform === "gemini-cli" ? "httpUrl" : "url";
|
|
705
903
|
const mcpSnippet = JSON.stringify(
|
|
706
904
|
{
|
|
707
905
|
mcpServers: {
|
|
@@ -720,6 +918,8 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
|
|
|
720
918
|
process.stderr.write("\n" + style.dim("Add this to your OpenClaw agent config:") + "\n\n");
|
|
721
919
|
} else if (platform === "claude-code") {
|
|
722
920
|
process.stderr.write("\n" + style.dim("Add this to your Claude Code MCP config:") + "\n\n");
|
|
921
|
+
} else if (platform === "claude-desktop") {
|
|
922
|
+
process.stderr.write("\n" + style.dim(`Add this to ${getClaudeDesktopConfigPath()}:`) + "\n\n");
|
|
723
923
|
} else if (platform === "windsurf") {
|
|
724
924
|
process.stderr.write(
|
|
725
925
|
"\n" + style.dim("Add this to ~/.codeium/windsurf/mcp_config.json:") + "\n\n"
|
|
@@ -741,6 +941,12 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
|
|
|
741
941
|
" Linux: ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json"
|
|
742
942
|
) + "\n\n"
|
|
743
943
|
);
|
|
944
|
+
} else if (platform === "gemini-cli") {
|
|
945
|
+
process.stderr.write(
|
|
946
|
+
"\n" + style.dim(
|
|
947
|
+
"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."
|
|
948
|
+
) + "\n\n"
|
|
949
|
+
);
|
|
744
950
|
} else {
|
|
745
951
|
process.stderr.write("\n" + style.dim("Add this to ~/.cursor/mcp.json:") + "\n\n");
|
|
746
952
|
}
|
|
@@ -764,6 +970,9 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
|
|
|
764
970
|
) + "\n"
|
|
765
971
|
);
|
|
766
972
|
}
|
|
973
|
+
if (platform === "claude-desktop") {
|
|
974
|
+
process.stderr.write(style.dim("Then restart Claude Desktop to load the MCP server.") + "\n");
|
|
975
|
+
}
|
|
767
976
|
if (platform === "cline") {
|
|
768
977
|
process.stderr.write(
|
|
769
978
|
style.dim(
|
|
@@ -780,7 +989,6 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
|
|
|
780
989
|
);
|
|
781
990
|
}
|
|
782
991
|
}
|
|
783
|
-
var DEFAULT_SHIELD_API_BASE_URL = "https://api.multicorn.ai";
|
|
784
992
|
async function runInit(explicitBaseUrl) {
|
|
785
993
|
if (!process.stdin.isTTY) {
|
|
786
994
|
process.stderr.write(
|
|
@@ -865,10 +1073,11 @@ async function runInit(explicitBaseUrl) {
|
|
|
865
1073
|
};
|
|
866
1074
|
let configuring = true;
|
|
867
1075
|
while (configuring) {
|
|
1076
|
+
let postSaveNativeSkipNote = null;
|
|
868
1077
|
const selection = await promptPlatformSelection(ask);
|
|
869
1078
|
const selectedPlatform = PLATFORM_BY_SELECTION[selection] ?? "cursor";
|
|
870
1079
|
const selectedLabel = PLATFORM_LABELS[selection - 1] ?? "Cursor";
|
|
871
|
-
if (selection ===
|
|
1080
|
+
if (selection === 8) {
|
|
872
1081
|
const raw = existing !== null ? { ...existing } : {};
|
|
873
1082
|
raw["apiKey"] = apiKey;
|
|
874
1083
|
raw["baseUrl"] = resolvedBaseUrl;
|
|
@@ -883,7 +1092,7 @@ async function runInit(explicitBaseUrl) {
|
|
|
883
1092
|
);
|
|
884
1093
|
process.stderr.write(
|
|
885
1094
|
"\n" + style.bold("Try it:") + " " + style.cyan(
|
|
886
|
-
"npx multicorn-
|
|
1095
|
+
"npx multicorn-shield --wrap npx @modelcontextprotocol/server-filesystem /tmp"
|
|
887
1096
|
) + "\n"
|
|
888
1097
|
);
|
|
889
1098
|
} catch (error) {
|
|
@@ -933,7 +1142,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
933
1142
|
}
|
|
934
1143
|
if (detection.status === "not-found") {
|
|
935
1144
|
process.stderr.write(
|
|
936
|
-
style.red("\u2717") + " OpenClaw is not installed. Install OpenClaw first, then run npx multicorn-
|
|
1145
|
+
style.red("\u2717") + " OpenClaw is not installed. Install OpenClaw first, then run npx multicorn-shield init again.\n"
|
|
937
1146
|
);
|
|
938
1147
|
rl.close();
|
|
939
1148
|
return null;
|
|
@@ -1047,8 +1256,22 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
1047
1256
|
});
|
|
1048
1257
|
setupSucceeded = true;
|
|
1049
1258
|
} catch (error) {
|
|
1050
|
-
|
|
1051
|
-
|
|
1259
|
+
if (error instanceof NativePluginPrerequisiteMissingError) {
|
|
1260
|
+
postSaveNativeSkipNote = nativePluginSkippedSaveNote(
|
|
1261
|
+
"npx multicorn-shield init",
|
|
1262
|
+
"Windsurf"
|
|
1263
|
+
);
|
|
1264
|
+
configuredAgents.push({
|
|
1265
|
+
selection,
|
|
1266
|
+
platform: selectedPlatform,
|
|
1267
|
+
platformLabel: selectedLabel,
|
|
1268
|
+
agentName
|
|
1269
|
+
});
|
|
1270
|
+
setupSucceeded = true;
|
|
1271
|
+
} else {
|
|
1272
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1273
|
+
process.stderr.write(style.red("\u2717 ") + detail + "\n");
|
|
1274
|
+
}
|
|
1052
1275
|
}
|
|
1053
1276
|
} else {
|
|
1054
1277
|
const { targetUrl, shortName } = await promptProxyConfig(ask, agentName);
|
|
@@ -1092,6 +1315,91 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
1092
1315
|
setupSucceeded = true;
|
|
1093
1316
|
}
|
|
1094
1317
|
}
|
|
1318
|
+
} else if (selection === 7) {
|
|
1319
|
+
const geminiMode = await promptGeminiCliIntegrationMode(ask);
|
|
1320
|
+
if (geminiMode === "native") {
|
|
1321
|
+
try {
|
|
1322
|
+
await installGeminiCliNativeHooks(ask);
|
|
1323
|
+
process.stderr.write("\n" + style.bold("Shield Gemini CLI hooks installed") + "\n\n");
|
|
1324
|
+
process.stderr.write(
|
|
1325
|
+
style.dim("Hook scripts: ") + style.cyan(getGeminiCliHooksInstallDir()) + "\n"
|
|
1326
|
+
);
|
|
1327
|
+
process.stderr.write(
|
|
1328
|
+
style.dim("Settings updated at ") + style.cyan("~/.gemini/settings.json") + "\n"
|
|
1329
|
+
);
|
|
1330
|
+
process.stderr.write(
|
|
1331
|
+
style.dim(
|
|
1332
|
+
"The Shield hook runs with your user permissions. It intercepts Gemini CLI tool calls to check permissions and log activity. Review the scripts if that is a concern."
|
|
1333
|
+
) + "\n"
|
|
1334
|
+
);
|
|
1335
|
+
configuredAgents.push({
|
|
1336
|
+
selection,
|
|
1337
|
+
platform: selectedPlatform,
|
|
1338
|
+
platformLabel: selectedLabel,
|
|
1339
|
+
agentName,
|
|
1340
|
+
geminiCliIntegration: "native"
|
|
1341
|
+
});
|
|
1342
|
+
setupSucceeded = true;
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
if (error instanceof NativePluginPrerequisiteMissingError) {
|
|
1345
|
+
postSaveNativeSkipNote = nativePluginSkippedSaveNote(
|
|
1346
|
+
"npx multicorn-shield init",
|
|
1347
|
+
"Gemini CLI"
|
|
1348
|
+
);
|
|
1349
|
+
configuredAgents.push({
|
|
1350
|
+
selection,
|
|
1351
|
+
platform: selectedPlatform,
|
|
1352
|
+
platformLabel: selectedLabel,
|
|
1353
|
+
agentName
|
|
1354
|
+
});
|
|
1355
|
+
setupSucceeded = true;
|
|
1356
|
+
} else {
|
|
1357
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1358
|
+
process.stderr.write(style.red("\u2717 ") + detail + "\n");
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
} else {
|
|
1362
|
+
const { targetUrl, shortName } = await promptProxyConfig(ask, agentName);
|
|
1363
|
+
let proxyUrl = "";
|
|
1364
|
+
let created = false;
|
|
1365
|
+
while (!created) {
|
|
1366
|
+
const spinner = withSpinner("Creating proxy config...");
|
|
1367
|
+
try {
|
|
1368
|
+
proxyUrl = await createProxyConfig(
|
|
1369
|
+
resolvedBaseUrl,
|
|
1370
|
+
apiKey,
|
|
1371
|
+
agentName,
|
|
1372
|
+
targetUrl,
|
|
1373
|
+
shortName,
|
|
1374
|
+
selectedPlatform
|
|
1375
|
+
);
|
|
1376
|
+
spinner.stop(true, "Proxy config created!");
|
|
1377
|
+
created = true;
|
|
1378
|
+
} catch (error) {
|
|
1379
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1380
|
+
spinner.stop(false, detail);
|
|
1381
|
+
const retry = await ask("Try again? (Y/n) ");
|
|
1382
|
+
if (retry.trim().toLowerCase() === "n") {
|
|
1383
|
+
break;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
if (created && proxyUrl.length > 0) {
|
|
1388
|
+
process.stderr.write("\n" + style.bold("Your Shield proxy URL:") + "\n");
|
|
1389
|
+
process.stderr.write(" " + style.cyan(proxyUrl) + "\n");
|
|
1390
|
+
printPlatformSnippet(selectedPlatform, proxyUrl, shortName, apiKey);
|
|
1391
|
+
configuredAgents.push({
|
|
1392
|
+
selection,
|
|
1393
|
+
platform: selectedPlatform,
|
|
1394
|
+
platformLabel: selectedLabel,
|
|
1395
|
+
agentName,
|
|
1396
|
+
shortName,
|
|
1397
|
+
proxyUrl,
|
|
1398
|
+
geminiCliIntegration: "hosted"
|
|
1399
|
+
});
|
|
1400
|
+
setupSucceeded = true;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1095
1403
|
} else if (selection === 5) {
|
|
1096
1404
|
const clineMode = await promptClineIntegrationMode(ask);
|
|
1097
1405
|
if (clineMode === "native") {
|
|
@@ -1112,8 +1420,22 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
1112
1420
|
});
|
|
1113
1421
|
setupSucceeded = true;
|
|
1114
1422
|
} catch (error) {
|
|
1115
|
-
|
|
1116
|
-
|
|
1423
|
+
if (error instanceof NativePluginPrerequisiteMissingError) {
|
|
1424
|
+
postSaveNativeSkipNote = nativePluginSkippedSaveNote(
|
|
1425
|
+
"npx multicorn-shield init",
|
|
1426
|
+
"Cline"
|
|
1427
|
+
);
|
|
1428
|
+
configuredAgents.push({
|
|
1429
|
+
selection,
|
|
1430
|
+
platform: selectedPlatform,
|
|
1431
|
+
platformLabel: selectedLabel,
|
|
1432
|
+
agentName
|
|
1433
|
+
});
|
|
1434
|
+
setupSucceeded = true;
|
|
1435
|
+
} else {
|
|
1436
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1437
|
+
process.stderr.write(style.red("\u2717 ") + detail + "\n");
|
|
1438
|
+
}
|
|
1117
1439
|
}
|
|
1118
1440
|
} else {
|
|
1119
1441
|
const { targetUrl, shortName } = await promptProxyConfig(ask, agentName);
|
|
@@ -1215,9 +1537,14 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
1215
1537
|
style.green("\u2713") + ` Config saved to ${style.cyan(CONFIG_PATH)}
|
|
1216
1538
|
`
|
|
1217
1539
|
);
|
|
1540
|
+
if (postSaveNativeSkipNote != null) {
|
|
1541
|
+
process.stderr.write(postSaveNativeSkipNote);
|
|
1542
|
+
postSaveNativeSkipNote = null;
|
|
1543
|
+
}
|
|
1218
1544
|
} catch (error) {
|
|
1219
1545
|
const detail = error instanceof Error ? error.message : String(error);
|
|
1220
1546
|
process.stderr.write(style.red(`Failed to save config: ${detail}`) + "\n");
|
|
1547
|
+
postSaveNativeSkipNote = null;
|
|
1221
1548
|
}
|
|
1222
1549
|
}
|
|
1223
1550
|
const another = await ask("\nConnect another agent? (Y/n) ");
|
|
@@ -1291,6 +1618,22 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
1291
1618
|
"\n" + style.bold("To complete your Cline hosted-proxy setup:") + "\n 1. If you don't have Cline yet, install it from the VS Code marketplace\n 2. Open your Cline MCP settings file and paste the config snippet shown above\n 3. Restart Cline or reload the VS Code window\n"
|
|
1292
1619
|
);
|
|
1293
1620
|
}
|
|
1621
|
+
const geminiCliNativeConfigured = configuredAgents.some(
|
|
1622
|
+
(a) => a.platform === "gemini-cli" && a.geminiCliIntegration === "native"
|
|
1623
|
+
);
|
|
1624
|
+
const geminiCliHostedConfigured = configuredAgents.some(
|
|
1625
|
+
(a) => a.platform === "gemini-cli" && a.geminiCliIntegration === "hosted"
|
|
1626
|
+
);
|
|
1627
|
+
if (geminiCliNativeConfigured) {
|
|
1628
|
+
blocks.push(
|
|
1629
|
+
"\n" + style.bold("Gemini CLI native hooks:") + "\n Your Gemini CLI hooks are installed. Restart Gemini CLI to activate Shield governance.\n"
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
1632
|
+
if (geminiCliHostedConfigured) {
|
|
1633
|
+
blocks.push(
|
|
1634
|
+
"\n" + style.bold("To complete your Gemini CLI setup:") + "\n 1. Open " + style.cyan("~/.gemini/settings.json") + "\n 2. Paste the config snippet shown above\n 3. Restart Gemini CLI, then run /mcp to verify\n"
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1294
1637
|
if (blocks.length > 0) {
|
|
1295
1638
|
process.stderr.write("\n" + style.bold(style.violet("Next steps")) + "\n");
|
|
1296
1639
|
process.stderr.write(blocks.join("") + "\n");
|
|
@@ -1298,22 +1641,96 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
1298
1641
|
}
|
|
1299
1642
|
return lastConfig;
|
|
1300
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
|
+
});
|
|
1301
1707
|
|
|
1302
1708
|
// src/types/index.ts
|
|
1303
|
-
var PERMISSION_LEVELS
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
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
|
+
});
|
|
1310
1721
|
|
|
1311
1722
|
// src/scopes/scope-parser.ts
|
|
1312
|
-
var VALID_PERMISSION_LEVELS = new Set(Object.values(PERMISSION_LEVELS));
|
|
1313
|
-
[...VALID_PERMISSION_LEVELS].join(", ");
|
|
1314
1723
|
function formatScope(scope) {
|
|
1315
1724
|
return `${scope.permissionLevel}:${scope.service}`;
|
|
1316
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
|
+
});
|
|
1317
1734
|
|
|
1318
1735
|
// src/scopes/scope-validator.ts
|
|
1319
1736
|
function validateScopeAccess(grantedScopes, requested) {
|
|
@@ -1341,6 +1758,11 @@ function hasScope(grantedScopes, requested) {
|
|
|
1341
1758
|
(granted) => granted.service === requested.service && granted.permissionLevel === requested.permissionLevel
|
|
1342
1759
|
);
|
|
1343
1760
|
}
|
|
1761
|
+
var init_scope_validator = __esm({
|
|
1762
|
+
"src/scopes/scope-validator.ts"() {
|
|
1763
|
+
init_scope_parser();
|
|
1764
|
+
}
|
|
1765
|
+
});
|
|
1344
1766
|
|
|
1345
1767
|
// src/logger/action-logger.ts
|
|
1346
1768
|
function createActionLogger(config) {
|
|
@@ -1514,6 +1936,10 @@ function createActionLogger(config) {
|
|
|
1514
1936
|
function sleep(ms) {
|
|
1515
1937
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1516
1938
|
}
|
|
1939
|
+
var init_action_logger = __esm({
|
|
1940
|
+
"src/logger/action-logger.ts"() {
|
|
1941
|
+
}
|
|
1942
|
+
});
|
|
1517
1943
|
|
|
1518
1944
|
// src/spending/spending-checker.ts
|
|
1519
1945
|
function createSpendingChecker(config) {
|
|
@@ -1646,13 +2072,12 @@ function validateLimits(limits) {
|
|
|
1646
2072
|
function dollarsToCents(dollars) {
|
|
1647
2073
|
return Math.round(dollars * 100);
|
|
1648
2074
|
}
|
|
2075
|
+
var init_spending_checker = __esm({
|
|
2076
|
+
"src/spending/spending-checker.ts"() {
|
|
2077
|
+
}
|
|
2078
|
+
});
|
|
1649
2079
|
|
|
1650
2080
|
// src/proxy/interceptor.ts
|
|
1651
|
-
var BLOCKED_ERROR_CODE = -32e3;
|
|
1652
|
-
var SPENDING_BLOCKED_ERROR_CODE = -32001;
|
|
1653
|
-
var INTERNAL_ERROR_CODE = -32002;
|
|
1654
|
-
var SERVICE_UNREACHABLE_ERROR_CODE = -32003;
|
|
1655
|
-
var AUTH_ERROR_CODE = -32004;
|
|
1656
2081
|
function parseJsonRpcLine(line) {
|
|
1657
2082
|
const trimmed = line.trim();
|
|
1658
2083
|
if (trimmed.length === 0) return null;
|
|
@@ -1720,7 +2145,7 @@ function buildServiceUnreachableResponse(id, dashboardUrl) {
|
|
|
1720
2145
|
};
|
|
1721
2146
|
}
|
|
1722
2147
|
function buildAuthErrorResponse(id) {
|
|
1723
|
-
const message = "Action blocked: Shield API key is invalid or has been revoked. Run npx multicorn-
|
|
2148
|
+
const message = "Action blocked: Shield API key is invalid or has been revoked. Run npx multicorn-shield init to reconfigure.";
|
|
1724
2149
|
return {
|
|
1725
2150
|
jsonrpc: "2.0",
|
|
1726
2151
|
id,
|
|
@@ -1752,9 +2177,16 @@ function capitalize(str) {
|
|
|
1752
2177
|
const first = str[0];
|
|
1753
2178
|
return first !== void 0 ? first.toUpperCase() + str.slice(1) : str;
|
|
1754
2179
|
}
|
|
1755
|
-
var
|
|
1756
|
-
var
|
|
1757
|
-
|
|
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
|
+
});
|
|
1758
2190
|
function cacheKey(agentName, apiKey) {
|
|
1759
2191
|
return createHash("sha256").update(`${agentName}:${apiKey}`).digest("hex").slice(0, 16);
|
|
1760
2192
|
}
|
|
@@ -1825,10 +2257,14 @@ async function saveCachedScopes(agentName, agentId, scopes, apiKey) {
|
|
|
1825
2257
|
function isScopesCacheFile(value) {
|
|
1826
2258
|
return typeof value === "object" && value !== null;
|
|
1827
2259
|
}
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
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
|
+
});
|
|
1832
2268
|
function deriveDashboardUrl(baseUrl) {
|
|
1833
2269
|
try {
|
|
1834
2270
|
const url = new URL(baseUrl);
|
|
@@ -1853,13 +2289,6 @@ function deriveDashboardUrl(baseUrl) {
|
|
|
1853
2289
|
return "https://app.multicorn.ai";
|
|
1854
2290
|
}
|
|
1855
2291
|
}
|
|
1856
|
-
var ShieldAuthError = class _ShieldAuthError extends Error {
|
|
1857
|
-
constructor(message) {
|
|
1858
|
-
super(message);
|
|
1859
|
-
this.name = "ShieldAuthError";
|
|
1860
|
-
Object.setPrototypeOf(this, _ShieldAuthError.prototype);
|
|
1861
|
-
}
|
|
1862
|
-
};
|
|
1863
2292
|
async function findAgentByName(agentName, apiKey, baseUrl) {
|
|
1864
2293
|
let response;
|
|
1865
2294
|
try {
|
|
@@ -2052,13 +2481,25 @@ function isPermissionShape(value) {
|
|
|
2052
2481
|
const obj = value;
|
|
2053
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");
|
|
2054
2483
|
}
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
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
|
+
});
|
|
2058
2499
|
function createProxyServer(config) {
|
|
2059
2500
|
if (!config.baseUrl.startsWith("https://") && !config.baseUrl.startsWith("http://localhost") && !config.baseUrl.startsWith("http://127.0.0.1")) {
|
|
2060
2501
|
throw new Error(
|
|
2061
|
-
`[multicorn-
|
|
2502
|
+
`[multicorn-shield] Base URL must use HTTPS. Received: "${config.baseUrl}". Use https:// or http://localhost for local development.`
|
|
2062
2503
|
);
|
|
2063
2504
|
}
|
|
2064
2505
|
let child = null;
|
|
@@ -2155,7 +2596,7 @@ function createProxyServer(config) {
|
|
|
2155
2596
|
if (actionLogger !== null) {
|
|
2156
2597
|
if (!config.agentName || config.agentName.trim().length === 0) {
|
|
2157
2598
|
process.stderr.write(
|
|
2158
|
-
"[multicorn-
|
|
2599
|
+
"[multicorn-shield] Cannot log action: agent name not resolved\n"
|
|
2159
2600
|
);
|
|
2160
2601
|
} else {
|
|
2161
2602
|
config.logger.debug("Logging blocked action (post-consent).", {
|
|
@@ -2185,7 +2626,7 @@ function createProxyServer(config) {
|
|
|
2185
2626
|
if (actionLogger !== null) {
|
|
2186
2627
|
if (!config.agentName || config.agentName.trim().length === 0) {
|
|
2187
2628
|
process.stderr.write(
|
|
2188
|
-
"[multicorn-
|
|
2629
|
+
"[multicorn-shield] Cannot log action: agent name not resolved\n"
|
|
2189
2630
|
);
|
|
2190
2631
|
} else {
|
|
2191
2632
|
config.logger.debug("Logging blocked action (spending).", {
|
|
@@ -2214,7 +2655,7 @@ function createProxyServer(config) {
|
|
|
2214
2655
|
}
|
|
2215
2656
|
if (actionLogger !== null) {
|
|
2216
2657
|
if (!config.agentName || config.agentName.trim().length === 0) {
|
|
2217
|
-
process.stderr.write("[multicorn-
|
|
2658
|
+
process.stderr.write("[multicorn-shield] Cannot log action: agent name not resolved\n");
|
|
2218
2659
|
} else {
|
|
2219
2660
|
config.logger.debug("Logging approved action.", {
|
|
2220
2661
|
agent: config.agentName,
|
|
@@ -2296,7 +2737,7 @@ function createProxyServer(config) {
|
|
|
2296
2737
|
agent: config.agentName
|
|
2297
2738
|
});
|
|
2298
2739
|
process.stderr.write(
|
|
2299
|
-
"\nError: API key was rejected by the Multicorn service.\nCheck your key at https://app.multicorn.ai/settings#api-keys or run `npx multicorn-
|
|
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"
|
|
2300
2741
|
);
|
|
2301
2742
|
throw new Error("API key was rejected by the Multicorn service.");
|
|
2302
2743
|
}
|
|
@@ -2369,12 +2810,17 @@ function extractCostCents(args) {
|
|
|
2369
2810
|
if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return 0;
|
|
2370
2811
|
return dollarsToCents(amount);
|
|
2371
2812
|
}
|
|
2372
|
-
var
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
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
|
+
});
|
|
2378
2824
|
function createLogger(level, output = process.stderr) {
|
|
2379
2825
|
const minLevel = LOG_LEVELS[level];
|
|
2380
2826
|
function write(logLevel, msg, data) {
|
|
@@ -2405,8 +2851,93 @@ function createLogger(level, output = process.stderr) {
|
|
|
2405
2851
|
function isValidLogLevel(value) {
|
|
2406
2852
|
return typeof value === "string" && Object.hasOwn(LOG_LEVELS, value);
|
|
2407
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
|
+
});
|
|
2408
2933
|
|
|
2409
|
-
// bin/multicorn-
|
|
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
|
+
});
|
|
2410
2941
|
function parseArgs(argv) {
|
|
2411
2942
|
const args = argv.slice(2);
|
|
2412
2943
|
let subcommand = "help";
|
|
@@ -2429,7 +2960,7 @@ function parseArgs(argv) {
|
|
|
2429
2960
|
const name = args[i + 1];
|
|
2430
2961
|
if (name === void 0 || name.startsWith("-")) {
|
|
2431
2962
|
process.stderr.write("Error: delete-agent requires an agent name.\n");
|
|
2432
|
-
process.stderr.write("Example: npx multicorn-
|
|
2963
|
+
process.stderr.write("Example: npx multicorn-shield delete-agent my-agent\n");
|
|
2433
2964
|
process.exit(1);
|
|
2434
2965
|
}
|
|
2435
2966
|
deleteAgentName = name;
|
|
@@ -2481,7 +3012,7 @@ function parseArgs(argv) {
|
|
|
2481
3012
|
}
|
|
2482
3013
|
if (remaining.length === 0) {
|
|
2483
3014
|
process.stderr.write("Error: --wrap requires a command to run.\n");
|
|
2484
|
-
process.stderr.write("Example: npx multicorn-
|
|
3015
|
+
process.stderr.write("Example: npx multicorn-shield --wrap my-mcp-server\n");
|
|
2485
3016
|
process.exit(1);
|
|
2486
3017
|
}
|
|
2487
3018
|
wrapCommand = remaining[0] ?? "";
|
|
@@ -2534,19 +3065,22 @@ function parseArgs(argv) {
|
|
|
2534
3065
|
function printHelp() {
|
|
2535
3066
|
process.stderr.write(
|
|
2536
3067
|
[
|
|
2537
|
-
"multicorn-
|
|
3068
|
+
"multicorn-shield: MCP permission proxy and Shield setup",
|
|
2538
3069
|
"",
|
|
2539
3070
|
"Usage:",
|
|
2540
|
-
" npx multicorn-
|
|
3071
|
+
" npx multicorn-shield init",
|
|
2541
3072
|
" Interactive setup. Saves API key to ~/.multicorn/config.json.",
|
|
2542
3073
|
"",
|
|
2543
|
-
" npx multicorn-
|
|
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",
|
|
2544
3078
|
" List configured agents and show which is the default.",
|
|
2545
3079
|
"",
|
|
2546
|
-
" npx multicorn-
|
|
3080
|
+
" npx multicorn-shield delete-agent <name>",
|
|
2547
3081
|
" Remove a saved agent.",
|
|
2548
3082
|
"",
|
|
2549
|
-
" npx multicorn-
|
|
3083
|
+
" npx multicorn-shield --wrap <command> [args...]",
|
|
2550
3084
|
" Start <command> as an MCP server and proxy all tool calls through",
|
|
2551
3085
|
" Shield's permission layer.",
|
|
2552
3086
|
"",
|
|
@@ -2558,14 +3092,22 @@ function printHelp() {
|
|
|
2558
3092
|
" --agent-name <name> Override agent name derived from the wrapped command",
|
|
2559
3093
|
"",
|
|
2560
3094
|
"Examples:",
|
|
2561
|
-
" npx multicorn-
|
|
2562
|
-
" npx multicorn-
|
|
2563
|
-
" npx multicorn-
|
|
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",
|
|
2564
3098
|
""
|
|
2565
3099
|
].join("\n")
|
|
2566
3100
|
);
|
|
2567
3101
|
}
|
|
2568
|
-
async function
|
|
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
|
+
}
|
|
2569
3111
|
const cli = parseArgs(process.argv);
|
|
2570
3112
|
const logger = createLogger(cli.logLevel);
|
|
2571
3113
|
if (cli.subcommand === "help") {
|
|
@@ -2580,13 +3122,13 @@ async function main() {
|
|
|
2580
3122
|
const config2 = await loadConfig();
|
|
2581
3123
|
if (config2 === null) {
|
|
2582
3124
|
process.stderr.write(
|
|
2583
|
-
"No config found. Run `npx multicorn-
|
|
3125
|
+
"No config found. Run `npx multicorn-shield init` to set up your API key.\n"
|
|
2584
3126
|
);
|
|
2585
3127
|
process.exit(1);
|
|
2586
3128
|
}
|
|
2587
3129
|
const agents = collectAgentsFromConfig(config2);
|
|
2588
3130
|
if (agents.length === 0) {
|
|
2589
|
-
process.stderr.write("No agents configured. Run `npx multicorn-
|
|
3131
|
+
process.stderr.write("No agents configured. Run `npx multicorn-shield init` to add one.\n");
|
|
2590
3132
|
process.exit(0);
|
|
2591
3133
|
}
|
|
2592
3134
|
const def = config2.defaultAgent;
|
|
@@ -2676,7 +3218,7 @@ async function resolveWrapConfig(cli, logger) {
|
|
|
2676
3218
|
return config;
|
|
2677
3219
|
}
|
|
2678
3220
|
process.stderr.write(
|
|
2679
|
-
"No API key found. Provide one via the --api-key flag, the MULTICORN_API_KEY environment variable, or run `npx multicorn-
|
|
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"
|
|
2680
3222
|
);
|
|
2681
3223
|
process.exit(1);
|
|
2682
3224
|
}
|
|
@@ -2704,14 +3246,32 @@ function deriveAgentName(command) {
|
|
|
2704
3246
|
const base = command.split("/").pop() ?? command;
|
|
2705
3247
|
return base.replace(/\.[cm]?[jt]s$/, "");
|
|
2706
3248
|
}
|
|
2707
|
-
var isDirectRun
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
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}
|
|
2712
3262
|
`);
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
}
|
|
3263
|
+
process.exit(1);
|
|
3264
|
+
});
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
});
|
|
2716
3268
|
|
|
2717
|
-
|
|
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
|
+
});
|