@sanctuary-framework/mcp-server 1.1.4 → 1.1.5

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
@@ -28583,8 +28583,69 @@ var init_hub = __esm({
28583
28583
  init_api_router();
28584
28584
  }
28585
28585
  });
28586
+ function localAgentsFilePath(storagePath) {
28587
+ return path.join(storagePath, "state", "_hub", "local-agents.json");
28588
+ }
28589
+ function readPersistedLocalAgents(storagePath) {
28590
+ const filePath = localAgentsFilePath(storagePath);
28591
+ if (!fs.existsSync(filePath)) return [];
28592
+ try {
28593
+ const raw = fs.readFileSync(filePath, "utf8");
28594
+ const parsed = JSON.parse(raw);
28595
+ if (!parsed || !Array.isArray(parsed.agents)) return [];
28596
+ return parsed.agents;
28597
+ } catch {
28598
+ return [];
28599
+ }
28600
+ }
28601
+ function writePersistedLocalAgents(storagePath, agents) {
28602
+ const filePath = localAgentsFilePath(storagePath);
28603
+ const dir = path.dirname(filePath);
28604
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
28605
+ const payload = {
28606
+ version: PERSISTED_VERSION,
28607
+ agents
28608
+ };
28609
+ const tmpPath = `${filePath}.tmp`;
28610
+ fs.writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}
28611
+ `, {
28612
+ mode: 384
28613
+ });
28614
+ fs.renameSync(tmpPath, filePath);
28615
+ fs.chmodSync(filePath, 384);
28616
+ }
28617
+ function upsertPersistedLocalAgent(storagePath, record) {
28618
+ const existing = readPersistedLocalAgents(storagePath);
28619
+ const idx = existing.findIndex((r) => r.agent_id === record.agent_id);
28620
+ let next;
28621
+ if (idx >= 0) {
28622
+ const prior = existing[idx];
28623
+ if (prior === void 0) {
28624
+ next = [...existing, record];
28625
+ } else {
28626
+ const updated = {
28627
+ ...record,
28628
+ wrapped_at: prior.wrapped_at,
28629
+ last_activity_at: (/* @__PURE__ */ new Date()).toISOString()
28630
+ };
28631
+ next = [...existing];
28632
+ next[idx] = updated;
28633
+ }
28634
+ } else {
28635
+ next = [...existing, record];
28636
+ }
28637
+ writePersistedLocalAgents(storagePath, next);
28638
+ return next;
28639
+ }
28640
+ var PERSISTED_VERSION;
28641
+ var init_agent_registry_persistence = __esm({
28642
+ "src/hub/agent-registry-persistence.ts"() {
28643
+ PERSISTED_VERSION = "1.1";
28644
+ }
28645
+ });
28586
28646
  function buildV11Bindings(inputs) {
28587
- const registry = new InMemoryLocalAgentRegistry([]);
28647
+ const seed = inputs.storagePath !== void 0 ? readPersistedLocalAgents(inputs.storagePath) : [];
28648
+ const registry = new InMemoryLocalAgentRegistry(seed);
28588
28649
  const hubService = new HubService({
28589
28650
  identityId: inputs.identityId,
28590
28651
  fortressId: inputs.fortressId,
@@ -28622,6 +28683,7 @@ var init_wiring = __esm({
28622
28683
  "src/dashboard/v1_1/wiring.ts"() {
28623
28684
  init_hub();
28624
28685
  init_errors4();
28686
+ init_agent_registry_persistence();
28625
28687
  CapabilityErrorAgentController = class {
28626
28688
  fail(action) {
28627
28689
  throw new HubCapabilityError(
@@ -30654,7 +30716,12 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
30654
30716
  buildV11Bindings({
30655
30717
  identityId: embeddedHubIdentityId,
30656
30718
  fortressId: fortressIdFromStoragePath(config.storage_path),
30657
- auditLog
30719
+ auditLog,
30720
+ // v1.1.5 (Finding Z): rehydrate the hub agent registry from
30721
+ // `<storagePath>/state/_hub/local-agents.json` so the embedded
30722
+ // dashboard surfaces wraps performed by prior `sanctuary wrap`
30723
+ // invocations against this same fortress.
30724
+ storagePath: config.storage_path
30658
30725
  })
30659
30726
  );
30660
30727
  await dashboard.start();
@@ -31297,6 +31364,7 @@ __export(cli_exports, {
31297
31364
  PORT_FALLBACK_ATTEMPTS: () => PORT_FALLBACK_ATTEMPTS,
31298
31365
  formatMcpServerCount: () => formatMcpServerCount,
31299
31366
  formatWrapSuccess: () => formatWrapSuccess,
31367
+ formatWrapSuccessNoDashboard: () => formatWrapSuccessNoDashboard,
31300
31368
  parseCocoonArgs: () => parseCocoonArgs,
31301
31369
  parseWrapArgs: () => parseWrapArgs,
31302
31370
  promoteFortressToStoragePath: () => promoteFortressToStoragePath,
@@ -31569,6 +31637,28 @@ async function runWrap(options, deps = {}) {
31569
31637
  backupPath
31570
31638
  );
31571
31639
  if (!verifyOk) process.exit(1);
31640
+ try {
31641
+ upsertPersistedLocalAgent(
31642
+ storagePath,
31643
+ buildLocalAgentRecord({
31644
+ storagePath,
31645
+ platform: agentConfig.platform
31646
+ })
31647
+ );
31648
+ } catch (err) {
31649
+ console.error(
31650
+ ` Note: v1.1 hub agent record not persisted (${err.message}). Re-run \`sanctuary wrap\` to retry, or check storage permissions on ${storagePath}.`
31651
+ );
31652
+ }
31653
+ if (options.noDashboard) {
31654
+ const toolName2 = toolNameFor(agentConfig.platform, agentConfig.servers);
31655
+ printWrapSuccessNoDashboard({
31656
+ toolName: toolName2,
31657
+ version: readPackageVersion(),
31658
+ toolCount: countUpstreamTools(upstreamServers),
31659
+ serverCount: upstreamServers.length});
31660
+ return;
31661
+ }
31572
31662
  const authToken = generateAuthToken();
31573
31663
  const startFn = deps.startDashboard ?? ((opts) => startDashboard({
31574
31664
  port: opts.port,
@@ -31608,7 +31698,12 @@ async function runWrap(options, deps = {}) {
31608
31698
  buildV11Bindings({
31609
31699
  identityId: `fortress:${storagePath}`,
31610
31700
  fortressId: fortressIdFromStoragePath(storagePath),
31611
- auditLog: wrapAuditLog
31701
+ auditLog: wrapAuditLog,
31702
+ // v1.1.5 (Finding Z): rehydrate from the file the upsert
31703
+ // above just wrote, so the registry the wrap-auto dashboard
31704
+ // serves contains this wrap plus any prior wraps against the
31705
+ // same fortress.
31706
+ storagePath
31612
31707
  })
31613
31708
  );
31614
31709
  dashboard.setV11LoopbackAutoAuth(true);
@@ -31748,6 +31843,32 @@ function formatWrapSuccess(info) {
31748
31843
  function printWrapSuccess(info) {
31749
31844
  console.error(formatWrapSuccess(info));
31750
31845
  }
31846
+ function formatWrapSuccessNoDashboard(info) {
31847
+ const g = (s) => `\x1B[32m${s}\x1B[0m`;
31848
+ const d = (s) => `\x1B[2m${s}\x1B[0m`;
31849
+ const b = (s) => `\x1B[1m${s}\x1B[0m`;
31850
+ const check = "\u2713";
31851
+ const lines = [];
31852
+ lines.push("");
31853
+ lines.push(
31854
+ ` ${g(check)} Wrapped ${b(info.toolName)} with Sanctuary v${info.version}`
31855
+ );
31856
+ lines.push(
31857
+ ` ${g(check)} ${info.toolCount} tools registered across ${info.serverCount} upstream server${info.serverCount !== 1 ? "s" : ""}`
31858
+ );
31859
+ lines.push(
31860
+ ` ${d("Dashboard spawn skipped per --no-dashboard. Run `sanctuary dashboard` separately for a persistent dashboard.")}`
31861
+ );
31862
+ lines.push("");
31863
+ lines.push(
31864
+ ` ${b("Your agent is protected.")} L1 Full / L2 Degraded (no TEE) / L3 Full / L4 Full.`
31865
+ );
31866
+ lines.push("");
31867
+ return lines.join("\n");
31868
+ }
31869
+ function printWrapSuccessNoDashboard(info) {
31870
+ console.error(formatWrapSuccessNoDashboard(info));
31871
+ }
31751
31872
  async function verifyRewrittenConfig(configPath, backupPath) {
31752
31873
  try {
31753
31874
  const raw = await promises.readFile(configPath, "utf-8");
@@ -31864,6 +31985,57 @@ function toolNameFor(platform4, _servers) {
31864
31985
  return "your agent";
31865
31986
  }
31866
31987
  }
31988
+ function harnessKindForPlatform(platform4) {
31989
+ switch (platform4) {
31990
+ case "openclaw":
31991
+ return "openclaw";
31992
+ case "hermes":
31993
+ return "hermes";
31994
+ case "claude-code":
31995
+ return "claude_code";
31996
+ case "cursor":
31997
+ return "cursor";
31998
+ case "cline":
31999
+ return "cline";
32000
+ case "generic":
32001
+ return "generic_mcp";
32002
+ default: {
32003
+ return "other";
32004
+ }
32005
+ }
32006
+ }
32007
+ function buildLocalAgentRecord(input) {
32008
+ const harness = harnessKindForPlatform(input.platform);
32009
+ const fortressId = fortressIdFromStoragePath(input.storagePath);
32010
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
32011
+ return {
32012
+ version: "1.1",
32013
+ agent_id: `agent:${harness}:${fortressId}`,
32014
+ identity_id: `fortress:${input.storagePath}`,
32015
+ harness,
32016
+ model_provider: {
32017
+ vendor: "unknown",
32018
+ model_id: "unknown",
32019
+ runs_locally: false
32020
+ },
32021
+ policy_id: "unbound",
32022
+ status: "active",
32023
+ budget_summary: {
32024
+ last_refreshed_at: nowIso
32025
+ },
32026
+ last_activity_at: nowIso,
32027
+ wrapped_at: nowIso,
32028
+ capabilities: {
32029
+ can_pause: false,
32030
+ can_resume: false,
32031
+ can_restart: false,
32032
+ can_unwrap: true,
32033
+ can_lockdown: false,
32034
+ can_chat: false,
32035
+ can_change_template: false
32036
+ }
32037
+ };
32038
+ }
31867
32039
  function countUpstreamTools(servers) {
31868
32040
  return servers.length === 0 ? 0 : servers.length;
31869
32041
  }
@@ -31924,6 +32096,9 @@ function parseWrapArgs(argv) {
31924
32096
  case "--no-open":
31925
32097
  options.noOpen = true;
31926
32098
  break;
32099
+ case "--no-dashboard":
32100
+ options.noDashboard = true;
32101
+ break;
31927
32102
  case "--fortress":
31928
32103
  options.fortress = argv[++i];
31929
32104
  break;
@@ -31964,6 +32139,11 @@ function printWrapHelp() {
31964
32139
  --port <port> Preferred dashboard port (default: 3501)
31965
32140
  --dry-run Show what would happen without making changes
31966
32141
  --no-open Do not auto-open the dashboard in a browser
32142
+ --no-dashboard Do not spawn a per-call dashboard server. Wrap still
32143
+ persists the agent record so a separately-running
32144
+ \`sanctuary dashboard\` (or a later wrap) sees the
32145
+ harness. Use this for the clean operator setup
32146
+ (one persistent dashboard + many wraps).
31967
32147
  --help, -h Show this help
31968
32148
 
31969
32149
  What happens:
@@ -31981,6 +32161,7 @@ var init_cli2 = __esm({
31981
32161
  init_passphrase();
31982
32162
  init_dashboard2();
31983
32163
  init_wiring();
32164
+ init_agent_registry_persistence();
31984
32165
  init_filesystem();
31985
32166
  init_key_derivation();
31986
32167
  init_encoding();
@@ -35618,7 +35799,12 @@ Refusing to start the dashboard while the reset-history marker is unreadable.`
35618
35799
  buildV11Bindings({
35619
35800
  identityId: hubIdentityId,
35620
35801
  fortressId: fortressIdFromStoragePath(config.storage_path),
35621
- auditLog
35802
+ auditLog,
35803
+ // v1.1.5 (Finding Z): rehydrate the hub agent registry from
35804
+ // `<storagePath>/state/_hub/local-agents.json` so the standalone
35805
+ // dashboard surfaces wraps performed by prior `sanctuary wrap`
35806
+ // invocations against this same fortress.
35807
+ storagePath: config.storage_path
35622
35808
  })
35623
35809
  );
35624
35810
  const hostIsLoopback = dashboardHost === "127.0.0.1" || dashboardHost === "::1" || dashboardHost === "localhost";
@@ -35648,6 +35834,10 @@ Refusing to start the dashboard while the reset-history marker is unreadable.`
35648
35834
  console.error(`Sanctuary Dashboard v${SANCTUARY_VERSION} (standalone mode)`);
35649
35835
  console.error(`Storage: ${config.storage_path}`);
35650
35836
  console.error(`Identities loaded: ${loadResult.loaded}`);
35837
+ const persistedAgentsCount = readPersistedLocalAgents(
35838
+ config.storage_path
35839
+ ).length;
35840
+ console.error(`Local agents loaded: ${persistedAgentsCount}`);
35651
35841
  console.error(`Listening: http://${dashboardHost}:${dashboardPort}`);
35652
35842
  if (loadResult.total > 0 && loadResult.loaded === 0) {
35653
35843
  const service = keychainServiceFor(config.storage_path, os.homedir());
@@ -35706,6 +35896,7 @@ var init_dashboard_standalone = __esm({
35706
35896
  init_recovery_key_disclosure();
35707
35897
  init_discovery();
35708
35898
  init_wiring();
35899
+ init_agent_registry_persistence();
35709
35900
  }
35710
35901
  });
35711
35902