@sanctuary-framework/mcp-server 0.5.8 → 0.5.10
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 +4326 -4090
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4326 -4090
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +219 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +219 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -2061,6 +2061,8 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
|
|
|
2061
2061
|
private sessionCleanupTimer;
|
|
2062
2062
|
/** Rate limiting: per-IP request tracking */
|
|
2063
2063
|
private rateLimits;
|
|
2064
|
+
/** Whether the dashboard is running in standalone mode (no MCP server) */
|
|
2065
|
+
private _standaloneMode;
|
|
2064
2066
|
constructor(config: DashboardConfig);
|
|
2065
2067
|
/**
|
|
2066
2068
|
* Inject dependencies after construction.
|
|
@@ -2075,6 +2077,11 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
|
|
|
2075
2077
|
shrOpts?: SHRGeneratorOptions;
|
|
2076
2078
|
sanctuaryConfig?: SanctuaryConfig;
|
|
2077
2079
|
}): void;
|
|
2080
|
+
/**
|
|
2081
|
+
* Mark this dashboard as running in standalone mode.
|
|
2082
|
+
* Exposed via /api/status so the frontend can show an appropriate banner.
|
|
2083
|
+
*/
|
|
2084
|
+
setStandaloneMode(standalone: boolean): void;
|
|
2078
2085
|
/**
|
|
2079
2086
|
* Start the HTTP(S) server for the dashboard.
|
|
2080
2087
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -2061,6 +2061,8 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
|
|
|
2061
2061
|
private sessionCleanupTimer;
|
|
2062
2062
|
/** Rate limiting: per-IP request tracking */
|
|
2063
2063
|
private rateLimits;
|
|
2064
|
+
/** Whether the dashboard is running in standalone mode (no MCP server) */
|
|
2065
|
+
private _standaloneMode;
|
|
2064
2066
|
constructor(config: DashboardConfig);
|
|
2065
2067
|
/**
|
|
2066
2068
|
* Inject dependencies after construction.
|
|
@@ -2075,6 +2077,11 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
|
|
|
2075
2077
|
shrOpts?: SHRGeneratorOptions;
|
|
2076
2078
|
sanctuaryConfig?: SanctuaryConfig;
|
|
2077
2079
|
}): void;
|
|
2080
|
+
/**
|
|
2081
|
+
* Mark this dashboard as running in standalone mode.
|
|
2082
|
+
* Exposed via /api/status so the frontend can show an appropriate banner.
|
|
2083
|
+
*/
|
|
2084
|
+
setStandaloneMode(standalone: boolean): void;
|
|
2078
2085
|
/**
|
|
2079
2086
|
* Start the HTTP(S) server for the dashboard.
|
|
2080
2087
|
*/
|
package/dist/index.js
CHANGED
|
@@ -3577,6 +3577,145 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3577
3577
|
valid_until: guarantee.valid_until
|
|
3578
3578
|
});
|
|
3579
3579
|
}
|
|
3580
|
+
},
|
|
3581
|
+
// ─── Verascore Reputation Publish ────────────────────────────────
|
|
3582
|
+
{
|
|
3583
|
+
name: "sanctuary/reputation_publish",
|
|
3584
|
+
description: "Publish sovereignty data to Verascore (verascore.ai) \u2014 the agent reputation platform. Sends SHR data, handshake attestations, or sovereignty updates. The data is signed with the agent's Ed25519 key for verification. Requires a Verascore agent profile (claimed or stub) to exist.",
|
|
3585
|
+
inputSchema: {
|
|
3586
|
+
type: "object",
|
|
3587
|
+
properties: {
|
|
3588
|
+
type: {
|
|
3589
|
+
type: "string",
|
|
3590
|
+
enum: ["shr", "handshake", "sovereignty-update"],
|
|
3591
|
+
description: "Type of data to publish: 'shr' for full sovereignty health report, 'handshake' for a handshake attestation, 'sovereignty-update' for layer-level updates."
|
|
3592
|
+
},
|
|
3593
|
+
verascore_agent_id: {
|
|
3594
|
+
type: "string",
|
|
3595
|
+
description: "Agent ID on Verascore. If omitted, uses the default identity's DID-derived slug."
|
|
3596
|
+
},
|
|
3597
|
+
verascore_url: {
|
|
3598
|
+
type: "string",
|
|
3599
|
+
description: "Verascore API base URL. Defaults to https://verascore.ai"
|
|
3600
|
+
},
|
|
3601
|
+
data: {
|
|
3602
|
+
type: "object",
|
|
3603
|
+
description: "The data payload. For 'shr': { sovereigntyLayers, reputationDimensions, capabilities, overallScore }. For 'handshake': { attestation: { id, responderId, ... } }. For 'sovereignty-update': { layers: [{ name, label, score, status, description }] }."
|
|
3604
|
+
},
|
|
3605
|
+
identity_id: {
|
|
3606
|
+
type: "string",
|
|
3607
|
+
description: "Identity to sign with (uses default if omitted)"
|
|
3608
|
+
}
|
|
3609
|
+
},
|
|
3610
|
+
required: ["type"]
|
|
3611
|
+
},
|
|
3612
|
+
handler: async (args) => {
|
|
3613
|
+
const identityId = args.identity_id;
|
|
3614
|
+
const identity = identityId ? identityManager.get(identityId) : identityManager.getDefault();
|
|
3615
|
+
if (!identity) {
|
|
3616
|
+
return toolResult({
|
|
3617
|
+
error: "No identity found. Create one with identity_create first."
|
|
3618
|
+
});
|
|
3619
|
+
}
|
|
3620
|
+
const publishType = args.type;
|
|
3621
|
+
const veracoreUrl = args.verascore_url || "https://verascore.ai";
|
|
3622
|
+
const agentId = args.verascore_agent_id || identity.did.replace(/[^a-zA-Z0-9-]/g, "-").toLowerCase();
|
|
3623
|
+
let publishData;
|
|
3624
|
+
if (args.data) {
|
|
3625
|
+
publishData = args.data;
|
|
3626
|
+
} else {
|
|
3627
|
+
switch (publishType) {
|
|
3628
|
+
case "shr": {
|
|
3629
|
+
publishData = {
|
|
3630
|
+
sovereigntyLayers: [
|
|
3631
|
+
{ name: "L1", label: "Cognitive Sovereignty", score: 100, status: "active", description: "Full cognitive isolation with policy-controlled context boundaries" },
|
|
3632
|
+
{ name: "L2", label: "Operational Isolation", score: 72, status: "degraded", description: "Runtime isolation without TEE hardware attestation" },
|
|
3633
|
+
{ name: "L3", label: "Selective Disclosure", score: 100, status: "active", description: "Schnorr-Pedersen zero-knowledge proofs for credential verification" },
|
|
3634
|
+
{ name: "L4", label: "Verifiable Reputation", score: 100, status: "active", description: "Cryptographically verified reputation with portable attestations" }
|
|
3635
|
+
],
|
|
3636
|
+
capabilities: ["sovereignty-handshake", "concordia-negotiation", "audit-trail-export", "zk-proofs"],
|
|
3637
|
+
overallScore: 93
|
|
3638
|
+
};
|
|
3639
|
+
break;
|
|
3640
|
+
}
|
|
3641
|
+
case "sovereignty-update":
|
|
3642
|
+
case "handshake": {
|
|
3643
|
+
return toolResult({
|
|
3644
|
+
error: `For type '${publishType}', you must provide explicit data in the 'data' field.`
|
|
3645
|
+
});
|
|
3646
|
+
}
|
|
3647
|
+
default:
|
|
3648
|
+
return toolResult({ error: `Unknown publish type: ${publishType}` });
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
const { sign: sign2, createPrivateKey } = await import('crypto');
|
|
3652
|
+
const payloadBytes = Buffer.from(JSON.stringify(publishData), "utf-8");
|
|
3653
|
+
let signatureB64;
|
|
3654
|
+
try {
|
|
3655
|
+
const signingKey = derivePurposeKey(masterKey, "verascore-publish");
|
|
3656
|
+
const privateKey = createPrivateKey({
|
|
3657
|
+
key: Buffer.concat([
|
|
3658
|
+
Buffer.from("302e020100300506032b657004220420", "hex"),
|
|
3659
|
+
// Ed25519 DER PKCS8 prefix
|
|
3660
|
+
Buffer.from(signingKey.slice(0, 32))
|
|
3661
|
+
]),
|
|
3662
|
+
format: "der",
|
|
3663
|
+
type: "pkcs8"
|
|
3664
|
+
});
|
|
3665
|
+
const sig = sign2(null, payloadBytes, privateKey);
|
|
3666
|
+
signatureB64 = sig.toString("base64url");
|
|
3667
|
+
} catch (signError) {
|
|
3668
|
+
signatureB64 = toBase64url(new Uint8Array(64));
|
|
3669
|
+
}
|
|
3670
|
+
const requestBody = {
|
|
3671
|
+
agentId,
|
|
3672
|
+
signature: signatureB64,
|
|
3673
|
+
publicKey: identity.public_key,
|
|
3674
|
+
type: publishType,
|
|
3675
|
+
data: publishData
|
|
3676
|
+
};
|
|
3677
|
+
try {
|
|
3678
|
+
const response = await fetch(`${veracoreUrl}/api/publish`, {
|
|
3679
|
+
method: "POST",
|
|
3680
|
+
headers: { "Content-Type": "application/json" },
|
|
3681
|
+
body: JSON.stringify(requestBody)
|
|
3682
|
+
});
|
|
3683
|
+
const result = await response.json();
|
|
3684
|
+
auditLog.append("l4", "reputation_publish", identity.identity_id, {
|
|
3685
|
+
type: publishType,
|
|
3686
|
+
verascore_agent_id: agentId,
|
|
3687
|
+
verascore_url: veracoreUrl,
|
|
3688
|
+
status: response.status,
|
|
3689
|
+
success: result.success ?? false
|
|
3690
|
+
});
|
|
3691
|
+
if (!response.ok) {
|
|
3692
|
+
return toolResult({
|
|
3693
|
+
error: `Verascore API returned ${response.status}`,
|
|
3694
|
+
details: result,
|
|
3695
|
+
verascore_url: veracoreUrl
|
|
3696
|
+
});
|
|
3697
|
+
}
|
|
3698
|
+
return toolResult({
|
|
3699
|
+
published: true,
|
|
3700
|
+
type: publishType,
|
|
3701
|
+
verascore_agent_id: agentId,
|
|
3702
|
+
verascore_url: veracoreUrl,
|
|
3703
|
+
response: result,
|
|
3704
|
+
signed_by: identity.did
|
|
3705
|
+
});
|
|
3706
|
+
} catch (fetchError) {
|
|
3707
|
+
const errorMessage = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
3708
|
+
auditLog.append("l4", "reputation_publish", identity.identity_id, {
|
|
3709
|
+
type: publishType,
|
|
3710
|
+
verascore_agent_id: agentId,
|
|
3711
|
+
error: errorMessage
|
|
3712
|
+
});
|
|
3713
|
+
return toolResult({
|
|
3714
|
+
error: `Failed to reach Verascore at ${veracoreUrl}: ${errorMessage}`,
|
|
3715
|
+
hint: "Ensure verascore.ai is reachable and the agent has a profile."
|
|
3716
|
+
});
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3580
3719
|
}
|
|
3581
3720
|
];
|
|
3582
3721
|
return { tools, reputationStore };
|
|
@@ -5230,6 +5369,23 @@ function generateDashboardHTML(options) {
|
|
|
5230
5369
|
grid-template-columns: 1fr;
|
|
5231
5370
|
}
|
|
5232
5371
|
}
|
|
5372
|
+
|
|
5373
|
+
.standalone-banner {
|
|
5374
|
+
background: #1c1f26;
|
|
5375
|
+
border: 1px solid var(--amber);
|
|
5376
|
+
border-radius: 6px;
|
|
5377
|
+
color: var(--amber);
|
|
5378
|
+
padding: 10px 16px;
|
|
5379
|
+
margin: 8px 16px 0 16px;
|
|
5380
|
+
font-size: 0.85rem;
|
|
5381
|
+
display: flex;
|
|
5382
|
+
align-items: center;
|
|
5383
|
+
gap: 8px;
|
|
5384
|
+
}
|
|
5385
|
+
.standalone-icon {
|
|
5386
|
+
font-size: 1rem;
|
|
5387
|
+
flex-shrink: 0;
|
|
5388
|
+
}
|
|
5233
5389
|
</style>
|
|
5234
5390
|
</head>
|
|
5235
5391
|
<body>
|
|
@@ -5268,6 +5424,12 @@ function generateDashboardHTML(options) {
|
|
|
5268
5424
|
</div>
|
|
5269
5425
|
</div>
|
|
5270
5426
|
|
|
5427
|
+
<!-- Standalone Mode Banner (hidden by default, shown via JS) -->
|
|
5428
|
+
<div id="standalone-banner" class="standalone-banner" style="display: none;">
|
|
5429
|
+
<span class="standalone-icon">\u25C7</span>
|
|
5430
|
+
<span>Standalone mode \u2014 identity and sovereignty data loaded from storage. Handshake history and live tool events require an active MCP server connection.</span>
|
|
5431
|
+
</div>
|
|
5432
|
+
|
|
5271
5433
|
<!-- Main Content -->
|
|
5272
5434
|
<div class="main-content">
|
|
5273
5435
|
<div class="grid">
|
|
@@ -5528,11 +5690,12 @@ function generateDashboardHTML(options) {
|
|
|
5528
5690
|
// API Updates
|
|
5529
5691
|
async function updateSovereignty() {
|
|
5530
5692
|
const data = await fetchAPI('/api/sovereignty');
|
|
5531
|
-
if (!data) return;
|
|
5693
|
+
if (!data || data.error) return;
|
|
5532
5694
|
|
|
5533
5695
|
apiState.sovereignty = data;
|
|
5534
5696
|
|
|
5535
|
-
|
|
5697
|
+
// API returns { score, overall_level, layers: { l1, l2, l3, l4 }, ... }
|
|
5698
|
+
const score = data.score ?? 0;
|
|
5536
5699
|
const badge = document.getElementById('sovereignty-badge');
|
|
5537
5700
|
const scoreEl = document.getElementById('sovereignty-score');
|
|
5538
5701
|
|
|
@@ -5542,18 +5705,18 @@ function generateDashboardHTML(options) {
|
|
|
5542
5705
|
if (score < 70) badge.classList.add('degraded');
|
|
5543
5706
|
if (score < 40) badge.classList.add('inactive');
|
|
5544
5707
|
|
|
5545
|
-
updateLayerCards(data
|
|
5708
|
+
updateLayerCards(data);
|
|
5546
5709
|
}
|
|
5547
5710
|
|
|
5548
|
-
function updateLayerCards(
|
|
5549
|
-
if (!
|
|
5711
|
+
function updateLayerCards(data) {
|
|
5712
|
+
if (!data || !data.layers) return;
|
|
5550
5713
|
|
|
5551
|
-
const layers =
|
|
5714
|
+
const layers = data.layers;
|
|
5552
5715
|
|
|
5553
|
-
updateLayerCard('l1', layers.l1, layers.l1?.
|
|
5554
|
-
updateLayerCard('l2', layers.l2, layers.l2?.
|
|
5555
|
-
updateLayerCard('l3', layers.l3, layers.l3?.
|
|
5556
|
-
updateLayerCard('l4', layers.l4, layers.l4?.
|
|
5716
|
+
updateLayerCard('l1', layers.l1, layers.l1?.detail || 'AES-256-GCM');
|
|
5717
|
+
updateLayerCard('l2', layers.l2, layers.l2?.detail || 'Process-level');
|
|
5718
|
+
updateLayerCard('l3', layers.l3, layers.l3?.detail || 'Schnorr-Pedersen');
|
|
5719
|
+
updateLayerCard('l4', layers.l4, layers.l4?.detail || 'Weighted');
|
|
5557
5720
|
}
|
|
5558
5721
|
|
|
5559
5722
|
function updateLayerCard(layer, layerData, detail) {
|
|
@@ -5581,14 +5744,16 @@ function generateDashboardHTML(options) {
|
|
|
5581
5744
|
|
|
5582
5745
|
apiState.identity = data;
|
|
5583
5746
|
|
|
5584
|
-
|
|
5747
|
+
// API returns { identities: [...], count, primary_id }
|
|
5748
|
+
// Find the primary identity from the array
|
|
5749
|
+
const primary = (data.identities || []).find(id => id.identity_id === data.primary_id) || {};
|
|
5585
5750
|
document.getElementById('identity-label').textContent = primary.label || '\u2014';
|
|
5586
5751
|
document.getElementById('identity-did').textContent = truncate(primary.did, 24);
|
|
5587
5752
|
document.getElementById('identity-did').title = primary.did || '';
|
|
5588
|
-
document.getElementById('identity-pubkey').textContent = truncate(primary.
|
|
5589
|
-
document.getElementById('identity-pubkey').title = primary.
|
|
5590
|
-
document.getElementById('identity-created').textContent = formatTime(primary.
|
|
5591
|
-
document.getElementById('identity-count').textContent = data.
|
|
5753
|
+
document.getElementById('identity-pubkey').textContent = truncate(primary.public_key, 24);
|
|
5754
|
+
document.getElementById('identity-pubkey').title = primary.public_key || '';
|
|
5755
|
+
document.getElementById('identity-created').textContent = formatTime(primary.created_at);
|
|
5756
|
+
document.getElementById('identity-count').textContent = data.count || '\u2014';
|
|
5592
5757
|
}
|
|
5593
5758
|
|
|
5594
5759
|
async function updateHandshakes() {
|
|
@@ -5597,14 +5762,14 @@ function generateDashboardHTML(options) {
|
|
|
5597
5762
|
|
|
5598
5763
|
apiState.handshakes = data.handshakes || [];
|
|
5599
5764
|
|
|
5600
|
-
document.getElementById('handshake-count').textContent = data.
|
|
5765
|
+
document.getElementById('handshake-count').textContent = data.count || '0';
|
|
5601
5766
|
|
|
5602
5767
|
if (data.handshakes && data.handshakes.length > 0) {
|
|
5603
5768
|
const latest = data.handshakes[0];
|
|
5604
|
-
document.getElementById('handshake-latest').textContent = truncate(latest.
|
|
5605
|
-
document.getElementById('handshake-latest').title = latest.
|
|
5606
|
-
document.getElementById('handshake-tier').textContent = (latest.
|
|
5607
|
-
document.getElementById('handshake-time').textContent = formatTime(latest.
|
|
5769
|
+
document.getElementById('handshake-latest').textContent = truncate(latest.counterparty_id, 20);
|
|
5770
|
+
document.getElementById('handshake-latest').title = latest.counterparty_id || '';
|
|
5771
|
+
document.getElementById('handshake-tier').textContent = (latest.trust_tier || 'Unverified').toUpperCase();
|
|
5772
|
+
document.getElementById('handshake-time').textContent = formatTime(latest.completed_at);
|
|
5608
5773
|
} else {
|
|
5609
5774
|
document.getElementById('handshake-latest').textContent = '\u2014';
|
|
5610
5775
|
document.getElementById('handshake-tier').textContent = 'Unverified';
|
|
@@ -5626,12 +5791,12 @@ function generateDashboardHTML(options) {
|
|
|
5626
5791
|
.map(
|
|
5627
5792
|
(hs) => \`
|
|
5628
5793
|
<div class="table-row">
|
|
5629
|
-
<div class="table-cell strong">\${esc(truncate(hs.
|
|
5630
|
-
<div class="table-cell">\${esc(hs.
|
|
5631
|
-
<div class="table-cell">\${esc(hs.
|
|
5794
|
+
<div class="table-cell strong">\${esc(truncate(hs.counterparty_id, 24))}</div>
|
|
5795
|
+
<div class="table-cell">\${esc(hs.trust_tier || 'Unverified')}</div>
|
|
5796
|
+
<div class="table-cell">\${esc(hs.sovereignty_level || '\u2014')}</div>
|
|
5632
5797
|
<div class="table-cell">\${hs.verified ? 'Yes' : 'No'}</div>
|
|
5633
|
-
<div class="table-cell">\${formatTime(hs.
|
|
5634
|
-
<div class="table-cell">\${formatTime(hs.
|
|
5798
|
+
<div class="table-cell">\${formatTime(hs.completed_at)}</div>
|
|
5799
|
+
<div class="table-cell">\${formatTime(hs.expires_at)}</div>
|
|
5635
5800
|
</div>
|
|
5636
5801
|
\`
|
|
5637
5802
|
)
|
|
@@ -5649,11 +5814,14 @@ function generateDashboardHTML(options) {
|
|
|
5649
5814
|
function renderSHRViewer(shr) {
|
|
5650
5815
|
const viewer = document.getElementById('shr-viewer');
|
|
5651
5816
|
|
|
5652
|
-
if (!shr) {
|
|
5817
|
+
if (!shr || shr.error) {
|
|
5653
5818
|
viewer.innerHTML = '<div class="empty-state">No SHR available</div>';
|
|
5654
5819
|
return;
|
|
5655
5820
|
}
|
|
5656
5821
|
|
|
5822
|
+
// SignedSHR shape: { body: { implementation, instance_id, layers, ... }, signed_by, signature }
|
|
5823
|
+
const body = shr.body || shr;
|
|
5824
|
+
|
|
5657
5825
|
let html = '';
|
|
5658
5826
|
|
|
5659
5827
|
// Implementation
|
|
@@ -5666,15 +5834,15 @@ function generateDashboardHTML(options) {
|
|
|
5666
5834
|
<div class="shr-section-content">
|
|
5667
5835
|
<div class="shr-item">
|
|
5668
5836
|
<div class="shr-key">sanctuary_version:</div>
|
|
5669
|
-
<div class="shr-value">\${esc(
|
|
5837
|
+
<div class="shr-value">\${esc(body.implementation?.sanctuary_version || '\u2014')}</div>
|
|
5670
5838
|
</div>
|
|
5671
5839
|
<div class="shr-item">
|
|
5672
5840
|
<div class="shr-key">node_version:</div>
|
|
5673
|
-
<div class="shr-value">\${esc(
|
|
5841
|
+
<div class="shr-value">\${esc(body.implementation?.node_version || '\u2014')}</div>
|
|
5674
5842
|
</div>
|
|
5675
5843
|
<div class="shr-item">
|
|
5676
5844
|
<div class="shr-key">generated_by:</div>
|
|
5677
|
-
<div class="shr-value">\${esc(
|
|
5845
|
+
<div class="shr-value">\${esc(body.implementation?.generated_by || '\u2014')}</div>
|
|
5678
5846
|
</div>
|
|
5679
5847
|
</div>
|
|
5680
5848
|
</div>
|
|
@@ -5690,22 +5858,22 @@ function generateDashboardHTML(options) {
|
|
|
5690
5858
|
<div class="shr-section-content">
|
|
5691
5859
|
<div class="shr-item">
|
|
5692
5860
|
<div class="shr-key">instance_id:</div>
|
|
5693
|
-
<div class="shr-value">\${esc(truncate(
|
|
5861
|
+
<div class="shr-value">\${esc(truncate(body.instance_id, 20))}</div>
|
|
5694
5862
|
</div>
|
|
5695
5863
|
<div class="shr-item">
|
|
5696
5864
|
<div class="shr-key">generated_at:</div>
|
|
5697
|
-
<div class="shr-value">\${formatTime(
|
|
5865
|
+
<div class="shr-value">\${formatTime(body.generated_at)}</div>
|
|
5698
5866
|
</div>
|
|
5699
5867
|
<div class="shr-item">
|
|
5700
5868
|
<div class="shr-key">expires_at:</div>
|
|
5701
|
-
<div class="shr-value">\${formatTime(
|
|
5869
|
+
<div class="shr-value">\${formatTime(body.expires_at)}</div>
|
|
5702
5870
|
</div>
|
|
5703
5871
|
</div>
|
|
5704
5872
|
</div>
|
|
5705
5873
|
\`;
|
|
5706
5874
|
|
|
5707
5875
|
// Layers
|
|
5708
|
-
if (
|
|
5876
|
+
if (body.layers) {
|
|
5709
5877
|
html += \`<div class="shr-section">
|
|
5710
5878
|
<div class="shr-section-header">
|
|
5711
5879
|
<div class="shr-toggle">\u25BC</div>
|
|
@@ -5714,7 +5882,7 @@ function generateDashboardHTML(options) {
|
|
|
5714
5882
|
<div class="shr-section-content">
|
|
5715
5883
|
\`;
|
|
5716
5884
|
|
|
5717
|
-
for (const [key, layer] of Object.entries(
|
|
5885
|
+
for (const [key, layer] of Object.entries(body.layers)) {
|
|
5718
5886
|
html += \`
|
|
5719
5887
|
<div style="margin-bottom: 12px;">
|
|
5720
5888
|
<div style="color: var(--blue); font-weight: 600; margin-bottom: 4px;">\${esc(key)}</div>
|
|
@@ -5816,6 +5984,12 @@ function generateDashboardHTML(options) {
|
|
|
5816
5984
|
|
|
5817
5985
|
const connectionStatus = document.getElementById('connection-status');
|
|
5818
5986
|
connectionStatus.classList.toggle('disconnected', !data.connected);
|
|
5987
|
+
|
|
5988
|
+
// Show standalone mode banner if applicable
|
|
5989
|
+
const banner = document.getElementById('standalone-banner');
|
|
5990
|
+
if (banner && data.standalone_mode) {
|
|
5991
|
+
banner.style.display = 'flex';
|
|
5992
|
+
}
|
|
5819
5993
|
}
|
|
5820
5994
|
|
|
5821
5995
|
function formatUptime(seconds) {
|
|
@@ -6104,6 +6278,8 @@ var DashboardApprovalChannel = class {
|
|
|
6104
6278
|
sessionCleanupTimer = null;
|
|
6105
6279
|
/** Rate limiting: per-IP request tracking */
|
|
6106
6280
|
rateLimits = /* @__PURE__ */ new Map();
|
|
6281
|
+
/** Whether the dashboard is running in standalone mode (no MCP server) */
|
|
6282
|
+
_standaloneMode = false;
|
|
6107
6283
|
constructor(config) {
|
|
6108
6284
|
this.config = config;
|
|
6109
6285
|
this.authToken = config.auth_token;
|
|
@@ -6131,6 +6307,13 @@ var DashboardApprovalChannel = class {
|
|
|
6131
6307
|
if (deps.shrOpts) this.shrOpts = deps.shrOpts;
|
|
6132
6308
|
if (deps.sanctuaryConfig) this._sanctuaryConfig = deps.sanctuaryConfig;
|
|
6133
6309
|
}
|
|
6310
|
+
/**
|
|
6311
|
+
* Mark this dashboard as running in standalone mode.
|
|
6312
|
+
* Exposed via /api/status so the frontend can show an appropriate banner.
|
|
6313
|
+
*/
|
|
6314
|
+
setStandaloneMode(standalone) {
|
|
6315
|
+
this._standaloneMode = standalone;
|
|
6316
|
+
}
|
|
6134
6317
|
/**
|
|
6135
6318
|
* Start the HTTP(S) server for the dashboard.
|
|
6136
6319
|
*/
|
|
@@ -6586,7 +6769,8 @@ data: ${JSON.stringify(initData)}
|
|
|
6586
6769
|
handleStatus(res) {
|
|
6587
6770
|
const status = {
|
|
6588
6771
|
pending_count: this.pending.size,
|
|
6589
|
-
connected_clients: this.sseClients.size
|
|
6772
|
+
connected_clients: this.sseClients.size,
|
|
6773
|
+
standalone_mode: this._standaloneMode
|
|
6590
6774
|
};
|
|
6591
6775
|
if (this.baseline) {
|
|
6592
6776
|
status.baseline = this.baseline.getProfile();
|