@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.js CHANGED
@@ -12,7 +12,7 @@ import { argon2id } from 'hash-wasm';
12
12
  import { hkdf } from '@noble/hashes/hkdf';
13
13
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
14
14
  import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
15
- import { statSync, existsSync, readFileSync, mkdirSync } from 'fs';
15
+ import { existsSync, readFileSync, statSync, mkdirSync, writeFileSync, renameSync, chmodSync } from 'fs';
16
16
  import { fileURLToPath } from 'url';
17
17
  import { exec, execSync, spawn } from 'child_process';
18
18
  import { createServer as createServer$2, get as get$1 } from 'http';
@@ -28580,8 +28580,69 @@ var init_hub = __esm({
28580
28580
  init_api_router();
28581
28581
  }
28582
28582
  });
28583
+ function localAgentsFilePath(storagePath) {
28584
+ return join(storagePath, "state", "_hub", "local-agents.json");
28585
+ }
28586
+ function readPersistedLocalAgents(storagePath) {
28587
+ const filePath = localAgentsFilePath(storagePath);
28588
+ if (!existsSync(filePath)) return [];
28589
+ try {
28590
+ const raw = readFileSync(filePath, "utf8");
28591
+ const parsed = JSON.parse(raw);
28592
+ if (!parsed || !Array.isArray(parsed.agents)) return [];
28593
+ return parsed.agents;
28594
+ } catch {
28595
+ return [];
28596
+ }
28597
+ }
28598
+ function writePersistedLocalAgents(storagePath, agents) {
28599
+ const filePath = localAgentsFilePath(storagePath);
28600
+ const dir = dirname(filePath);
28601
+ mkdirSync(dir, { recursive: true, mode: 448 });
28602
+ const payload = {
28603
+ version: PERSISTED_VERSION,
28604
+ agents
28605
+ };
28606
+ const tmpPath = `${filePath}.tmp`;
28607
+ writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}
28608
+ `, {
28609
+ mode: 384
28610
+ });
28611
+ renameSync(tmpPath, filePath);
28612
+ chmodSync(filePath, 384);
28613
+ }
28614
+ function upsertPersistedLocalAgent(storagePath, record) {
28615
+ const existing = readPersistedLocalAgents(storagePath);
28616
+ const idx = existing.findIndex((r) => r.agent_id === record.agent_id);
28617
+ let next;
28618
+ if (idx >= 0) {
28619
+ const prior = existing[idx];
28620
+ if (prior === void 0) {
28621
+ next = [...existing, record];
28622
+ } else {
28623
+ const updated = {
28624
+ ...record,
28625
+ wrapped_at: prior.wrapped_at,
28626
+ last_activity_at: (/* @__PURE__ */ new Date()).toISOString()
28627
+ };
28628
+ next = [...existing];
28629
+ next[idx] = updated;
28630
+ }
28631
+ } else {
28632
+ next = [...existing, record];
28633
+ }
28634
+ writePersistedLocalAgents(storagePath, next);
28635
+ return next;
28636
+ }
28637
+ var PERSISTED_VERSION;
28638
+ var init_agent_registry_persistence = __esm({
28639
+ "src/hub/agent-registry-persistence.ts"() {
28640
+ PERSISTED_VERSION = "1.1";
28641
+ }
28642
+ });
28583
28643
  function buildV11Bindings(inputs) {
28584
- const registry = new InMemoryLocalAgentRegistry([]);
28644
+ const seed = inputs.storagePath !== void 0 ? readPersistedLocalAgents(inputs.storagePath) : [];
28645
+ const registry = new InMemoryLocalAgentRegistry(seed);
28585
28646
  const hubService = new HubService({
28586
28647
  identityId: inputs.identityId,
28587
28648
  fortressId: inputs.fortressId,
@@ -28619,6 +28680,7 @@ var init_wiring = __esm({
28619
28680
  "src/dashboard/v1_1/wiring.ts"() {
28620
28681
  init_hub();
28621
28682
  init_errors4();
28683
+ init_agent_registry_persistence();
28622
28684
  CapabilityErrorAgentController = class {
28623
28685
  fail(action) {
28624
28686
  throw new HubCapabilityError(
@@ -30651,7 +30713,12 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
30651
30713
  buildV11Bindings({
30652
30714
  identityId: embeddedHubIdentityId,
30653
30715
  fortressId: fortressIdFromStoragePath(config.storage_path),
30654
- auditLog
30716
+ auditLog,
30717
+ // v1.1.5 (Finding Z): rehydrate the hub agent registry from
30718
+ // `<storagePath>/state/_hub/local-agents.json` so the embedded
30719
+ // dashboard surfaces wraps performed by prior `sanctuary wrap`
30720
+ // invocations against this same fortress.
30721
+ storagePath: config.storage_path
30655
30722
  })
30656
30723
  );
30657
30724
  await dashboard.start();
@@ -31294,6 +31361,7 @@ __export(cli_exports, {
31294
31361
  PORT_FALLBACK_ATTEMPTS: () => PORT_FALLBACK_ATTEMPTS,
31295
31362
  formatMcpServerCount: () => formatMcpServerCount,
31296
31363
  formatWrapSuccess: () => formatWrapSuccess,
31364
+ formatWrapSuccessNoDashboard: () => formatWrapSuccessNoDashboard,
31297
31365
  parseCocoonArgs: () => parseCocoonArgs,
31298
31366
  parseWrapArgs: () => parseWrapArgs,
31299
31367
  promoteFortressToStoragePath: () => promoteFortressToStoragePath,
@@ -31566,6 +31634,28 @@ async function runWrap(options, deps = {}) {
31566
31634
  backupPath
31567
31635
  );
31568
31636
  if (!verifyOk) process.exit(1);
31637
+ try {
31638
+ upsertPersistedLocalAgent(
31639
+ storagePath,
31640
+ buildLocalAgentRecord({
31641
+ storagePath,
31642
+ platform: agentConfig.platform
31643
+ })
31644
+ );
31645
+ } catch (err) {
31646
+ console.error(
31647
+ ` Note: v1.1 hub agent record not persisted (${err.message}). Re-run \`sanctuary wrap\` to retry, or check storage permissions on ${storagePath}.`
31648
+ );
31649
+ }
31650
+ if (options.noDashboard) {
31651
+ const toolName2 = toolNameFor(agentConfig.platform, agentConfig.servers);
31652
+ printWrapSuccessNoDashboard({
31653
+ toolName: toolName2,
31654
+ version: readPackageVersion(),
31655
+ toolCount: countUpstreamTools(upstreamServers),
31656
+ serverCount: upstreamServers.length});
31657
+ return;
31658
+ }
31569
31659
  const authToken = generateAuthToken();
31570
31660
  const startFn = deps.startDashboard ?? ((opts) => startDashboard({
31571
31661
  port: opts.port,
@@ -31605,7 +31695,12 @@ async function runWrap(options, deps = {}) {
31605
31695
  buildV11Bindings({
31606
31696
  identityId: `fortress:${storagePath}`,
31607
31697
  fortressId: fortressIdFromStoragePath(storagePath),
31608
- auditLog: wrapAuditLog
31698
+ auditLog: wrapAuditLog,
31699
+ // v1.1.5 (Finding Z): rehydrate from the file the upsert
31700
+ // above just wrote, so the registry the wrap-auto dashboard
31701
+ // serves contains this wrap plus any prior wraps against the
31702
+ // same fortress.
31703
+ storagePath
31609
31704
  })
31610
31705
  );
31611
31706
  dashboard.setV11LoopbackAutoAuth(true);
@@ -31745,6 +31840,32 @@ function formatWrapSuccess(info) {
31745
31840
  function printWrapSuccess(info) {
31746
31841
  console.error(formatWrapSuccess(info));
31747
31842
  }
31843
+ function formatWrapSuccessNoDashboard(info) {
31844
+ const g = (s) => `\x1B[32m${s}\x1B[0m`;
31845
+ const d = (s) => `\x1B[2m${s}\x1B[0m`;
31846
+ const b = (s) => `\x1B[1m${s}\x1B[0m`;
31847
+ const check = "\u2713";
31848
+ const lines = [];
31849
+ lines.push("");
31850
+ lines.push(
31851
+ ` ${g(check)} Wrapped ${b(info.toolName)} with Sanctuary v${info.version}`
31852
+ );
31853
+ lines.push(
31854
+ ` ${g(check)} ${info.toolCount} tools registered across ${info.serverCount} upstream server${info.serverCount !== 1 ? "s" : ""}`
31855
+ );
31856
+ lines.push(
31857
+ ` ${d("Dashboard spawn skipped per --no-dashboard. Run `sanctuary dashboard` separately for a persistent dashboard.")}`
31858
+ );
31859
+ lines.push("");
31860
+ lines.push(
31861
+ ` ${b("Your agent is protected.")} L1 Full / L2 Degraded (no TEE) / L3 Full / L4 Full.`
31862
+ );
31863
+ lines.push("");
31864
+ return lines.join("\n");
31865
+ }
31866
+ function printWrapSuccessNoDashboard(info) {
31867
+ console.error(formatWrapSuccessNoDashboard(info));
31868
+ }
31748
31869
  async function verifyRewrittenConfig(configPath, backupPath) {
31749
31870
  try {
31750
31871
  const raw = await readFile(configPath, "utf-8");
@@ -31861,6 +31982,57 @@ function toolNameFor(platform4, _servers) {
31861
31982
  return "your agent";
31862
31983
  }
31863
31984
  }
31985
+ function harnessKindForPlatform(platform4) {
31986
+ switch (platform4) {
31987
+ case "openclaw":
31988
+ return "openclaw";
31989
+ case "hermes":
31990
+ return "hermes";
31991
+ case "claude-code":
31992
+ return "claude_code";
31993
+ case "cursor":
31994
+ return "cursor";
31995
+ case "cline":
31996
+ return "cline";
31997
+ case "generic":
31998
+ return "generic_mcp";
31999
+ default: {
32000
+ return "other";
32001
+ }
32002
+ }
32003
+ }
32004
+ function buildLocalAgentRecord(input) {
32005
+ const harness = harnessKindForPlatform(input.platform);
32006
+ const fortressId = fortressIdFromStoragePath(input.storagePath);
32007
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
32008
+ return {
32009
+ version: "1.1",
32010
+ agent_id: `agent:${harness}:${fortressId}`,
32011
+ identity_id: `fortress:${input.storagePath}`,
32012
+ harness,
32013
+ model_provider: {
32014
+ vendor: "unknown",
32015
+ model_id: "unknown",
32016
+ runs_locally: false
32017
+ },
32018
+ policy_id: "unbound",
32019
+ status: "active",
32020
+ budget_summary: {
32021
+ last_refreshed_at: nowIso
32022
+ },
32023
+ last_activity_at: nowIso,
32024
+ wrapped_at: nowIso,
32025
+ capabilities: {
32026
+ can_pause: false,
32027
+ can_resume: false,
32028
+ can_restart: false,
32029
+ can_unwrap: true,
32030
+ can_lockdown: false,
32031
+ can_chat: false,
32032
+ can_change_template: false
32033
+ }
32034
+ };
32035
+ }
31864
32036
  function countUpstreamTools(servers) {
31865
32037
  return servers.length === 0 ? 0 : servers.length;
31866
32038
  }
@@ -31921,6 +32093,9 @@ function parseWrapArgs(argv) {
31921
32093
  case "--no-open":
31922
32094
  options.noOpen = true;
31923
32095
  break;
32096
+ case "--no-dashboard":
32097
+ options.noDashboard = true;
32098
+ break;
31924
32099
  case "--fortress":
31925
32100
  options.fortress = argv[++i];
31926
32101
  break;
@@ -31961,6 +32136,11 @@ function printWrapHelp() {
31961
32136
  --port <port> Preferred dashboard port (default: 3501)
31962
32137
  --dry-run Show what would happen without making changes
31963
32138
  --no-open Do not auto-open the dashboard in a browser
32139
+ --no-dashboard Do not spawn a per-call dashboard server. Wrap still
32140
+ persists the agent record so a separately-running
32141
+ \`sanctuary dashboard\` (or a later wrap) sees the
32142
+ harness. Use this for the clean operator setup
32143
+ (one persistent dashboard + many wraps).
31964
32144
  --help, -h Show this help
31965
32145
 
31966
32146
  What happens:
@@ -31978,6 +32158,7 @@ var init_cli2 = __esm({
31978
32158
  init_passphrase();
31979
32159
  init_dashboard2();
31980
32160
  init_wiring();
32161
+ init_agent_registry_persistence();
31981
32162
  init_filesystem();
31982
32163
  init_key_derivation();
31983
32164
  init_encoding();
@@ -35615,7 +35796,12 @@ Refusing to start the dashboard while the reset-history marker is unreadable.`
35615
35796
  buildV11Bindings({
35616
35797
  identityId: hubIdentityId,
35617
35798
  fortressId: fortressIdFromStoragePath(config.storage_path),
35618
- auditLog
35799
+ auditLog,
35800
+ // v1.1.5 (Finding Z): rehydrate the hub agent registry from
35801
+ // `<storagePath>/state/_hub/local-agents.json` so the standalone
35802
+ // dashboard surfaces wraps performed by prior `sanctuary wrap`
35803
+ // invocations against this same fortress.
35804
+ storagePath: config.storage_path
35619
35805
  })
35620
35806
  );
35621
35807
  const hostIsLoopback = dashboardHost === "127.0.0.1" || dashboardHost === "::1" || dashboardHost === "localhost";
@@ -35645,6 +35831,10 @@ Refusing to start the dashboard while the reset-history marker is unreadable.`
35645
35831
  console.error(`Sanctuary Dashboard v${SANCTUARY_VERSION} (standalone mode)`);
35646
35832
  console.error(`Storage: ${config.storage_path}`);
35647
35833
  console.error(`Identities loaded: ${loadResult.loaded}`);
35834
+ const persistedAgentsCount = readPersistedLocalAgents(
35835
+ config.storage_path
35836
+ ).length;
35837
+ console.error(`Local agents loaded: ${persistedAgentsCount}`);
35648
35838
  console.error(`Listening: http://${dashboardHost}:${dashboardPort}`);
35649
35839
  if (loadResult.total > 0 && loadResult.loaded === 0) {
35650
35840
  const service = keychainServiceFor(config.storage_path, homedir());
@@ -35703,6 +35893,7 @@ var init_dashboard_standalone = __esm({
35703
35893
  init_recovery_key_disclosure();
35704
35894
  init_discovery();
35705
35895
  init_wiring();
35896
+ init_agent_registry_persistence();
35706
35897
  }
35707
35898
  });
35708
35899