multicorn-shield 0.6.0 → 0.6.2
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/README.md +2 -2
- package/dist/multicorn-proxy.js +102 -50
- package/dist/shield-extension.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
# Multicorn Shield
|
|
4
4
|
|
|
@@ -196,7 +196,7 @@ With the dashboard you can:
|
|
|
196
196
|
The dashboard works with both the SDK integration and the MCP proxy. No extra setup needed.
|
|
197
197
|
|
|
198
198
|
<p align="center">
|
|
199
|
-
<img src="https://
|
|
199
|
+
<img src="https://multicorn.ai/images/og-image-shield.png" alt="Dashboard overview showing total actions, blocked count, spend, and live activity feed" width="800" />
|
|
200
200
|
</p>
|
|
201
201
|
|
|
202
202
|
<p align="center">
|
package/dist/multicorn-proxy.js
CHANGED
|
@@ -94,32 +94,69 @@ function collectAgentsFromConfig(cfg) {
|
|
|
94
94
|
}
|
|
95
95
|
return [];
|
|
96
96
|
}
|
|
97
|
-
async function
|
|
97
|
+
async function parseConfigFile() {
|
|
98
98
|
try {
|
|
99
99
|
const raw = await readFile(CONFIG_PATH, "utf8");
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const next = { ...obj };
|
|
112
|
-
delete next["agentName"];
|
|
113
|
-
delete next["platform"];
|
|
114
|
-
next["agents"] = [{ name: agentNameRaw, platform }];
|
|
115
|
-
next["defaultAgent"] = agentNameRaw;
|
|
116
|
-
const migrated = next;
|
|
117
|
-
await saveConfig(migrated);
|
|
118
|
-
return migrated;
|
|
119
|
-
} catch {
|
|
120
|
-
return null;
|
|
100
|
+
try {
|
|
101
|
+
return { kind: "ok", value: JSON.parse(raw) };
|
|
102
|
+
} catch {
|
|
103
|
+
return { kind: "parseError" };
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
107
|
+
return { kind: "missing" };
|
|
108
|
+
}
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
return { kind: "readError", message };
|
|
121
111
|
}
|
|
122
112
|
}
|
|
113
|
+
function isAllowedShieldApiBaseUrl(url) {
|
|
114
|
+
return url.startsWith("https://") || url.startsWith("http://localhost") || url.startsWith("http://127.0.0.1");
|
|
115
|
+
}
|
|
116
|
+
async function loadConfig() {
|
|
117
|
+
const result = await parseConfigFile();
|
|
118
|
+
if (result.kind !== "ok") return null;
|
|
119
|
+
const parsed = result.value;
|
|
120
|
+
if (!isProxyConfig(parsed)) return null;
|
|
121
|
+
const obj = parsed;
|
|
122
|
+
const agentNameRaw = obj["agentName"];
|
|
123
|
+
const agentsRaw = obj["agents"];
|
|
124
|
+
const hasNonEmptyAgents = Array.isArray(agentsRaw) && agentsRaw.length > 0 && agentsRaw.every((e) => isAgentEntry(e));
|
|
125
|
+
const needsMigrate = typeof agentNameRaw === "string" && agentNameRaw.length > 0 && !hasNonEmptyAgents;
|
|
126
|
+
if (!needsMigrate) {
|
|
127
|
+
return parsed;
|
|
128
|
+
}
|
|
129
|
+
const platform = typeof obj["platform"] === "string" && obj["platform"].length > 0 ? obj["platform"] : "unknown";
|
|
130
|
+
const next = { ...obj };
|
|
131
|
+
delete next["agentName"];
|
|
132
|
+
delete next["platform"];
|
|
133
|
+
next["agents"] = [{ name: agentNameRaw, platform }];
|
|
134
|
+
next["defaultAgent"] = agentNameRaw;
|
|
135
|
+
const migrated = next;
|
|
136
|
+
await saveConfig(migrated);
|
|
137
|
+
return migrated;
|
|
138
|
+
}
|
|
139
|
+
async function readBaseUrlFromConfig() {
|
|
140
|
+
const result = await parseConfigFile();
|
|
141
|
+
if (result.kind === "missing") return void 0;
|
|
142
|
+
if (result.kind === "readError") {
|
|
143
|
+
process.stderr.write(
|
|
144
|
+
style.yellow(`Warning: could not read base URL from config file: ${result.message}`) + "\n"
|
|
145
|
+
);
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
if (result.kind === "parseError") {
|
|
149
|
+
process.stderr.write(
|
|
150
|
+
style.yellow("Warning: could not parse ~/.multicorn/config.json as JSON.") + "\n"
|
|
151
|
+
);
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
const parsed = result.value;
|
|
155
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
156
|
+
const u = parsed["baseUrl"];
|
|
157
|
+
if (typeof u !== "string" || u.length === 0) return void 0;
|
|
158
|
+
return u;
|
|
159
|
+
}
|
|
123
160
|
async function deleteAgentByName(name) {
|
|
124
161
|
const config = await loadConfig();
|
|
125
162
|
if (config === null) return false;
|
|
@@ -507,7 +544,8 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
|
|
|
507
544
|
);
|
|
508
545
|
}
|
|
509
546
|
}
|
|
510
|
-
|
|
547
|
+
var DEFAULT_SHIELD_API_BASE_URL = "https://api.multicorn.ai";
|
|
548
|
+
async function runInit(explicitBaseUrl) {
|
|
511
549
|
if (!process.stdin.isTTY) {
|
|
512
550
|
process.stderr.write(
|
|
513
551
|
style.red("Error: interactive terminal required. Cannot run init with piped input.") + "\n"
|
|
@@ -525,16 +563,29 @@ async function runInit(baseUrl = "https://api.multicorn.ai") {
|
|
|
525
563
|
style.dim("Get your API key at https://app.multicorn.ai/settings/api-keys") + "\n\n"
|
|
526
564
|
);
|
|
527
565
|
const existing = await loadConfig().catch(() => null);
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
566
|
+
let resolvedBaseUrl;
|
|
567
|
+
if (explicitBaseUrl !== void 0 && explicitBaseUrl.trim().length > 0) {
|
|
568
|
+
resolvedBaseUrl = explicitBaseUrl.trim();
|
|
569
|
+
} else if (existing !== null && existing.baseUrl.length > 0) {
|
|
570
|
+
resolvedBaseUrl = existing.baseUrl;
|
|
571
|
+
} else {
|
|
572
|
+
const fromFile = await readBaseUrlFromConfig();
|
|
573
|
+
if (fromFile !== void 0 && fromFile.length > 0) {
|
|
574
|
+
resolvedBaseUrl = fromFile;
|
|
531
575
|
} else {
|
|
532
576
|
const envBaseUrl = process.env["MULTICORN_BASE_URL"];
|
|
533
|
-
|
|
534
|
-
baseUrl = envBaseUrl;
|
|
535
|
-
}
|
|
577
|
+
resolvedBaseUrl = envBaseUrl !== void 0 && envBaseUrl.trim().length > 0 ? envBaseUrl.trim() : DEFAULT_SHIELD_API_BASE_URL;
|
|
536
578
|
}
|
|
537
579
|
}
|
|
580
|
+
if (!isAllowedShieldApiBaseUrl(resolvedBaseUrl)) {
|
|
581
|
+
process.stderr.write(
|
|
582
|
+
style.red(
|
|
583
|
+
"Base URL must use HTTPS (or http://localhost for local development). Received a non-HTTPS URL from config. Use --base-url to override."
|
|
584
|
+
) + "\n"
|
|
585
|
+
);
|
|
586
|
+
rl.close();
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
538
589
|
let apiKey = "";
|
|
539
590
|
if (existing !== null && existing.apiKey.startsWith("mcs_") && existing.apiKey.length >= 8) {
|
|
540
591
|
const masked = "mcs_..." + existing.apiKey.slice(-4);
|
|
@@ -554,7 +605,7 @@ async function runInit(baseUrl = "https://api.multicorn.ai") {
|
|
|
554
605
|
const spinner = withSpinner("Validating key...");
|
|
555
606
|
let result;
|
|
556
607
|
try {
|
|
557
|
-
result = await validateApiKey(key,
|
|
608
|
+
result = await validateApiKey(key, resolvedBaseUrl);
|
|
558
609
|
} catch (error) {
|
|
559
610
|
spinner.stop(false, "Validation failed");
|
|
560
611
|
throw error;
|
|
@@ -566,18 +617,11 @@ async function runInit(baseUrl = "https://api.multicorn.ai") {
|
|
|
566
617
|
spinner.stop(true, "Key validated");
|
|
567
618
|
apiKey = key;
|
|
568
619
|
}
|
|
569
|
-
if (!baseUrl.startsWith("https://") && !baseUrl.startsWith("http://localhost") && !baseUrl.startsWith("http://127.0.0.1")) {
|
|
570
|
-
process.stderr.write(
|
|
571
|
-
style.red(`\u2717 Shield API base URL must use HTTPS. Got: ${baseUrl}`) + "\n"
|
|
572
|
-
);
|
|
573
|
-
rl.close();
|
|
574
|
-
return null;
|
|
575
|
-
}
|
|
576
620
|
const configuredAgents = [];
|
|
577
621
|
let currentAgents = collectAgentsFromConfig(existing);
|
|
578
622
|
let lastConfig = {
|
|
579
623
|
apiKey,
|
|
580
|
-
baseUrl,
|
|
624
|
+
baseUrl: resolvedBaseUrl,
|
|
581
625
|
...currentAgents.length > 0 ? {
|
|
582
626
|
agents: currentAgents,
|
|
583
627
|
defaultAgent: existing !== null && typeof existing.defaultAgent === "string" && existing.defaultAgent.length > 0 ? existing.defaultAgent : currentAgents[currentAgents.length - 1]?.name ?? ""
|
|
@@ -657,7 +701,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
657
701
|
}
|
|
658
702
|
const spinner = withSpinner("Updating OpenClaw config...");
|
|
659
703
|
try {
|
|
660
|
-
const result = await updateOpenClawConfigIfPresent(apiKey,
|
|
704
|
+
const result = await updateOpenClawConfigIfPresent(apiKey, resolvedBaseUrl, agentName);
|
|
661
705
|
if (result === "not-found") {
|
|
662
706
|
spinner.stop(false, "OpenClaw config disappeared unexpectedly.");
|
|
663
707
|
rl.close();
|
|
@@ -712,7 +756,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
712
756
|
const spinner = withSpinner("Creating proxy config...");
|
|
713
757
|
try {
|
|
714
758
|
proxyUrl = await createProxyConfig(
|
|
715
|
-
|
|
759
|
+
resolvedBaseUrl,
|
|
716
760
|
apiKey,
|
|
717
761
|
agentName,
|
|
718
762
|
targetUrl,
|
|
@@ -750,7 +794,7 @@ An agent for ${selectedLabel} already exists: ${style.cyan(existingForPlatform.n
|
|
|
750
794
|
currentAgents.push({ name: agentName, platform: selectedPlatform });
|
|
751
795
|
const raw = existing !== null ? { ...existing } : {};
|
|
752
796
|
raw["apiKey"] = apiKey;
|
|
753
|
-
raw["baseUrl"] =
|
|
797
|
+
raw["baseUrl"] = resolvedBaseUrl;
|
|
754
798
|
raw["agents"] = currentAgents;
|
|
755
799
|
raw["defaultAgent"] = agentName;
|
|
756
800
|
delete raw["agentName"];
|
|
@@ -1920,7 +1964,7 @@ function parseArgs(argv) {
|
|
|
1920
1964
|
let wrapCommand = "";
|
|
1921
1965
|
let wrapArgs = [];
|
|
1922
1966
|
let logLevel = "info";
|
|
1923
|
-
let baseUrl =
|
|
1967
|
+
let baseUrl = void 0;
|
|
1924
1968
|
let dashboardUrl = "";
|
|
1925
1969
|
let agentName = "";
|
|
1926
1970
|
let deleteAgentName = "";
|
|
@@ -2098,13 +2142,13 @@ async function main() {
|
|
|
2098
2142
|
`);
|
|
2099
2143
|
return;
|
|
2100
2144
|
}
|
|
2101
|
-
if (
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2145
|
+
if (cli.baseUrl !== void 0 && cli.baseUrl.length > 0) {
|
|
2146
|
+
if (!isAllowedShieldApiBaseUrl(cli.baseUrl)) {
|
|
2147
|
+
process.stderr.write(
|
|
2148
|
+
"Error: --base-url must use HTTPS. Received a non-HTTPS URL.\nUse https:// or http://localhost for local development.\n"
|
|
2149
|
+
);
|
|
2150
|
+
process.exit(1);
|
|
2151
|
+
}
|
|
2108
2152
|
}
|
|
2109
2153
|
const config = await loadConfig();
|
|
2110
2154
|
if (config === null) {
|
|
@@ -2113,8 +2157,16 @@ Use https:// or http://localhost for local development.
|
|
|
2113
2157
|
);
|
|
2114
2158
|
process.exit(1);
|
|
2115
2159
|
}
|
|
2160
|
+
const finalBaseUrl = cli.baseUrl !== void 0 && cli.baseUrl.length > 0 ? cli.baseUrl : config.baseUrl;
|
|
2161
|
+
if (cli.baseUrl === void 0 || cli.baseUrl.length === 0) {
|
|
2162
|
+
if (!isAllowedShieldApiBaseUrl(finalBaseUrl)) {
|
|
2163
|
+
process.stderr.write(
|
|
2164
|
+
"Error: --base-url must use HTTPS. Received a non-HTTPS URL.\nUse https:// or http://localhost for local development.\n"
|
|
2165
|
+
);
|
|
2166
|
+
process.exit(1);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2116
2169
|
const agentName = resolveWrapAgentName(cli, config);
|
|
2117
|
-
const finalBaseUrl = cli.baseUrl !== "https://api.multicorn.ai" ? cli.baseUrl : config.baseUrl;
|
|
2118
2170
|
const finalDashboardUrl = cli.dashboardUrl !== "" ? cli.dashboardUrl : deriveDashboardUrl(finalBaseUrl);
|
|
2119
2171
|
const legacyPlatform = config.platform;
|
|
2120
2172
|
const platformForServer = typeof legacyPlatform === "string" && legacyPlatform.length > 0 ? legacyPlatform : "other-mcp";
|
package/dist/shield-extension.js
CHANGED
|
@@ -22358,7 +22358,7 @@ async function writeExtensionBackup(claudeDesktopConfigPath, mcpServers) {
|
|
|
22358
22358
|
|
|
22359
22359
|
// package.json
|
|
22360
22360
|
var package_default = {
|
|
22361
|
-
version: "0.6.
|
|
22361
|
+
version: "0.6.2"};
|
|
22362
22362
|
|
|
22363
22363
|
// src/package-meta.ts
|
|
22364
22364
|
var PACKAGE_VERSION = package_default.version;
|