@sanctuary-framework/mcp-server 0.10.0 → 0.10.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/dist/cli.cjs CHANGED
@@ -2040,6 +2040,7 @@ var init_audit_log = __esm({
2040
2040
  maxTotalSizeBytes;
2041
2041
  maxEntries;
2042
2042
  rotationInFlight = false;
2043
+ pendingWrites = /* @__PURE__ */ new Set();
2043
2044
  constructor(storage, masterKey, config) {
2044
2045
  this.storage = storage;
2045
2046
  this.encryptionKey = derivePurposeKey(masterKey, "audit-log");
@@ -2048,6 +2049,15 @@ var init_audit_log = __esm({
2048
2049
  }
2049
2050
  /**
2050
2051
  * Append an audit entry.
2052
+ *
2053
+ * The on-disk persist is async and tracked via `pendingWrites`. Long-lived
2054
+ * callers (the main MCP server) can ignore that tracking and let writes
2055
+ * drain naturally. Short-lived callers — the `sanctuary secrets` CLI which
2056
+ * `process.exit()`s immediately after returning from a broker mutation —
2057
+ * MUST await `flush()` before exiting, or in-flight writes get killed
2058
+ * with the event loop and the entry is silently lost. That was the
2059
+ * v0.10.0-rc.2 soak failure mode where `secrets audit` returned empty
2060
+ * after a clean 7-verb lifecycle.
2051
2061
  */
2052
2062
  append(layer, operation, identityId, details, result = "success") {
2053
2063
  const entry = {
@@ -2059,8 +2069,22 @@ var init_audit_log = __esm({
2059
2069
  details
2060
2070
  };
2061
2071
  this.entries.push(entry);
2062
- this.persistEntry(entry).catch(() => {
2072
+ const writePromise = this.persistEntry(entry).catch(() => {
2063
2073
  });
2074
+ this.pendingWrites.add(writePromise);
2075
+ void writePromise.finally(() => this.pendingWrites.delete(writePromise));
2076
+ }
2077
+ /**
2078
+ * Wait for every in-flight `append()` persist (and its rotation pass) to
2079
+ * settle. Safe to call multiple times — newly-appended entries during a
2080
+ * flush are also awaited. Re-entrant only at the granularity of "drain
2081
+ * everything queued so far". Short-lived CLIs MUST call this before
2082
+ * `process.exit()` to keep audit writes durable.
2083
+ */
2084
+ async flush() {
2085
+ while (this.pendingWrites.size > 0) {
2086
+ await Promise.allSettled([...this.pendingWrites]);
2087
+ }
2064
2088
  }
2065
2089
  async persistEntry(entry) {
2066
2090
  const key = `${Date.now()}-${this.counter++}`;
@@ -2071,7 +2095,7 @@ var init_audit_log = __esm({
2071
2095
  key,
2072
2096
  stringToBytes(JSON.stringify(encrypted))
2073
2097
  );
2074
- this.maybeRotate().catch(() => {
2098
+ await this.maybeRotate().catch(() => {
2075
2099
  });
2076
2100
  }
2077
2101
  /**
@@ -24131,11 +24155,13 @@ var init_runtime = __esm({
24131
24155
  var cli_exports = {};
24132
24156
  __export(cli_exports, {
24133
24157
  COCOON_GOVERNOR_DEFAULTS: () => COCOON_GOVERNOR_DEFAULTS,
24158
+ PORT_FALLBACK_ATTEMPTS: () => PORT_FALLBACK_ATTEMPTS,
24134
24159
  formatWrapSuccess: () => formatWrapSuccess,
24135
24160
  parseCocoonArgs: () => parseCocoonArgs,
24136
24161
  parseWrapArgs: () => parseWrapArgs,
24137
24162
  runCocoon: () => runCocoon,
24138
- runWrap: () => runWrap
24163
+ runWrap: () => runWrap,
24164
+ startDashboardWithFallback: () => startDashboardWithFallback
24139
24165
  });
24140
24166
  async function runWrap(options, deps = {}) {
24141
24167
  if (options.unwrap) {
@@ -24379,7 +24405,8 @@ async function runCocoon(options) {
24379
24405
  }
24380
24406
  async function startDashboardWithFallback(startFn, preferredPort, authToken, serverVersion) {
24381
24407
  let lastErr;
24382
- for (let port = preferredPort; port <= MAX_PORT; port++) {
24408
+ for (let i = 0; i < PORT_FALLBACK_ATTEMPTS; i++) {
24409
+ const port = preferredPort + i;
24383
24410
  try {
24384
24411
  const handle = await startFn({
24385
24412
  port,
@@ -24398,8 +24425,9 @@ async function startDashboardWithFallback(startFn, preferredPort, authToken, ser
24398
24425
  if (!isAddressInUse(err)) throw err;
24399
24426
  }
24400
24427
  }
24428
+ const lastPort = preferredPort + PORT_FALLBACK_ATTEMPTS - 1;
24401
24429
  throw new Error(
24402
- `No free dashboard port in range ${preferredPort}-${MAX_PORT}: ${lastErr?.message ?? "unknown"}`
24430
+ `No free dashboard port in the ${PORT_FALLBACK_ATTEMPTS} ports starting at ${preferredPort} (tried ${preferredPort}-${lastPort}): ${lastErr?.message ?? "unknown"}`
24403
24431
  );
24404
24432
  }
24405
24433
  function isAddressInUse(err) {
@@ -24647,7 +24675,7 @@ function printWrapHelp() {
24647
24675
  5. Every tool call is logged, scanned, and tier-gated
24648
24676
  `);
24649
24677
  }
24650
- var COCOON_GOVERNOR_DEFAULTS, MAX_PORT, parseCocoonArgs;
24678
+ var COCOON_GOVERNOR_DEFAULTS, PORT_FALLBACK_ATTEMPTS, parseCocoonArgs;
24651
24679
  var init_cli = __esm({
24652
24680
  "src/cocoon/cli.ts"() {
24653
24681
  init_config_reader();
@@ -24661,7 +24689,7 @@ var init_cli = __esm({
24661
24689
  rate_limit_per_tool: 20,
24662
24690
  lifetime_limit: 1e3
24663
24691
  };
24664
- MAX_PORT = 3510;
24692
+ PORT_FALLBACK_ATTEMPTS = 20;
24665
24693
  parseCocoonArgs = parseWrapArgs;
24666
24694
  }
24667
24695
  });
@@ -26165,6 +26193,7 @@ async function openBroker(opts = {}) {
26165
26193
  return {
26166
26194
  broker,
26167
26195
  close: async () => {
26196
+ await auditLog.flush();
26168
26197
  }
26169
26198
  };
26170
26199
  }
@@ -27189,12 +27218,12 @@ function optionalNumber(args, key) {
27189
27218
  const v = args[key];
27190
27219
  return typeof v === "number" && Number.isFinite(v) ? v : void 0;
27191
27220
  }
27192
- var require4, PKG_VERSION3;
27221
+ var PKG_VERSION3;
27193
27222
  var init_broker_server = __esm({
27194
27223
  "src/mcp/broker-server.ts"() {
27224
+ init_config();
27195
27225
  init_token_issuer();
27196
- require4 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
27197
- ({ version: PKG_VERSION3 } = require4("../../package.json"));
27226
+ PKG_VERSION3 = SANCTUARY_VERSION;
27198
27227
  }
27199
27228
  });
27200
27229
 
@@ -27613,8 +27642,8 @@ async function checkForUpdate(currentVersion) {
27613
27642
  } catch {
27614
27643
  }
27615
27644
  }
27616
- var require5 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
27617
- var { version: PKG_VERSION4 } = require5("../package.json");
27645
+ var require4 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
27646
+ var { version: PKG_VERSION4 } = require4("../package.json");
27618
27647
  async function main() {
27619
27648
  const args = process.argv.slice(2);
27620
27649
  let passphrase = process.env.SANCTUARY_PASSPHRASE;