@sanctuary-framework/mcp-server 1.1.1 → 1.1.3
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 +266 -84
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +272 -90
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +179 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +179 -77
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -5495,6 +5495,23 @@ interface DashboardHandle {
|
|
|
5495
5495
|
* `agent_id`.
|
|
5496
5496
|
*/
|
|
5497
5497
|
publishAgentStatus: (snapshot: unknown) => void;
|
|
5498
|
+
/**
|
|
5499
|
+
* v1.1.2 hotfix (Finding V): bind v1.1 hub bindings to this dashboard
|
|
5500
|
+
* instance so /v1.1, /api/hub/*, and /api/identities serve the v1.1
|
|
5501
|
+
* surface. Pass null to detach. PR #82 wired these routes only on the
|
|
5502
|
+
* principal-policy DashboardApprovalChannel; the wrap-auto operator
|
|
5503
|
+
* dashboard (this server) needed the same wiring for the wrap-emitted
|
|
5504
|
+
* URL to expose the v1.1 surfaces the v1.1.1 release notes claim.
|
|
5505
|
+
*/
|
|
5506
|
+
setV11Bindings: (bindings: V11Bindings | null) => void;
|
|
5507
|
+
/**
|
|
5508
|
+
* v1.1.2: enable loopback auto-auth for /api/hub/* + /api/identities.
|
|
5509
|
+
* When true, requests from 127.0.0.1 / ::1 bypass the bearer-token
|
|
5510
|
+
* check (mirrors the principal-policy dashboard's _autoAuthLocalhost
|
|
5511
|
+
* flag). Set true when the dashboard binds to a loopback host and the
|
|
5512
|
+
* caller has independently authenticated the operator.
|
|
5513
|
+
*/
|
|
5514
|
+
setV11LoopbackAutoAuth: (enabled: boolean) => void;
|
|
5498
5515
|
}
|
|
5499
5516
|
declare function startDashboardServer(options: DashboardServerOptions): Promise<DashboardHandle>;
|
|
5500
5517
|
|
package/dist/index.d.ts
CHANGED
|
@@ -5495,6 +5495,23 @@ interface DashboardHandle {
|
|
|
5495
5495
|
* `agent_id`.
|
|
5496
5496
|
*/
|
|
5497
5497
|
publishAgentStatus: (snapshot: unknown) => void;
|
|
5498
|
+
/**
|
|
5499
|
+
* v1.1.2 hotfix (Finding V): bind v1.1 hub bindings to this dashboard
|
|
5500
|
+
* instance so /v1.1, /api/hub/*, and /api/identities serve the v1.1
|
|
5501
|
+
* surface. Pass null to detach. PR #82 wired these routes only on the
|
|
5502
|
+
* principal-policy DashboardApprovalChannel; the wrap-auto operator
|
|
5503
|
+
* dashboard (this server) needed the same wiring for the wrap-emitted
|
|
5504
|
+
* URL to expose the v1.1 surfaces the v1.1.1 release notes claim.
|
|
5505
|
+
*/
|
|
5506
|
+
setV11Bindings: (bindings: V11Bindings | null) => void;
|
|
5507
|
+
/**
|
|
5508
|
+
* v1.1.2: enable loopback auto-auth for /api/hub/* + /api/identities.
|
|
5509
|
+
* When true, requests from 127.0.0.1 / ::1 bypass the bearer-token
|
|
5510
|
+
* check (mirrors the principal-policy dashboard's _autoAuthLocalhost
|
|
5511
|
+
* flag). Set true when the dashboard binds to a loopback host and the
|
|
5512
|
+
* caller has independently authenticated the operator.
|
|
5513
|
+
*/
|
|
5514
|
+
setV11LoopbackAutoAuth: (enabled: boolean) => void;
|
|
5498
5515
|
}
|
|
5499
5516
|
declare function startDashboardServer(options: DashboardServerOptions): Promise<DashboardHandle>;
|
|
5500
5517
|
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import { fileURLToPath } from 'url';
|
|
|
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 'readline/promises';
|
|
22
|
+
import { createInterface } from 'readline/promises';
|
|
23
23
|
|
|
24
24
|
var __defProp = Object.defineProperty;
|
|
25
25
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -11138,6 +11138,20 @@ async function handleRequest(deps, req, res) {
|
|
|
11138
11138
|
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
11139
11139
|
const method = (req.method ?? "GET").toUpperCase();
|
|
11140
11140
|
const path = url.pathname;
|
|
11141
|
+
if (deps.v11Bindings) {
|
|
11142
|
+
const handled = await dispatchV11Request(
|
|
11143
|
+
{
|
|
11144
|
+
bindings: deps.v11Bindings,
|
|
11145
|
+
...deps.authToken !== void 0 ? { authToken: deps.authToken } : {},
|
|
11146
|
+
loopbackAutoAuth: deps.loopbackAutoAuth ?? false
|
|
11147
|
+
},
|
|
11148
|
+
req,
|
|
11149
|
+
res,
|
|
11150
|
+
url,
|
|
11151
|
+
method
|
|
11152
|
+
);
|
|
11153
|
+
if (handled) return true;
|
|
11154
|
+
}
|
|
11141
11155
|
if (!isAuthorized(deps, req, url)) {
|
|
11142
11156
|
writeJSON(res, 401, { error: "unauthorized" });
|
|
11143
11157
|
return true;
|
|
@@ -12767,6 +12781,47 @@ function handleDashboardV11Route(deps, req, res) {
|
|
|
12767
12781
|
return true;
|
|
12768
12782
|
}
|
|
12769
12783
|
|
|
12784
|
+
// src/dashboard/v1_1/dispatch.ts
|
|
12785
|
+
async function dispatchV11Request(inputs, req, res, url, method) {
|
|
12786
|
+
const { bindings, authToken, loopbackAutoAuth } = inputs;
|
|
12787
|
+
if (method === "GET" && (url.pathname === "/v1.1" || url.pathname === "/v1.1/")) {
|
|
12788
|
+
return handleDashboardV11Route(
|
|
12789
|
+
{
|
|
12790
|
+
identityId: bindings.identityId,
|
|
12791
|
+
fortressId: bindings.fortressId,
|
|
12792
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
12793
|
+
},
|
|
12794
|
+
req,
|
|
12795
|
+
res
|
|
12796
|
+
);
|
|
12797
|
+
}
|
|
12798
|
+
if (url.pathname.startsWith("/api/hub/")) {
|
|
12799
|
+
const authConfig = {
|
|
12800
|
+
loopbackAutoAuth,
|
|
12801
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
12802
|
+
};
|
|
12803
|
+
return handleHubRoute(
|
|
12804
|
+
{ authConfig, service: bindings.hubService },
|
|
12805
|
+
req,
|
|
12806
|
+
res
|
|
12807
|
+
);
|
|
12808
|
+
}
|
|
12809
|
+
if (method === "GET" && url.pathname === "/api/identities") {
|
|
12810
|
+
const authConfig = {
|
|
12811
|
+
loopbackAutoAuth,
|
|
12812
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
12813
|
+
};
|
|
12814
|
+
const aliasReq = Object.create(req);
|
|
12815
|
+
aliasReq.url = "/api/hub/agents" + url.search;
|
|
12816
|
+
return handleHubRoute(
|
|
12817
|
+
{ authConfig, service: bindings.hubService },
|
|
12818
|
+
aliasReq,
|
|
12819
|
+
res
|
|
12820
|
+
);
|
|
12821
|
+
}
|
|
12822
|
+
return false;
|
|
12823
|
+
}
|
|
12824
|
+
|
|
12770
12825
|
// src/principal-policy/dashboard.ts
|
|
12771
12826
|
var SESSION_TTL_REMOTE_MS = 5 * 60 * 1e3;
|
|
12772
12827
|
var SESSION_TTL_LOCAL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -12893,45 +12948,17 @@ var DashboardApprovalChannel = class {
|
|
|
12893
12948
|
*/
|
|
12894
12949
|
async dispatchV11(req, res, url, method) {
|
|
12895
12950
|
if (!this.v11Bindings) return false;
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
}
|
|
12908
|
-
if (url.pathname.startsWith("/api/hub/")) {
|
|
12909
|
-
const authConfig = {
|
|
12910
|
-
loopbackAutoAuth: this._autoAuthLocalhost,
|
|
12911
|
-
...this.authToken !== void 0 ? { authToken: this.authToken } : {}
|
|
12912
|
-
};
|
|
12913
|
-
const handled = await handleHubRoute(
|
|
12914
|
-
{ authConfig, service: this.v11Bindings.hubService },
|
|
12915
|
-
req,
|
|
12916
|
-
res
|
|
12917
|
-
);
|
|
12918
|
-
return handled;
|
|
12919
|
-
}
|
|
12920
|
-
if (method === "GET" && url.pathname === "/api/identities") {
|
|
12921
|
-
const authConfig = {
|
|
12922
|
-
loopbackAutoAuth: this._autoAuthLocalhost,
|
|
12923
|
-
...this.authToken !== void 0 ? { authToken: this.authToken } : {}
|
|
12924
|
-
};
|
|
12925
|
-
const aliasReq = Object.create(req);
|
|
12926
|
-
aliasReq.url = "/api/hub/agents" + url.search;
|
|
12927
|
-
const handled = await handleHubRoute(
|
|
12928
|
-
{ authConfig, service: this.v11Bindings.hubService },
|
|
12929
|
-
aliasReq,
|
|
12930
|
-
res
|
|
12931
|
-
);
|
|
12932
|
-
return handled;
|
|
12933
|
-
}
|
|
12934
|
-
return false;
|
|
12951
|
+
return dispatchV11Request(
|
|
12952
|
+
{
|
|
12953
|
+
bindings: this.v11Bindings,
|
|
12954
|
+
...this.authToken !== void 0 ? { authToken: this.authToken } : {},
|
|
12955
|
+
loopbackAutoAuth: this._autoAuthLocalhost
|
|
12956
|
+
},
|
|
12957
|
+
req,
|
|
12958
|
+
res,
|
|
12959
|
+
url,
|
|
12960
|
+
method
|
|
12961
|
+
);
|
|
12935
12962
|
}
|
|
12936
12963
|
/**
|
|
12937
12964
|
* v0.10.2: enable (or disable) the loopback auto-auth fast path. See
|
|
@@ -26328,14 +26355,41 @@ function createComplianceTools(deps) {
|
|
|
26328
26355
|
init_random();
|
|
26329
26356
|
init_encoding();
|
|
26330
26357
|
var RECOVERY_KEY_FILENAME = "recovery-key.txt";
|
|
26331
|
-
|
|
26358
|
+
var RECOVERY_KEY_COPY = {
|
|
26359
|
+
fileName: RECOVERY_KEY_FILENAME,
|
|
26360
|
+
bannerHeader: "SANCTUARY: First Run, Recovery Key Generated",
|
|
26361
|
+
bannerSecretLabel: "Recovery Key",
|
|
26362
|
+
bannerSaveLine: "SAVE THIS KEY. It will not be shown again.",
|
|
26363
|
+
bannerLossLine: "Without it, your encrypted state is unrecoverable.",
|
|
26364
|
+
fileWarningHeader: "SANCTUARY RECOVERY KEY, DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.",
|
|
26365
|
+
fileSecretLabel: "Recovery key:",
|
|
26366
|
+
fileBody: "This file was created on first init. Sanctuary will NOT regenerate this file on\nsubsequent runs and will NOT display the key again. After moving this file off\nthe host (encrypted backup, password manager, paper safe), delete it from the\nfortress directory. Do NOT keep it in the fortress; the recovery key bypasses\nthe cocoon passphrase by design.\n",
|
|
26367
|
+
promptLabel: "recovery key"
|
|
26368
|
+
};
|
|
26369
|
+
var RecoveryKeyConfirmationDeclinedError = class extends Error {
|
|
26370
|
+
constructor() {
|
|
26371
|
+
super(
|
|
26372
|
+
"Recovery key confirmation declined. Save the recovery key (printed above and written to recovery-key.txt) before re-running init."
|
|
26373
|
+
);
|
|
26374
|
+
this.name = "RecoveryKeyConfirmationDeclinedError";
|
|
26375
|
+
}
|
|
26376
|
+
};
|
|
26377
|
+
var RecoveryKeyConfirmationNonInteractiveError = class extends Error {
|
|
26378
|
+
constructor() {
|
|
26379
|
+
super(
|
|
26380
|
+
"Recovery key confirmation requires an interactive terminal. Re-run with --no-confirm for CI/scripted use, or run from a TTY."
|
|
26381
|
+
);
|
|
26382
|
+
this.name = "RecoveryKeyConfirmationNonInteractiveError";
|
|
26383
|
+
}
|
|
26384
|
+
};
|
|
26385
|
+
function printSecretBanner(secret, filePath, copy, output = process.stderr) {
|
|
26332
26386
|
const lines = [
|
|
26333
|
-
|
|
26387
|
+
copy.bannerHeader,
|
|
26334
26388
|
"",
|
|
26335
|
-
|
|
26389
|
+
`${copy.bannerSecretLabel}: ${secret}`,
|
|
26336
26390
|
"",
|
|
26337
|
-
|
|
26338
|
-
|
|
26391
|
+
copy.bannerSaveLine,
|
|
26392
|
+
copy.bannerLossLine,
|
|
26339
26393
|
"",
|
|
26340
26394
|
"Plaintext copy written to:",
|
|
26341
26395
|
` ${filePath}`,
|
|
@@ -26354,8 +26408,8 @@ ${bottom}
|
|
|
26354
26408
|
|
|
26355
26409
|
`);
|
|
26356
26410
|
}
|
|
26357
|
-
async function
|
|
26358
|
-
const filePath = join(opts.storagePath,
|
|
26411
|
+
async function writeSecretFile(opts) {
|
|
26412
|
+
const filePath = join(opts.storagePath, opts.copy.fileName);
|
|
26359
26413
|
try {
|
|
26360
26414
|
await access(filePath, constants.F_OK);
|
|
26361
26415
|
return { filePath, written: false };
|
|
@@ -26365,40 +26419,76 @@ async function writeRecoveryKeyFile(opts) {
|
|
|
26365
26419
|
const now = (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
26366
26420
|
const fortressLine = opts.fortressId ? `Fortress: ${opts.fortressId}
|
|
26367
26421
|
` : "";
|
|
26368
|
-
const content =
|
|
26422
|
+
const content = `${opts.copy.fileWarningHeader}
|
|
26369
26423
|
Generated: ${now}
|
|
26370
26424
|
` + fortressLine + `
|
|
26371
|
-
|
|
26372
|
-
${opts.
|
|
26373
|
-
|
|
26374
|
-
|
|
26375
|
-
subsequent runs and will NOT display the key again. After moving this file off
|
|
26376
|
-
the host (encrypted backup, password manager, paper safe), delete it from the
|
|
26377
|
-
fortress directory. Do NOT keep it in the fortress; the recovery key bypasses
|
|
26378
|
-
the cocoon passphrase by design.
|
|
26379
|
-
`;
|
|
26425
|
+
${opts.copy.fileSecretLabel}
|
|
26426
|
+
${opts.secret}
|
|
26427
|
+
|
|
26428
|
+
` + opts.copy.fileBody;
|
|
26380
26429
|
await writeFile(filePath, content, { mode: 384 });
|
|
26381
26430
|
return { filePath, written: true };
|
|
26382
26431
|
}
|
|
26383
|
-
async function
|
|
26384
|
-
const
|
|
26432
|
+
async function confirmSecretSaved(copy, declinedError, nonInteractiveError, io) {
|
|
26433
|
+
const input = io?.input ?? process.stdin;
|
|
26434
|
+
const output = io?.output ?? process.stderr;
|
|
26435
|
+
const realStdin = !io && process.stdin.isTTY !== true;
|
|
26436
|
+
if (realStdin) {
|
|
26437
|
+
throw new nonInteractiveError();
|
|
26438
|
+
}
|
|
26439
|
+
const rl = createInterface({ input, output });
|
|
26440
|
+
try {
|
|
26441
|
+
const answer = await rl.question(
|
|
26442
|
+
`Have you saved the ${copy.promptLabel}? [y/N] `
|
|
26443
|
+
);
|
|
26444
|
+
const normalized = answer.trim().toLowerCase();
|
|
26445
|
+
if (normalized !== "y" && normalized !== "yes") {
|
|
26446
|
+
throw new declinedError();
|
|
26447
|
+
}
|
|
26448
|
+
} finally {
|
|
26449
|
+
rl.close();
|
|
26450
|
+
}
|
|
26451
|
+
}
|
|
26452
|
+
async function discloseSecret(opts, copy, declinedError, nonInteractiveError) {
|
|
26453
|
+
const mode = opts.mode ?? "interactive";
|
|
26454
|
+
const writeOpts = {
|
|
26385
26455
|
storagePath: opts.storagePath,
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26389
|
-
|
|
26390
|
-
|
|
26391
|
-
|
|
26392
|
-
|
|
26393
|
-
|
|
26394
|
-
);
|
|
26395
|
-
{
|
|
26456
|
+
secret: opts.secret,
|
|
26457
|
+
copy
|
|
26458
|
+
};
|
|
26459
|
+
if (opts.fortressId !== void 0) writeOpts.fortressId = opts.fortressId;
|
|
26460
|
+
if (opts.now !== void 0) writeOpts.now = opts.now;
|
|
26461
|
+
const fileResult = await writeSecretFile(writeOpts);
|
|
26462
|
+
printSecretBanner(opts.secret, fileResult.filePath, copy, opts.io?.output);
|
|
26463
|
+
if (mode === "no-confirm" || mode === "stdio-server") {
|
|
26396
26464
|
return {
|
|
26397
26465
|
filePath: fileResult.filePath,
|
|
26398
26466
|
fileWritten: fileResult.written,
|
|
26399
26467
|
confirmed: false
|
|
26400
26468
|
};
|
|
26401
26469
|
}
|
|
26470
|
+
await confirmSecretSaved(copy, declinedError, nonInteractiveError, opts.io);
|
|
26471
|
+
return {
|
|
26472
|
+
filePath: fileResult.filePath,
|
|
26473
|
+
fileWritten: fileResult.written,
|
|
26474
|
+
confirmed: true
|
|
26475
|
+
};
|
|
26476
|
+
}
|
|
26477
|
+
async function discloseRecoveryKey(opts) {
|
|
26478
|
+
const internalOpts = {
|
|
26479
|
+
secret: opts.recoveryKey,
|
|
26480
|
+
storagePath: opts.storagePath
|
|
26481
|
+
};
|
|
26482
|
+
if (opts.fortressId !== void 0) internalOpts.fortressId = opts.fortressId;
|
|
26483
|
+
if (opts.mode !== void 0) internalOpts.mode = opts.mode;
|
|
26484
|
+
if (opts.now !== void 0) internalOpts.now = opts.now;
|
|
26485
|
+
if (opts.io !== void 0) internalOpts.io = opts.io;
|
|
26486
|
+
return discloseSecret(
|
|
26487
|
+
internalOpts,
|
|
26488
|
+
RECOVERY_KEY_COPY,
|
|
26489
|
+
RecoveryKeyConfirmationDeclinedError,
|
|
26490
|
+
RecoveryKeyConfirmationNonInteractiveError
|
|
26491
|
+
);
|
|
26402
26492
|
}
|
|
26403
26493
|
|
|
26404
26494
|
// src/hub/agent-registry.ts
|
|
@@ -28820,14 +28910,18 @@ async function startDashboardServer(options) {
|
|
|
28820
28910
|
}
|
|
28821
28911
|
}
|
|
28822
28912
|
};
|
|
28823
|
-
|
|
28824
|
-
|
|
28825
|
-
authToken: options.authToken,
|
|
28826
|
-
approvals: options.approvals,
|
|
28827
|
-
onEvent
|
|
28828
|
-
};
|
|
28913
|
+
let v11Bindings = null;
|
|
28914
|
+
let v11LoopbackAutoAuth = false;
|
|
28829
28915
|
const server = createServer$2(async (req, res) => {
|
|
28830
28916
|
try {
|
|
28917
|
+
const deps = {
|
|
28918
|
+
sources: options.sources,
|
|
28919
|
+
authToken: options.authToken,
|
|
28920
|
+
approvals: options.approvals,
|
|
28921
|
+
onEvent,
|
|
28922
|
+
v11Bindings,
|
|
28923
|
+
loopbackAutoAuth: v11LoopbackAutoAuth
|
|
28924
|
+
};
|
|
28831
28925
|
const served = await handleRequest(deps, req, res);
|
|
28832
28926
|
if (!served) {
|
|
28833
28927
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
@@ -28865,7 +28959,13 @@ async function startDashboardServer(options) {
|
|
|
28865
28959
|
publishActivity: (entry) => publish({ type: "activity", data: entry }),
|
|
28866
28960
|
publishApproval: (approval) => publish({ type: "approval", data: approval }),
|
|
28867
28961
|
publishInbox: (item) => publish({ type: "inbox", data: item }),
|
|
28868
|
-
publishAgentStatus: (snapshot) => publish({ type: "agent_status", data: snapshot })
|
|
28962
|
+
publishAgentStatus: (snapshot) => publish({ type: "agent_status", data: snapshot }),
|
|
28963
|
+
setV11Bindings: (bindings) => {
|
|
28964
|
+
v11Bindings = bindings;
|
|
28965
|
+
},
|
|
28966
|
+
setV11LoopbackAutoAuth: (enabled) => {
|
|
28967
|
+
v11LoopbackAutoAuth = enabled;
|
|
28968
|
+
}
|
|
28869
28969
|
};
|
|
28870
28970
|
}
|
|
28871
28971
|
|
|
@@ -29528,7 +29628,9 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
|
|
|
29528
29628
|
if (recoveryKey) {
|
|
29529
29629
|
await discloseRecoveryKey({
|
|
29530
29630
|
recoveryKey,
|
|
29531
|
-
storagePath: config.storage_path
|
|
29631
|
+
storagePath: config.storage_path,
|
|
29632
|
+
mode: "stdio-server"
|
|
29633
|
+
});
|
|
29532
29634
|
}
|
|
29533
29635
|
return {
|
|
29534
29636
|
server,
|