@sanctuary-framework/mcp-server 1.1.4 → 1.1.6
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 +206 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +207 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +34 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.js +35 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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 {
|
|
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';
|
|
@@ -28136,6 +28136,13 @@ var init_hub_service = __esm({
|
|
|
28136
28136
|
nowIso() {
|
|
28137
28137
|
return this.now().toISOString();
|
|
28138
28138
|
}
|
|
28139
|
+
refreshPersistedLocalAgents() {
|
|
28140
|
+
const readPersistedLocalAgents2 = this.deps.readPersistedLocalAgents;
|
|
28141
|
+
if (!readPersistedLocalAgents2) return;
|
|
28142
|
+
for (const record of readPersistedLocalAgents2()) {
|
|
28143
|
+
this.deps.agentRegistry.put(record);
|
|
28144
|
+
}
|
|
28145
|
+
}
|
|
28139
28146
|
// ── Inbox ───────────────────────────────────────────────────────────
|
|
28140
28147
|
listInbox() {
|
|
28141
28148
|
const items = aggregateInbox(this.deps.inboxSources, this.inboxStore);
|
|
@@ -28147,6 +28154,7 @@ var init_hub_service = __esm({
|
|
|
28147
28154
|
}
|
|
28148
28155
|
// ── Agents ──────────────────────────────────────────────────────────
|
|
28149
28156
|
listAgents(filter) {
|
|
28157
|
+
this.refreshPersistedLocalAgents();
|
|
28150
28158
|
const safeFilter = {
|
|
28151
28159
|
...filter ?? {},
|
|
28152
28160
|
identity_id: this.deps.identityId
|
|
@@ -28580,12 +28588,76 @@ var init_hub = __esm({
|
|
|
28580
28588
|
init_api_router();
|
|
28581
28589
|
}
|
|
28582
28590
|
});
|
|
28591
|
+
function localAgentsFilePath(storagePath) {
|
|
28592
|
+
return join(storagePath, "state", "_hub", "local-agents.json");
|
|
28593
|
+
}
|
|
28594
|
+
function readPersistedLocalAgents(storagePath) {
|
|
28595
|
+
const filePath = localAgentsFilePath(storagePath);
|
|
28596
|
+
if (!existsSync(filePath)) return [];
|
|
28597
|
+
try {
|
|
28598
|
+
const raw = readFileSync(filePath, "utf8");
|
|
28599
|
+
const parsed = JSON.parse(raw);
|
|
28600
|
+
if (!parsed || !Array.isArray(parsed.agents)) return [];
|
|
28601
|
+
return parsed.agents;
|
|
28602
|
+
} catch {
|
|
28603
|
+
return [];
|
|
28604
|
+
}
|
|
28605
|
+
}
|
|
28606
|
+
function writePersistedLocalAgents(storagePath, agents) {
|
|
28607
|
+
const filePath = localAgentsFilePath(storagePath);
|
|
28608
|
+
const dir = dirname(filePath);
|
|
28609
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
28610
|
+
const payload = {
|
|
28611
|
+
version: PERSISTED_VERSION,
|
|
28612
|
+
agents
|
|
28613
|
+
};
|
|
28614
|
+
const tmpPath = `${filePath}.tmp`;
|
|
28615
|
+
writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}
|
|
28616
|
+
`, {
|
|
28617
|
+
mode: 384
|
|
28618
|
+
});
|
|
28619
|
+
renameSync(tmpPath, filePath);
|
|
28620
|
+
chmodSync(filePath, 384);
|
|
28621
|
+
}
|
|
28622
|
+
function upsertPersistedLocalAgent(storagePath, record) {
|
|
28623
|
+
const existing = readPersistedLocalAgents(storagePath);
|
|
28624
|
+
const idx = existing.findIndex((r) => r.agent_id === record.agent_id);
|
|
28625
|
+
let next;
|
|
28626
|
+
if (idx >= 0) {
|
|
28627
|
+
const prior = existing[idx];
|
|
28628
|
+
if (prior === void 0) {
|
|
28629
|
+
next = [...existing, record];
|
|
28630
|
+
} else {
|
|
28631
|
+
const updated = {
|
|
28632
|
+
...record,
|
|
28633
|
+
wrapped_at: prior.wrapped_at,
|
|
28634
|
+
last_activity_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
28635
|
+
};
|
|
28636
|
+
next = [...existing];
|
|
28637
|
+
next[idx] = updated;
|
|
28638
|
+
}
|
|
28639
|
+
} else {
|
|
28640
|
+
next = [...existing, record];
|
|
28641
|
+
}
|
|
28642
|
+
writePersistedLocalAgents(storagePath, next);
|
|
28643
|
+
return next;
|
|
28644
|
+
}
|
|
28645
|
+
var PERSISTED_VERSION;
|
|
28646
|
+
var init_agent_registry_persistence = __esm({
|
|
28647
|
+
"src/hub/agent-registry-persistence.ts"() {
|
|
28648
|
+
PERSISTED_VERSION = "1.1";
|
|
28649
|
+
}
|
|
28650
|
+
});
|
|
28583
28651
|
function buildV11Bindings(inputs) {
|
|
28584
|
-
const
|
|
28652
|
+
const seed = inputs.storagePath !== void 0 ? readPersistedLocalAgents(inputs.storagePath) : [];
|
|
28653
|
+
const registry = new InMemoryLocalAgentRegistry(seed);
|
|
28654
|
+
const storagePath = inputs.storagePath;
|
|
28655
|
+
const readPersisted = storagePath !== void 0 ? () => readPersistedLocalAgents(storagePath) : void 0;
|
|
28585
28656
|
const hubService = new HubService({
|
|
28586
28657
|
identityId: inputs.identityId,
|
|
28587
28658
|
fortressId: inputs.fortressId,
|
|
28588
28659
|
agentRegistry: registry,
|
|
28660
|
+
...readPersisted ? { readPersistedLocalAgents: readPersisted } : {},
|
|
28589
28661
|
inboxSources: {
|
|
28590
28662
|
listPendingApprovals: () => [],
|
|
28591
28663
|
listRecentBlockedEgress: () => [],
|
|
@@ -28619,6 +28691,7 @@ var init_wiring = __esm({
|
|
|
28619
28691
|
"src/dashboard/v1_1/wiring.ts"() {
|
|
28620
28692
|
init_hub();
|
|
28621
28693
|
init_errors4();
|
|
28694
|
+
init_agent_registry_persistence();
|
|
28622
28695
|
CapabilityErrorAgentController = class {
|
|
28623
28696
|
fail(action) {
|
|
28624
28697
|
throw new HubCapabilityError(
|
|
@@ -30651,7 +30724,12 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
|
|
|
30651
30724
|
buildV11Bindings({
|
|
30652
30725
|
identityId: embeddedHubIdentityId,
|
|
30653
30726
|
fortressId: fortressIdFromStoragePath(config.storage_path),
|
|
30654
|
-
auditLog
|
|
30727
|
+
auditLog,
|
|
30728
|
+
// v1.1.5 (Finding Z): rehydrate the hub agent registry from
|
|
30729
|
+
// `<storagePath>/state/_hub/local-agents.json` so the embedded
|
|
30730
|
+
// dashboard surfaces wraps performed by prior `sanctuary wrap`
|
|
30731
|
+
// invocations against this same fortress.
|
|
30732
|
+
storagePath: config.storage_path
|
|
30655
30733
|
})
|
|
30656
30734
|
);
|
|
30657
30735
|
await dashboard.start();
|
|
@@ -31294,6 +31372,7 @@ __export(cli_exports, {
|
|
|
31294
31372
|
PORT_FALLBACK_ATTEMPTS: () => PORT_FALLBACK_ATTEMPTS,
|
|
31295
31373
|
formatMcpServerCount: () => formatMcpServerCount,
|
|
31296
31374
|
formatWrapSuccess: () => formatWrapSuccess,
|
|
31375
|
+
formatWrapSuccessNoDashboard: () => formatWrapSuccessNoDashboard,
|
|
31297
31376
|
parseCocoonArgs: () => parseCocoonArgs,
|
|
31298
31377
|
parseWrapArgs: () => parseWrapArgs,
|
|
31299
31378
|
promoteFortressToStoragePath: () => promoteFortressToStoragePath,
|
|
@@ -31566,6 +31645,28 @@ async function runWrap(options, deps = {}) {
|
|
|
31566
31645
|
backupPath
|
|
31567
31646
|
);
|
|
31568
31647
|
if (!verifyOk) process.exit(1);
|
|
31648
|
+
try {
|
|
31649
|
+
upsertPersistedLocalAgent(
|
|
31650
|
+
storagePath,
|
|
31651
|
+
buildLocalAgentRecord({
|
|
31652
|
+
storagePath,
|
|
31653
|
+
platform: agentConfig.platform
|
|
31654
|
+
})
|
|
31655
|
+
);
|
|
31656
|
+
} catch (err) {
|
|
31657
|
+
console.error(
|
|
31658
|
+
` Note: v1.1 hub agent record not persisted (${err.message}). Re-run \`sanctuary wrap\` to retry, or check storage permissions on ${storagePath}.`
|
|
31659
|
+
);
|
|
31660
|
+
}
|
|
31661
|
+
if (options.noDashboard) {
|
|
31662
|
+
const toolName2 = toolNameFor(agentConfig.platform, agentConfig.servers);
|
|
31663
|
+
printWrapSuccessNoDashboard({
|
|
31664
|
+
toolName: toolName2,
|
|
31665
|
+
version: readPackageVersion(),
|
|
31666
|
+
toolCount: countUpstreamTools(upstreamServers),
|
|
31667
|
+
serverCount: upstreamServers.length});
|
|
31668
|
+
return;
|
|
31669
|
+
}
|
|
31569
31670
|
const authToken = generateAuthToken();
|
|
31570
31671
|
const startFn = deps.startDashboard ?? ((opts) => startDashboard({
|
|
31571
31672
|
port: opts.port,
|
|
@@ -31605,7 +31706,12 @@ async function runWrap(options, deps = {}) {
|
|
|
31605
31706
|
buildV11Bindings({
|
|
31606
31707
|
identityId: `fortress:${storagePath}`,
|
|
31607
31708
|
fortressId: fortressIdFromStoragePath(storagePath),
|
|
31608
|
-
auditLog: wrapAuditLog
|
|
31709
|
+
auditLog: wrapAuditLog,
|
|
31710
|
+
// v1.1.5 (Finding Z): rehydrate from the file the upsert
|
|
31711
|
+
// above just wrote, so the registry the wrap-auto dashboard
|
|
31712
|
+
// serves contains this wrap plus any prior wraps against the
|
|
31713
|
+
// same fortress.
|
|
31714
|
+
storagePath
|
|
31609
31715
|
})
|
|
31610
31716
|
);
|
|
31611
31717
|
dashboard.setV11LoopbackAutoAuth(true);
|
|
@@ -31745,6 +31851,32 @@ function formatWrapSuccess(info) {
|
|
|
31745
31851
|
function printWrapSuccess(info) {
|
|
31746
31852
|
console.error(formatWrapSuccess(info));
|
|
31747
31853
|
}
|
|
31854
|
+
function formatWrapSuccessNoDashboard(info) {
|
|
31855
|
+
const g = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
31856
|
+
const d = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
31857
|
+
const b = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
31858
|
+
const check = "\u2713";
|
|
31859
|
+
const lines = [];
|
|
31860
|
+
lines.push("");
|
|
31861
|
+
lines.push(
|
|
31862
|
+
` ${g(check)} Wrapped ${b(info.toolName)} with Sanctuary v${info.version}`
|
|
31863
|
+
);
|
|
31864
|
+
lines.push(
|
|
31865
|
+
` ${g(check)} ${info.toolCount} tools registered across ${info.serverCount} upstream server${info.serverCount !== 1 ? "s" : ""}`
|
|
31866
|
+
);
|
|
31867
|
+
lines.push(
|
|
31868
|
+
` ${d("Dashboard spawn skipped per --no-dashboard. Run `sanctuary dashboard` separately for a persistent dashboard.")}`
|
|
31869
|
+
);
|
|
31870
|
+
lines.push("");
|
|
31871
|
+
lines.push(
|
|
31872
|
+
` ${b("Your agent is protected.")} L1 Full / L2 Degraded (no TEE) / L3 Full / L4 Full.`
|
|
31873
|
+
);
|
|
31874
|
+
lines.push("");
|
|
31875
|
+
return lines.join("\n");
|
|
31876
|
+
}
|
|
31877
|
+
function printWrapSuccessNoDashboard(info) {
|
|
31878
|
+
console.error(formatWrapSuccessNoDashboard(info));
|
|
31879
|
+
}
|
|
31748
31880
|
async function verifyRewrittenConfig(configPath, backupPath) {
|
|
31749
31881
|
try {
|
|
31750
31882
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -31861,6 +31993,57 @@ function toolNameFor(platform4, _servers) {
|
|
|
31861
31993
|
return "your agent";
|
|
31862
31994
|
}
|
|
31863
31995
|
}
|
|
31996
|
+
function harnessKindForPlatform(platform4) {
|
|
31997
|
+
switch (platform4) {
|
|
31998
|
+
case "openclaw":
|
|
31999
|
+
return "openclaw";
|
|
32000
|
+
case "hermes":
|
|
32001
|
+
return "hermes";
|
|
32002
|
+
case "claude-code":
|
|
32003
|
+
return "claude_code";
|
|
32004
|
+
case "cursor":
|
|
32005
|
+
return "cursor";
|
|
32006
|
+
case "cline":
|
|
32007
|
+
return "cline";
|
|
32008
|
+
case "generic":
|
|
32009
|
+
return "generic_mcp";
|
|
32010
|
+
default: {
|
|
32011
|
+
return "other";
|
|
32012
|
+
}
|
|
32013
|
+
}
|
|
32014
|
+
}
|
|
32015
|
+
function buildLocalAgentRecord(input) {
|
|
32016
|
+
const harness = harnessKindForPlatform(input.platform);
|
|
32017
|
+
const fortressId = fortressIdFromStoragePath(input.storagePath);
|
|
32018
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
32019
|
+
return {
|
|
32020
|
+
version: "1.1",
|
|
32021
|
+
agent_id: `agent:${harness}:${fortressId}`,
|
|
32022
|
+
identity_id: `fortress:${input.storagePath}`,
|
|
32023
|
+
harness,
|
|
32024
|
+
model_provider: {
|
|
32025
|
+
vendor: "unknown",
|
|
32026
|
+
model_id: "unknown",
|
|
32027
|
+
runs_locally: false
|
|
32028
|
+
},
|
|
32029
|
+
policy_id: "unbound",
|
|
32030
|
+
status: "active",
|
|
32031
|
+
budget_summary: {
|
|
32032
|
+
last_refreshed_at: nowIso
|
|
32033
|
+
},
|
|
32034
|
+
last_activity_at: nowIso,
|
|
32035
|
+
wrapped_at: nowIso,
|
|
32036
|
+
capabilities: {
|
|
32037
|
+
can_pause: false,
|
|
32038
|
+
can_resume: false,
|
|
32039
|
+
can_restart: false,
|
|
32040
|
+
can_unwrap: true,
|
|
32041
|
+
can_lockdown: false,
|
|
32042
|
+
can_chat: false,
|
|
32043
|
+
can_change_template: false
|
|
32044
|
+
}
|
|
32045
|
+
};
|
|
32046
|
+
}
|
|
31864
32047
|
function countUpstreamTools(servers) {
|
|
31865
32048
|
return servers.length === 0 ? 0 : servers.length;
|
|
31866
32049
|
}
|
|
@@ -31921,6 +32104,9 @@ function parseWrapArgs(argv) {
|
|
|
31921
32104
|
case "--no-open":
|
|
31922
32105
|
options.noOpen = true;
|
|
31923
32106
|
break;
|
|
32107
|
+
case "--no-dashboard":
|
|
32108
|
+
options.noDashboard = true;
|
|
32109
|
+
break;
|
|
31924
32110
|
case "--fortress":
|
|
31925
32111
|
options.fortress = argv[++i];
|
|
31926
32112
|
break;
|
|
@@ -31961,6 +32147,11 @@ function printWrapHelp() {
|
|
|
31961
32147
|
--port <port> Preferred dashboard port (default: 3501)
|
|
31962
32148
|
--dry-run Show what would happen without making changes
|
|
31963
32149
|
--no-open Do not auto-open the dashboard in a browser
|
|
32150
|
+
--no-dashboard Do not spawn a per-call dashboard server. Wrap still
|
|
32151
|
+
persists the agent record so a separately-running
|
|
32152
|
+
\`sanctuary dashboard\` (or a later wrap) sees the
|
|
32153
|
+
harness. Use this for the clean operator setup
|
|
32154
|
+
(one persistent dashboard + many wraps).
|
|
31964
32155
|
--help, -h Show this help
|
|
31965
32156
|
|
|
31966
32157
|
What happens:
|
|
@@ -31978,6 +32169,7 @@ var init_cli2 = __esm({
|
|
|
31978
32169
|
init_passphrase();
|
|
31979
32170
|
init_dashboard2();
|
|
31980
32171
|
init_wiring();
|
|
32172
|
+
init_agent_registry_persistence();
|
|
31981
32173
|
init_filesystem();
|
|
31982
32174
|
init_key_derivation();
|
|
31983
32175
|
init_encoding();
|
|
@@ -35615,7 +35807,12 @@ Refusing to start the dashboard while the reset-history marker is unreadable.`
|
|
|
35615
35807
|
buildV11Bindings({
|
|
35616
35808
|
identityId: hubIdentityId,
|
|
35617
35809
|
fortressId: fortressIdFromStoragePath(config.storage_path),
|
|
35618
|
-
auditLog
|
|
35810
|
+
auditLog,
|
|
35811
|
+
// v1.1.5 (Finding Z): rehydrate the hub agent registry from
|
|
35812
|
+
// `<storagePath>/state/_hub/local-agents.json` so the standalone
|
|
35813
|
+
// dashboard surfaces wraps performed by prior `sanctuary wrap`
|
|
35814
|
+
// invocations against this same fortress.
|
|
35815
|
+
storagePath: config.storage_path
|
|
35619
35816
|
})
|
|
35620
35817
|
);
|
|
35621
35818
|
const hostIsLoopback = dashboardHost === "127.0.0.1" || dashboardHost === "::1" || dashboardHost === "localhost";
|
|
@@ -35645,6 +35842,10 @@ Refusing to start the dashboard while the reset-history marker is unreadable.`
|
|
|
35645
35842
|
console.error(`Sanctuary Dashboard v${SANCTUARY_VERSION} (standalone mode)`);
|
|
35646
35843
|
console.error(`Storage: ${config.storage_path}`);
|
|
35647
35844
|
console.error(`Identities loaded: ${loadResult.loaded}`);
|
|
35845
|
+
const persistedAgentsCount = readPersistedLocalAgents(
|
|
35846
|
+
config.storage_path
|
|
35847
|
+
).length;
|
|
35848
|
+
console.error(`Local agents loaded: ${persistedAgentsCount}`);
|
|
35648
35849
|
console.error(`Listening: http://${dashboardHost}:${dashboardPort}`);
|
|
35649
35850
|
if (loadResult.total > 0 && loadResult.loaded === 0) {
|
|
35650
35851
|
const service = keychainServiceFor(config.storage_path, homedir());
|
|
@@ -35703,6 +35904,7 @@ var init_dashboard_standalone = __esm({
|
|
|
35703
35904
|
init_recovery_key_disclosure();
|
|
35704
35905
|
init_discovery();
|
|
35705
35906
|
init_wiring();
|
|
35907
|
+
init_agent_registry_persistence();
|
|
35706
35908
|
}
|
|
35707
35909
|
});
|
|
35708
35910
|
|