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 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
@@ -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.0",
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 child = spawn("npx", ["multicorn-proxy"], {
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
- PORT: String(port),
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 < 30; i++) {
5578
- await sleep3(500);
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
- throw new Error(
5591
- `Could not start the local proxy on port ${String(proxyPort)}. Check the log at ${join(PIDFILE_DIR, "proxy.log")}.`
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");
@@ -4985,10 +4985,95 @@ async function restoreClaudeDesktopMcpFromBackup() {
4985
4985
 
4986
4986
  // package.json
4987
4987
  var package_default = {
4988
- version: "1.11.0"};
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 child = spawn("npx", ["multicorn-proxy"], {
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
- PORT: String(port),
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 < 30; i++) {
5331
- await sleep3(500);
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
- throw new Error(
5344
- `Could not start the local proxy on port ${String(proxyPort)}. Check the log at ${join(PIDFILE_DIR, "proxy.log")}.`
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();