@sanctuary-framework/mcp-server 0.10.2 → 0.10.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/cli.cjs +96 -18
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +96 -18
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -27423,10 +27423,64 @@ var init_multi_server = __esm({
|
|
|
27423
27423
|
// src/dashboard-standalone.ts
|
|
27424
27424
|
var dashboard_standalone_exports = {};
|
|
27425
27425
|
__export(dashboard_standalone_exports, {
|
|
27426
|
+
discoverableSubTenants: () => discoverableSubTenants,
|
|
27427
|
+
renderTenantDiscoveryHint: () => renderTenantDiscoveryHint,
|
|
27426
27428
|
startStandaloneDashboard: () => startStandaloneDashboard
|
|
27427
27429
|
});
|
|
27430
|
+
async function discoverableSubTenants(currentStoragePath) {
|
|
27431
|
+
let all;
|
|
27432
|
+
try {
|
|
27433
|
+
all = await discoverTenants();
|
|
27434
|
+
} catch {
|
|
27435
|
+
return [];
|
|
27436
|
+
}
|
|
27437
|
+
return all.filter((t) => t.storage_path !== currentStoragePath && t.initialized);
|
|
27438
|
+
}
|
|
27439
|
+
function renderTenantDiscoveryHint(tenants) {
|
|
27440
|
+
if (tenants.length === 0) {
|
|
27441
|
+
return `No wrapped tenants discovered on this host.
|
|
27442
|
+
Run \`sanctuary wrap\` to create one, or set SANCTUARY_STORAGE_PATH
|
|
27443
|
+
if your tenant lives outside ~/.sanctuary/.`;
|
|
27444
|
+
}
|
|
27445
|
+
const lines = tenants.map((t) => {
|
|
27446
|
+
const runtime = t.runtime ? ` (running on :${t.runtime.dashboard_port})` : "";
|
|
27447
|
+
return ` \u2022 ${t.name}${runtime}
|
|
27448
|
+
storage: ${t.storage_path}
|
|
27449
|
+
keychain: ${t.keychain_service}`;
|
|
27450
|
+
});
|
|
27451
|
+
if (tenants.length === 1) {
|
|
27452
|
+
return `Detected 1 wrapped tenant on this host:
|
|
27453
|
+
` + lines.join("\n") + `
|
|
27454
|
+
|
|
27455
|
+
Boot the dashboard against it with:
|
|
27456
|
+
sanctuary dashboard --tenant ${tenants[0].name}
|
|
27457
|
+
`;
|
|
27458
|
+
}
|
|
27459
|
+
return `Detected ${tenants.length} wrapped tenants on this host:
|
|
27460
|
+
` + lines.join("\n") + `
|
|
27461
|
+
|
|
27462
|
+
Pick one explicitly:
|
|
27463
|
+
sanctuary dashboard --tenant <name>
|
|
27464
|
+
|
|
27465
|
+
Or browse all of them in the multi-tenant overview (no decryption):
|
|
27466
|
+
sanctuary dashboard --multi
|
|
27467
|
+
`;
|
|
27468
|
+
}
|
|
27428
27469
|
async function startStandaloneDashboard(options = {}) {
|
|
27429
27470
|
process.env.SANCTUARY_DASHBOARD_ENABLED = "true";
|
|
27471
|
+
if (options.tenant !== void 0) {
|
|
27472
|
+
const match = await findTenant(options.tenant);
|
|
27473
|
+
if (!match) {
|
|
27474
|
+
const available = await discoverTenants();
|
|
27475
|
+
const names = available.map((t) => t.name).join(", ") || "(none \u2014 run `sanctuary wrap`)";
|
|
27476
|
+
throw new Error(
|
|
27477
|
+
`Sanctuary Dashboard: --tenant "${options.tenant}" did not match any wrapped tenant.
|
|
27478
|
+
Available tenants: ${names}
|
|
27479
|
+
List details with \`sanctuary agents\`.`
|
|
27480
|
+
);
|
|
27481
|
+
}
|
|
27482
|
+
process.env.SANCTUARY_STORAGE_PATH = match.storage_path;
|
|
27483
|
+
}
|
|
27430
27484
|
const config = await loadConfig(options.configPath);
|
|
27431
27485
|
await promises.mkdir(config.storage_path, { recursive: true, mode: 448 });
|
|
27432
27486
|
const storage = new FilesystemStorage(`${config.storage_path}/state`);
|
|
@@ -27476,12 +27530,16 @@ async function startStandaloneDashboard(options = {}) {
|
|
|
27476
27530
|
} else {
|
|
27477
27531
|
const { hashToString: hashToString2 } = await Promise.resolve().then(() => (init_hashing(), hashing_exports));
|
|
27478
27532
|
const { stringToBytes: stringToBytes2, bytesToString: bytesToString2, fromBase64url: fromBase64url2, constantTimeEqual: constantTimeEqual2 } = await Promise.resolve().then(() => (init_encoding(), encoding_exports));
|
|
27533
|
+
const otherTenants = await discoverableSubTenants(config.storage_path);
|
|
27479
27534
|
const existingHash = await storage.read("_meta", "recovery-key-hash");
|
|
27480
27535
|
if (existingHash) {
|
|
27481
27536
|
const envRecoveryKey = process.env.SANCTUARY_RECOVERY_KEY;
|
|
27482
27537
|
if (!envRecoveryKey) {
|
|
27483
27538
|
throw new Error(
|
|
27484
|
-
|
|
27539
|
+
`Sanctuary Dashboard: Existing encrypted data found at ${config.storage_path} but no credentials provided.
|
|
27540
|
+
Provide SANCTUARY_PASSPHRASE or SANCTUARY_RECOVERY_KEY to start the dashboard against this storage path.
|
|
27541
|
+
|
|
27542
|
+
` + (otherTenants.length > 0 ? renderTenantDiscoveryHint(otherTenants) + "\n" : "") + `See server/docs/keychain-schema.md for the keychain layout.`
|
|
27485
27543
|
);
|
|
27486
27544
|
}
|
|
27487
27545
|
let recoveryKeyBytes;
|
|
@@ -27512,7 +27570,19 @@ async function startStandaloneDashboard(options = {}) {
|
|
|
27512
27570
|
const hasKeyParams = existingNamespaces.some((e) => e.key === "key-params");
|
|
27513
27571
|
if (hasKeyParams) {
|
|
27514
27572
|
throw new Error(
|
|
27515
|
-
|
|
27573
|
+
`Sanctuary Dashboard: Existing encrypted data found at ${config.storage_path} (passphrase-protected).
|
|
27574
|
+
No passphrase was supplied via --passphrase, SANCTUARY_PASSPHRASE,
|
|
27575
|
+
or the per-tenant Keychain item ${keychainServiceFor(config.storage_path, os.homedir())}.
|
|
27576
|
+
|
|
27577
|
+
` + (otherTenants.length > 0 ? renderTenantDiscoveryHint(otherTenants) + "\n" : "") + `See server/docs/keychain-schema.md for the keychain layout and recovery options.`
|
|
27578
|
+
);
|
|
27579
|
+
}
|
|
27580
|
+
if (otherTenants.length > 0) {
|
|
27581
|
+
throw new Error(
|
|
27582
|
+
`Sanctuary Dashboard: ${config.storage_path} has no Sanctuary state, but other wrapped tenants exist on this host.
|
|
27583
|
+
Refusing to generate a new recovery key over the default root \u2014 that would obscure the existing tenants.
|
|
27584
|
+
|
|
27585
|
+
` + renderTenantDiscoveryHint(otherTenants)
|
|
27516
27586
|
);
|
|
27517
27587
|
}
|
|
27518
27588
|
console.error(
|
|
@@ -27602,27 +27672,26 @@ async function startStandaloneDashboard(options = {}) {
|
|
|
27602
27672
|
if (loadResult.total > 0 && loadResult.loaded === 0) {
|
|
27603
27673
|
const service = keychainServiceFor(config.storage_path, os.homedir());
|
|
27604
27674
|
const sourceLabel = passphraseSource === "option" ? "--passphrase option" : passphraseSource === "env" ? "SANCTUARY_PASSPHRASE env var" : passphraseSource === "keychain" ? `macOS Keychain (service ${service})` : passphraseSource === "fallback-file" ? "encrypted fallback file" : "recovery key";
|
|
27675
|
+
const otherTenants = await discoverableSubTenants(config.storage_path);
|
|
27676
|
+
const hint = otherTenants.length > 0 ? `
|
|
27677
|
+
${renderTenantDiscoveryHint(otherTenants).split("\n").join("\n ")}
|
|
27678
|
+
` : "";
|
|
27605
27679
|
console.error(
|
|
27606
27680
|
`
|
|
27607
27681
|
\u26A0 WARNING: Encrypted identities found but NONE loaded
|
|
27608
27682
|
${loadResult.total} encrypted identity file(s) in ${config.storage_path}/state/_identities/
|
|
27609
27683
|
0 could be decrypted with the master key derived from the ${sourceLabel}.
|
|
27610
27684
|
|
|
27611
|
-
The dashboard will show empty panels.
|
|
27612
|
-
|
|
27613
|
-
service
|
|
27614
|
-
|
|
27615
|
-
|
|
27616
|
-
|
|
27617
|
-
|
|
27618
|
-
|
|
27619
|
-
|
|
27620
|
-
|
|
27621
|
-
|
|
27622
|
-
If this tenant's passphrase lives only in a different Keychain item
|
|
27623
|
-
or on another machine, restore it before this dashboard can read
|
|
27624
|
-
any state. Sanctuary will never auto-regenerate \u2014 that would
|
|
27625
|
-
permanently destroy the data encrypted under the prior key.
|
|
27685
|
+
The dashboard will show empty panels. Each wrapped tenant has its
|
|
27686
|
+
own passphrase under its own per-tenant Keychain service
|
|
27687
|
+
(this tenant's service: ${service}) \u2014 there is no global master
|
|
27688
|
+
credential. Setting SANCTUARY_PASSPHRASE here will not help unless
|
|
27689
|
+
that value is the passphrase that originally encrypted the
|
|
27690
|
+
identity files at this storage path.
|
|
27691
|
+
` + hint + `
|
|
27692
|
+
Diagnostic recipes: server/docs/keychain-schema.md
|
|
27693
|
+
Sanctuary will never auto-regenerate \u2014 that would permanently
|
|
27694
|
+
destroy the data encrypted under the prior key.
|
|
27626
27695
|
`
|
|
27627
27696
|
);
|
|
27628
27697
|
} else if (loadResult.failed > 0) {
|
|
@@ -27653,6 +27722,7 @@ var init_dashboard_standalone = __esm({
|
|
|
27653
27722
|
init_sovereignty_profile();
|
|
27654
27723
|
init_runtime();
|
|
27655
27724
|
init_passphrase();
|
|
27725
|
+
init_discovery();
|
|
27656
27726
|
}
|
|
27657
27727
|
});
|
|
27658
27728
|
|
|
@@ -27821,6 +27891,7 @@ async function runStandaloneDashboard(args) {
|
|
|
27821
27891
|
let port;
|
|
27822
27892
|
let host;
|
|
27823
27893
|
let multi = false;
|
|
27894
|
+
let tenant;
|
|
27824
27895
|
for (let i = 0; i < args.length; i++) {
|
|
27825
27896
|
if (args[i] === "--passphrase" && args[i + 1]) {
|
|
27826
27897
|
console.error(
|
|
@@ -27833,6 +27904,8 @@ async function runStandaloneDashboard(args) {
|
|
|
27833
27904
|
host = args[++i];
|
|
27834
27905
|
} else if (args[i] === "--multi") {
|
|
27835
27906
|
multi = true;
|
|
27907
|
+
} else if (args[i] === "--tenant" && args[i + 1]) {
|
|
27908
|
+
tenant = args[++i];
|
|
27836
27909
|
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
27837
27910
|
printDashboardHelp();
|
|
27838
27911
|
process.exit(0);
|
|
@@ -27862,7 +27935,8 @@ async function runStandaloneDashboard(args) {
|
|
|
27862
27935
|
await startStandaloneDashboard2({
|
|
27863
27936
|
passphrase,
|
|
27864
27937
|
port,
|
|
27865
|
-
host
|
|
27938
|
+
host,
|
|
27939
|
+
...tenant !== void 0 ? { tenant } : {}
|
|
27866
27940
|
});
|
|
27867
27941
|
console.error(`
|
|
27868
27942
|
Sanctuary Dashboard running (standalone mode). Press Ctrl+C to stop.
|
|
@@ -27996,6 +28070,10 @@ Usage:
|
|
|
27996
28070
|
Options:
|
|
27997
28071
|
--port <port> Dashboard port (default: from config or 3501; 3500 for --multi)
|
|
27998
28072
|
--host <host> Bind address (default: 127.0.0.1)
|
|
28073
|
+
--tenant <name> Boot against a specific wrapped tenant by the name printed
|
|
28074
|
+
by \`sanctuary agents\`. Resolves the per-tenant storage
|
|
28075
|
+
path and Keychain entry automatically. Use this on multi-
|
|
28076
|
+
tenant hosts instead of guessing SANCTUARY_PASSPHRASE.
|
|
27999
28077
|
--multi Start the multi-agent overview instead of a single-tenant
|
|
28000
28078
|
dashboard. Does not decrypt any tenant state \u2014 scans every
|
|
28001
28079
|
tenant on the host and deep-links into per-tenant dashboards.
|