@sanctuary-framework/mcp-server 0.10.5 → 1.0.0-rc.1
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 +1925 -241
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1927 -243
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1226 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1228 -12
- package/dist/index.js.map +1 -1
- package/package.json +21 -2
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { mkdir, writeFile, readFile, stat, unlink, readdir, chmod, access, copyFile } from 'fs/promises';
|
|
3
3
|
import { join, dirname, basename, resolve } from 'path';
|
|
4
|
-
import {
|
|
4
|
+
import { platform, homedir, userInfo, hostname } from 'os';
|
|
5
5
|
import { createRequire } from 'module';
|
|
6
6
|
import { randomBytes as randomBytes$1, createHmac } from 'crypto';
|
|
7
7
|
import { gcm } from '@noble/ciphers/aes.js';
|
|
@@ -15,10 +15,11 @@ import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprot
|
|
|
15
15
|
import { createServer as createServer$2, get as get$1 } from 'http';
|
|
16
16
|
import { get, createServer as createServer$1 } from 'https';
|
|
17
17
|
import { exec, execSync, spawn } from 'child_process';
|
|
18
|
-
import { statSync, existsSync, mkdirSync } from 'fs';
|
|
18
|
+
import { statSync, existsSync, readFileSync, mkdirSync } from 'fs';
|
|
19
19
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
20
20
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
21
21
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
22
|
+
import { fileURLToPath } from 'url';
|
|
22
23
|
import { createInterface } from 'readline';
|
|
23
24
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
24
25
|
|
|
@@ -843,7 +844,8 @@ var init_state_store = __esm({
|
|
|
843
844
|
"_handshake",
|
|
844
845
|
"_shr",
|
|
845
846
|
"_sovereignty_profile",
|
|
846
|
-
"_context_gate_policies"
|
|
847
|
+
"_context_gate_policies",
|
|
848
|
+
"_fortress_mode"
|
|
847
849
|
];
|
|
848
850
|
StateStore = class _StateStore {
|
|
849
851
|
storage;
|
|
@@ -1886,7 +1888,8 @@ var init_tools = __esm({
|
|
|
1886
1888
|
"_handshake",
|
|
1887
1889
|
"_shr",
|
|
1888
1890
|
"_sovereignty_profile",
|
|
1889
|
-
"_context_gate_policies"
|
|
1891
|
+
"_context_gate_policies",
|
|
1892
|
+
"_fortress_mode"
|
|
1890
1893
|
];
|
|
1891
1894
|
IdentityManager = class {
|
|
1892
1895
|
storage;
|
|
@@ -4450,8 +4453,12 @@ var init_loader = __esm({
|
|
|
4450
4453
|
// Clears all runtime governance state — always requires approval
|
|
4451
4454
|
"sanctuary_bootstrap",
|
|
4452
4455
|
// Creates new Ed25519 identity + publishes — always requires approval
|
|
4453
|
-
"sanctuary_export_identity_bundle"
|
|
4456
|
+
"sanctuary_export_identity_bundle",
|
|
4454
4457
|
// Exports portable identity — always requires approval
|
|
4458
|
+
// WP-MVP-2 Operator Console: federation-node-join requires explicit
|
|
4459
|
+
// operator confirmation per Key 8. No auto-approve path. The console's
|
|
4460
|
+
// JoinApprover drives this gate via `MeshConsoleClient.makeJoinApprover`.
|
|
4461
|
+
"federation_node_join"
|
|
4455
4462
|
],
|
|
4456
4463
|
tier2_anomaly: DEFAULT_TIER2,
|
|
4457
4464
|
tier3_always_allow: [
|
|
@@ -6510,6 +6517,28 @@ function generateDashboardHTML(options) {
|
|
|
6510
6517
|
</div>
|
|
6511
6518
|
</div>
|
|
6512
6519
|
|
|
6520
|
+
<!--
|
|
6521
|
+
Mesh Health panel (WP-MVP-3 Follow-up #3).
|
|
6522
|
+
Subscribes to the existing /events SSE channel \u2014 no new transport.
|
|
6523
|
+
Render shape: per-node row with presence + flags + rollup.
|
|
6524
|
+
On open alert: list inline with operator-decision CTAs (rollback /
|
|
6525
|
+
split-brain / canonical-audit promotion).
|
|
6526
|
+
On post-recovery prompt: render rotation-prompt overlay with
|
|
6527
|
+
broker-credential list + "rotate now" buttons (rotation flow itself
|
|
6528
|
+
is the existing v0.10.x broker rotation surface).
|
|
6529
|
+
-->
|
|
6530
|
+
<div class="mesh-health-panel" id="mesh-health-panel">
|
|
6531
|
+
<div class="panel-header">
|
|
6532
|
+
<div class="panel-title">Mesh Health</div>
|
|
6533
|
+
<span class="card-value" id="mesh-health-updated-at" style="font-size: 11px; color: var(--text-secondary);">\u2014</span>
|
|
6534
|
+
</div>
|
|
6535
|
+
<div id="mesh-health-rows" class="mesh-health-rows">
|
|
6536
|
+
<div class="empty-state">Waiting for mesh health data\u2026</div>
|
|
6537
|
+
</div>
|
|
6538
|
+
<div id="mesh-health-alerts" class="mesh-health-alerts" style="margin-top: 12px;"></div>
|
|
6539
|
+
<div id="mesh-post-recovery-prompt" class="mesh-post-recovery-prompt" style="margin-top: 12px; display: none;"></div>
|
|
6540
|
+
</div>
|
|
6541
|
+
|
|
6513
6542
|
<!-- Sovereignty Profile Panel -->
|
|
6514
6543
|
<div class="profile-panel" id="sovereignty-profile-panel">
|
|
6515
6544
|
<div class="panel-header">
|
|
@@ -6712,6 +6741,11 @@ function generateDashboardHTML(options) {
|
|
|
6712
6741
|
// SEC-038: Do NOT embed the long-lived auth token in page source.
|
|
6713
6742
|
// Use only the session token stored in sessionStorage by the login flow.
|
|
6714
6743
|
const AUTH_TOKEN = sessionStorage.getItem('authToken') || '';
|
|
6744
|
+
// v0.10.6: server-baked flag mirroring _autoAuthLocalhost. When true,
|
|
6745
|
+
// the init-time auth gate does NOT redirect to '/' on empty AUTH_TOKEN,
|
|
6746
|
+
// because the server already admitted this loopback caller without a
|
|
6747
|
+
// bearer token. See dashboard-html.ts generateDashboardHTML() doc.
|
|
6748
|
+
const LOOPBACK_AUTH = ${JSON.stringify(options.loopbackAutoAuth === true)};
|
|
6715
6749
|
const TIMEOUT_SECONDS = ${options.timeoutSeconds};
|
|
6716
6750
|
const API_BASE = '';
|
|
6717
6751
|
|
|
@@ -7214,12 +7248,96 @@ function generateDashboardHTML(options) {
|
|
|
7214
7248
|
loadProxyServers();
|
|
7215
7249
|
});
|
|
7216
7250
|
|
|
7251
|
+
// \u2500\u2500 Mesh Health (WP-MVP-3 Follow-up #3) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7252
|
+
// The federation FailureModeDetector pushes per-tick snapshots and
|
|
7253
|
+
// per-detection alerts via the existing /events channel. Renders are
|
|
7254
|
+
// best-effort \u2014 if the panel DOM is absent (older HTML cache), we
|
|
7255
|
+
// silently skip rather than crashing.
|
|
7256
|
+
eventSource.addEventListener('mesh-health', (e) => {
|
|
7257
|
+
try {
|
|
7258
|
+
const snap = JSON.parse(e.data);
|
|
7259
|
+
renderMeshHealth(snap);
|
|
7260
|
+
} catch (err) { console.error('mesh-health render failed', err); }
|
|
7261
|
+
});
|
|
7262
|
+
eventSource.addEventListener('mesh-failure-mode-alert', (e) => {
|
|
7263
|
+
try {
|
|
7264
|
+
const alert = JSON.parse(e.data);
|
|
7265
|
+
appendMeshAlert(alert);
|
|
7266
|
+
} catch (err) { console.error('mesh alert render failed', err); }
|
|
7267
|
+
});
|
|
7268
|
+
eventSource.addEventListener('mesh-post-recovery-prompt', (e) => {
|
|
7269
|
+
try {
|
|
7270
|
+
const prompt = JSON.parse(e.data);
|
|
7271
|
+
renderMeshPostRecoveryPrompt(prompt);
|
|
7272
|
+
} catch (err) { console.error('mesh post-recovery render failed', err); }
|
|
7273
|
+
});
|
|
7274
|
+
|
|
7217
7275
|
eventSource.onerror = () => {
|
|
7218
7276
|
console.error('SSE error');
|
|
7219
7277
|
setTimeout(setupSSE, 5000);
|
|
7220
7278
|
};
|
|
7221
7279
|
}
|
|
7222
7280
|
|
|
7281
|
+
// \u2500\u2500 Mesh Health rendering (WP-MVP-3 Follow-up #3) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7282
|
+
function renderMeshHealth(snap) {
|
|
7283
|
+
const updatedAt = document.getElementById('mesh-health-updated-at');
|
|
7284
|
+
const rows = document.getElementById('mesh-health-rows');
|
|
7285
|
+
if (!rows || !snap) return;
|
|
7286
|
+
if (updatedAt) updatedAt.textContent = formatTime(snap.generated_at);
|
|
7287
|
+
const nodes = Array.isArray(snap.nodes) ? snap.nodes : [];
|
|
7288
|
+
if (nodes.length === 0) {
|
|
7289
|
+
rows.innerHTML = '<div class="empty-state">No mesh nodes seen yet.</div>';
|
|
7290
|
+
return;
|
|
7291
|
+
}
|
|
7292
|
+
rows.innerHTML = nodes.map((n) => {
|
|
7293
|
+
const flags = (n.flags || []).map((f) => '<span class="mesh-flag">' + esc(f) + '</span>').join(' ');
|
|
7294
|
+
return '<div class="mesh-row" data-rollup="' + esc(n.rollup) + '">' +
|
|
7295
|
+
'<span class="mesh-node-id">' + esc(n.node_id) + '</span>' +
|
|
7296
|
+
'<span class="mesh-presence">' + esc(n.presence) + '</span>' +
|
|
7297
|
+
'<span class="mesh-rollup">' + esc(n.rollup) + '</span>' +
|
|
7298
|
+
'<span class="mesh-flags">' + flags + '</span>' +
|
|
7299
|
+
'</div>';
|
|
7300
|
+
}).join('');
|
|
7301
|
+
const alertsBox = document.getElementById('mesh-health-alerts');
|
|
7302
|
+
if (alertsBox && Array.isArray(snap.open_alerts)) {
|
|
7303
|
+
alertsBox.innerHTML = snap.open_alerts.map((a) =>
|
|
7304
|
+
'<div class="mesh-alert" data-mode="' + esc(a.mode) + '">' +
|
|
7305
|
+
'<div class="mesh-alert-mode">' + esc(a.mode) + ' \u2014 ' + esc(a.target_node) + '</div>' +
|
|
7306
|
+
'<div class="mesh-alert-message">' + esc(a.message) + '</div>' +
|
|
7307
|
+
'</div>'
|
|
7308
|
+
).join('');
|
|
7309
|
+
}
|
|
7310
|
+
}
|
|
7311
|
+
|
|
7312
|
+
function appendMeshAlert(alert) {
|
|
7313
|
+
const alertsBox = document.getElementById('mesh-health-alerts');
|
|
7314
|
+
if (!alertsBox || !alert) return;
|
|
7315
|
+
const html = '<div class="mesh-alert" data-mode="' + esc(alert.mode) + '">' +
|
|
7316
|
+
'<div class="mesh-alert-mode">' + esc(alert.mode) + ' \u2014 ' + esc(alert.target_node) + '</div>' +
|
|
7317
|
+
'<div class="mesh-alert-message">' + esc(alert.message) + '</div>' +
|
|
7318
|
+
'</div>';
|
|
7319
|
+
alertsBox.insertAdjacentHTML('afterbegin', html);
|
|
7320
|
+
}
|
|
7321
|
+
|
|
7322
|
+
function renderMeshPostRecoveryPrompt(prompt) {
|
|
7323
|
+
const box = document.getElementById('mesh-post-recovery-prompt');
|
|
7324
|
+
if (!box || !prompt) return;
|
|
7325
|
+
box.style.display = 'block';
|
|
7326
|
+
const creds = Array.isArray(prompt.credentials) ? prompt.credentials : [];
|
|
7327
|
+
box.innerHTML = '<div class="mesh-rotation-banner">' +
|
|
7328
|
+
'<strong>Post-rotation hygiene:</strong> we just rotated your fortress root key. ' +
|
|
7329
|
+
'Rotate externally-scoped broker credentials that third parties may still associate with ' +
|
|
7330
|
+
'the pre-rotation key material.' +
|
|
7331
|
+
'</div>' +
|
|
7332
|
+
'<ul class="mesh-rotation-list">' +
|
|
7333
|
+
creds.map((c) => '<li>' +
|
|
7334
|
+
'<span class="mesh-cred-name">' + esc(c.secret_name) + '</span>' +
|
|
7335
|
+
'<button class="mesh-cred-rotate" data-secret="' + esc(c.secret_name) + '">' +
|
|
7336
|
+
'rotate now</button>' +
|
|
7337
|
+
'</li>').join('') +
|
|
7338
|
+
'</ul>';
|
|
7339
|
+
}
|
|
7340
|
+
|
|
7223
7341
|
// Activity Feed
|
|
7224
7342
|
function addActivityItem(item) {
|
|
7225
7343
|
activityLog.unshift(item);
|
|
@@ -7819,7 +7937,13 @@ function generateDashboardHTML(options) {
|
|
|
7819
7937
|
|
|
7820
7938
|
// Initialize
|
|
7821
7939
|
async function initialize() {
|
|
7822
|
-
|
|
7940
|
+
// v0.10.6: gate on BOTH sessionStorage and the server-baked loopback
|
|
7941
|
+
// auto-auth mirror. Pre-fix, a fresh loopback tab had empty
|
|
7942
|
+
// sessionStorage.authToken AND was admitted by the server via
|
|
7943
|
+
// _autoAuthLocalhost \u2014 this single-operand gate redirected to '/'
|
|
7944
|
+
// which reloaded the same page, which redirected again, infinitely.
|
|
7945
|
+
// See generateDashboardHTML() header comment for full threat model.
|
|
7946
|
+
if (!AUTH_TOKEN && !LOOPBACK_AUTH) {
|
|
7823
7947
|
redirectToLogin();
|
|
7824
7948
|
return;
|
|
7825
7949
|
}
|
|
@@ -8864,7 +8988,11 @@ var init_dashboard = __esm({
|
|
|
8864
8988
|
this.sessionTTLMs = isLocalhost ? SESSION_TTL_LOCAL_MS : SESSION_TTL_REMOTE_MS;
|
|
8865
8989
|
this.dashboardHTML = generateDashboardHTML({
|
|
8866
8990
|
timeoutSeconds: config.timeout_seconds,
|
|
8867
|
-
serverVersion: SANCTUARY_VERSION
|
|
8991
|
+
serverVersion: SANCTUARY_VERSION,
|
|
8992
|
+
// Construction-time default; real value is set by setAutoAuthLocalhost()
|
|
8993
|
+
// below (which regenerates this HTML). Default false preserves the
|
|
8994
|
+
// pre-v0.10.6 remote-deployment behavior when auto-auth is not enabled.
|
|
8995
|
+
loopbackAutoAuth: this._autoAuthLocalhost
|
|
8868
8996
|
});
|
|
8869
8997
|
this.loginHTML = generateLoginHTML({ serverVersion: SANCTUARY_VERSION });
|
|
8870
8998
|
this.sessionCleanupTimer = setInterval(() => this.cleanupSessions(), 6e4);
|
|
@@ -8900,6 +9028,11 @@ var init_dashboard = __esm({
|
|
|
8900
9028
|
*/
|
|
8901
9029
|
setAutoAuthLocalhost(enabled) {
|
|
8902
9030
|
this._autoAuthLocalhost = enabled;
|
|
9031
|
+
this.dashboardHTML = generateDashboardHTML({
|
|
9032
|
+
timeoutSeconds: this.config.timeout_seconds,
|
|
9033
|
+
serverVersion: SANCTUARY_VERSION,
|
|
9034
|
+
loopbackAutoAuth: this._autoAuthLocalhost
|
|
9035
|
+
});
|
|
8903
9036
|
}
|
|
8904
9037
|
/**
|
|
8905
9038
|
* v0.10.2: is this request from a loopback interface? We treat the
|
|
@@ -9784,6 +9917,27 @@ data: ${JSON.stringify(data)}
|
|
|
9784
9917
|
broadcastProtectionStatus(data) {
|
|
9785
9918
|
this.broadcastSSE("protection-status", data);
|
|
9786
9919
|
}
|
|
9920
|
+
// ── Mesh-health surface (WP-MVP-3 Follow-up #3) ─────────────────────
|
|
9921
|
+
//
|
|
9922
|
+
// The federation FailureModeDetector pushes per-tick health snapshots and
|
|
9923
|
+
// per-detection alerts here; the existing /events SSE channel transports
|
|
9924
|
+
// them to the browser. No new transport.
|
|
9925
|
+
//
|
|
9926
|
+
// Spec §8 + §9. Spawn-prompt acceptance criterion 7: "Mesh Health dashboard
|
|
9927
|
+
// panel renders via existing SSE /events channel — no new transport. Every
|
|
9928
|
+
// state transition produces an observable SSE event."
|
|
9929
|
+
/** Push a Mesh Health snapshot (full re-render trigger on the client). */
|
|
9930
|
+
broadcastMeshHealth(snapshot) {
|
|
9931
|
+
this.broadcastSSE("mesh-health", snapshot);
|
|
9932
|
+
}
|
|
9933
|
+
/** Push a single failure-mode alert (incremental client update). */
|
|
9934
|
+
broadcastMeshFailureModeAlert(alert) {
|
|
9935
|
+
this.broadcastSSE("mesh-failure-mode-alert", alert);
|
|
9936
|
+
}
|
|
9937
|
+
/** Push a post-recovery prompt update (master rotation hygiene flow). */
|
|
9938
|
+
broadcastMeshPostRecoveryPrompt(prompt) {
|
|
9939
|
+
this.broadcastSSE("mesh-post-recovery-prompt", prompt);
|
|
9940
|
+
}
|
|
9787
9941
|
/**
|
|
9788
9942
|
* Open a URL in the system's default browser.
|
|
9789
9943
|
* Cross-platform: macOS (open), Linux (xdg-open), Windows (start).
|
|
@@ -13180,7 +13334,7 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
13180
13334
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13181
13335
|
const canonicalBytes = canonicalize(outcome);
|
|
13182
13336
|
const canonicalString = new TextDecoder().decode(canonicalBytes);
|
|
13183
|
-
const
|
|
13337
|
+
const sha2568 = createCommitment(canonicalString);
|
|
13184
13338
|
let pedersenData;
|
|
13185
13339
|
if (includePedersen && Number.isInteger(outcome.rounds) && outcome.rounds >= 0) {
|
|
13186
13340
|
const pedersen = createPedersenCommitment(outcome.rounds);
|
|
@@ -13192,7 +13346,7 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
13192
13346
|
const commitmentPayload = {
|
|
13193
13347
|
bridge_commitment_id: commitmentId,
|
|
13194
13348
|
session_id: outcome.session_id,
|
|
13195
|
-
sha256_commitment:
|
|
13349
|
+
sha256_commitment: sha2568.commitment,
|
|
13196
13350
|
terms_hash: outcome.terms_hash,
|
|
13197
13351
|
committer_did: identity.did,
|
|
13198
13352
|
committed_at: now,
|
|
@@ -13203,8 +13357,8 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
13203
13357
|
return {
|
|
13204
13358
|
bridge_commitment_id: commitmentId,
|
|
13205
13359
|
session_id: outcome.session_id,
|
|
13206
|
-
sha256_commitment:
|
|
13207
|
-
blinding_factor:
|
|
13360
|
+
sha256_commitment: sha2568.commitment,
|
|
13361
|
+
blinding_factor: sha2568.blinding_factor,
|
|
13208
13362
|
committer_did: identity.did,
|
|
13209
13363
|
signature: toBase64url(signature),
|
|
13210
13364
|
pedersen_commitment: pedersenData,
|
|
@@ -22049,7 +22203,13 @@ function l3Card(l3) {
|
|
|
22049
22203
|
);
|
|
22050
22204
|
}
|
|
22051
22205
|
function l4Card(l4) {
|
|
22052
|
-
|
|
22206
|
+
let score;
|
|
22207
|
+
if (l4.score != null) {
|
|
22208
|
+
score = `<div class="score-block"><span class="score-value">${escHtml2(l4.score)}</span><span class="score-label">Verascore</span></div>`;
|
|
22209
|
+
} else {
|
|
22210
|
+
const claimText = l4.claim_cta ?? "Claim your profile at verascore.ai";
|
|
22211
|
+
score = `<div class="claim-block"><a class="claim-link" href="${VERASCORE_CLAIM_URL}" target="_blank" rel="noopener noreferrer">${escHtml2(claimText)}</a></div>`;
|
|
22212
|
+
}
|
|
22053
22213
|
return layerCard(
|
|
22054
22214
|
l4,
|
|
22055
22215
|
`<div class="layer-cta">${score}</div>${l4EvidenceBlock(l4)}`
|
|
@@ -22711,221 +22871,1349 @@ details.audit-details .audit-filters { display: flex; gap: 6px; padding: 0 18px
|
|
|
22711
22871
|
</body>
|
|
22712
22872
|
</html>`;
|
|
22713
22873
|
}
|
|
22714
|
-
var HERO_COPY;
|
|
22874
|
+
var HERO_COPY, VERASCORE_CLAIM_URL;
|
|
22715
22875
|
var init_html = __esm({
|
|
22716
22876
|
"src/dashboard/html.ts"() {
|
|
22717
22877
|
init_multi_html();
|
|
22718
22878
|
HERO_COPY = "Your agent is protected.";
|
|
22879
|
+
VERASCORE_CLAIM_URL = "https://www.verascore.ai";
|
|
22719
22880
|
}
|
|
22720
22881
|
});
|
|
22721
22882
|
|
|
22722
|
-
// src/
|
|
22723
|
-
function
|
|
22724
|
-
|
|
22725
|
-
let diff = 0;
|
|
22726
|
-
for (let i = 0; i < a.length; i++) {
|
|
22727
|
-
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
22728
|
-
}
|
|
22729
|
-
return diff === 0;
|
|
22883
|
+
// src/policy-engine/constants.ts
|
|
22884
|
+
function isPolicySlot(value) {
|
|
22885
|
+
return typeof value === "string" && POLICY_SLOTS.includes(value);
|
|
22730
22886
|
}
|
|
22731
|
-
|
|
22732
|
-
|
|
22733
|
-
|
|
22734
|
-
|
|
22887
|
+
var COMPILED_POLICY_SCHEMA_VERSION, POLICY_UPDATE_EVENT_TYPE, POLICY_SLOTS, CHANNEL_TEMPLATE_IDS, BUDGET_UNITS;
|
|
22888
|
+
var init_constants = __esm({
|
|
22889
|
+
"src/policy-engine/constants.ts"() {
|
|
22890
|
+
COMPILED_POLICY_SCHEMA_VERSION = "0.1";
|
|
22891
|
+
POLICY_UPDATE_EVENT_TYPE = "policy_update";
|
|
22892
|
+
POLICY_SLOTS = [
|
|
22893
|
+
"memory",
|
|
22894
|
+
"credentials",
|
|
22895
|
+
"plans",
|
|
22896
|
+
"outputs"
|
|
22897
|
+
];
|
|
22898
|
+
CHANNEL_TEMPLATE_IDS = [
|
|
22899
|
+
"read-outputs-only",
|
|
22900
|
+
"bidirectional-sync",
|
|
22901
|
+
"credential-share-scoped",
|
|
22902
|
+
"plan-inspect-read-only",
|
|
22903
|
+
"escrow-handoff"
|
|
22904
|
+
];
|
|
22905
|
+
BUDGET_UNITS = ["tokens", "usd"];
|
|
22735
22906
|
}
|
|
22736
|
-
|
|
22737
|
-
|
|
22738
|
-
|
|
22739
|
-
function isAuthorized(deps, req, url) {
|
|
22740
|
-
if (!deps.authToken) return true;
|
|
22741
|
-
const token = extractToken(req, url);
|
|
22742
|
-
if (!token) return false;
|
|
22743
|
-
return constantTimeEquals(token, deps.authToken);
|
|
22744
|
-
}
|
|
22745
|
-
function writeJSON(res, status, payload) {
|
|
22746
|
-
res.writeHead(status, {
|
|
22747
|
-
"Content-Type": "application/json",
|
|
22748
|
-
"Cache-Control": "no-store"
|
|
22749
|
-
});
|
|
22750
|
-
res.end(JSON.stringify(payload));
|
|
22751
|
-
}
|
|
22752
|
-
function writeText(res, status, body, contentType = "text/plain") {
|
|
22753
|
-
res.writeHead(status, {
|
|
22754
|
-
"Content-Type": contentType,
|
|
22755
|
-
"Cache-Control": "no-store"
|
|
22756
|
-
});
|
|
22757
|
-
res.end(body);
|
|
22907
|
+
});
|
|
22908
|
+
function isTemplateName(value) {
|
|
22909
|
+
return typeof value === "string" && TEMPLATE_NAMES.includes(value);
|
|
22758
22910
|
}
|
|
22759
|
-
|
|
22760
|
-
|
|
22761
|
-
|
|
22762
|
-
const method = (req.method ?? "GET").toUpperCase();
|
|
22763
|
-
const path = url.pathname;
|
|
22764
|
-
if (!isAuthorized(deps, req, url)) {
|
|
22765
|
-
writeJSON(res, 401, { error: "unauthorized" });
|
|
22766
|
-
return true;
|
|
22911
|
+
function validateMetadata(name, data) {
|
|
22912
|
+
if (typeof data !== "object" || data === null) {
|
|
22913
|
+
throw new TemplateValidationError(name, "template.json must be an object");
|
|
22767
22914
|
}
|
|
22768
|
-
|
|
22769
|
-
|
|
22770
|
-
|
|
22915
|
+
const d = data;
|
|
22916
|
+
if (typeof d.name !== "string" || d.name !== name) {
|
|
22917
|
+
throw new TemplateValidationError(name, `template.json name must be "${name}"`);
|
|
22771
22918
|
}
|
|
22772
|
-
if (
|
|
22773
|
-
|
|
22774
|
-
const html = renderDashboardHTML({ snapshot, authToken: deps.authToken });
|
|
22775
|
-
writeText(res, 200, html, "text/html; charset=utf-8");
|
|
22776
|
-
return true;
|
|
22919
|
+
if (typeof d.version !== "string" || !/^\d+\.\d+\.\d+$/.test(d.version)) {
|
|
22920
|
+
throw new TemplateValidationError(name, "template.json version must be semver");
|
|
22777
22921
|
}
|
|
22778
|
-
if (
|
|
22779
|
-
|
|
22780
|
-
|
|
22781
|
-
|
|
22922
|
+
if (typeof d.channel !== "string" || !CHANNEL_TEMPLATE_IDS.includes(d.channel)) {
|
|
22923
|
+
throw new TemplateValidationError(
|
|
22924
|
+
name,
|
|
22925
|
+
`template.json channel must be one of: ${CHANNEL_TEMPLATE_IDS.join(", ")}`
|
|
22926
|
+
);
|
|
22782
22927
|
}
|
|
22783
|
-
|
|
22784
|
-
|
|
22785
|
-
|
|
22786
|
-
|
|
22787
|
-
|
|
22788
|
-
|
|
22789
|
-
|
|
22928
|
+
if (typeof d.tier !== "string" || !["A", "B", "C"].includes(d.tier)) {
|
|
22929
|
+
throw new TemplateValidationError(name, "template.json tier must be A, B, or C");
|
|
22930
|
+
}
|
|
22931
|
+
if (typeof d.target_archetype !== "string" || d.target_archetype.length === 0) {
|
|
22932
|
+
throw new TemplateValidationError(name, "template.json target_archetype must be a non-empty string");
|
|
22933
|
+
}
|
|
22934
|
+
if (typeof d.description !== "string" || d.description.length === 0) {
|
|
22935
|
+
throw new TemplateValidationError(name, "template.json description must be a non-empty string");
|
|
22936
|
+
}
|
|
22937
|
+
}
|
|
22938
|
+
function validateDefaults(name, data) {
|
|
22939
|
+
if (typeof data !== "object" || data === null) {
|
|
22940
|
+
throw new TemplateValidationError(name, "defaults.json must be an object");
|
|
22941
|
+
}
|
|
22942
|
+
const d = data;
|
|
22943
|
+
if (!Array.isArray(d.egress)) {
|
|
22944
|
+
throw new TemplateValidationError(name, "defaults.json egress must be an array");
|
|
22945
|
+
}
|
|
22946
|
+
for (const entry of d.egress) {
|
|
22947
|
+
if (typeof entry !== "object" || entry === null) {
|
|
22948
|
+
throw new TemplateValidationError(name, "defaults.json egress entry must be an object");
|
|
22790
22949
|
}
|
|
22791
|
-
const
|
|
22792
|
-
|
|
22793
|
-
|
|
22794
|
-
|
|
22795
|
-
|
|
22796
|
-
|
|
22950
|
+
const e = entry;
|
|
22951
|
+
if (typeof e.destination !== "string" || e.destination.length === 0) {
|
|
22952
|
+
throw new TemplateValidationError(name, "egress entry destination must be a non-empty string");
|
|
22953
|
+
}
|
|
22954
|
+
if (!Array.isArray(e.methods)) {
|
|
22955
|
+
throw new TemplateValidationError(name, "egress entry methods must be an array");
|
|
22797
22956
|
}
|
|
22798
|
-
return true;
|
|
22799
22957
|
}
|
|
22800
|
-
if (
|
|
22801
|
-
|
|
22802
|
-
|
|
22958
|
+
if (typeof d.budgets !== "object" || d.budgets === null) {
|
|
22959
|
+
throw new TemplateValidationError(name, "defaults.json budgets must be an object");
|
|
22960
|
+
}
|
|
22961
|
+
if (typeof d.retention !== "object" || d.retention === null) {
|
|
22962
|
+
throw new TemplateValidationError(name, "defaults.json retention must be an object");
|
|
22963
|
+
}
|
|
22964
|
+
const ret = d.retention;
|
|
22965
|
+
if (typeof ret.windows !== "object" || ret.windows === null) {
|
|
22966
|
+
throw new TemplateValidationError(name, "defaults.json retention.windows must be an object");
|
|
22803
22967
|
}
|
|
22804
|
-
return false;
|
|
22805
22968
|
}
|
|
22806
|
-
|
|
22807
|
-
|
|
22808
|
-
"
|
|
22809
|
-
|
|
22810
|
-
|
|
22811
|
-
|
|
22812
|
-
|
|
22813
|
-
|
|
22814
|
-
|
|
22815
|
-
|
|
22816
|
-
|
|
22817
|
-
`);
|
|
22818
|
-
const unsubscribe = deps.onEvent ? deps.onEvent((event) => {
|
|
22819
|
-
try {
|
|
22820
|
-
res.write(`event: ${event.type}
|
|
22821
|
-
data: ${JSON.stringify(event.data)}
|
|
22822
|
-
|
|
22823
|
-
`);
|
|
22824
|
-
} catch {
|
|
22969
|
+
function validateCommitments(name, data) {
|
|
22970
|
+
if (typeof data !== "object" || data === null) {
|
|
22971
|
+
throw new TemplateValidationError(name, "commitments.json must be an object");
|
|
22972
|
+
}
|
|
22973
|
+
const d = data;
|
|
22974
|
+
if (!Array.isArray(d.shapes)) {
|
|
22975
|
+
throw new TemplateValidationError(name, "commitments.json shapes must be an array");
|
|
22976
|
+
}
|
|
22977
|
+
for (const shape of d.shapes) {
|
|
22978
|
+
if (typeof shape !== "object" || shape === null) {
|
|
22979
|
+
throw new TemplateValidationError(name, "commitment shape must be an object");
|
|
22825
22980
|
}
|
|
22826
|
-
|
|
22827
|
-
|
|
22828
|
-
|
|
22829
|
-
try {
|
|
22830
|
-
res.write(": keepalive\n\n");
|
|
22831
|
-
} catch {
|
|
22981
|
+
const s = shape;
|
|
22982
|
+
if (typeof s.commitment_class !== "string" || s.commitment_class.length === 0) {
|
|
22983
|
+
throw new TemplateValidationError(name, "commitment shape commitment_class must be a non-empty string");
|
|
22832
22984
|
}
|
|
22833
|
-
|
|
22834
|
-
|
|
22835
|
-
|
|
22836
|
-
|
|
22837
|
-
|
|
22838
|
-
|
|
22839
|
-
|
|
22985
|
+
if (typeof s.example_deliverable !== "string") {
|
|
22986
|
+
throw new TemplateValidationError(name, "commitment shape example_deliverable must be a string");
|
|
22987
|
+
}
|
|
22988
|
+
if (typeof s.example_deadline_or_terminal !== "string") {
|
|
22989
|
+
throw new TemplateValidationError(name, "commitment shape example_deadline_or_terminal must be a string");
|
|
22990
|
+
}
|
|
22991
|
+
}
|
|
22840
22992
|
}
|
|
22841
|
-
|
|
22842
|
-
|
|
22843
|
-
|
|
22844
|
-
|
|
22993
|
+
function lintOnboarding(_name, content) {
|
|
22994
|
+
const violations = [];
|
|
22995
|
+
if (content.includes("\u2014")) {
|
|
22996
|
+
violations.push("onboarding.md contains em-dashes (U+2014). Replace with commas, semicolons, colons, or parentheses.");
|
|
22845
22997
|
}
|
|
22846
|
-
|
|
22847
|
-
|
|
22848
|
-
|
|
22849
|
-
|
|
22850
|
-
|
|
22851
|
-
|
|
22852
|
-
|
|
22853
|
-
|
|
22854
|
-
|
|
22855
|
-
|
|
22856
|
-
|
|
22857
|
-
|
|
22858
|
-
|
|
22859
|
-
|
|
22860
|
-
|
|
22861
|
-
|
|
22862
|
-
|
|
22863
|
-
|
|
22864
|
-
|
|
22865
|
-
|
|
22866
|
-
|
|
22867
|
-
|
|
22998
|
+
if (content.includes("\u2013")) {
|
|
22999
|
+
violations.push("onboarding.md contains en-dashes (U+2013). Replace with hyphens or other punctuation.");
|
|
23000
|
+
}
|
|
23001
|
+
return { clean: violations.length === 0, violations };
|
|
23002
|
+
}
|
|
23003
|
+
function resolveTemplatesDir() {
|
|
23004
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
23005
|
+
const thisDir = dirname(thisFile);
|
|
23006
|
+
if (thisDir.includes("/dist/")) {
|
|
23007
|
+
return thisDir.replace("/dist/templates", "/src/templates");
|
|
23008
|
+
}
|
|
23009
|
+
return thisDir;
|
|
23010
|
+
}
|
|
23011
|
+
function loadTemplateBundle(name, templatesDir) {
|
|
23012
|
+
const dir = join(templatesDir, name);
|
|
23013
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) {
|
|
23014
|
+
throw new TemplateValidationError(name, `template directory not found: ${dir}`);
|
|
23015
|
+
}
|
|
23016
|
+
const metadataRaw = JSON.parse(readFileSync(join(dir, "template.json"), "utf-8"));
|
|
23017
|
+
validateMetadata(name, metadataRaw);
|
|
23018
|
+
const policyEnglish = readFileSync(join(dir, "policy.md"), "utf-8").trim();
|
|
23019
|
+
if (policyEnglish.length === 0) {
|
|
23020
|
+
throw new TemplateValidationError(name, "policy.md must not be empty");
|
|
23021
|
+
}
|
|
23022
|
+
const defaultsRaw = JSON.parse(readFileSync(join(dir, "defaults.json"), "utf-8"));
|
|
23023
|
+
validateDefaults(name, defaultsRaw);
|
|
23024
|
+
const commitmentsRaw = JSON.parse(readFileSync(join(dir, "commitments.json"), "utf-8"));
|
|
23025
|
+
validateCommitments(name, commitmentsRaw);
|
|
23026
|
+
const onboarding = readFileSync(join(dir, "onboarding.md"), "utf-8").trim();
|
|
23027
|
+
if (onboarding.length === 0) {
|
|
23028
|
+
throw new TemplateValidationError(name, "onboarding.md must not be empty");
|
|
23029
|
+
}
|
|
23030
|
+
const lint = lintOnboarding(name, onboarding);
|
|
23031
|
+
if (!lint.clean) {
|
|
23032
|
+
throw new TemplateValidationError(name, lint.violations.join("; "));
|
|
23033
|
+
}
|
|
23034
|
+
return {
|
|
23035
|
+
metadata: metadataRaw,
|
|
23036
|
+
policy_english: policyEnglish,
|
|
23037
|
+
defaults: defaultsRaw,
|
|
23038
|
+
commitments: commitmentsRaw,
|
|
23039
|
+
onboarding
|
|
22868
23040
|
};
|
|
22869
|
-
|
|
22870
|
-
|
|
22871
|
-
|
|
22872
|
-
|
|
22873
|
-
|
|
22874
|
-
|
|
22875
|
-
|
|
22876
|
-
|
|
22877
|
-
|
|
22878
|
-
|
|
22879
|
-
|
|
22880
|
-
|
|
22881
|
-
|
|
22882
|
-
|
|
23041
|
+
}
|
|
23042
|
+
function ensureLoaded() {
|
|
23043
|
+
if (_cache) return _cache;
|
|
23044
|
+
_cache = /* @__PURE__ */ new Map();
|
|
23045
|
+
const dir = resolveTemplatesDir();
|
|
23046
|
+
for (const name of TEMPLATE_NAMES) {
|
|
23047
|
+
_cache.set(name, loadTemplateBundle(name, dir));
|
|
23048
|
+
}
|
|
23049
|
+
return _cache;
|
|
23050
|
+
}
|
|
23051
|
+
function listTemplates() {
|
|
23052
|
+
const cache = ensureLoaded();
|
|
23053
|
+
return TEMPLATE_NAMES.map((name) => {
|
|
23054
|
+
const bundle = cache.get(name);
|
|
23055
|
+
return {
|
|
23056
|
+
metadata: bundle.metadata,
|
|
23057
|
+
onboarding: bundle.onboarding
|
|
23058
|
+
};
|
|
22883
23059
|
});
|
|
22884
|
-
|
|
22885
|
-
|
|
22886
|
-
|
|
22887
|
-
|
|
22888
|
-
|
|
22889
|
-
|
|
23060
|
+
}
|
|
23061
|
+
function getTemplate2(name) {
|
|
23062
|
+
if (!isTemplateName(name)) return null;
|
|
23063
|
+
const cache = ensureLoaded();
|
|
23064
|
+
return cache.get(name) ?? null;
|
|
23065
|
+
}
|
|
23066
|
+
function getTemplateEntry(name) {
|
|
23067
|
+
const bundle = getTemplate2(name);
|
|
23068
|
+
if (!bundle) return null;
|
|
23069
|
+
return { metadata: bundle.metadata, onboarding: bundle.onboarding };
|
|
23070
|
+
}
|
|
23071
|
+
var TEMPLATE_NAMES, TemplateValidationError, _cache;
|
|
23072
|
+
var init_registry2 = __esm({
|
|
23073
|
+
"src/templates/registry.ts"() {
|
|
23074
|
+
init_constants();
|
|
23075
|
+
TEMPLATE_NAMES = [
|
|
23076
|
+
"research-assistant",
|
|
23077
|
+
"coding-assistant",
|
|
23078
|
+
"ops-runner",
|
|
23079
|
+
"planner",
|
|
23080
|
+
"handoff-coordinator",
|
|
23081
|
+
"x-miner",
|
|
23082
|
+
"github-miner"
|
|
23083
|
+
];
|
|
23084
|
+
TemplateValidationError = class extends Error {
|
|
23085
|
+
constructor(templateName, message) {
|
|
23086
|
+
super(`template "${templateName}": ${message}`);
|
|
23087
|
+
this.name = "TemplateValidationError";
|
|
23088
|
+
}
|
|
23089
|
+
};
|
|
23090
|
+
_cache = null;
|
|
23091
|
+
}
|
|
23092
|
+
});
|
|
23093
|
+
|
|
23094
|
+
// src/policy-engine/null-policy.ts
|
|
23095
|
+
function buildNullPolicy(params) {
|
|
23096
|
+
const denyRule = (slot) => ({
|
|
23097
|
+
slot,
|
|
23098
|
+
mode: "deny",
|
|
23099
|
+
grants: []
|
|
22890
23100
|
});
|
|
22891
|
-
const actualPort = (() => {
|
|
22892
|
-
const addr = server.address();
|
|
22893
|
-
if (addr && typeof addr === "object") return addr.port;
|
|
22894
|
-
return port;
|
|
22895
|
-
})();
|
|
22896
|
-
const url = `http://${host}:${actualPort}`;
|
|
22897
23101
|
return {
|
|
22898
|
-
|
|
22899
|
-
|
|
22900
|
-
|
|
22901
|
-
|
|
22902
|
-
|
|
22903
|
-
|
|
22904
|
-
|
|
22905
|
-
|
|
22906
|
-
|
|
23102
|
+
schema_version: "0.1",
|
|
23103
|
+
agent_id: params.agent_id,
|
|
23104
|
+
fortress_id: params.fortress_id,
|
|
23105
|
+
policy_version: 0,
|
|
23106
|
+
slots: {
|
|
23107
|
+
memory: denyRule("memory"),
|
|
23108
|
+
credentials: denyRule("credentials"),
|
|
23109
|
+
plans: denyRule("plans"),
|
|
23110
|
+
outputs: denyRule("outputs")
|
|
23111
|
+
},
|
|
23112
|
+
capabilities: {
|
|
23113
|
+
concordia_commitment_classes: [],
|
|
23114
|
+
honeypot_skill_ids: [],
|
|
23115
|
+
is_sentinel: false
|
|
23116
|
+
},
|
|
23117
|
+
auto_trigger_ladder: {
|
|
23118
|
+
honeypot_auto_freeze: true,
|
|
23119
|
+
threshold_rule_action: "operator_approved",
|
|
23120
|
+
ml_anomaly_action: "operator_approved"
|
|
23121
|
+
},
|
|
23122
|
+
source_english: "(null policy \u2014 no operator authoring yet; hermetic default denies all four slots)",
|
|
23123
|
+
compiled_at: "1970-01-01T00:00:00.000Z"
|
|
22907
23124
|
};
|
|
22908
23125
|
}
|
|
22909
|
-
var
|
|
22910
|
-
|
|
22911
|
-
"src/dashboard/server.ts"() {
|
|
22912
|
-
init_api();
|
|
22913
|
-
DEFAULT_PORT = 3501;
|
|
22914
|
-
DEFAULT_HOST = "127.0.0.1";
|
|
23126
|
+
var init_null_policy = __esm({
|
|
23127
|
+
"src/policy-engine/null-policy.ts"() {
|
|
22915
23128
|
}
|
|
22916
23129
|
});
|
|
22917
23130
|
|
|
22918
|
-
// src/
|
|
22919
|
-
|
|
22920
|
-
|
|
22921
|
-
|
|
22922
|
-
|
|
22923
|
-
|
|
22924
|
-
|
|
22925
|
-
|
|
22926
|
-
|
|
22927
|
-
|
|
22928
|
-
|
|
23131
|
+
// src/policy-engine/channel-templates.ts
|
|
23132
|
+
function basePolicy(params) {
|
|
23133
|
+
if (params.merge_into) {
|
|
23134
|
+
const p2 = {
|
|
23135
|
+
...params.merge_into,
|
|
23136
|
+
policy_version: params.policy_version,
|
|
23137
|
+
compiled_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23138
|
+
slots: {
|
|
23139
|
+
memory: cloneRule(params.merge_into.slots.memory),
|
|
23140
|
+
credentials: cloneRule(params.merge_into.slots.credentials),
|
|
23141
|
+
plans: cloneRule(params.merge_into.slots.plans),
|
|
23142
|
+
outputs: cloneRule(params.merge_into.slots.outputs)
|
|
23143
|
+
},
|
|
23144
|
+
capabilities: {
|
|
23145
|
+
...params.merge_into.capabilities,
|
|
23146
|
+
concordia_commitment_classes: [
|
|
23147
|
+
...params.merge_into.capabilities.concordia_commitment_classes
|
|
23148
|
+
],
|
|
23149
|
+
honeypot_skill_ids: [...params.merge_into.capabilities.honeypot_skill_ids]
|
|
23150
|
+
},
|
|
23151
|
+
auto_trigger_ladder: { ...params.merge_into.auto_trigger_ladder }
|
|
23152
|
+
};
|
|
23153
|
+
if (params.parent_version !== void 0) p2.parent_version = params.parent_version;
|
|
23154
|
+
else delete p2.parent_version;
|
|
23155
|
+
return p2;
|
|
23156
|
+
}
|
|
23157
|
+
const p = buildNullPolicy({
|
|
23158
|
+
agent_id: params.agent_id,
|
|
23159
|
+
fortress_id: params.fortress_id
|
|
23160
|
+
});
|
|
23161
|
+
p.policy_version = params.policy_version;
|
|
23162
|
+
p.compiled_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
23163
|
+
if (params.parent_version !== void 0) p.parent_version = params.parent_version;
|
|
23164
|
+
return p;
|
|
23165
|
+
}
|
|
23166
|
+
function cloneRule(r) {
|
|
23167
|
+
return {
|
|
23168
|
+
slot: r.slot,
|
|
23169
|
+
mode: r.mode,
|
|
23170
|
+
grants: r.grants.map((g) => ({ ...g, scope: g.scope ? { ...g.scope } : void 0 }))
|
|
23171
|
+
};
|
|
23172
|
+
}
|
|
23173
|
+
function grantOn(policy, slot, grant) {
|
|
23174
|
+
const rule = policy.slots[slot];
|
|
23175
|
+
rule.mode = "grant";
|
|
23176
|
+
const dup = rule.grants.find(
|
|
23177
|
+
(g) => g.counterparty === grant.counterparty && g.action === grant.action
|
|
23178
|
+
);
|
|
23179
|
+
if (dup) {
|
|
23180
|
+
if (grant.scope) {
|
|
23181
|
+
dup.scope = { ...dup.scope ?? {}, ...grant.scope };
|
|
23182
|
+
}
|
|
23183
|
+
return;
|
|
23184
|
+
}
|
|
23185
|
+
rule.grants.push(grant);
|
|
23186
|
+
rule.grants.sort((a, b) => {
|
|
23187
|
+
if (a.counterparty === b.counterparty) return a.action.localeCompare(b.action);
|
|
23188
|
+
return a.counterparty.localeCompare(b.counterparty);
|
|
23189
|
+
});
|
|
23190
|
+
}
|
|
23191
|
+
function applyChannelTemplate(id, params) {
|
|
23192
|
+
const entry = REGISTRY[id];
|
|
23193
|
+
if (!entry) {
|
|
23194
|
+
throw new Error(`unknown channel template: ${id}`);
|
|
23195
|
+
}
|
|
23196
|
+
return entry.factory(params);
|
|
23197
|
+
}
|
|
23198
|
+
var readOutputsOnly, bidirectionalSync, credentialShareScoped, planInspectReadOnly, escrowHandoff, REGISTRY;
|
|
23199
|
+
var init_channel_templates = __esm({
|
|
23200
|
+
"src/policy-engine/channel-templates.ts"() {
|
|
23201
|
+
init_constants();
|
|
23202
|
+
init_null_policy();
|
|
23203
|
+
readOutputsOnly = (params) => {
|
|
23204
|
+
const p = basePolicy(params);
|
|
23205
|
+
p.source_english = `${params.counterparty} may read ${params.agent_id}'s outputs \u2014 read-only, no other access.`;
|
|
23206
|
+
grantOn(p, "outputs", {
|
|
23207
|
+
counterparty: params.counterparty,
|
|
23208
|
+
action: "read",
|
|
23209
|
+
...params.scope ? { scope: { ...params.scope } } : {}
|
|
23210
|
+
});
|
|
23211
|
+
return p;
|
|
23212
|
+
};
|
|
23213
|
+
bidirectionalSync = (params) => {
|
|
23214
|
+
const p = basePolicy(params);
|
|
23215
|
+
p.source_english = `Bidirectional sync between ${params.agent_id} and ${params.counterparty} on memory + outputs.`;
|
|
23216
|
+
for (const slot of ["memory", "outputs"]) {
|
|
23217
|
+
for (const action of ["read", "subscribe"]) {
|
|
23218
|
+
grantOn(p, slot, {
|
|
23219
|
+
counterparty: params.counterparty,
|
|
23220
|
+
action,
|
|
23221
|
+
...params.scope ? { scope: { ...params.scope } } : {}
|
|
23222
|
+
});
|
|
23223
|
+
}
|
|
23224
|
+
}
|
|
23225
|
+
return p;
|
|
23226
|
+
};
|
|
23227
|
+
credentialShareScoped = (params) => {
|
|
23228
|
+
const p = basePolicy(params);
|
|
23229
|
+
const credentialId = (params.scope && typeof params.scope.credential_id === "string" ? params.scope.credential_id : void 0) ?? "(unspecified)";
|
|
23230
|
+
p.source_english = `${params.agent_id} may share credential "${credentialId}" with ${params.counterparty}; no other credential access.`;
|
|
23231
|
+
grantOn(p, "credentials", {
|
|
23232
|
+
counterparty: params.counterparty,
|
|
23233
|
+
action: "share",
|
|
23234
|
+
scope: {
|
|
23235
|
+
credential_id: credentialId,
|
|
23236
|
+
...params.scope ?? {}
|
|
23237
|
+
}
|
|
23238
|
+
});
|
|
23239
|
+
return p;
|
|
23240
|
+
};
|
|
23241
|
+
planInspectReadOnly = (params) => {
|
|
23242
|
+
const p = basePolicy(params);
|
|
23243
|
+
p.source_english = `${params.counterparty} may read-only inspect ${params.agent_id}'s plans.`;
|
|
23244
|
+
grantOn(p, "plans", {
|
|
23245
|
+
counterparty: params.counterparty,
|
|
23246
|
+
action: "read",
|
|
23247
|
+
...params.scope ? { scope: { ...params.scope } } : {}
|
|
23248
|
+
});
|
|
23249
|
+
return p;
|
|
23250
|
+
};
|
|
23251
|
+
escrowHandoff = (params) => {
|
|
23252
|
+
const p = basePolicy(params);
|
|
23253
|
+
p.source_english = `${params.agent_id} may escrow-handoff outputs and plan-read to ${params.counterparty}. Commitment class: intra-mesh-escrow.`;
|
|
23254
|
+
grantOn(p, "plans", {
|
|
23255
|
+
counterparty: params.counterparty,
|
|
23256
|
+
action: "read",
|
|
23257
|
+
...params.scope ? { scope: { ...params.scope } } : {}
|
|
23258
|
+
});
|
|
23259
|
+
for (const action of ["read", "subscribe"]) {
|
|
23260
|
+
grantOn(p, "outputs", {
|
|
23261
|
+
counterparty: params.counterparty,
|
|
23262
|
+
action,
|
|
23263
|
+
...params.scope ? { scope: { ...params.scope } } : {}
|
|
23264
|
+
});
|
|
23265
|
+
}
|
|
23266
|
+
const cls = "intra-mesh-escrow";
|
|
23267
|
+
if (!p.capabilities.concordia_commitment_classes.includes(cls)) {
|
|
23268
|
+
p.capabilities.concordia_commitment_classes.push(cls);
|
|
23269
|
+
p.capabilities.concordia_commitment_classes.sort();
|
|
23270
|
+
}
|
|
23271
|
+
return p;
|
|
23272
|
+
};
|
|
23273
|
+
REGISTRY = {
|
|
23274
|
+
"read-outputs-only": {
|
|
23275
|
+
id: "read-outputs-only",
|
|
23276
|
+
label: "Read outputs only",
|
|
23277
|
+
description: "Counterparty may read this agent's outputs. No memory, credentials, or plans access.",
|
|
23278
|
+
factory: readOutputsOnly
|
|
23279
|
+
},
|
|
23280
|
+
"bidirectional-sync": {
|
|
23281
|
+
id: "bidirectional-sync",
|
|
23282
|
+
label: "Bidirectional memory + output sync",
|
|
23283
|
+
description: "Both agents may read and subscribe to each other's memory and outputs. Credentials and plans remain hermetic.",
|
|
23284
|
+
factory: bidirectionalSync
|
|
23285
|
+
},
|
|
23286
|
+
"credential-share-scoped": {
|
|
23287
|
+
id: "credential-share-scoped",
|
|
23288
|
+
label: "Scoped credential share",
|
|
23289
|
+
description: "Share one specific credential with counterparty. Requires scope.credential_id. No broad credential access.",
|
|
23290
|
+
factory: credentialShareScoped
|
|
23291
|
+
},
|
|
23292
|
+
"plan-inspect-read-only": {
|
|
23293
|
+
id: "plan-inspect-read-only",
|
|
23294
|
+
label: "Plan inspect (read-only)",
|
|
23295
|
+
description: "Counterparty may read-only inspect this agent's plans. Intended for supervisor / sentinel patterns.",
|
|
23296
|
+
factory: planInspectReadOnly
|
|
23297
|
+
},
|
|
23298
|
+
"escrow-handoff": {
|
|
23299
|
+
id: "escrow-handoff",
|
|
23300
|
+
label: "Escrow handoff",
|
|
23301
|
+
description: "Escrow-style handoff. Counterparty reads plan, reads + subscribes to outputs, and the intra-mesh-escrow commitment class is declared. Memory and credentials remain hermetic.",
|
|
23302
|
+
factory: escrowHandoff
|
|
23303
|
+
}
|
|
23304
|
+
};
|
|
23305
|
+
}
|
|
23306
|
+
});
|
|
23307
|
+
|
|
23308
|
+
// src/mesh/errors.ts
|
|
23309
|
+
var MeshError, MeshEnvelopeError, MeshReservedExtensionKeyError, MeshReservedEventTypeError;
|
|
23310
|
+
var init_errors = __esm({
|
|
23311
|
+
"src/mesh/errors.ts"() {
|
|
23312
|
+
MeshError = class extends Error {
|
|
23313
|
+
constructor(message) {
|
|
23314
|
+
super(message);
|
|
23315
|
+
this.name = "MeshError";
|
|
23316
|
+
}
|
|
23317
|
+
};
|
|
23318
|
+
MeshEnvelopeError = class extends MeshError {
|
|
23319
|
+
constructor(message) {
|
|
23320
|
+
super(message);
|
|
23321
|
+
this.name = "MeshEnvelopeError";
|
|
23322
|
+
}
|
|
23323
|
+
};
|
|
23324
|
+
MeshReservedExtensionKeyError = class extends MeshEnvelopeError {
|
|
23325
|
+
constructor(key) {
|
|
23326
|
+
super(
|
|
23327
|
+
`v0.1 emitters MUST NOT populate reserved extension_envelope key: ${key}`
|
|
23328
|
+
);
|
|
23329
|
+
this.name = "MeshReservedExtensionKeyError";
|
|
23330
|
+
}
|
|
23331
|
+
};
|
|
23332
|
+
MeshReservedEventTypeError = class extends MeshEnvelopeError {
|
|
23333
|
+
constructor(eventType) {
|
|
23334
|
+
super(
|
|
23335
|
+
`v0.1 emitters MUST NOT emit reserved-namespace event_type: ${eventType}`
|
|
23336
|
+
);
|
|
23337
|
+
this.name = "MeshReservedEventTypeError";
|
|
23338
|
+
}
|
|
23339
|
+
};
|
|
23340
|
+
}
|
|
23341
|
+
});
|
|
23342
|
+
|
|
23343
|
+
// src/mesh/canonical-json.ts
|
|
23344
|
+
function canonicalize2(value) {
|
|
23345
|
+
if (value === void 0) {
|
|
23346
|
+
throw new MeshCanonicalJsonError(
|
|
23347
|
+
"canonicalize(): top-level undefined is not serializable"
|
|
23348
|
+
);
|
|
23349
|
+
}
|
|
23350
|
+
return encode(value);
|
|
23351
|
+
}
|
|
23352
|
+
function encode(value) {
|
|
23353
|
+
if (value === null) return "null";
|
|
23354
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
23355
|
+
if (typeof value === "number") {
|
|
23356
|
+
if (!Number.isFinite(value)) {
|
|
23357
|
+
throw new MeshCanonicalJsonError(
|
|
23358
|
+
`canonicalize(): non-finite number (${String(value)}) is not serializable`
|
|
23359
|
+
);
|
|
23360
|
+
}
|
|
23361
|
+
return JSON.stringify(value);
|
|
23362
|
+
}
|
|
23363
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
23364
|
+
if (Array.isArray(value)) return encodeArray(value);
|
|
23365
|
+
if (typeof value === "object") return encodeObject(value);
|
|
23366
|
+
throw new MeshCanonicalJsonError(
|
|
23367
|
+
`canonicalize(): unsupported type ${typeof value}`
|
|
23368
|
+
);
|
|
23369
|
+
}
|
|
23370
|
+
function encodeArray(arr) {
|
|
23371
|
+
const parts = [];
|
|
23372
|
+
for (const item of arr) {
|
|
23373
|
+
parts.push(item === void 0 ? "null" : encode(item));
|
|
23374
|
+
}
|
|
23375
|
+
return "[" + parts.join(",") + "]";
|
|
23376
|
+
}
|
|
23377
|
+
function encodeObject(obj) {
|
|
23378
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
23379
|
+
const parts = [];
|
|
23380
|
+
for (const k of keys) {
|
|
23381
|
+
parts.push(JSON.stringify(k) + ":" + encode(obj[k]));
|
|
23382
|
+
}
|
|
23383
|
+
return "{" + parts.join(",") + "}";
|
|
23384
|
+
}
|
|
23385
|
+
function canonicalizeToBytes(value) {
|
|
23386
|
+
return new TextEncoder().encode(canonicalize2(value));
|
|
23387
|
+
}
|
|
23388
|
+
var MeshCanonicalJsonError;
|
|
23389
|
+
var init_canonical_json = __esm({
|
|
23390
|
+
"src/mesh/canonical-json.ts"() {
|
|
23391
|
+
init_errors();
|
|
23392
|
+
MeshCanonicalJsonError = class extends MeshError {
|
|
23393
|
+
constructor(message) {
|
|
23394
|
+
super(message);
|
|
23395
|
+
this.name = "MeshCanonicalJsonError";
|
|
23396
|
+
}
|
|
23397
|
+
};
|
|
23398
|
+
}
|
|
23399
|
+
});
|
|
23400
|
+
|
|
23401
|
+
// src/policy-engine/errors.ts
|
|
23402
|
+
var PolicyEngineError, CompiledPolicyShapeError;
|
|
23403
|
+
var init_errors2 = __esm({
|
|
23404
|
+
"src/policy-engine/errors.ts"() {
|
|
23405
|
+
PolicyEngineError = class extends Error {
|
|
23406
|
+
constructor(message) {
|
|
23407
|
+
super(message);
|
|
23408
|
+
this.name = "PolicyEngineError";
|
|
23409
|
+
}
|
|
23410
|
+
};
|
|
23411
|
+
CompiledPolicyShapeError = class extends PolicyEngineError {
|
|
23412
|
+
constructor(message) {
|
|
23413
|
+
super(message);
|
|
23414
|
+
this.name = "CompiledPolicyShapeError";
|
|
23415
|
+
}
|
|
23416
|
+
};
|
|
23417
|
+
}
|
|
23418
|
+
});
|
|
23419
|
+
|
|
23420
|
+
// src/policy-engine/canonical-policy.ts
|
|
23421
|
+
function checkSlotGrant(value, path) {
|
|
23422
|
+
if (typeof value !== "object" || value === null) {
|
|
23423
|
+
throw new CompiledPolicyShapeError(`${path}: grant must be an object`);
|
|
23424
|
+
}
|
|
23425
|
+
const g = value;
|
|
23426
|
+
if (typeof g.counterparty !== "string" || g.counterparty.length === 0) {
|
|
23427
|
+
throw new CompiledPolicyShapeError(
|
|
23428
|
+
`${path}.counterparty must be a non-empty string`
|
|
23429
|
+
);
|
|
23430
|
+
}
|
|
23431
|
+
if (typeof g.action !== "string" || g.action.length === 0) {
|
|
23432
|
+
throw new CompiledPolicyShapeError(
|
|
23433
|
+
`${path}.action must be a non-empty string`
|
|
23434
|
+
);
|
|
23435
|
+
}
|
|
23436
|
+
if (g.scope !== void 0) {
|
|
23437
|
+
if (typeof g.scope !== "object" || g.scope === null || Array.isArray(g.scope)) {
|
|
23438
|
+
throw new CompiledPolicyShapeError(
|
|
23439
|
+
`${path}.scope must be an object when present`
|
|
23440
|
+
);
|
|
23441
|
+
}
|
|
23442
|
+
}
|
|
23443
|
+
if (g.max_uses_per_day !== void 0) {
|
|
23444
|
+
if (typeof g.max_uses_per_day !== "number" || !Number.isInteger(g.max_uses_per_day) || g.max_uses_per_day < 0) {
|
|
23445
|
+
throw new CompiledPolicyShapeError(
|
|
23446
|
+
`${path}.max_uses_per_day must be a non-negative integer when present`
|
|
23447
|
+
);
|
|
23448
|
+
}
|
|
23449
|
+
}
|
|
23450
|
+
}
|
|
23451
|
+
function checkSlotRule(slot, value, path) {
|
|
23452
|
+
if (typeof value !== "object" || value === null) {
|
|
23453
|
+
throw new CompiledPolicyShapeError(`${path}: slot rule must be an object`);
|
|
23454
|
+
}
|
|
23455
|
+
const r = value;
|
|
23456
|
+
if (r.slot !== slot) {
|
|
23457
|
+
throw new CompiledPolicyShapeError(
|
|
23458
|
+
`${path}.slot expected ${slot}, got ${String(r.slot)}`
|
|
23459
|
+
);
|
|
23460
|
+
}
|
|
23461
|
+
if (r.mode !== "deny" && r.mode !== "grant") {
|
|
23462
|
+
throw new CompiledPolicyShapeError(
|
|
23463
|
+
`${path}.mode must be "deny" or "grant"`
|
|
23464
|
+
);
|
|
23465
|
+
}
|
|
23466
|
+
if (!Array.isArray(r.grants)) {
|
|
23467
|
+
throw new CompiledPolicyShapeError(
|
|
23468
|
+
`${path}.grants must be an array (possibly empty)`
|
|
23469
|
+
);
|
|
23470
|
+
}
|
|
23471
|
+
if (r.mode === "deny" && r.grants.length > 0) {
|
|
23472
|
+
throw new CompiledPolicyShapeError(
|
|
23473
|
+
`${path}: deny-mode slot rule must carry zero grants`
|
|
23474
|
+
);
|
|
23475
|
+
}
|
|
23476
|
+
r.grants.forEach((g, i) => checkSlotGrant(g, `${path}.grants[${i}]`));
|
|
23477
|
+
}
|
|
23478
|
+
function validateCompiledPolicyShape(candidate) {
|
|
23479
|
+
if (typeof candidate !== "object" || candidate === null) {
|
|
23480
|
+
throw new CompiledPolicyShapeError("compiled policy must be an object");
|
|
23481
|
+
}
|
|
23482
|
+
const p = candidate;
|
|
23483
|
+
if (p.schema_version !== COMPILED_POLICY_SCHEMA_VERSION) {
|
|
23484
|
+
throw new CompiledPolicyShapeError(
|
|
23485
|
+
`schema_version ${String(p.schema_version)} not supported at v0.1 (expected "${COMPILED_POLICY_SCHEMA_VERSION}")`
|
|
23486
|
+
);
|
|
23487
|
+
}
|
|
23488
|
+
if (typeof p.agent_id !== "string" || p.agent_id.length === 0) {
|
|
23489
|
+
throw new CompiledPolicyShapeError("agent_id must be a non-empty string");
|
|
23490
|
+
}
|
|
23491
|
+
if (typeof p.fortress_id !== "string" || p.fortress_id.length === 0) {
|
|
23492
|
+
throw new CompiledPolicyShapeError(
|
|
23493
|
+
"fortress_id must be a non-empty string"
|
|
23494
|
+
);
|
|
23495
|
+
}
|
|
23496
|
+
if (typeof p.policy_version !== "number" || !Number.isInteger(p.policy_version) || p.policy_version < 0) {
|
|
23497
|
+
throw new CompiledPolicyShapeError(
|
|
23498
|
+
"policy_version must be a non-negative integer"
|
|
23499
|
+
);
|
|
23500
|
+
}
|
|
23501
|
+
if (p.parent_version !== void 0 && (typeof p.parent_version !== "number" || !Number.isInteger(p.parent_version) || p.parent_version < 0)) {
|
|
23502
|
+
throw new CompiledPolicyShapeError(
|
|
23503
|
+
"parent_version must be a non-negative integer when present"
|
|
23504
|
+
);
|
|
23505
|
+
}
|
|
23506
|
+
if (typeof p.slots !== "object" || p.slots === null) {
|
|
23507
|
+
throw new CompiledPolicyShapeError("slots must be an object");
|
|
23508
|
+
}
|
|
23509
|
+
const slots = p.slots;
|
|
23510
|
+
const slotKeys = Object.keys(slots).sort();
|
|
23511
|
+
const expectedSlotKeys = [...POLICY_SLOTS].sort();
|
|
23512
|
+
if (slotKeys.length !== expectedSlotKeys.length || !slotKeys.every((k, i) => k === expectedSlotKeys[i])) {
|
|
23513
|
+
throw new CompiledPolicyShapeError(
|
|
23514
|
+
`slots must contain exactly ${expectedSlotKeys.join(", ")}; got ${slotKeys.join(", ") || "(none)"}`
|
|
23515
|
+
);
|
|
23516
|
+
}
|
|
23517
|
+
for (const slot of POLICY_SLOTS) {
|
|
23518
|
+
checkSlotRule(slot, slots[slot], `slots.${slot}`);
|
|
23519
|
+
}
|
|
23520
|
+
if (typeof p.capabilities !== "object" || p.capabilities === null) {
|
|
23521
|
+
throw new CompiledPolicyShapeError("capabilities must be an object");
|
|
23522
|
+
}
|
|
23523
|
+
const caps = p.capabilities;
|
|
23524
|
+
if (!Array.isArray(caps.concordia_commitment_classes) || !caps.concordia_commitment_classes.every((c) => typeof c === "string")) {
|
|
23525
|
+
throw new CompiledPolicyShapeError(
|
|
23526
|
+
"capabilities.concordia_commitment_classes must be a string[]"
|
|
23527
|
+
);
|
|
23528
|
+
}
|
|
23529
|
+
if (!Array.isArray(caps.honeypot_skill_ids) || !caps.honeypot_skill_ids.every((c) => typeof c === "string")) {
|
|
23530
|
+
throw new CompiledPolicyShapeError(
|
|
23531
|
+
"capabilities.honeypot_skill_ids must be a string[]"
|
|
23532
|
+
);
|
|
23533
|
+
}
|
|
23534
|
+
if (typeof caps.is_sentinel !== "boolean") {
|
|
23535
|
+
throw new CompiledPolicyShapeError(
|
|
23536
|
+
"capabilities.is_sentinel must be boolean"
|
|
23537
|
+
);
|
|
23538
|
+
}
|
|
23539
|
+
if (typeof p.auto_trigger_ladder !== "object" || p.auto_trigger_ladder === null) {
|
|
23540
|
+
throw new CompiledPolicyShapeError(
|
|
23541
|
+
"auto_trigger_ladder must be an object"
|
|
23542
|
+
);
|
|
23543
|
+
}
|
|
23544
|
+
const lad = p.auto_trigger_ladder;
|
|
23545
|
+
if (typeof lad.honeypot_auto_freeze !== "boolean") {
|
|
23546
|
+
throw new CompiledPolicyShapeError(
|
|
23547
|
+
"auto_trigger_ladder.honeypot_auto_freeze must be boolean"
|
|
23548
|
+
);
|
|
23549
|
+
}
|
|
23550
|
+
if (lad.threshold_rule_action !== "operator_approved" && lad.threshold_rule_action !== "auto") {
|
|
23551
|
+
throw new CompiledPolicyShapeError(
|
|
23552
|
+
'auto_trigger_ladder.threshold_rule_action must be "operator_approved" or "auto"'
|
|
23553
|
+
);
|
|
23554
|
+
}
|
|
23555
|
+
if (lad.ml_anomaly_action !== "operator_approved" && lad.ml_anomaly_action !== "auto") {
|
|
23556
|
+
throw new CompiledPolicyShapeError(
|
|
23557
|
+
'auto_trigger_ladder.ml_anomaly_action must be "operator_approved" or "auto"'
|
|
23558
|
+
);
|
|
23559
|
+
}
|
|
23560
|
+
if (typeof p.source_english !== "string") {
|
|
23561
|
+
throw new CompiledPolicyShapeError("source_english must be a string");
|
|
23562
|
+
}
|
|
23563
|
+
if (typeof p.compiled_at !== "string" || Number.isNaN(Date.parse(p.compiled_at))) {
|
|
23564
|
+
throw new CompiledPolicyShapeError(
|
|
23565
|
+
"compiled_at must be an ISO8601 timestamp string"
|
|
23566
|
+
);
|
|
23567
|
+
}
|
|
23568
|
+
for (const key of slotKeys) {
|
|
23569
|
+
if (!isPolicySlot(key)) {
|
|
23570
|
+
throw new CompiledPolicyShapeError(
|
|
23571
|
+
`slots.${key} is not a recognized policy slot`
|
|
23572
|
+
);
|
|
23573
|
+
}
|
|
23574
|
+
}
|
|
23575
|
+
if (p.egress !== void 0) {
|
|
23576
|
+
checkEgressPolicy(p.egress, "egress");
|
|
23577
|
+
}
|
|
23578
|
+
if (p.budgets !== void 0) {
|
|
23579
|
+
checkBudgetPolicy(p.budgets, "budgets");
|
|
23580
|
+
}
|
|
23581
|
+
if (p.retention !== void 0) {
|
|
23582
|
+
checkRetentionPolicy(p.retention, "retention");
|
|
23583
|
+
}
|
|
23584
|
+
}
|
|
23585
|
+
function checkEgressPolicy(value, path) {
|
|
23586
|
+
if (typeof value !== "object" || value === null) {
|
|
23587
|
+
throw new CompiledPolicyShapeError(`${path} must be an object`);
|
|
23588
|
+
}
|
|
23589
|
+
const eg = value;
|
|
23590
|
+
if (!Array.isArray(eg.allowlist)) {
|
|
23591
|
+
throw new CompiledPolicyShapeError(`${path}.allowlist must be an array`);
|
|
23592
|
+
}
|
|
23593
|
+
for (let i = 0; i < eg.allowlist.length; i++) {
|
|
23594
|
+
const rule = eg.allowlist[i];
|
|
23595
|
+
if (typeof rule !== "object" || rule === null) {
|
|
23596
|
+
throw new CompiledPolicyShapeError(
|
|
23597
|
+
`${path}.allowlist[${i}] must be an object`
|
|
23598
|
+
);
|
|
23599
|
+
}
|
|
23600
|
+
const r = rule;
|
|
23601
|
+
if (typeof r.destination !== "string" || r.destination.length === 0) {
|
|
23602
|
+
throw new CompiledPolicyShapeError(
|
|
23603
|
+
`${path}.allowlist[${i}].destination must be a non-empty string`
|
|
23604
|
+
);
|
|
23605
|
+
}
|
|
23606
|
+
if (!Array.isArray(r.methods) || !r.methods.every((m) => typeof m === "string")) {
|
|
23607
|
+
throw new CompiledPolicyShapeError(
|
|
23608
|
+
`${path}.allowlist[${i}].methods must be a string[]`
|
|
23609
|
+
);
|
|
23610
|
+
}
|
|
23611
|
+
}
|
|
23612
|
+
}
|
|
23613
|
+
function checkBudgetLimit(value, path) {
|
|
23614
|
+
if (typeof value !== "object" || value === null) {
|
|
23615
|
+
throw new CompiledPolicyShapeError(`${path} must be an object`);
|
|
23616
|
+
}
|
|
23617
|
+
const lim = value;
|
|
23618
|
+
if (typeof lim.amount !== "number" || lim.amount <= 0 || !Number.isFinite(lim.amount)) {
|
|
23619
|
+
throw new CompiledPolicyShapeError(
|
|
23620
|
+
`${path}.amount must be a positive finite number`
|
|
23621
|
+
);
|
|
23622
|
+
}
|
|
23623
|
+
if (!BUDGET_UNITS.includes(lim.unit)) {
|
|
23624
|
+
throw new CompiledPolicyShapeError(
|
|
23625
|
+
`${path}.unit must be one of: ${BUDGET_UNITS.join(", ")}`
|
|
23626
|
+
);
|
|
23627
|
+
}
|
|
23628
|
+
if (lim.soft_warn_threshold !== void 0) {
|
|
23629
|
+
if (typeof lim.soft_warn_threshold !== "number" || lim.soft_warn_threshold <= 0 || lim.soft_warn_threshold >= 1) {
|
|
23630
|
+
throw new CompiledPolicyShapeError(
|
|
23631
|
+
`${path}.soft_warn_threshold must be in (0, 1) when present`
|
|
23632
|
+
);
|
|
23633
|
+
}
|
|
23634
|
+
}
|
|
23635
|
+
}
|
|
23636
|
+
function checkBudgetPolicy(value, path) {
|
|
23637
|
+
if (typeof value !== "object" || value === null) {
|
|
23638
|
+
throw new CompiledPolicyShapeError(`${path} must be an object`);
|
|
23639
|
+
}
|
|
23640
|
+
const bp = value;
|
|
23641
|
+
if (bp.daily !== void 0) checkBudgetLimit(bp.daily, `${path}.daily`);
|
|
23642
|
+
if (bp.monthly !== void 0) checkBudgetLimit(bp.monthly, `${path}.monthly`);
|
|
23643
|
+
if (bp.daily === void 0 && bp.monthly === void 0) {
|
|
23644
|
+
throw new CompiledPolicyShapeError(
|
|
23645
|
+
`${path} must have at least one of daily or monthly`
|
|
23646
|
+
);
|
|
23647
|
+
}
|
|
23648
|
+
}
|
|
23649
|
+
function checkRetentionPolicy(value, path) {
|
|
23650
|
+
if (typeof value !== "object" || value === null) {
|
|
23651
|
+
throw new CompiledPolicyShapeError(`${path} must be an object`);
|
|
23652
|
+
}
|
|
23653
|
+
const rp = value;
|
|
23654
|
+
if (typeof rp.windows !== "object" || rp.windows === null) {
|
|
23655
|
+
throw new CompiledPolicyShapeError(`${path}.windows must be an object`);
|
|
23656
|
+
}
|
|
23657
|
+
const wins = rp.windows;
|
|
23658
|
+
for (const key of Object.keys(wins)) {
|
|
23659
|
+
if (!isPolicySlot(key)) {
|
|
23660
|
+
throw new CompiledPolicyShapeError(
|
|
23661
|
+
`${path}.windows.${key} is not a recognized policy slot`
|
|
23662
|
+
);
|
|
23663
|
+
}
|
|
23664
|
+
const w = wins[key];
|
|
23665
|
+
if (typeof w !== "object" || w === null) {
|
|
23666
|
+
throw new CompiledPolicyShapeError(
|
|
23667
|
+
`${path}.windows.${key} must be an object`
|
|
23668
|
+
);
|
|
23669
|
+
}
|
|
23670
|
+
const win = w;
|
|
23671
|
+
if (typeof win.max_age_seconds !== "number" || !Number.isInteger(win.max_age_seconds) || win.max_age_seconds <= 0) {
|
|
23672
|
+
throw new CompiledPolicyShapeError(
|
|
23673
|
+
`${path}.windows.${key}.max_age_seconds must be a positive integer`
|
|
23674
|
+
);
|
|
23675
|
+
}
|
|
23676
|
+
if (win.archive !== void 0 && typeof win.archive !== "boolean") {
|
|
23677
|
+
throw new CompiledPolicyShapeError(
|
|
23678
|
+
`${path}.windows.${key}.archive must be boolean when present`
|
|
23679
|
+
);
|
|
23680
|
+
}
|
|
23681
|
+
}
|
|
23682
|
+
}
|
|
23683
|
+
function encodePolicyBlob(policy) {
|
|
23684
|
+
validateCompiledPolicyShape(policy);
|
|
23685
|
+
return toBase64url(canonicalizeToBytes(policy));
|
|
23686
|
+
}
|
|
23687
|
+
var init_canonical_policy = __esm({
|
|
23688
|
+
"src/policy-engine/canonical-policy.ts"() {
|
|
23689
|
+
init_canonical_json();
|
|
23690
|
+
init_encoding();
|
|
23691
|
+
init_constants();
|
|
23692
|
+
init_errors2();
|
|
23693
|
+
}
|
|
23694
|
+
});
|
|
23695
|
+
|
|
23696
|
+
// src/mesh/constants.ts
|
|
23697
|
+
function isReservedEventType(s) {
|
|
23698
|
+
return RESERVED_EVENT_TYPE_PREFIXES.some((p) => s.startsWith(p));
|
|
23699
|
+
}
|
|
23700
|
+
function isReservedExtensionKey(k) {
|
|
23701
|
+
return RESERVED_EXTENSION_ENVELOPE_KEYS.includes(k);
|
|
23702
|
+
}
|
|
23703
|
+
var PROTOCOL_VERSION, RESERVED_EVENT_TYPE_PREFIXES, RESERVED_EXTENSION_ENVELOPE_KEYS;
|
|
23704
|
+
var init_constants2 = __esm({
|
|
23705
|
+
"src/mesh/constants.ts"() {
|
|
23706
|
+
PROTOCOL_VERSION = "0.1";
|
|
23707
|
+
RESERVED_EVENT_TYPE_PREFIXES = [
|
|
23708
|
+
"EXTENSION_",
|
|
23709
|
+
"cross_fortress_",
|
|
23710
|
+
"multi_master_"
|
|
23711
|
+
];
|
|
23712
|
+
RESERVED_EXTENSION_ENVELOPE_KEYS = [
|
|
23713
|
+
"cross_fortress_read_grant",
|
|
23714
|
+
"cross_fortress_read_query",
|
|
23715
|
+
"cross_fortress_read_response",
|
|
23716
|
+
"multi_master_policy_merge",
|
|
23717
|
+
"audit_replication_full_n_way",
|
|
23718
|
+
"auto_promote_canonical_audit",
|
|
23719
|
+
"agent_live_migration"
|
|
23720
|
+
];
|
|
23721
|
+
}
|
|
23722
|
+
});
|
|
23723
|
+
var init_trust_root = __esm({
|
|
23724
|
+
"src/mesh/trust-root.ts"() {
|
|
23725
|
+
init_encoding();
|
|
23726
|
+
init_identity();
|
|
23727
|
+
init_random();
|
|
23728
|
+
init_canonical_json();
|
|
23729
|
+
init_constants2();
|
|
23730
|
+
init_errors();
|
|
23731
|
+
}
|
|
23732
|
+
});
|
|
23733
|
+
function packSignedEvent(params) {
|
|
23734
|
+
if (isReservedEventType(params.event_type)) {
|
|
23735
|
+
throw new MeshReservedEventTypeError(params.event_type);
|
|
23736
|
+
}
|
|
23737
|
+
const ext = params.extension_envelope ?? {};
|
|
23738
|
+
for (const key of Object.keys(ext)) {
|
|
23739
|
+
if (isReservedExtensionKey(key)) {
|
|
23740
|
+
throw new MeshReservedExtensionKeyError(key);
|
|
23741
|
+
}
|
|
23742
|
+
}
|
|
23743
|
+
const payloadBytes = canonicalizeToBytes(params.payload);
|
|
23744
|
+
const payload_hash = toBase64url(sha256(payloadBytes));
|
|
23745
|
+
const body = {
|
|
23746
|
+
protocol_version: PROTOCOL_VERSION,
|
|
23747
|
+
event_type: params.event_type,
|
|
23748
|
+
event_id: generateEventId(),
|
|
23749
|
+
emitter_node: params.emitter_node,
|
|
23750
|
+
emitter_principal: params.emitter_principal,
|
|
23751
|
+
fortress_id: params.fortress_id,
|
|
23752
|
+
causal_parents: params.causal_parents ?? [],
|
|
23753
|
+
payload: params.payload,
|
|
23754
|
+
payload_hash,
|
|
23755
|
+
emitted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23756
|
+
monotonic_seq: params.monotonic_seq,
|
|
23757
|
+
extension_envelope: ext
|
|
23758
|
+
};
|
|
23759
|
+
const bytesToSign = canonicalizeToBytes(body);
|
|
23760
|
+
const nodeSig = ed25519.sign(bytesToSign, params.node_private_key);
|
|
23761
|
+
const evt = {
|
|
23762
|
+
...body,
|
|
23763
|
+
node_signature: toBase64url(nodeSig)
|
|
23764
|
+
};
|
|
23765
|
+
if (params.principal_private_key) {
|
|
23766
|
+
const principalSig = ed25519.sign(bytesToSign, params.principal_private_key);
|
|
23767
|
+
evt.principal_signature = toBase64url(principalSig);
|
|
23768
|
+
}
|
|
23769
|
+
return evt;
|
|
23770
|
+
}
|
|
23771
|
+
function generateEventId() {
|
|
23772
|
+
const bytes = randomBytes(16);
|
|
23773
|
+
let hex = "";
|
|
23774
|
+
for (const b of bytes) hex += b.toString(16).padStart(2, "0");
|
|
23775
|
+
return hex;
|
|
23776
|
+
}
|
|
23777
|
+
var init_envelope = __esm({
|
|
23778
|
+
"src/mesh/envelope.ts"() {
|
|
23779
|
+
init_encoding();
|
|
23780
|
+
init_random();
|
|
23781
|
+
init_canonical_json();
|
|
23782
|
+
init_constants2();
|
|
23783
|
+
init_errors();
|
|
23784
|
+
init_trust_root();
|
|
23785
|
+
}
|
|
23786
|
+
});
|
|
23787
|
+
|
|
23788
|
+
// src/policy-engine/envelope.ts
|
|
23789
|
+
function packPolicyUpdate(params) {
|
|
23790
|
+
validateCompiledPolicyShape(params.policy);
|
|
23791
|
+
const blob = encodePolicyBlob(params.policy);
|
|
23792
|
+
const payload = {
|
|
23793
|
+
agent_id: params.policy.agent_id,
|
|
23794
|
+
policy_version: params.policy.policy_version,
|
|
23795
|
+
policy_blob: blob,
|
|
23796
|
+
...params.policy.parent_version !== void 0 ? { parent_version: params.policy.parent_version } : {}
|
|
23797
|
+
};
|
|
23798
|
+
return packSignedEvent({
|
|
23799
|
+
event_type: POLICY_UPDATE_EVENT_TYPE,
|
|
23800
|
+
emitter_node: params.emitter_node,
|
|
23801
|
+
emitter_principal: params.emitter_principal,
|
|
23802
|
+
fortress_id: params.policy.fortress_id,
|
|
23803
|
+
causal_parents: params.causal_parents,
|
|
23804
|
+
payload,
|
|
23805
|
+
monotonic_seq: params.monotonic_seq,
|
|
23806
|
+
node_private_key: params.node_private_key,
|
|
23807
|
+
principal_private_key: params.principal_private_key
|
|
23808
|
+
});
|
|
23809
|
+
}
|
|
23810
|
+
var init_envelope2 = __esm({
|
|
23811
|
+
"src/policy-engine/envelope.ts"() {
|
|
23812
|
+
init_constants();
|
|
23813
|
+
init_canonical_policy();
|
|
23814
|
+
init_errors2();
|
|
23815
|
+
init_envelope();
|
|
23816
|
+
init_errors();
|
|
23817
|
+
}
|
|
23818
|
+
});
|
|
23819
|
+
|
|
23820
|
+
// src/templates/init.ts
|
|
23821
|
+
function deterministicCompiledAt(version) {
|
|
23822
|
+
const [major, minor, patch] = version.split(".").map(Number);
|
|
23823
|
+
const epoch = /* @__PURE__ */ new Date("2026-01-01T00:00:00.000Z");
|
|
23824
|
+
epoch.setUTCDate(epoch.getUTCDate() + (major - 1) * 365 + minor * 30 + patch);
|
|
23825
|
+
return epoch.toISOString();
|
|
23826
|
+
}
|
|
23827
|
+
function buildCompiledPolicyFromTemplate(bundle, params) {
|
|
23828
|
+
const policy = applyChannelTemplate(
|
|
23829
|
+
bundle.metadata.channel,
|
|
23830
|
+
{
|
|
23831
|
+
agent_id: params.agent_id,
|
|
23832
|
+
counterparty: params.counterparty,
|
|
23833
|
+
fortress_id: params.fortress_id,
|
|
23834
|
+
policy_version: params.policy_version
|
|
23835
|
+
}
|
|
23836
|
+
);
|
|
23837
|
+
policy.compiled_at = deterministicCompiledAt(bundle.metadata.version);
|
|
23838
|
+
policy.source_english = bundle.policy_english;
|
|
23839
|
+
if (bundle.defaults.egress.length > 0) {
|
|
23840
|
+
const egress = {
|
|
23841
|
+
allowlist: bundle.defaults.egress.map((e) => ({
|
|
23842
|
+
destination: e.destination,
|
|
23843
|
+
methods: [...e.methods]
|
|
23844
|
+
}))
|
|
23845
|
+
};
|
|
23846
|
+
policy.egress = egress;
|
|
23847
|
+
}
|
|
23848
|
+
const budgets = {};
|
|
23849
|
+
if (bundle.defaults.budgets.daily) {
|
|
23850
|
+
budgets.daily = { ...bundle.defaults.budgets.daily };
|
|
23851
|
+
}
|
|
23852
|
+
if (bundle.defaults.budgets.monthly) {
|
|
23853
|
+
budgets.monthly = { ...bundle.defaults.budgets.monthly };
|
|
23854
|
+
}
|
|
23855
|
+
if (budgets.daily || budgets.monthly) {
|
|
23856
|
+
policy.budgets = budgets;
|
|
23857
|
+
}
|
|
23858
|
+
const windows = bundle.defaults.retention.windows;
|
|
23859
|
+
if (Object.keys(windows).length > 0) {
|
|
23860
|
+
const retention = {
|
|
23861
|
+
windows: {}
|
|
23862
|
+
};
|
|
23863
|
+
for (const [slot, win] of Object.entries(windows)) {
|
|
23864
|
+
if (win) {
|
|
23865
|
+
retention.windows[slot] = { ...win };
|
|
23866
|
+
}
|
|
23867
|
+
}
|
|
23868
|
+
policy.retention = retention;
|
|
23869
|
+
}
|
|
23870
|
+
const classes = bundle.commitments.shapes.map((s) => s.commitment_class);
|
|
23871
|
+
for (const cls of classes) {
|
|
23872
|
+
if (!policy.capabilities.concordia_commitment_classes.includes(cls)) {
|
|
23873
|
+
policy.capabilities.concordia_commitment_classes.push(cls);
|
|
23874
|
+
}
|
|
23875
|
+
}
|
|
23876
|
+
policy.capabilities.concordia_commitment_classes.sort();
|
|
23877
|
+
validateCompiledPolicyShape(policy);
|
|
23878
|
+
return policy;
|
|
23879
|
+
}
|
|
23880
|
+
function initTemplate(params) {
|
|
23881
|
+
const bundle = getTemplate2(params.template_name);
|
|
23882
|
+
if (!bundle) {
|
|
23883
|
+
throw new Error(`unknown template: ${params.template_name}`);
|
|
23884
|
+
}
|
|
23885
|
+
const compiled = buildCompiledPolicyFromTemplate(bundle, {
|
|
23886
|
+
agent_id: params.agent_id,
|
|
23887
|
+
fortress_id: params.fortress_id,
|
|
23888
|
+
counterparty: params.counterparty ?? "*",
|
|
23889
|
+
policy_version: params.policy_version ?? 1
|
|
23890
|
+
});
|
|
23891
|
+
const signed_event = packPolicyUpdate({
|
|
23892
|
+
policy: compiled,
|
|
23893
|
+
emitter_node: params.emitter_node,
|
|
23894
|
+
emitter_principal: params.emitter_principal,
|
|
23895
|
+
monotonic_seq: params.monotonic_seq,
|
|
23896
|
+
node_private_key: params.node_private_key,
|
|
23897
|
+
principal_private_key: params.principal_private_key
|
|
23898
|
+
});
|
|
23899
|
+
return { compiled, signed_event, bundle };
|
|
23900
|
+
}
|
|
23901
|
+
var init_init = __esm({
|
|
23902
|
+
"src/templates/init.ts"() {
|
|
23903
|
+
init_channel_templates();
|
|
23904
|
+
init_canonical_policy();
|
|
23905
|
+
init_envelope2();
|
|
23906
|
+
init_registry2();
|
|
23907
|
+
}
|
|
23908
|
+
});
|
|
23909
|
+
function constantTimeEquals(a, b) {
|
|
23910
|
+
if (a.length !== b.length) return false;
|
|
23911
|
+
let diff = 0;
|
|
23912
|
+
for (let i = 0; i < a.length; i++) {
|
|
23913
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
23914
|
+
}
|
|
23915
|
+
return diff === 0;
|
|
23916
|
+
}
|
|
23917
|
+
function extractToken(req, url) {
|
|
23918
|
+
const header = req.headers.authorization;
|
|
23919
|
+
if (header && header.startsWith("Bearer ")) {
|
|
23920
|
+
return header.slice(7).trim();
|
|
23921
|
+
}
|
|
23922
|
+
const q = url.searchParams.get("token");
|
|
23923
|
+
return q ?? null;
|
|
23924
|
+
}
|
|
23925
|
+
function isAuthorized(deps, req, url) {
|
|
23926
|
+
if (!deps.authToken) return true;
|
|
23927
|
+
const token = extractToken(req, url);
|
|
23928
|
+
if (!token) return false;
|
|
23929
|
+
return constantTimeEquals(token, deps.authToken);
|
|
23930
|
+
}
|
|
23931
|
+
function writeJSON(res, status, payload) {
|
|
23932
|
+
res.writeHead(status, {
|
|
23933
|
+
"Content-Type": "application/json",
|
|
23934
|
+
"Cache-Control": "no-store"
|
|
23935
|
+
});
|
|
23936
|
+
res.end(JSON.stringify(payload));
|
|
23937
|
+
}
|
|
23938
|
+
async function readJSONBody(req) {
|
|
23939
|
+
const chunks = [];
|
|
23940
|
+
let size = 0;
|
|
23941
|
+
const MAX = 256 * 1024;
|
|
23942
|
+
for await (const chunk of req) {
|
|
23943
|
+
size += chunk.length;
|
|
23944
|
+
if (size > MAX) throw new Error("request body too large");
|
|
23945
|
+
chunks.push(chunk);
|
|
23946
|
+
}
|
|
23947
|
+
const body = Buffer.concat(chunks).toString("utf-8");
|
|
23948
|
+
if (!body) return {};
|
|
23949
|
+
return JSON.parse(body);
|
|
23950
|
+
}
|
|
23951
|
+
function generateEphemeralKey() {
|
|
23952
|
+
return new Uint8Array(randomBytes$1(32));
|
|
23953
|
+
}
|
|
23954
|
+
function writeText(res, status, body, contentType = "text/plain") {
|
|
23955
|
+
res.writeHead(status, {
|
|
23956
|
+
"Content-Type": contentType,
|
|
23957
|
+
"Cache-Control": "no-store"
|
|
23958
|
+
});
|
|
23959
|
+
res.end(body);
|
|
23960
|
+
}
|
|
23961
|
+
async function handleRequest(deps, req, res) {
|
|
23962
|
+
const host = req.headers.host || "localhost";
|
|
23963
|
+
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
23964
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
23965
|
+
const path = url.pathname;
|
|
23966
|
+
if (!isAuthorized(deps, req, url)) {
|
|
23967
|
+
writeJSON(res, 401, { error: "unauthorized" });
|
|
23968
|
+
return true;
|
|
23969
|
+
}
|
|
23970
|
+
if (method === "GET" && path === "/api/health") {
|
|
23971
|
+
writeJSON(res, 200, { ok: true, mode: deps.sources.mode });
|
|
23972
|
+
return true;
|
|
23973
|
+
}
|
|
23974
|
+
if (method === "GET" && (path === "/" || path === "/index.html")) {
|
|
23975
|
+
const snapshot = await getProtectionSnapshot(deps.sources);
|
|
23976
|
+
const html = renderDashboardHTML({ snapshot, authToken: deps.authToken });
|
|
23977
|
+
writeText(res, 200, html, "text/html; charset=utf-8");
|
|
23978
|
+
return true;
|
|
23979
|
+
}
|
|
23980
|
+
if (method === "GET" && path === "/api/snapshot") {
|
|
23981
|
+
const snapshot = await getProtectionSnapshot(deps.sources);
|
|
23982
|
+
writeJSON(res, 200, snapshot);
|
|
23983
|
+
return true;
|
|
23984
|
+
}
|
|
23985
|
+
const approvalMatch = /^\/api\/approvals\/([^/]+)\/(allow|deny)$/.exec(path);
|
|
23986
|
+
if (method === "POST" && approvalMatch) {
|
|
23987
|
+
const id = decodeURIComponent(approvalMatch[1]);
|
|
23988
|
+
const action = approvalMatch[2];
|
|
23989
|
+
if (!deps.approvals) {
|
|
23990
|
+
writeJSON(res, 503, { error: "approvals_unavailable" });
|
|
23991
|
+
return true;
|
|
23992
|
+
}
|
|
23993
|
+
const handler = action === "allow" ? deps.approvals.allow : deps.approvals.deny;
|
|
23994
|
+
try {
|
|
23995
|
+
const ok2 = await handler(id);
|
|
23996
|
+
writeJSON(res, ok2 ? 200 : 404, { id, action, ok: ok2 });
|
|
23997
|
+
} catch (err) {
|
|
23998
|
+
writeJSON(res, 500, { error: "approval_failed", message: err.message });
|
|
23999
|
+
}
|
|
24000
|
+
return true;
|
|
24001
|
+
}
|
|
24002
|
+
if (method === "GET" && path === "/api/stream") {
|
|
24003
|
+
await handleStream(deps, res);
|
|
24004
|
+
return true;
|
|
24005
|
+
}
|
|
24006
|
+
if (method === "GET" && path === "/api/templates") {
|
|
24007
|
+
try {
|
|
24008
|
+
const templates = listTemplates();
|
|
24009
|
+
writeJSON(res, 200, { templates });
|
|
24010
|
+
} catch (err) {
|
|
24011
|
+
writeJSON(res, 500, {
|
|
24012
|
+
error: "template_load_failed",
|
|
24013
|
+
message: err.message
|
|
24014
|
+
});
|
|
24015
|
+
}
|
|
24016
|
+
return true;
|
|
24017
|
+
}
|
|
24018
|
+
const templateMatch = /^\/api\/templates\/([^/]+)$/.exec(path);
|
|
24019
|
+
if (method === "GET" && templateMatch) {
|
|
24020
|
+
const name = decodeURIComponent(templateMatch[1]);
|
|
24021
|
+
try {
|
|
24022
|
+
const entry = getTemplateEntry(name);
|
|
24023
|
+
if (!entry) {
|
|
24024
|
+
writeJSON(res, 404, { error: "template_not_found", name });
|
|
24025
|
+
return true;
|
|
24026
|
+
}
|
|
24027
|
+
writeJSON(res, 200, entry);
|
|
24028
|
+
} catch (err) {
|
|
24029
|
+
writeJSON(res, 500, {
|
|
24030
|
+
error: "template_load_failed",
|
|
24031
|
+
message: err.message
|
|
24032
|
+
});
|
|
24033
|
+
}
|
|
24034
|
+
return true;
|
|
24035
|
+
}
|
|
24036
|
+
const initMatch = /^\/api\/templates\/([^/]+)\/init$/.exec(path);
|
|
24037
|
+
if (method === "POST" && initMatch) {
|
|
24038
|
+
const name = decodeURIComponent(initMatch[1]);
|
|
24039
|
+
try {
|
|
24040
|
+
const bundle = getTemplate2(name);
|
|
24041
|
+
if (!bundle) {
|
|
24042
|
+
writeJSON(res, 404, { error: "template_not_found", name });
|
|
24043
|
+
return true;
|
|
24044
|
+
}
|
|
24045
|
+
const body = await readJSONBody(req);
|
|
24046
|
+
if (!body.agent_name || typeof body.agent_name !== "string") {
|
|
24047
|
+
writeJSON(res, 400, {
|
|
24048
|
+
error: "validation_error",
|
|
24049
|
+
message: "agent_name is required and must be a string"
|
|
24050
|
+
});
|
|
24051
|
+
return true;
|
|
24052
|
+
}
|
|
24053
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(body.agent_name)) {
|
|
24054
|
+
writeJSON(res, 400, {
|
|
24055
|
+
error: "validation_error",
|
|
24056
|
+
message: "agent_name must contain only alphanumeric characters, hyphens, and underscores"
|
|
24057
|
+
});
|
|
24058
|
+
return true;
|
|
24059
|
+
}
|
|
24060
|
+
const nodeId = deps.nodeId ?? "dashboard-node";
|
|
24061
|
+
const nodePrivateKey = deps.nodePrivateKey ?? generateEphemeralKey();
|
|
24062
|
+
const principalId = deps.principalId ?? "dashboard-principal";
|
|
24063
|
+
const fortressId = deps.fortressId ?? "default";
|
|
24064
|
+
const result = initTemplate({
|
|
24065
|
+
template_name: name,
|
|
24066
|
+
agent_id: body.agent_name,
|
|
24067
|
+
fortress_id: fortressId,
|
|
24068
|
+
counterparty: "*",
|
|
24069
|
+
policy_version: 1,
|
|
24070
|
+
emitter_node: nodeId,
|
|
24071
|
+
emitter_principal: principalId,
|
|
24072
|
+
monotonic_seq: 1,
|
|
24073
|
+
node_private_key: nodePrivateKey
|
|
24074
|
+
});
|
|
24075
|
+
writeJSON(res, 200, {
|
|
24076
|
+
agent_id: body.agent_name,
|
|
24077
|
+
signed_event_id: result.signed_event.event_id,
|
|
24078
|
+
policy_version: result.compiled.policy_version,
|
|
24079
|
+
template_name: name,
|
|
24080
|
+
attestation_panel_url: `/console#agent_roster`
|
|
24081
|
+
});
|
|
24082
|
+
} catch (err) {
|
|
24083
|
+
writeJSON(res, 500, {
|
|
24084
|
+
error: "template_init_failed",
|
|
24085
|
+
message: err.message
|
|
24086
|
+
});
|
|
24087
|
+
}
|
|
24088
|
+
return true;
|
|
24089
|
+
}
|
|
24090
|
+
return false;
|
|
24091
|
+
}
|
|
24092
|
+
async function handleStream(deps, res) {
|
|
24093
|
+
res.writeHead(200, {
|
|
24094
|
+
"Content-Type": "text/event-stream",
|
|
24095
|
+
"Cache-Control": "no-cache, no-transform",
|
|
24096
|
+
Connection: "keep-alive",
|
|
24097
|
+
"X-Accel-Buffering": "no"
|
|
24098
|
+
});
|
|
24099
|
+
const snapshot = await getProtectionSnapshot(deps.sources);
|
|
24100
|
+
res.write(`event: snapshot
|
|
24101
|
+
data: ${JSON.stringify(snapshot)}
|
|
24102
|
+
|
|
24103
|
+
`);
|
|
24104
|
+
const unsubscribe = deps.onEvent ? deps.onEvent((event) => {
|
|
24105
|
+
try {
|
|
24106
|
+
res.write(`event: ${event.type}
|
|
24107
|
+
data: ${JSON.stringify(event.data)}
|
|
24108
|
+
|
|
24109
|
+
`);
|
|
24110
|
+
} catch {
|
|
24111
|
+
}
|
|
24112
|
+
}) : () => {
|
|
24113
|
+
};
|
|
24114
|
+
const keepAlive = setInterval(() => {
|
|
24115
|
+
try {
|
|
24116
|
+
res.write(": keepalive\n\n");
|
|
24117
|
+
} catch {
|
|
24118
|
+
}
|
|
24119
|
+
}, 25e3);
|
|
24120
|
+
const cleanup = () => {
|
|
24121
|
+
clearInterval(keepAlive);
|
|
24122
|
+
unsubscribe();
|
|
24123
|
+
};
|
|
24124
|
+
res.on("close", cleanup);
|
|
24125
|
+
res.on("error", cleanup);
|
|
24126
|
+
}
|
|
24127
|
+
var init_api = __esm({
|
|
24128
|
+
"src/dashboard/api.ts"() {
|
|
24129
|
+
init_aggregator();
|
|
24130
|
+
init_html();
|
|
24131
|
+
init_registry2();
|
|
24132
|
+
init_init();
|
|
24133
|
+
}
|
|
24134
|
+
});
|
|
24135
|
+
async function startDashboardServer(options) {
|
|
24136
|
+
const port = options.port ?? DEFAULT_PORT;
|
|
24137
|
+
const host = options.host ?? DEFAULT_HOST;
|
|
24138
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
24139
|
+
const onEvent = (listener) => {
|
|
24140
|
+
listeners.add(listener);
|
|
24141
|
+
return () => listeners.delete(listener);
|
|
24142
|
+
};
|
|
24143
|
+
const publish = (event) => {
|
|
24144
|
+
for (const listener of listeners) {
|
|
24145
|
+
try {
|
|
24146
|
+
listener(event);
|
|
24147
|
+
} catch {
|
|
24148
|
+
}
|
|
24149
|
+
}
|
|
24150
|
+
};
|
|
24151
|
+
const deps = {
|
|
24152
|
+
sources: options.sources,
|
|
24153
|
+
authToken: options.authToken,
|
|
24154
|
+
approvals: options.approvals,
|
|
24155
|
+
onEvent
|
|
24156
|
+
};
|
|
24157
|
+
const server = createServer$2(async (req, res) => {
|
|
24158
|
+
try {
|
|
24159
|
+
const served = await handleRequest(deps, req, res);
|
|
24160
|
+
if (!served) {
|
|
24161
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
24162
|
+
res.end(JSON.stringify({ error: "not_found", path: req.url }));
|
|
24163
|
+
}
|
|
24164
|
+
} catch (err) {
|
|
24165
|
+
try {
|
|
24166
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
24167
|
+
res.end(JSON.stringify({ error: "internal", message: err.message }));
|
|
24168
|
+
} catch {
|
|
24169
|
+
}
|
|
24170
|
+
}
|
|
24171
|
+
});
|
|
24172
|
+
await new Promise((resolve2, reject) => {
|
|
24173
|
+
server.once("error", reject);
|
|
24174
|
+
server.listen(port, host, () => {
|
|
24175
|
+
server.off("error", reject);
|
|
24176
|
+
resolve2();
|
|
24177
|
+
});
|
|
24178
|
+
});
|
|
24179
|
+
const actualPort = (() => {
|
|
24180
|
+
const addr = server.address();
|
|
24181
|
+
if (addr && typeof addr === "object") return addr.port;
|
|
24182
|
+
return port;
|
|
24183
|
+
})();
|
|
24184
|
+
const url = `http://${host}:${actualPort}`;
|
|
24185
|
+
return {
|
|
24186
|
+
url,
|
|
24187
|
+
port: actualPort,
|
|
24188
|
+
host,
|
|
24189
|
+
stop: () => new Promise((resolve2, reject) => {
|
|
24190
|
+
server.close((err) => err ? reject(err) : resolve2());
|
|
24191
|
+
}),
|
|
24192
|
+
publish,
|
|
24193
|
+
publishActivity: (entry) => publish({ type: "activity", data: entry }),
|
|
24194
|
+
publishApproval: (approval) => publish({ type: "approval", data: approval })
|
|
24195
|
+
};
|
|
24196
|
+
}
|
|
24197
|
+
var DEFAULT_PORT, DEFAULT_HOST;
|
|
24198
|
+
var init_server = __esm({
|
|
24199
|
+
"src/dashboard/server.ts"() {
|
|
24200
|
+
init_api();
|
|
24201
|
+
DEFAULT_PORT = 3501;
|
|
24202
|
+
DEFAULT_HOST = "127.0.0.1";
|
|
24203
|
+
}
|
|
24204
|
+
});
|
|
24205
|
+
|
|
24206
|
+
// src/dashboard/index.ts
|
|
24207
|
+
async function startDashboard(options) {
|
|
24208
|
+
const activity = options.initialActivity ? [...options.initialActivity] : [];
|
|
24209
|
+
const pending = options.initialPendingApprovals ? [...options.initialPendingApprovals] : [];
|
|
24210
|
+
const sources = {
|
|
24211
|
+
mode: options.mode,
|
|
24212
|
+
server_version: options.serverVersion,
|
|
24213
|
+
...options.auditLog ? { auditLog: options.auditLog } : {},
|
|
24214
|
+
...options.identityManager ? { identityManager: options.identityManager } : {},
|
|
24215
|
+
...options.clientManager ? { clientManager: options.clientManager } : {},
|
|
24216
|
+
...options.baseline ? { baseline: options.baseline } : {},
|
|
22929
24217
|
...options.policy ? { policy: options.policy } : {},
|
|
22930
24218
|
...options.reputation ? { reputation: options.reputation } : {},
|
|
22931
24219
|
...options.teeAvailable != null ? { teeAvailable: options.teeAvailable } : {},
|
|
@@ -23674,6 +24962,90 @@ var init_paths = __esm({
|
|
|
23674
24962
|
DEFAULT_DASHBOARD_PORT = 3501;
|
|
23675
24963
|
}
|
|
23676
24964
|
});
|
|
24965
|
+
function getPlatformPaths() {
|
|
24966
|
+
const home = homedir();
|
|
24967
|
+
return {
|
|
24968
|
+
"openclaw": [
|
|
24969
|
+
join(home, ".openclaw", "openclaw.json"),
|
|
24970
|
+
join(home, ".openclaw", "config.json"),
|
|
24971
|
+
join(home, "Library", "Application Support", "OpenClaw", "openclaw.json"),
|
|
24972
|
+
join(home, "Library", "Application Support", "OpenClaw", "config.json")
|
|
24973
|
+
],
|
|
24974
|
+
// Hermes Agent (NousResearch, v0.9.0) canonicals live under ~/.hermes.
|
|
24975
|
+
// Hermes ships `cli-config.yaml` as the primary surface per upstream docs.
|
|
24976
|
+
// Sanctuary wrap v1.0 detects the JSON variant only: operators who keep
|
|
24977
|
+
// YAML can still wrap via `sanctuary wrap --wrap <path>` after exporting
|
|
24978
|
+
// to JSON. YAML-native detection is flagged as a v1.x follow-up.
|
|
24979
|
+
"hermes": [
|
|
24980
|
+
join(home, ".hermes", "cli-config.json"),
|
|
24981
|
+
join(home, ".hermes", "config.json"),
|
|
24982
|
+
join(home, ".config", "hermes", "cli-config.json")
|
|
24983
|
+
],
|
|
24984
|
+
// Claude Code's modern canonical surface is ~/.claude.json (`claude mcp
|
|
24985
|
+
// add` writes here). The legacy ~/.claude/settings.json shape predates
|
|
24986
|
+
// it and is still respected if present. Probe order = preference order:
|
|
24987
|
+
// wrap operates on the first one that exists, and bootstraps a fresh
|
|
24988
|
+
// ~/.claude.json when neither is present (per the cli.ts bootstrap).
|
|
24989
|
+
"claude-code": [
|
|
24990
|
+
join(home, ".claude.json"),
|
|
24991
|
+
join(home, ".claude", "settings.json"),
|
|
24992
|
+
join(home, ".config", "claude-code", "settings.json")
|
|
24993
|
+
],
|
|
24994
|
+
"cursor": [
|
|
24995
|
+
join(home, ".cursor", "mcp.json")
|
|
24996
|
+
],
|
|
24997
|
+
// Cline is a VS Code extension (saoudrizwan.claude-dev). Its MCP settings
|
|
24998
|
+
// live under the VS Code globalStorage tree, which is OS-specific. We
|
|
24999
|
+
// enumerate the three supported OS layouts; at detection time only the
|
|
25000
|
+
// one matching the running OS will exist.
|
|
25001
|
+
"cline": [
|
|
25002
|
+
// macOS
|
|
25003
|
+
join(
|
|
25004
|
+
home,
|
|
25005
|
+
"Library",
|
|
25006
|
+
"Application Support",
|
|
25007
|
+
"Code",
|
|
25008
|
+
"User",
|
|
25009
|
+
"globalStorage",
|
|
25010
|
+
"saoudrizwan.claude-dev",
|
|
25011
|
+
"settings",
|
|
25012
|
+
"cline_mcp_settings.json"
|
|
25013
|
+
),
|
|
25014
|
+
// Linux
|
|
25015
|
+
join(
|
|
25016
|
+
home,
|
|
25017
|
+
".config",
|
|
25018
|
+
"Code",
|
|
25019
|
+
"User",
|
|
25020
|
+
"globalStorage",
|
|
25021
|
+
"saoudrizwan.claude-dev",
|
|
25022
|
+
"settings",
|
|
25023
|
+
"cline_mcp_settings.json"
|
|
25024
|
+
),
|
|
25025
|
+
// Windows (honour APPDATA when set, otherwise reconstruct under home)
|
|
25026
|
+
process.env.APPDATA ? join(
|
|
25027
|
+
process.env.APPDATA,
|
|
25028
|
+
"Code",
|
|
25029
|
+
"User",
|
|
25030
|
+
"globalStorage",
|
|
25031
|
+
"saoudrizwan.claude-dev",
|
|
25032
|
+
"settings",
|
|
25033
|
+
"cline_mcp_settings.json"
|
|
25034
|
+
) : join(
|
|
25035
|
+
home,
|
|
25036
|
+
"AppData",
|
|
25037
|
+
"Roaming",
|
|
25038
|
+
"Code",
|
|
25039
|
+
"User",
|
|
25040
|
+
"globalStorage",
|
|
25041
|
+
"saoudrizwan.claude-dev",
|
|
25042
|
+
"settings",
|
|
25043
|
+
"cline_mcp_settings.json"
|
|
25044
|
+
)
|
|
25045
|
+
],
|
|
25046
|
+
"generic": []
|
|
25047
|
+
};
|
|
25048
|
+
}
|
|
23677
25049
|
function backupDir() {
|
|
23678
25050
|
return join(resolveStoragePath(), "backup");
|
|
23679
25051
|
}
|
|
@@ -23717,7 +25089,7 @@ async function detectAgentConfigWithDiagnostics(platform4, configPath) {
|
|
|
23717
25089
|
return { config, pathsChecked, errors };
|
|
23718
25090
|
}
|
|
23719
25091
|
if (platform4) {
|
|
23720
|
-
const paths =
|
|
25092
|
+
const paths = getPlatformPaths()[platform4];
|
|
23721
25093
|
for (const path of paths) {
|
|
23722
25094
|
pathsChecked.push(path);
|
|
23723
25095
|
const { config, error } = await readConfigFileWithError(path, platform4);
|
|
@@ -23726,7 +25098,7 @@ async function detectAgentConfigWithDiagnostics(platform4, configPath) {
|
|
|
23726
25098
|
}
|
|
23727
25099
|
return { config: null, pathsChecked, errors };
|
|
23728
25100
|
}
|
|
23729
|
-
for (const [plat, paths] of Object.entries(
|
|
25101
|
+
for (const [plat, paths] of Object.entries(getPlatformPaths())) {
|
|
23730
25102
|
for (const path of paths) {
|
|
23731
25103
|
pathsChecked.push(path);
|
|
23732
25104
|
const { config, error } = await readConfigFileWithError(path, plat);
|
|
@@ -23784,7 +25156,7 @@ function extractServers(config, platform4) {
|
|
|
23784
25156
|
const mcpServers = obj.mcpServers;
|
|
23785
25157
|
if (mcpServers && typeof mcpServers === "object") {
|
|
23786
25158
|
for (const [name, serverConfig] of Object.entries(mcpServers)) {
|
|
23787
|
-
if (name
|
|
25159
|
+
if (isCanonicalSanctuaryName(name)) continue;
|
|
23788
25160
|
const entry = parseServerEntry(name, serverConfig);
|
|
23789
25161
|
if (entry) servers.push(entry);
|
|
23790
25162
|
}
|
|
@@ -23794,7 +25166,27 @@ function extractServers(config, platform4) {
|
|
|
23794
25166
|
const mcpServers = obj.mcpServers;
|
|
23795
25167
|
if (mcpServers && typeof mcpServers === "object") {
|
|
23796
25168
|
for (const [name, serverConfig] of Object.entries(mcpServers)) {
|
|
23797
|
-
if (name
|
|
25169
|
+
if (isCanonicalSanctuaryName(name)) continue;
|
|
25170
|
+
const entry = parseServerEntry(name, serverConfig);
|
|
25171
|
+
if (entry) servers.push(entry);
|
|
25172
|
+
}
|
|
25173
|
+
}
|
|
25174
|
+
}
|
|
25175
|
+
if (platform4 === "hermes") {
|
|
25176
|
+
const mcpServers = obj.mcp_servers;
|
|
25177
|
+
if (mcpServers && typeof mcpServers === "object") {
|
|
25178
|
+
for (const [name, serverConfig] of Object.entries(mcpServers)) {
|
|
25179
|
+
if (isCanonicalSanctuaryName(name)) continue;
|
|
25180
|
+
const entry = parseServerEntry(name, serverConfig);
|
|
25181
|
+
if (entry) servers.push(entry);
|
|
25182
|
+
}
|
|
25183
|
+
}
|
|
25184
|
+
}
|
|
25185
|
+
if (platform4 === "cline") {
|
|
25186
|
+
const mcpServers = obj.mcpServers;
|
|
25187
|
+
if (mcpServers && typeof mcpServers === "object") {
|
|
25188
|
+
for (const [name, serverConfig] of Object.entries(mcpServers)) {
|
|
25189
|
+
if (isCanonicalSanctuaryName(name)) continue;
|
|
23798
25190
|
const entry = parseServerEntry(name, serverConfig);
|
|
23799
25191
|
if (entry) servers.push(entry);
|
|
23800
25192
|
}
|
|
@@ -23802,6 +25194,9 @@ function extractServers(config, platform4) {
|
|
|
23802
25194
|
}
|
|
23803
25195
|
return servers;
|
|
23804
25196
|
}
|
|
25197
|
+
function isCanonicalSanctuaryName(name) {
|
|
25198
|
+
return name.toLowerCase() === "sanctuary";
|
|
25199
|
+
}
|
|
23805
25200
|
function parseServerEntry(name, config) {
|
|
23806
25201
|
if (!config || typeof config !== "object") return null;
|
|
23807
25202
|
const c = config;
|
|
@@ -23840,6 +25235,8 @@ async function rewriteConfigForCocoon(agentConfig, sanctuaryCommand, sanctuaryAr
|
|
|
23840
25235
|
if (agentConfig.platform === "openclaw") {
|
|
23841
25236
|
const existingMcp = raw.mcp ?? {};
|
|
23842
25237
|
existingServers = existingMcp.servers ?? {};
|
|
25238
|
+
} else if (agentConfig.platform === "hermes") {
|
|
25239
|
+
existingServers = raw.mcp_servers ?? {};
|
|
23843
25240
|
} else {
|
|
23844
25241
|
existingServers = raw.mcpServers ?? {};
|
|
23845
25242
|
}
|
|
@@ -23883,6 +25280,14 @@ async function rewriteConfigForCocoon(agentConfig, sanctuaryCommand, sanctuaryAr
|
|
|
23883
25280
|
}
|
|
23884
25281
|
};
|
|
23885
25282
|
delete rewritten.mcpServers;
|
|
25283
|
+
} else if (agentConfig.platform === "hermes") {
|
|
25284
|
+
rewritten = {
|
|
25285
|
+
...raw,
|
|
25286
|
+
mcp_servers: {
|
|
25287
|
+
...existingServers,
|
|
25288
|
+
sanctuary: sanctuaryEntry
|
|
25289
|
+
}
|
|
25290
|
+
};
|
|
23886
25291
|
} else {
|
|
23887
25292
|
rewritten = {
|
|
23888
25293
|
...raw,
|
|
@@ -23895,26 +25300,9 @@ async function rewriteConfigForCocoon(agentConfig, sanctuaryCommand, sanctuaryAr
|
|
|
23895
25300
|
await writeFile(agentConfig.configPath, JSON.stringify(rewritten, null, 2), { mode: 384 });
|
|
23896
25301
|
return agentConfig.configPath;
|
|
23897
25302
|
}
|
|
23898
|
-
var PLATFORM_PATHS;
|
|
23899
25303
|
var init_config_reader = __esm({
|
|
23900
25304
|
"src/cocoon/config-reader.ts"() {
|
|
23901
25305
|
init_paths();
|
|
23902
|
-
PLATFORM_PATHS = {
|
|
23903
|
-
"openclaw": [
|
|
23904
|
-
join(homedir(), ".openclaw", "openclaw.json"),
|
|
23905
|
-
join(homedir(), ".openclaw", "config.json"),
|
|
23906
|
-
join(homedir(), "Library", "Application Support", "OpenClaw", "openclaw.json"),
|
|
23907
|
-
join(homedir(), "Library", "Application Support", "OpenClaw", "config.json")
|
|
23908
|
-
],
|
|
23909
|
-
"claude-code": [
|
|
23910
|
-
join(homedir(), ".claude", "settings.json"),
|
|
23911
|
-
join(homedir(), ".config", "claude-code", "settings.json")
|
|
23912
|
-
],
|
|
23913
|
-
"cursor": [
|
|
23914
|
-
join(homedir(), ".cursor", "mcp.json")
|
|
23915
|
-
],
|
|
23916
|
-
"generic": []
|
|
23917
|
-
};
|
|
23918
25306
|
}
|
|
23919
25307
|
});
|
|
23920
25308
|
|
|
@@ -24220,13 +25608,46 @@ async function runWrap(options, deps = {}) {
|
|
|
24220
25608
|
}
|
|
24221
25609
|
let platformHint;
|
|
24222
25610
|
if (options.openclaw) platformHint = "openclaw";
|
|
25611
|
+
else if (options.hermes) platformHint = "hermes";
|
|
24223
25612
|
else if (options.claudeCode) platformHint = "claude-code";
|
|
24224
25613
|
else if (options.cursor) platformHint = "cursor";
|
|
24225
|
-
|
|
25614
|
+
else if (options.cline) platformHint = "cline";
|
|
25615
|
+
let detection = await detectAgentConfigWithDiagnostics(
|
|
24226
25616
|
platformHint,
|
|
24227
25617
|
options.wrap
|
|
24228
25618
|
);
|
|
24229
|
-
|
|
25619
|
+
let agentConfig = detection.config;
|
|
25620
|
+
if (!agentConfig && platformHint && !options.wrap) {
|
|
25621
|
+
const candidatePaths = getPlatformPaths()[platformHint];
|
|
25622
|
+
const canonicalPath = candidatePaths[0];
|
|
25623
|
+
if (canonicalPath) {
|
|
25624
|
+
try {
|
|
25625
|
+
await mkdir(dirname(canonicalPath), { recursive: true, mode: 448 });
|
|
25626
|
+
await writeFile(canonicalPath, "{}", { mode: 384 });
|
|
25627
|
+
console.error(
|
|
25628
|
+
`
|
|
25629
|
+
No existing ${platformHint} config found.`
|
|
25630
|
+
);
|
|
25631
|
+
console.error(
|
|
25632
|
+
` Bootstrapped a fresh config at ${canonicalPath}.
|
|
25633
|
+
`
|
|
25634
|
+
);
|
|
25635
|
+
detection = await detectAgentConfigWithDiagnostics(
|
|
25636
|
+
platformHint,
|
|
25637
|
+
options.wrap
|
|
25638
|
+
);
|
|
25639
|
+
agentConfig = detection.config;
|
|
25640
|
+
} catch (err) {
|
|
25641
|
+
console.error(
|
|
25642
|
+
`
|
|
25643
|
+
Sanctuary: could not bootstrap ${platformHint} config at ${canonicalPath}`
|
|
25644
|
+
);
|
|
25645
|
+
console.error(` Error: ${err.message}
|
|
25646
|
+
`);
|
|
25647
|
+
process.exit(1);
|
|
25648
|
+
}
|
|
25649
|
+
}
|
|
25650
|
+
}
|
|
24230
25651
|
if (!agentConfig) {
|
|
24231
25652
|
console.error(`
|
|
24232
25653
|
Sanctuary \u2014 Configuration Not Found
|
|
@@ -24238,7 +25659,7 @@ async function runWrap(options, deps = {}) {
|
|
|
24238
25659
|
} else {
|
|
24239
25660
|
console.error(" Could not auto-detect any agent configuration.");
|
|
24240
25661
|
console.error(
|
|
24241
|
-
" Use --openclaw, --claude-code, --cursor, or --wrap /path/to/config.json"
|
|
25662
|
+
" Use --openclaw, --hermes, --claude-code, --cursor, --cline, or --wrap /path/to/config.json"
|
|
24242
25663
|
);
|
|
24243
25664
|
}
|
|
24244
25665
|
if (detection.pathsChecked.length > 0) {
|
|
@@ -24256,25 +25677,25 @@ async function runWrap(options, deps = {}) {
|
|
|
24256
25677
|
console.error("");
|
|
24257
25678
|
process.exit(1);
|
|
24258
25679
|
}
|
|
24259
|
-
|
|
25680
|
+
const hasSanctuaryInRaw = rawConfigContainsSanctuary(
|
|
25681
|
+
agentConfig.rawConfig,
|
|
25682
|
+
agentConfig.platform
|
|
25683
|
+
);
|
|
25684
|
+
if (hasSanctuaryInRaw) {
|
|
24260
25685
|
console.error(
|
|
24261
25686
|
`
|
|
24262
|
-
|
|
25687
|
+
Sanctuary already wrapped: updating the existing Sanctuary entry.
|
|
25688
|
+
`
|
|
24263
25689
|
);
|
|
24264
|
-
|
|
24265
|
-
`);
|
|
24266
|
-
process.exit(1);
|
|
24267
|
-
}
|
|
24268
|
-
const hasSanctuary = agentConfig.servers.some(
|
|
24269
|
-
(s) => s.name.toLowerCase() === "sanctuary"
|
|
24270
|
-
);
|
|
24271
|
-
if (hasSanctuary) {
|
|
25690
|
+
} else if (agentConfig.servers.length === 0) {
|
|
24272
25691
|
console.error(
|
|
24273
25692
|
`
|
|
24274
|
-
|
|
25693
|
+
Found ${agentConfig.platform} config at ${agentConfig.configPath} with no MCP servers yet.`
|
|
25694
|
+
);
|
|
25695
|
+
console.error(
|
|
25696
|
+
` Sanctuary will be installed as the only MCP server.
|
|
25697
|
+
`
|
|
24275
25698
|
);
|
|
24276
|
-
console.error(` Re-wrapping will update the existing Sanctuary entry.
|
|
24277
|
-
`);
|
|
24278
25699
|
}
|
|
24279
25700
|
console.error(`
|
|
24280
25701
|
Sanctuary wrap`);
|
|
@@ -24550,7 +25971,7 @@ async function verifyRewrittenConfig(configPath, backupPath) {
|
|
|
24550
25971
|
await restoreFromBackup(configPath, backupPath);
|
|
24551
25972
|
return false;
|
|
24552
25973
|
}
|
|
24553
|
-
const servers = parsed.mcp?.servers ?? parsed.mcpServers ?? {};
|
|
25974
|
+
const servers = parsed.mcp?.servers ?? parsed.mcpServers ?? parsed.mcp_servers ?? {};
|
|
24554
25975
|
if (!servers.sanctuary) {
|
|
24555
25976
|
console.error(`
|
|
24556
25977
|
Verification FAILED: No sanctuary entry in rewritten config.`);
|
|
@@ -24641,10 +26062,14 @@ function toolNameFor(platform4, _servers) {
|
|
|
24641
26062
|
switch (platform4) {
|
|
24642
26063
|
case "openclaw":
|
|
24643
26064
|
return "OpenClaw";
|
|
26065
|
+
case "hermes":
|
|
26066
|
+
return "Hermes Agent";
|
|
24644
26067
|
case "claude-code":
|
|
24645
26068
|
return "Claude Code";
|
|
24646
26069
|
case "cursor":
|
|
24647
26070
|
return "Cursor";
|
|
26071
|
+
case "cline":
|
|
26072
|
+
return "Cline";
|
|
24648
26073
|
default:
|
|
24649
26074
|
return "your agent";
|
|
24650
26075
|
}
|
|
@@ -24655,6 +26080,23 @@ function countUpstreamTools(servers) {
|
|
|
24655
26080
|
function readPackageVersion() {
|
|
24656
26081
|
return SANCTUARY_VERSION;
|
|
24657
26082
|
}
|
|
26083
|
+
function rawConfigContainsSanctuary(raw, agentPlatform) {
|
|
26084
|
+
if (!raw || typeof raw !== "object") return false;
|
|
26085
|
+
const obj = raw;
|
|
26086
|
+
let serversBag;
|
|
26087
|
+
if (agentPlatform === "openclaw") {
|
|
26088
|
+
const mcp = obj.mcp;
|
|
26089
|
+
serversBag = mcp?.servers ?? obj.mcpServers;
|
|
26090
|
+
} else if (agentPlatform === "hermes") {
|
|
26091
|
+
serversBag = obj.mcp_servers;
|
|
26092
|
+
} else {
|
|
26093
|
+
serversBag = obj.mcpServers;
|
|
26094
|
+
}
|
|
26095
|
+
if (!serversBag || typeof serversBag !== "object") return false;
|
|
26096
|
+
return Object.keys(serversBag).some(
|
|
26097
|
+
(name) => name.toLowerCase() === "sanctuary"
|
|
26098
|
+
);
|
|
26099
|
+
}
|
|
24658
26100
|
function parseWrapArgs(argv) {
|
|
24659
26101
|
const options = {};
|
|
24660
26102
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -24665,12 +26107,18 @@ function parseWrapArgs(argv) {
|
|
|
24665
26107
|
case "--openclaw":
|
|
24666
26108
|
options.openclaw = true;
|
|
24667
26109
|
break;
|
|
26110
|
+
case "--hermes":
|
|
26111
|
+
options.hermes = true;
|
|
26112
|
+
break;
|
|
24668
26113
|
case "--claude-code":
|
|
24669
26114
|
options.claudeCode = true;
|
|
24670
26115
|
break;
|
|
24671
26116
|
case "--cursor":
|
|
24672
26117
|
options.cursor = true;
|
|
24673
26118
|
break;
|
|
26119
|
+
case "--cline":
|
|
26120
|
+
options.cline = true;
|
|
26121
|
+
break;
|
|
24674
26122
|
case "--unwrap":
|
|
24675
26123
|
options.unwrap = true;
|
|
24676
26124
|
break;
|
|
@@ -24700,15 +26148,19 @@ function printWrapHelp() {
|
|
|
24700
26148
|
|
|
24701
26149
|
Usage:
|
|
24702
26150
|
sanctuary wrap --openclaw Wrap OpenClaw
|
|
26151
|
+
sanctuary wrap --hermes Wrap Hermes Agent (NousResearch)
|
|
24703
26152
|
sanctuary wrap --claude-code Wrap Claude Code
|
|
24704
26153
|
sanctuary wrap --cursor Wrap Cursor
|
|
26154
|
+
sanctuary wrap --cline Wrap Cline (VS Code extension)
|
|
24705
26155
|
sanctuary wrap --wrap <path> Wrap a specific MCP config file
|
|
24706
26156
|
sanctuary wrap --unwrap Restore original config
|
|
24707
26157
|
|
|
24708
26158
|
Options:
|
|
24709
26159
|
--openclaw Auto-detect and wrap OpenClaw
|
|
26160
|
+
--hermes Auto-detect and wrap Hermes Agent
|
|
24710
26161
|
--claude-code Auto-detect and wrap Claude Code
|
|
24711
26162
|
--cursor Auto-detect and wrap Cursor
|
|
26163
|
+
--cline Auto-detect and wrap Cline (VS Code extension)
|
|
24712
26164
|
--wrap <path> Wrap a specific MCP config file
|
|
24713
26165
|
--unwrap Restore original config from backup
|
|
24714
26166
|
--passphrase <p> Override the stored passphrase (one-off)
|
|
@@ -26651,6 +28103,230 @@ var init_secrets = __esm({
|
|
|
26651
28103
|
STDIN_READ_DEADLINE_MS = 3e4;
|
|
26652
28104
|
}
|
|
26653
28105
|
});
|
|
28106
|
+
|
|
28107
|
+
// src/templates/cli.ts
|
|
28108
|
+
var cli_exports3 = {};
|
|
28109
|
+
__export(cli_exports3, {
|
|
28110
|
+
runTemplateCommand: () => runTemplateCommand
|
|
28111
|
+
});
|
|
28112
|
+
async function runTemplateCommand(args) {
|
|
28113
|
+
const out = args.out ?? process.stdout;
|
|
28114
|
+
const err = args.err ?? process.stderr;
|
|
28115
|
+
const [sub, ...rest] = args.argv;
|
|
28116
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
28117
|
+
printTemplateHelp(out);
|
|
28118
|
+
return 0;
|
|
28119
|
+
}
|
|
28120
|
+
switch (sub) {
|
|
28121
|
+
case "list":
|
|
28122
|
+
return cmdList2(out);
|
|
28123
|
+
case "init":
|
|
28124
|
+
return cmdInit(rest, out, err);
|
|
28125
|
+
default:
|
|
28126
|
+
err.write(`Unknown template subcommand: ${sub}
|
|
28127
|
+
`);
|
|
28128
|
+
printTemplateHelp(err);
|
|
28129
|
+
return 1;
|
|
28130
|
+
}
|
|
28131
|
+
}
|
|
28132
|
+
function cmdList2(out, _err) {
|
|
28133
|
+
const templates = listTemplates();
|
|
28134
|
+
out.write("\nSanctuary Template Library\n");
|
|
28135
|
+
out.write("=".repeat(60) + "\n\n");
|
|
28136
|
+
out.write(
|
|
28137
|
+
padRight("NAME", 24) + padRight("CHANNEL", 26) + padRight("TIER", 6) + "DESCRIPTION\n"
|
|
28138
|
+
);
|
|
28139
|
+
out.write("-".repeat(100) + "\n");
|
|
28140
|
+
for (const entry of templates) {
|
|
28141
|
+
const m = entry.metadata;
|
|
28142
|
+
out.write(
|
|
28143
|
+
padRight(m.name, 24) + padRight(m.channel, 26) + padRight(m.tier, 6) + m.description + "\n"
|
|
28144
|
+
);
|
|
28145
|
+
}
|
|
28146
|
+
out.write("\n");
|
|
28147
|
+
out.write(`Use "sanctuary template init <name> --agent-id <id>" to load a template.
|
|
28148
|
+
|
|
28149
|
+
`);
|
|
28150
|
+
return 0;
|
|
28151
|
+
}
|
|
28152
|
+
function padRight(str, len) {
|
|
28153
|
+
return str.length >= len ? str + " " : str + " ".repeat(len - str.length);
|
|
28154
|
+
}
|
|
28155
|
+
function cmdInit(argv, out, err) {
|
|
28156
|
+
let templateName;
|
|
28157
|
+
let agentId;
|
|
28158
|
+
let fortressId = "fortress-default";
|
|
28159
|
+
let counterparty = "*";
|
|
28160
|
+
for (let i = 0; i < argv.length; i++) {
|
|
28161
|
+
if (argv[i] === "--agent-id" && argv[i + 1]) {
|
|
28162
|
+
agentId = argv[++i];
|
|
28163
|
+
} else if (argv[i] === "--fortress-id" && argv[i + 1]) {
|
|
28164
|
+
fortressId = argv[++i];
|
|
28165
|
+
} else if (argv[i] === "--counterparty" && argv[i + 1]) {
|
|
28166
|
+
counterparty = argv[++i];
|
|
28167
|
+
} else if (argv[i] === "--help" || argv[i] === "-h") {
|
|
28168
|
+
printInitHelp(out);
|
|
28169
|
+
return 0;
|
|
28170
|
+
} else if (!argv[i].startsWith("--")) {
|
|
28171
|
+
templateName = argv[i];
|
|
28172
|
+
}
|
|
28173
|
+
}
|
|
28174
|
+
if (!templateName) {
|
|
28175
|
+
err.write("Error: template name is required.\n");
|
|
28176
|
+
err.write(`Available templates: ${TEMPLATE_NAMES.join(", ")}
|
|
28177
|
+
`);
|
|
28178
|
+
return 1;
|
|
28179
|
+
}
|
|
28180
|
+
if (!isTemplateName(templateName)) {
|
|
28181
|
+
err.write(`Error: unknown template "${templateName}".
|
|
28182
|
+
`);
|
|
28183
|
+
err.write(`Available templates: ${TEMPLATE_NAMES.join(", ")}
|
|
28184
|
+
`);
|
|
28185
|
+
return 1;
|
|
28186
|
+
}
|
|
28187
|
+
if (!agentId) {
|
|
28188
|
+
err.write("Error: --agent-id is required.\n");
|
|
28189
|
+
return 1;
|
|
28190
|
+
}
|
|
28191
|
+
const bundle = getTemplate2(templateName);
|
|
28192
|
+
if (!bundle) {
|
|
28193
|
+
err.write(`Error: template "${templateName}" not found.
|
|
28194
|
+
`);
|
|
28195
|
+
return 1;
|
|
28196
|
+
}
|
|
28197
|
+
const compiled = buildCompiledPolicyFromTemplate(bundle, {
|
|
28198
|
+
agent_id: agentId,
|
|
28199
|
+
fortress_id: fortressId,
|
|
28200
|
+
counterparty,
|
|
28201
|
+
policy_version: 1
|
|
28202
|
+
});
|
|
28203
|
+
const blob = encodePolicyBlob(compiled);
|
|
28204
|
+
out.write("\nTemplate initialized successfully.\n");
|
|
28205
|
+
out.write("=".repeat(60) + "\n\n");
|
|
28206
|
+
out.write(`Template: ${bundle.metadata.name}
|
|
28207
|
+
`);
|
|
28208
|
+
out.write(`Channel: ${bundle.metadata.channel}
|
|
28209
|
+
`);
|
|
28210
|
+
out.write(`Tier: ${bundle.metadata.tier}
|
|
28211
|
+
`);
|
|
28212
|
+
out.write(`Agent ID: ${compiled.agent_id}
|
|
28213
|
+
`);
|
|
28214
|
+
out.write(`Fortress ID: ${compiled.fortress_id}
|
|
28215
|
+
`);
|
|
28216
|
+
out.write(`Policy Version: ${compiled.policy_version}
|
|
28217
|
+
`);
|
|
28218
|
+
out.write(`Schema Version: ${compiled.schema_version}
|
|
28219
|
+
`);
|
|
28220
|
+
out.write("\n");
|
|
28221
|
+
out.write("Slot access:\n");
|
|
28222
|
+
for (const slot of ["memory", "credentials", "plans", "outputs"]) {
|
|
28223
|
+
const rule = compiled.slots[slot];
|
|
28224
|
+
if (rule.mode === "deny") {
|
|
28225
|
+
out.write(` ${slot}: deny (hermetic)
|
|
28226
|
+
`);
|
|
28227
|
+
} else {
|
|
28228
|
+
const grants = rule.grants.map(
|
|
28229
|
+
(g) => `${g.action} by ${g.counterparty}`
|
|
28230
|
+
);
|
|
28231
|
+
out.write(` ${slot}: ${grants.join(", ")}
|
|
28232
|
+
`);
|
|
28233
|
+
}
|
|
28234
|
+
}
|
|
28235
|
+
if (compiled.capabilities.concordia_commitment_classes.length > 0) {
|
|
28236
|
+
out.write(
|
|
28237
|
+
`
|
|
28238
|
+
Commitment classes: ${compiled.capabilities.concordia_commitment_classes.join(", ")}
|
|
28239
|
+
`
|
|
28240
|
+
);
|
|
28241
|
+
}
|
|
28242
|
+
if (compiled.egress) {
|
|
28243
|
+
out.write(`
|
|
28244
|
+
Egress allowlist (${compiled.egress.allowlist.length} entries):
|
|
28245
|
+
`);
|
|
28246
|
+
for (const rule of compiled.egress.allowlist) {
|
|
28247
|
+
const methods = rule.methods.length > 0 ? rule.methods.join(",") : "*";
|
|
28248
|
+
out.write(` ${rule.destination} [${methods}]
|
|
28249
|
+
`);
|
|
28250
|
+
}
|
|
28251
|
+
} else {
|
|
28252
|
+
out.write("\nEgress: hermetic (no outbound access)\n");
|
|
28253
|
+
}
|
|
28254
|
+
if (compiled.budgets) {
|
|
28255
|
+
out.write("\nBudgets:\n");
|
|
28256
|
+
if (compiled.budgets.daily) {
|
|
28257
|
+
out.write(
|
|
28258
|
+
` Daily: ${compiled.budgets.daily.amount} ${compiled.budgets.daily.unit}
|
|
28259
|
+
`
|
|
28260
|
+
);
|
|
28261
|
+
}
|
|
28262
|
+
if (compiled.budgets.monthly) {
|
|
28263
|
+
out.write(
|
|
28264
|
+
` Monthly: ${compiled.budgets.monthly.amount} ${compiled.budgets.monthly.unit}
|
|
28265
|
+
`
|
|
28266
|
+
);
|
|
28267
|
+
}
|
|
28268
|
+
}
|
|
28269
|
+
if (compiled.retention) {
|
|
28270
|
+
out.write("\nRetention windows:\n");
|
|
28271
|
+
for (const [slot, win] of Object.entries(compiled.retention.windows)) {
|
|
28272
|
+
if (win) {
|
|
28273
|
+
const days = Math.round(win.max_age_seconds / 86400);
|
|
28274
|
+
out.write(` ${slot}: ${days} days${win.archive ? " (archive)" : ""}
|
|
28275
|
+
`);
|
|
28276
|
+
}
|
|
28277
|
+
}
|
|
28278
|
+
}
|
|
28279
|
+
out.write(`
|
|
28280
|
+
Policy blob (base64url, ${blob.length} chars):
|
|
28281
|
+
`);
|
|
28282
|
+
out.write(` ${blob.slice(0, 80)}...
|
|
28283
|
+
|
|
28284
|
+
`);
|
|
28285
|
+
out.write(
|
|
28286
|
+
"To sign and emit this policy as a policy_update event, use the\npolicy engine's packPolicyUpdate with your node signing key.\n\n"
|
|
28287
|
+
);
|
|
28288
|
+
return 0;
|
|
28289
|
+
}
|
|
28290
|
+
function printTemplateHelp(out) {
|
|
28291
|
+
out.write(`
|
|
28292
|
+
sanctuary template \u2014 Manage Sanctuary policy templates.
|
|
28293
|
+
|
|
28294
|
+
Usage:
|
|
28295
|
+
sanctuary template list List available templates
|
|
28296
|
+
sanctuary template init <name> [options] Load a template
|
|
28297
|
+
|
|
28298
|
+
Options:
|
|
28299
|
+
--help, -h Show this help
|
|
28300
|
+
|
|
28301
|
+
Use "sanctuary template init --help" for init-specific options.
|
|
28302
|
+
`);
|
|
28303
|
+
}
|
|
28304
|
+
function printInitHelp(out) {
|
|
28305
|
+
out.write(`
|
|
28306
|
+
sanctuary template init \u2014 Load a template into a fortress.
|
|
28307
|
+
|
|
28308
|
+
Usage:
|
|
28309
|
+
sanctuary template init <name> --agent-id <id> [options]
|
|
28310
|
+
|
|
28311
|
+
Arguments:
|
|
28312
|
+
<name> Template name (see "sanctuary template list")
|
|
28313
|
+
|
|
28314
|
+
Options:
|
|
28315
|
+
--agent-id <id> Agent ID to bind the policy to (required)
|
|
28316
|
+
--fortress-id <id> Fortress ID (default: "fortress-default")
|
|
28317
|
+
--counterparty <id> Counterparty for channel grants (default: "*")
|
|
28318
|
+
--help, -h Show this help
|
|
28319
|
+
|
|
28320
|
+
Available templates: ${TEMPLATE_NAMES.join(", ")}
|
|
28321
|
+
`);
|
|
28322
|
+
}
|
|
28323
|
+
var init_cli3 = __esm({
|
|
28324
|
+
"src/templates/cli.ts"() {
|
|
28325
|
+
init_registry2();
|
|
28326
|
+
init_init();
|
|
28327
|
+
init_canonical_policy();
|
|
28328
|
+
}
|
|
28329
|
+
});
|
|
26654
28330
|
async function isTenantDir(path) {
|
|
26655
28331
|
const [hasState, hasProfile, hasFallback] = await Promise.all([
|
|
26656
28332
|
dirExists(join(path, "state")),
|
|
@@ -26862,7 +28538,7 @@ async function runAgentsCommand(args) {
|
|
|
26862
28538
|
try {
|
|
26863
28539
|
switch (sub) {
|
|
26864
28540
|
case "list":
|
|
26865
|
-
return await
|
|
28541
|
+
return await cmdList3(rest, ctx);
|
|
26866
28542
|
case "show":
|
|
26867
28543
|
return await cmdShow(rest, ctx);
|
|
26868
28544
|
case "status":
|
|
@@ -26921,11 +28597,11 @@ function passphraseLabel(tenant) {
|
|
|
26921
28597
|
return "not-init";
|
|
26922
28598
|
}
|
|
26923
28599
|
}
|
|
26924
|
-
function
|
|
28600
|
+
function padRight2(s, width) {
|
|
26925
28601
|
if (s.length >= width) return s;
|
|
26926
28602
|
return s + " ".repeat(width - s.length);
|
|
26927
28603
|
}
|
|
26928
|
-
async function
|
|
28604
|
+
async function cmdList3(argv, ctx) {
|
|
26929
28605
|
const json = hasJsonFlag(argv);
|
|
26930
28606
|
const tenants = await discoverTenants(ctx.discoverOpts);
|
|
26931
28607
|
if (json) {
|
|
@@ -26957,7 +28633,7 @@ async function cmdList2(argv, ctx) {
|
|
|
26957
28633
|
}));
|
|
26958
28634
|
const nameW = Math.max(4, ...rows.map((r) => r.tenant.name.length));
|
|
26959
28635
|
const pathW = Math.max(12, ...rows.map((r) => r.tenant.storage_path.length));
|
|
26960
|
-
const header =
|
|
28636
|
+
const header = padRight2("NAME", nameW) + " " + padRight2("STATUS", 8) + padRight2("DASH", 6) + padRight2("HOOK", 6) + padRight2("PP", 15) + padRight2("STORAGE", pathW);
|
|
26961
28637
|
ctx.out.write(header + "\n");
|
|
26962
28638
|
for (const { tenant, probe } of rows) {
|
|
26963
28639
|
const status = probe.running ? "running" : tenant.initialized ? "stopped" : "empty";
|
|
@@ -26965,7 +28641,7 @@ async function cmdList2(argv, ctx) {
|
|
|
26965
28641
|
const webhook = tenant.runtime?.webhook_callback_port != null ? String(tenant.runtime.webhook_callback_port) : "-";
|
|
26966
28642
|
const pp = passphraseLabel(tenant);
|
|
26967
28643
|
ctx.out.write(
|
|
26968
|
-
|
|
28644
|
+
padRight2(tenant.name, nameW) + " " + padRight2(status, 8) + padRight2(dashboard, 6) + padRight2(webhook, 6) + padRight2(pp, 15) + padRight2(tenant.storage_path, pathW) + "\n"
|
|
26969
28645
|
);
|
|
26970
28646
|
}
|
|
26971
28647
|
return 0;
|
|
@@ -27074,13 +28750,13 @@ async function cmdStatus(argv, ctx) {
|
|
|
27074
28750
|
const dash = tenant.runtime?.dashboard_port != null ? `:${tenant.runtime.dashboard_port}` : "";
|
|
27075
28751
|
const last = tenant.last_activity ? ` \xB7 last ${formatRelative2(tenant.last_activity)}` : "";
|
|
27076
28752
|
ctx.out.write(
|
|
27077
|
-
`${
|
|
28753
|
+
`${padRight2(tenant.name, nameW)} ${padRight2(state, 8)}${dash}${last}
|
|
27078
28754
|
`
|
|
27079
28755
|
);
|
|
27080
28756
|
}
|
|
27081
28757
|
return 0;
|
|
27082
28758
|
}
|
|
27083
|
-
var
|
|
28759
|
+
var init_cli4 = __esm({
|
|
27084
28760
|
"src/cli/agents/cli.ts"() {
|
|
27085
28761
|
init_discovery();
|
|
27086
28762
|
init_health();
|
|
@@ -27104,7 +28780,7 @@ var init_agents = __esm({
|
|
|
27104
28780
|
init_discovery();
|
|
27105
28781
|
init_health();
|
|
27106
28782
|
init_runtime();
|
|
27107
|
-
|
|
28783
|
+
init_cli4();
|
|
27108
28784
|
}
|
|
27109
28785
|
});
|
|
27110
28786
|
|
|
@@ -27618,8 +29294,8 @@ Refusing to generate a new recovery key over the default root \u2014 that would
|
|
|
27618
29294
|
const dashboardHost = options.host ?? config.dashboard.host;
|
|
27619
29295
|
let authToken = config.dashboard.auth_token;
|
|
27620
29296
|
if (authToken === "auto") {
|
|
27621
|
-
const { randomBytes:
|
|
27622
|
-
authToken =
|
|
29297
|
+
const { randomBytes: randomBytes8 } = await import('crypto');
|
|
29298
|
+
authToken = randomBytes8(32).toString("hex");
|
|
27623
29299
|
}
|
|
27624
29300
|
const dashboard = new DashboardApprovalChannel({
|
|
27625
29301
|
port: dashboardPort,
|
|
@@ -27844,6 +29520,11 @@ async function main() {
|
|
|
27844
29520
|
const code = await runSecretsCommand2({ argv: args.slice(1) });
|
|
27845
29521
|
process.exit(code);
|
|
27846
29522
|
}
|
|
29523
|
+
if (args[0] === "template") {
|
|
29524
|
+
const { runTemplateCommand: runTemplateCommand2 } = await Promise.resolve().then(() => (init_cli3(), cli_exports3));
|
|
29525
|
+
const code = await runTemplateCommand2({ argv: args.slice(1) });
|
|
29526
|
+
process.exit(code);
|
|
29527
|
+
}
|
|
27847
29528
|
if (args[0] === "agents") {
|
|
27848
29529
|
const { runAgentsCommand: runAgentsCommand2 } = await Promise.resolve().then(() => (init_agents(), agents_exports));
|
|
27849
29530
|
const code = await runAgentsCommand2({ argv: args.slice(1) });
|
|
@@ -28040,6 +29721,9 @@ Subcommands:
|
|
|
28040
29721
|
Use "sanctuary dashboard --help" for options.
|
|
28041
29722
|
Pass --multi to render the multi-tenant overview.
|
|
28042
29723
|
|
|
29724
|
+
template Manage policy templates (list, init).
|
|
29725
|
+
Use "sanctuary template --help" for options.
|
|
29726
|
+
|
|
28043
29727
|
agents List / inspect tenants on a multi-agent host.
|
|
28044
29728
|
Use "sanctuary agents --help" for options.
|
|
28045
29729
|
|