multicorn-shield 1.11.0 → 1.11.1
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 +7 -0
- package/dist/multicorn-proxy.js +100 -12
- package/dist/multicorn-shield.js +95 -11
- package/dist/server.js +3499 -0
- package/dist/shield-extension.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
- Bump `version` in `package.json` before publishing to npm.
|
|
11
11
|
|
|
12
|
+
## [Unreleased]
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- The local proxy server entry is now resolved by locating the multicorn-shield package root rather than a fixed relative climb, so `files` works from source, bundled dist, and npm installs.
|
|
17
|
+
|
|
12
18
|
## [1.11.0] - 2026-06-18
|
|
13
19
|
|
|
14
20
|
### Added
|
|
@@ -25,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
25
31
|
|
|
26
32
|
### Fixed
|
|
27
33
|
|
|
34
|
+
- `files` now starts the local proxy by spawning the bundled `dist/server.js` entry directly (with `PORT`, `SHIELD_API_BASE_URL`, and `ALLOW_PRIVATE_TARGETS`), instead of invoking the deprecated `multicorn-proxy` CLI alias. First-run setup works when no proxy is already listening on the port.
|
|
28
35
|
- Pinned transitive dependencies (`path-to-regexp`, `ws`, `hono`) via `pnpm.overrides` to clear high-severity audit advisories pulled in by `supergateway` and the MCP SDK.
|
|
29
36
|
|
|
30
37
|
## [1.10.0] - 2026-06-09
|
package/dist/multicorn-proxy.js
CHANGED
|
@@ -5062,7 +5062,7 @@ var init_package = __esm({
|
|
|
5062
5062
|
"package.json"() {
|
|
5063
5063
|
package_default = {
|
|
5064
5064
|
name: "multicorn-shield",
|
|
5065
|
-
version: "1.11.
|
|
5065
|
+
version: "1.11.1",
|
|
5066
5066
|
description: "The control layer for AI agents: permissions, consent, spending limits, and audit logging.",
|
|
5067
5067
|
license: "MIT",
|
|
5068
5068
|
author: "Multicorn AI Pty Ltd",
|
|
@@ -5122,7 +5122,7 @@ var init_package = __esm({
|
|
|
5122
5122
|
node: ">=20"
|
|
5123
5123
|
},
|
|
5124
5124
|
scripts: {
|
|
5125
|
-
build: "tsup",
|
|
5125
|
+
build: "tsup && node scripts/stage-local-proxy-server.mjs",
|
|
5126
5126
|
dev: "tsup --watch",
|
|
5127
5127
|
lint: "eslint . --no-warn-ignored && prettier --check .",
|
|
5128
5128
|
"lint:fix": "eslint --fix . --no-warn-ignored && prettier --write .",
|
|
@@ -5251,6 +5251,94 @@ var init_package_meta = __esm({
|
|
|
5251
5251
|
PACKAGE_VERSION = package_default.version;
|
|
5252
5252
|
}
|
|
5253
5253
|
});
|
|
5254
|
+
function findMulticornShieldPackageRoot(moduleDir = dirname(fileURLToPath(import.meta.url))) {
|
|
5255
|
+
let current = moduleDir;
|
|
5256
|
+
for (; ; ) {
|
|
5257
|
+
const pkgPath = join(current, "package.json");
|
|
5258
|
+
if (existsSync(pkgPath)) {
|
|
5259
|
+
try {
|
|
5260
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
5261
|
+
if (pkg.name === "multicorn-shield") {
|
|
5262
|
+
return current;
|
|
5263
|
+
}
|
|
5264
|
+
} catch {
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
const parent = dirname(current);
|
|
5268
|
+
if (parent === current) {
|
|
5269
|
+
break;
|
|
5270
|
+
}
|
|
5271
|
+
current = parent;
|
|
5272
|
+
}
|
|
5273
|
+
try {
|
|
5274
|
+
const req = createRequire(import.meta.url);
|
|
5275
|
+
return dirname(req.resolve("multicorn-shield/package.json"));
|
|
5276
|
+
} catch {
|
|
5277
|
+
throw new Error(NOT_FOUND_MESSAGE);
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
function resolveLocalProxyServerEntry(options) {
|
|
5281
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
5282
|
+
const candidates = [
|
|
5283
|
+
join(findMulticornShieldPackageRoot(moduleDir), "dist", "server.js")
|
|
5284
|
+
];
|
|
5285
|
+
try {
|
|
5286
|
+
const req = createRequire(import.meta.url);
|
|
5287
|
+
const proxyRoot = dirname(req.resolve("multicorn-proxy/package.json"));
|
|
5288
|
+
candidates.unshift(join(proxyRoot, "dist", "server.js"));
|
|
5289
|
+
} catch {
|
|
5290
|
+
}
|
|
5291
|
+
for (const path of candidates) {
|
|
5292
|
+
if (existsSync(path)) return path;
|
|
5293
|
+
}
|
|
5294
|
+
throw new Error(NOT_FOUND_MESSAGE);
|
|
5295
|
+
}
|
|
5296
|
+
function buildLocalProxySpawnEnv(port, apiBaseUrl) {
|
|
5297
|
+
return {
|
|
5298
|
+
PORT: String(port),
|
|
5299
|
+
HOST: "127.0.0.1",
|
|
5300
|
+
SHIELD_API_BASE_URL: apiBaseUrl,
|
|
5301
|
+
ALLOW_PRIVATE_TARGETS: "true"
|
|
5302
|
+
};
|
|
5303
|
+
}
|
|
5304
|
+
function buildLocalProxySpawnCommand(port, apiBaseUrl, execPath = process.execPath, serverEntryPath = resolveLocalProxyServerEntry()) {
|
|
5305
|
+
const env = buildLocalProxySpawnEnv(port, apiBaseUrl);
|
|
5306
|
+
return {
|
|
5307
|
+
executable: execPath,
|
|
5308
|
+
args: [serverEntryPath],
|
|
5309
|
+
env,
|
|
5310
|
+
serverEntryPath
|
|
5311
|
+
};
|
|
5312
|
+
}
|
|
5313
|
+
function readProxyLogTail(logPath, maxBytes = 8e3) {
|
|
5314
|
+
try {
|
|
5315
|
+
const content = readFileSync(logPath, "utf8");
|
|
5316
|
+
if (content.length <= maxBytes) return content;
|
|
5317
|
+
return content.slice(-maxBytes);
|
|
5318
|
+
} catch {
|
|
5319
|
+
return "";
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5322
|
+
function formatLocalProxyStartError(port, logPath, childExited, exitCode) {
|
|
5323
|
+
const logTail = readProxyLogTail(logPath);
|
|
5324
|
+
const reason = childExited ? `Proxy process exited early${exitCode === null ? "" : ` (code ${String(exitCode)})`}.` : `Proxy did not become ready on port ${String(port)} within the timeout.`;
|
|
5325
|
+
let message = `Could not start the local proxy on port ${String(port)}. ${reason}`;
|
|
5326
|
+
message += logTail.length > 0 ? `
|
|
5327
|
+
|
|
5328
|
+
Output from ${logPath}:
|
|
5329
|
+
${logTail.trimEnd()}` : `
|
|
5330
|
+
|
|
5331
|
+
See ${logPath} for details.`;
|
|
5332
|
+
return message;
|
|
5333
|
+
}
|
|
5334
|
+
var LOCAL_PROXY_READY_POLL_MS, LOCAL_PROXY_READY_MAX_POLLS, NOT_FOUND_MESSAGE;
|
|
5335
|
+
var init_local_proxy_start = __esm({
|
|
5336
|
+
"src/commands/local-proxy-start.ts"() {
|
|
5337
|
+
LOCAL_PROXY_READY_POLL_MS = 500;
|
|
5338
|
+
LOCAL_PROXY_READY_MAX_POLLS = 30;
|
|
5339
|
+
NOT_FOUND_MESSAGE = "Local proxy server entry (dist/server.js) not found. Reinstall multicorn-shield or run pnpm build.";
|
|
5340
|
+
}
|
|
5341
|
+
});
|
|
5254
5342
|
function pidfilePath(agent) {
|
|
5255
5343
|
return join(PIDFILE_DIR, `files-${agent}.pid`);
|
|
5256
5344
|
}
|
|
@@ -5537,19 +5625,17 @@ function startLocalProxyDetached(port, apiBaseUrl) {
|
|
|
5537
5625
|
const logFile = join(PIDFILE_DIR, "proxy.log");
|
|
5538
5626
|
const out = openSync(logFile, "a");
|
|
5539
5627
|
const err = openSync(logFile, "a");
|
|
5540
|
-
const
|
|
5628
|
+
const { executable, args, env: proxyEnv } = buildLocalProxySpawnCommand(port, apiBaseUrl);
|
|
5629
|
+
const child = spawn(executable, [...args], {
|
|
5541
5630
|
stdio: ["ignore", out, err],
|
|
5542
5631
|
detached: true,
|
|
5543
5632
|
env: {
|
|
5544
5633
|
...process.env,
|
|
5545
|
-
|
|
5546
|
-
HOST: "127.0.0.1",
|
|
5547
|
-
SHIELD_API_BASE_URL: apiBaseUrl,
|
|
5634
|
+
...proxyEnv
|
|
5548
5635
|
// SAFETY: blanket-allow for private targets is acceptable ONLY because
|
|
5549
5636
|
// this is a local single-user proxy. The only registered target is the
|
|
5550
5637
|
// filesystem server on the same machine. Do NOT copy this pattern to a
|
|
5551
5638
|
// multi-tenant or hosted proxy deployment.
|
|
5552
|
-
ALLOW_PRIVATE_TARGETS: "true"
|
|
5553
5639
|
}
|
|
5554
5640
|
});
|
|
5555
5641
|
child.unref();
|
|
@@ -5573,9 +5659,11 @@ async function ensureProxy(proxyPort, apiBaseUrl) {
|
|
|
5573
5659
|
`A server is already running on port ${String(proxyPort)} but it's not a healthy Shield proxy. Stop it or re-run with --proxy-port <n>.`
|
|
5574
5660
|
);
|
|
5575
5661
|
}
|
|
5662
|
+
const logFile = join(PIDFILE_DIR, "proxy.log");
|
|
5576
5663
|
const child = startLocalProxyDetached(proxyPort, apiBaseUrl);
|
|
5577
|
-
for (let i = 0; i <
|
|
5578
|
-
await sleep3(
|
|
5664
|
+
for (let i = 0; i < LOCAL_PROXY_READY_MAX_POLLS; i++) {
|
|
5665
|
+
await sleep3(LOCAL_PROXY_READY_POLL_MS);
|
|
5666
|
+
if (child.exitCode !== null || child.signalCode !== null) break;
|
|
5579
5667
|
if (await probeProxyHealth(proxyPort)) {
|
|
5580
5668
|
if (child.pid !== void 0) {
|
|
5581
5669
|
writeJsonFile(PROXY_REGISTRY, { pid: child.pid, port: proxyPort });
|
|
@@ -5587,9 +5675,8 @@ async function ensureProxy(proxyPort, apiBaseUrl) {
|
|
|
5587
5675
|
if (child.pid !== void 0) killWithEscalation(child.pid, true);
|
|
5588
5676
|
} catch {
|
|
5589
5677
|
}
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
);
|
|
5678
|
+
const childExited = child.exitCode !== null || child.signalCode !== null;
|
|
5679
|
+
throw new Error(formatLocalProxyStartError(proxyPort, logFile, childExited, child.exitCode));
|
|
5593
5680
|
}
|
|
5594
5681
|
async function ensureFsServer(realDir, requestedPort) {
|
|
5595
5682
|
const reg = readFsRegistry();
|
|
@@ -6196,6 +6283,7 @@ var DEFAULT_FS_PORT, DEFAULT_PROXY_PORT, PIDFILE_DIR, PROXY_REGISTRY, FS_REGISTR
|
|
|
6196
6283
|
var init_files = __esm({
|
|
6197
6284
|
"src/commands/files.ts"() {
|
|
6198
6285
|
init_config();
|
|
6286
|
+
init_local_proxy_start();
|
|
6199
6287
|
DEFAULT_FS_PORT = 3005;
|
|
6200
6288
|
DEFAULT_PROXY_PORT = 3001;
|
|
6201
6289
|
PIDFILE_DIR = process.env["MULTICORN_HOME"] ?? join(homedir(), ".multicorn");
|
package/dist/multicorn-shield.js
CHANGED
|
@@ -4985,10 +4985,95 @@ async function restoreClaudeDesktopMcpFromBackup() {
|
|
|
4985
4985
|
|
|
4986
4986
|
// package.json
|
|
4987
4987
|
var package_default = {
|
|
4988
|
-
version: "1.11.
|
|
4988
|
+
version: "1.11.1"};
|
|
4989
4989
|
|
|
4990
4990
|
// src/package-meta.ts
|
|
4991
4991
|
var PACKAGE_VERSION = package_default.version;
|
|
4992
|
+
var LOCAL_PROXY_READY_POLL_MS = 500;
|
|
4993
|
+
var LOCAL_PROXY_READY_MAX_POLLS = 30;
|
|
4994
|
+
var NOT_FOUND_MESSAGE = "Local proxy server entry (dist/server.js) not found. Reinstall multicorn-shield or run pnpm build.";
|
|
4995
|
+
function findMulticornShieldPackageRoot(moduleDir = dirname(fileURLToPath(import.meta.url))) {
|
|
4996
|
+
let current = moduleDir;
|
|
4997
|
+
for (; ; ) {
|
|
4998
|
+
const pkgPath = join(current, "package.json");
|
|
4999
|
+
if (existsSync(pkgPath)) {
|
|
5000
|
+
try {
|
|
5001
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
5002
|
+
if (pkg.name === "multicorn-shield") {
|
|
5003
|
+
return current;
|
|
5004
|
+
}
|
|
5005
|
+
} catch {
|
|
5006
|
+
}
|
|
5007
|
+
}
|
|
5008
|
+
const parent = dirname(current);
|
|
5009
|
+
if (parent === current) {
|
|
5010
|
+
break;
|
|
5011
|
+
}
|
|
5012
|
+
current = parent;
|
|
5013
|
+
}
|
|
5014
|
+
try {
|
|
5015
|
+
const req = createRequire(import.meta.url);
|
|
5016
|
+
return dirname(req.resolve("multicorn-shield/package.json"));
|
|
5017
|
+
} catch {
|
|
5018
|
+
throw new Error(NOT_FOUND_MESSAGE);
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
function resolveLocalProxyServerEntry(options) {
|
|
5022
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
5023
|
+
const candidates = [
|
|
5024
|
+
join(findMulticornShieldPackageRoot(moduleDir), "dist", "server.js")
|
|
5025
|
+
];
|
|
5026
|
+
try {
|
|
5027
|
+
const req = createRequire(import.meta.url);
|
|
5028
|
+
const proxyRoot = dirname(req.resolve("multicorn-proxy/package.json"));
|
|
5029
|
+
candidates.unshift(join(proxyRoot, "dist", "server.js"));
|
|
5030
|
+
} catch {
|
|
5031
|
+
}
|
|
5032
|
+
for (const path of candidates) {
|
|
5033
|
+
if (existsSync(path)) return path;
|
|
5034
|
+
}
|
|
5035
|
+
throw new Error(NOT_FOUND_MESSAGE);
|
|
5036
|
+
}
|
|
5037
|
+
function buildLocalProxySpawnEnv(port, apiBaseUrl) {
|
|
5038
|
+
return {
|
|
5039
|
+
PORT: String(port),
|
|
5040
|
+
HOST: "127.0.0.1",
|
|
5041
|
+
SHIELD_API_BASE_URL: apiBaseUrl,
|
|
5042
|
+
ALLOW_PRIVATE_TARGETS: "true"
|
|
5043
|
+
};
|
|
5044
|
+
}
|
|
5045
|
+
function buildLocalProxySpawnCommand(port, apiBaseUrl, execPath = process.execPath, serverEntryPath = resolveLocalProxyServerEntry()) {
|
|
5046
|
+
const env = buildLocalProxySpawnEnv(port, apiBaseUrl);
|
|
5047
|
+
return {
|
|
5048
|
+
executable: execPath,
|
|
5049
|
+
args: [serverEntryPath],
|
|
5050
|
+
env,
|
|
5051
|
+
serverEntryPath
|
|
5052
|
+
};
|
|
5053
|
+
}
|
|
5054
|
+
function readProxyLogTail(logPath, maxBytes = 8e3) {
|
|
5055
|
+
try {
|
|
5056
|
+
const content = readFileSync(logPath, "utf8");
|
|
5057
|
+
if (content.length <= maxBytes) return content;
|
|
5058
|
+
return content.slice(-maxBytes);
|
|
5059
|
+
} catch {
|
|
5060
|
+
return "";
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
function formatLocalProxyStartError(port, logPath, childExited, exitCode) {
|
|
5064
|
+
const logTail = readProxyLogTail(logPath);
|
|
5065
|
+
const reason = childExited ? `Proxy process exited early${exitCode === null ? "" : ` (code ${String(exitCode)})`}.` : `Proxy did not become ready on port ${String(port)} within the timeout.`;
|
|
5066
|
+
let message = `Could not start the local proxy on port ${String(port)}. ${reason}`;
|
|
5067
|
+
message += logTail.length > 0 ? `
|
|
5068
|
+
|
|
5069
|
+
Output from ${logPath}:
|
|
5070
|
+
${logTail.trimEnd()}` : `
|
|
5071
|
+
|
|
5072
|
+
See ${logPath} for details.`;
|
|
5073
|
+
return message;
|
|
5074
|
+
}
|
|
5075
|
+
|
|
5076
|
+
// src/commands/files.ts
|
|
4992
5077
|
var DEFAULT_FS_PORT = 3005;
|
|
4993
5078
|
var DEFAULT_PROXY_PORT = 3001;
|
|
4994
5079
|
var PIDFILE_DIR = process.env["MULTICORN_HOME"] ?? join(homedir(), ".multicorn");
|
|
@@ -5290,19 +5375,17 @@ function startLocalProxyDetached(port, apiBaseUrl) {
|
|
|
5290
5375
|
const logFile = join(PIDFILE_DIR, "proxy.log");
|
|
5291
5376
|
const out = openSync(logFile, "a");
|
|
5292
5377
|
const err = openSync(logFile, "a");
|
|
5293
|
-
const
|
|
5378
|
+
const { executable, args, env: proxyEnv } = buildLocalProxySpawnCommand(port, apiBaseUrl);
|
|
5379
|
+
const child = spawn(executable, [...args], {
|
|
5294
5380
|
stdio: ["ignore", out, err],
|
|
5295
5381
|
detached: true,
|
|
5296
5382
|
env: {
|
|
5297
5383
|
...process.env,
|
|
5298
|
-
|
|
5299
|
-
HOST: "127.0.0.1",
|
|
5300
|
-
SHIELD_API_BASE_URL: apiBaseUrl,
|
|
5384
|
+
...proxyEnv
|
|
5301
5385
|
// SAFETY: blanket-allow for private targets is acceptable ONLY because
|
|
5302
5386
|
// this is a local single-user proxy. The only registered target is the
|
|
5303
5387
|
// filesystem server on the same machine. Do NOT copy this pattern to a
|
|
5304
5388
|
// multi-tenant or hosted proxy deployment.
|
|
5305
|
-
ALLOW_PRIVATE_TARGETS: "true"
|
|
5306
5389
|
}
|
|
5307
5390
|
});
|
|
5308
5391
|
child.unref();
|
|
@@ -5326,9 +5409,11 @@ async function ensureProxy(proxyPort, apiBaseUrl) {
|
|
|
5326
5409
|
`A server is already running on port ${String(proxyPort)} but it's not a healthy Shield proxy. Stop it or re-run with --proxy-port <n>.`
|
|
5327
5410
|
);
|
|
5328
5411
|
}
|
|
5412
|
+
const logFile = join(PIDFILE_DIR, "proxy.log");
|
|
5329
5413
|
const child = startLocalProxyDetached(proxyPort, apiBaseUrl);
|
|
5330
|
-
for (let i = 0; i <
|
|
5331
|
-
await sleep3(
|
|
5414
|
+
for (let i = 0; i < LOCAL_PROXY_READY_MAX_POLLS; i++) {
|
|
5415
|
+
await sleep3(LOCAL_PROXY_READY_POLL_MS);
|
|
5416
|
+
if (child.exitCode !== null || child.signalCode !== null) break;
|
|
5332
5417
|
if (await probeProxyHealth(proxyPort)) {
|
|
5333
5418
|
if (child.pid !== void 0) {
|
|
5334
5419
|
writeJsonFile(PROXY_REGISTRY, { pid: child.pid, port: proxyPort });
|
|
@@ -5340,9 +5425,8 @@ async function ensureProxy(proxyPort, apiBaseUrl) {
|
|
|
5340
5425
|
if (child.pid !== void 0) killWithEscalation(child.pid, true);
|
|
5341
5426
|
} catch {
|
|
5342
5427
|
}
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
);
|
|
5428
|
+
const childExited = child.exitCode !== null || child.signalCode !== null;
|
|
5429
|
+
throw new Error(formatLocalProxyStartError(proxyPort, logFile, childExited, child.exitCode));
|
|
5346
5430
|
}
|
|
5347
5431
|
async function ensureFsServer(realDir, requestedPort) {
|
|
5348
5432
|
const reg = readFsRegistry();
|