@sanctuary-framework/mcp-server 1.1.2 → 1.1.4

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/index.cjs CHANGED
@@ -21,7 +21,7 @@ var url = require('url');
21
21
  var index_js = require('@modelcontextprotocol/sdk/client/index.js');
22
22
  var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
23
23
  var sse_js = require('@modelcontextprotocol/sdk/client/sse.js');
24
- require('readline/promises');
24
+ var promises$1 = require('readline/promises');
25
25
 
26
26
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
27
27
  var __defProp = Object.defineProperty;
@@ -12730,7 +12730,7 @@ function renderDashboardV11Html(options = {}) {
12730
12730
  streamUrl,
12731
12731
  identityId,
12732
12732
  fortressId
12733
- });
12733
+ }).replace(/</g, "\\u003c");
12734
12734
  const clientBlock = embedClient ? `<script type="module">${getClientScript()}</script>` : `<!-- client script omitted by render option -->`;
12735
12735
  return `<!doctype html>
12736
12736
  <html lang="en">
@@ -12761,7 +12761,7 @@ function renderDashboardV11Html(options = {}) {
12761
12761
  <aside class="fortress" id="fortress"><p class="muted">Loading fortress column.</p></aside>
12762
12762
  </div>
12763
12763
  <div id="toast-host" aria-live="polite"></div>
12764
- <script id="dashboard-config" type="application/json">${escHtml2(config)}</script>
12764
+ <script id="dashboard-config" type="application/json">${config}</script>
12765
12765
  ${clientBlock}
12766
12766
  </body>
12767
12767
  </html>`;
@@ -26358,14 +26358,41 @@ function createComplianceTools(deps) {
26358
26358
  init_random();
26359
26359
  init_encoding();
26360
26360
  var RECOVERY_KEY_FILENAME = "recovery-key.txt";
26361
- function printRecoveryKeyBanner(recoveryKey, filePath, output = process.stderr) {
26361
+ var RECOVERY_KEY_COPY = {
26362
+ fileName: RECOVERY_KEY_FILENAME,
26363
+ bannerHeader: "SANCTUARY: First Run, Recovery Key Generated",
26364
+ bannerSecretLabel: "Recovery Key",
26365
+ bannerSaveLine: "SAVE THIS KEY. It will not be shown again.",
26366
+ bannerLossLine: "Without it, your encrypted state is unrecoverable.",
26367
+ fileWarningHeader: "SANCTUARY RECOVERY KEY, DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.",
26368
+ fileSecretLabel: "Recovery key:",
26369
+ fileBody: "This file was created on first init. Sanctuary will NOT regenerate this file on\nsubsequent runs and will NOT display the key again. After moving this file off\nthe host (encrypted backup, password manager, paper safe), delete it from the\nfortress directory. Do NOT keep it in the fortress; the recovery key bypasses\nthe cocoon passphrase by design.\n",
26370
+ promptLabel: "recovery key"
26371
+ };
26372
+ var RecoveryKeyConfirmationDeclinedError = class extends Error {
26373
+ constructor() {
26374
+ super(
26375
+ "Recovery key confirmation declined. Save the recovery key (printed above and written to recovery-key.txt) before re-running init."
26376
+ );
26377
+ this.name = "RecoveryKeyConfirmationDeclinedError";
26378
+ }
26379
+ };
26380
+ var RecoveryKeyConfirmationNonInteractiveError = class extends Error {
26381
+ constructor() {
26382
+ super(
26383
+ "Recovery key confirmation requires an interactive terminal. Re-run with --no-confirm for CI/scripted use, or run from a TTY."
26384
+ );
26385
+ this.name = "RecoveryKeyConfirmationNonInteractiveError";
26386
+ }
26387
+ };
26388
+ function printSecretBanner(secret, filePath, copy, output = process.stderr) {
26362
26389
  const lines = [
26363
- "SANCTUARY: First Run, Recovery Key Generated",
26390
+ copy.bannerHeader,
26364
26391
  "",
26365
- `Recovery Key: ${recoveryKey}`,
26392
+ `${copy.bannerSecretLabel}: ${secret}`,
26366
26393
  "",
26367
- "SAVE THIS KEY. It will not be shown again.",
26368
- "Without it, your encrypted state is unrecoverable.",
26394
+ copy.bannerSaveLine,
26395
+ copy.bannerLossLine,
26369
26396
  "",
26370
26397
  "Plaintext copy written to:",
26371
26398
  ` ${filePath}`,
@@ -26384,8 +26411,8 @@ ${bottom}
26384
26411
 
26385
26412
  `);
26386
26413
  }
26387
- async function writeRecoveryKeyFile(opts) {
26388
- const filePath = path.join(opts.storagePath, RECOVERY_KEY_FILENAME);
26414
+ async function writeSecretFile(opts) {
26415
+ const filePath = path.join(opts.storagePath, opts.copy.fileName);
26389
26416
  try {
26390
26417
  await promises.access(filePath, promises.constants.F_OK);
26391
26418
  return { filePath, written: false };
@@ -26395,40 +26422,76 @@ async function writeRecoveryKeyFile(opts) {
26395
26422
  const now = (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
26396
26423
  const fortressLine = opts.fortressId ? `Fortress: ${opts.fortressId}
26397
26424
  ` : "";
26398
- const content = `SANCTUARY RECOVERY KEY \u2014 DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.
26425
+ const content = `${opts.copy.fileWarningHeader}
26399
26426
  Generated: ${now}
26400
26427
  ` + fortressLine + `
26401
- Recovery key:
26402
- ${opts.recoveryKey}
26403
-
26404
- This file was created on first init. Sanctuary will NOT regenerate this file on
26405
- subsequent runs and will NOT display the key again. After moving this file off
26406
- the host (encrypted backup, password manager, paper safe), delete it from the
26407
- fortress directory. Do NOT keep it in the fortress; the recovery key bypasses
26408
- the cocoon passphrase by design.
26409
- `;
26428
+ ${opts.copy.fileSecretLabel}
26429
+ ${opts.secret}
26430
+
26431
+ ` + opts.copy.fileBody;
26410
26432
  await promises.writeFile(filePath, content, { mode: 384 });
26411
26433
  return { filePath, written: true };
26412
26434
  }
26413
- async function discloseRecoveryKey(opts) {
26414
- const fileResult = await writeRecoveryKeyFile({
26435
+ async function confirmSecretSaved(copy, declinedError, nonInteractiveError, io) {
26436
+ const input = io?.input ?? process.stdin;
26437
+ const output = io?.output ?? process.stderr;
26438
+ const realStdin = !io && process.stdin.isTTY !== true;
26439
+ if (realStdin) {
26440
+ throw new nonInteractiveError();
26441
+ }
26442
+ const rl = promises$1.createInterface({ input, output });
26443
+ try {
26444
+ const answer = await rl.question(
26445
+ `Have you saved the ${copy.promptLabel}? [y/N] `
26446
+ );
26447
+ const normalized = answer.trim().toLowerCase();
26448
+ if (normalized !== "y" && normalized !== "yes") {
26449
+ throw new declinedError();
26450
+ }
26451
+ } finally {
26452
+ rl.close();
26453
+ }
26454
+ }
26455
+ async function discloseSecret(opts, copy, declinedError, nonInteractiveError) {
26456
+ const mode = opts.mode ?? "interactive";
26457
+ const writeOpts = {
26415
26458
  storagePath: opts.storagePath,
26416
- recoveryKey: opts.recoveryKey,
26417
- ...opts.fortressId !== void 0 ? { fortressId: opts.fortressId } : {},
26418
- ...opts.now !== void 0 ? { now: opts.now } : {}
26419
- });
26420
- printRecoveryKeyBanner(
26421
- opts.recoveryKey,
26422
- fileResult.filePath,
26423
- opts.io?.output
26424
- );
26425
- {
26459
+ secret: opts.secret,
26460
+ copy
26461
+ };
26462
+ if (opts.fortressId !== void 0) writeOpts.fortressId = opts.fortressId;
26463
+ if (opts.now !== void 0) writeOpts.now = opts.now;
26464
+ const fileResult = await writeSecretFile(writeOpts);
26465
+ printSecretBanner(opts.secret, fileResult.filePath, copy, opts.io?.output);
26466
+ if (mode === "no-confirm" || mode === "stdio-server") {
26426
26467
  return {
26427
26468
  filePath: fileResult.filePath,
26428
26469
  fileWritten: fileResult.written,
26429
26470
  confirmed: false
26430
26471
  };
26431
26472
  }
26473
+ await confirmSecretSaved(copy, declinedError, nonInteractiveError, opts.io);
26474
+ return {
26475
+ filePath: fileResult.filePath,
26476
+ fileWritten: fileResult.written,
26477
+ confirmed: true
26478
+ };
26479
+ }
26480
+ async function discloseRecoveryKey(opts) {
26481
+ const internalOpts = {
26482
+ secret: opts.recoveryKey,
26483
+ storagePath: opts.storagePath
26484
+ };
26485
+ if (opts.fortressId !== void 0) internalOpts.fortressId = opts.fortressId;
26486
+ if (opts.mode !== void 0) internalOpts.mode = opts.mode;
26487
+ if (opts.now !== void 0) internalOpts.now = opts.now;
26488
+ if (opts.io !== void 0) internalOpts.io = opts.io;
26489
+ return discloseSecret(
26490
+ internalOpts,
26491
+ RECOVERY_KEY_COPY,
26492
+ RecoveryKeyConfirmationDeclinedError,
26493
+ RecoveryKeyConfirmationNonInteractiveError
26494
+ );
26432
26495
  }
26433
26496
 
26434
26497
  // src/hub/agent-registry.ts
@@ -29568,7 +29631,9 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
29568
29631
  if (recoveryKey) {
29569
29632
  await discloseRecoveryKey({
29570
29633
  recoveryKey,
29571
- storagePath: config.storage_path});
29634
+ storagePath: config.storage_path,
29635
+ mode: "stdio-server"
29636
+ });
29572
29637
  }
29573
29638
  return {
29574
29639
  server,