@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/cli.cjs
CHANGED
|
@@ -11887,6 +11887,20 @@ async function handleRequest(deps, req, res) {
|
|
|
11887
11887
|
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
11888
11888
|
const method = (req.method ?? "GET").toUpperCase();
|
|
11889
11889
|
const path = url.pathname;
|
|
11890
|
+
if (deps.v11Bindings) {
|
|
11891
|
+
const handled = await dispatchV11Request(
|
|
11892
|
+
{
|
|
11893
|
+
bindings: deps.v11Bindings,
|
|
11894
|
+
...deps.authToken !== void 0 ? { authToken: deps.authToken } : {},
|
|
11895
|
+
loopbackAutoAuth: deps.loopbackAutoAuth ?? false
|
|
11896
|
+
},
|
|
11897
|
+
req,
|
|
11898
|
+
res,
|
|
11899
|
+
url,
|
|
11900
|
+
method
|
|
11901
|
+
);
|
|
11902
|
+
if (handled) return true;
|
|
11903
|
+
}
|
|
11890
11904
|
if (!isAuthorized(deps, req, url)) {
|
|
11891
11905
|
writeJSON(res, 401, { error: "unauthorized" });
|
|
11892
11906
|
return true;
|
|
@@ -12067,6 +12081,7 @@ var init_api = __esm({
|
|
|
12067
12081
|
init_registry();
|
|
12068
12082
|
init_init();
|
|
12069
12083
|
init_discovery();
|
|
12084
|
+
init_dispatch();
|
|
12070
12085
|
}
|
|
12071
12086
|
});
|
|
12072
12087
|
|
|
@@ -13602,6 +13617,53 @@ var init_v1_1 = __esm({
|
|
|
13602
13617
|
init_client();
|
|
13603
13618
|
}
|
|
13604
13619
|
});
|
|
13620
|
+
|
|
13621
|
+
// src/dashboard/v1_1/dispatch.ts
|
|
13622
|
+
async function dispatchV11Request(inputs, req, res, url, method) {
|
|
13623
|
+
const { bindings, authToken, loopbackAutoAuth } = inputs;
|
|
13624
|
+
if (method === "GET" && (url.pathname === "/v1.1" || url.pathname === "/v1.1/")) {
|
|
13625
|
+
return handleDashboardV11Route(
|
|
13626
|
+
{
|
|
13627
|
+
identityId: bindings.identityId,
|
|
13628
|
+
fortressId: bindings.fortressId,
|
|
13629
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
13630
|
+
},
|
|
13631
|
+
req,
|
|
13632
|
+
res
|
|
13633
|
+
);
|
|
13634
|
+
}
|
|
13635
|
+
if (url.pathname.startsWith("/api/hub/")) {
|
|
13636
|
+
const authConfig = {
|
|
13637
|
+
loopbackAutoAuth,
|
|
13638
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
13639
|
+
};
|
|
13640
|
+
return handleHubRoute(
|
|
13641
|
+
{ authConfig, service: bindings.hubService },
|
|
13642
|
+
req,
|
|
13643
|
+
res
|
|
13644
|
+
);
|
|
13645
|
+
}
|
|
13646
|
+
if (method === "GET" && url.pathname === "/api/identities") {
|
|
13647
|
+
const authConfig = {
|
|
13648
|
+
loopbackAutoAuth,
|
|
13649
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
13650
|
+
};
|
|
13651
|
+
const aliasReq = Object.create(req);
|
|
13652
|
+
aliasReq.url = "/api/hub/agents" + url.search;
|
|
13653
|
+
return handleHubRoute(
|
|
13654
|
+
{ authConfig, service: bindings.hubService },
|
|
13655
|
+
aliasReq,
|
|
13656
|
+
res
|
|
13657
|
+
);
|
|
13658
|
+
}
|
|
13659
|
+
return false;
|
|
13660
|
+
}
|
|
13661
|
+
var init_dispatch = __esm({
|
|
13662
|
+
"src/dashboard/v1_1/dispatch.ts"() {
|
|
13663
|
+
init_api_router();
|
|
13664
|
+
init_v1_1();
|
|
13665
|
+
}
|
|
13666
|
+
});
|
|
13605
13667
|
function isDashboardViewRoute(method, path) {
|
|
13606
13668
|
if (method !== "GET") return false;
|
|
13607
13669
|
return path === "/" || path === "/dashboard" || path === "/fortress" || path === "/events";
|
|
@@ -13614,8 +13676,7 @@ var init_dashboard = __esm({
|
|
|
13614
13676
|
init_dashboard_html();
|
|
13615
13677
|
init_fortress_view();
|
|
13616
13678
|
init_system_prompt_generator();
|
|
13617
|
-
|
|
13618
|
-
init_v1_1();
|
|
13679
|
+
init_dispatch();
|
|
13619
13680
|
SESSION_TTL_REMOTE_MS = 5 * 60 * 1e3;
|
|
13620
13681
|
SESSION_TTL_LOCAL_MS = 24 * 60 * 60 * 1e3;
|
|
13621
13682
|
MAX_SESSIONS = 1e3;
|
|
@@ -13737,45 +13798,17 @@ var init_dashboard = __esm({
|
|
|
13737
13798
|
*/
|
|
13738
13799
|
async dispatchV11(req, res, url, method) {
|
|
13739
13800
|
if (!this.v11Bindings) return false;
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
|
|
13750
|
-
|
|
13751
|
-
}
|
|
13752
|
-
if (url.pathname.startsWith("/api/hub/")) {
|
|
13753
|
-
const authConfig = {
|
|
13754
|
-
loopbackAutoAuth: this._autoAuthLocalhost,
|
|
13755
|
-
...this.authToken !== void 0 ? { authToken: this.authToken } : {}
|
|
13756
|
-
};
|
|
13757
|
-
const handled = await handleHubRoute(
|
|
13758
|
-
{ authConfig, service: this.v11Bindings.hubService },
|
|
13759
|
-
req,
|
|
13760
|
-
res
|
|
13761
|
-
);
|
|
13762
|
-
return handled;
|
|
13763
|
-
}
|
|
13764
|
-
if (method === "GET" && url.pathname === "/api/identities") {
|
|
13765
|
-
const authConfig = {
|
|
13766
|
-
loopbackAutoAuth: this._autoAuthLocalhost,
|
|
13767
|
-
...this.authToken !== void 0 ? { authToken: this.authToken } : {}
|
|
13768
|
-
};
|
|
13769
|
-
const aliasReq = Object.create(req);
|
|
13770
|
-
aliasReq.url = "/api/hub/agents" + url.search;
|
|
13771
|
-
const handled = await handleHubRoute(
|
|
13772
|
-
{ authConfig, service: this.v11Bindings.hubService },
|
|
13773
|
-
aliasReq,
|
|
13774
|
-
res
|
|
13775
|
-
);
|
|
13776
|
-
return handled;
|
|
13777
|
-
}
|
|
13778
|
-
return false;
|
|
13801
|
+
return dispatchV11Request(
|
|
13802
|
+
{
|
|
13803
|
+
bindings: this.v11Bindings,
|
|
13804
|
+
...this.authToken !== void 0 ? { authToken: this.authToken } : {},
|
|
13805
|
+
loopbackAutoAuth: this._autoAuthLocalhost
|
|
13806
|
+
},
|
|
13807
|
+
req,
|
|
13808
|
+
res,
|
|
13809
|
+
url,
|
|
13810
|
+
method
|
|
13811
|
+
);
|
|
13779
13812
|
}
|
|
13780
13813
|
/**
|
|
13781
13814
|
* v0.10.2: enable (or disable) the loopback auto-auth fast path. See
|
|
@@ -27503,14 +27536,14 @@ var init_generator2 = __esm({
|
|
|
27503
27536
|
]);
|
|
27504
27537
|
}
|
|
27505
27538
|
});
|
|
27506
|
-
function
|
|
27539
|
+
function printSecretBanner(secret, filePath, copy, output = process.stderr) {
|
|
27507
27540
|
const lines = [
|
|
27508
|
-
|
|
27541
|
+
copy.bannerHeader,
|
|
27509
27542
|
"",
|
|
27510
|
-
|
|
27543
|
+
`${copy.bannerSecretLabel}: ${secret}`,
|
|
27511
27544
|
"",
|
|
27512
|
-
|
|
27513
|
-
|
|
27545
|
+
copy.bannerSaveLine,
|
|
27546
|
+
copy.bannerLossLine,
|
|
27514
27547
|
"",
|
|
27515
27548
|
"Plaintext copy written to:",
|
|
27516
27549
|
` ${filePath}`,
|
|
@@ -27529,8 +27562,8 @@ ${bottom}
|
|
|
27529
27562
|
|
|
27530
27563
|
`);
|
|
27531
27564
|
}
|
|
27532
|
-
async function
|
|
27533
|
-
const filePath = path.join(opts.storagePath,
|
|
27565
|
+
async function writeSecretFile(opts) {
|
|
27566
|
+
const filePath = path.join(opts.storagePath, opts.copy.fileName);
|
|
27534
27567
|
try {
|
|
27535
27568
|
await promises.access(filePath, promises.constants.F_OK);
|
|
27536
27569
|
return { filePath, written: false };
|
|
@@ -27540,54 +27573,47 @@ async function writeRecoveryKeyFile(opts) {
|
|
|
27540
27573
|
const now = (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
27541
27574
|
const fortressLine = opts.fortressId ? `Fortress: ${opts.fortressId}
|
|
27542
27575
|
` : "";
|
|
27543
|
-
const content =
|
|
27576
|
+
const content = `${opts.copy.fileWarningHeader}
|
|
27544
27577
|
Generated: ${now}
|
|
27545
27578
|
` + fortressLine + `
|
|
27546
|
-
|
|
27547
|
-
${opts.
|
|
27548
|
-
|
|
27549
|
-
|
|
27550
|
-
subsequent runs and will NOT display the key again. After moving this file off
|
|
27551
|
-
the host (encrypted backup, password manager, paper safe), delete it from the
|
|
27552
|
-
fortress directory. Do NOT keep it in the fortress; the recovery key bypasses
|
|
27553
|
-
the cocoon passphrase by design.
|
|
27554
|
-
`;
|
|
27579
|
+
${opts.copy.fileSecretLabel}
|
|
27580
|
+
${opts.secret}
|
|
27581
|
+
|
|
27582
|
+
` + opts.copy.fileBody;
|
|
27555
27583
|
await promises.writeFile(filePath, content, { mode: 384 });
|
|
27556
27584
|
return { filePath, written: true };
|
|
27557
27585
|
}
|
|
27558
|
-
async function
|
|
27586
|
+
async function confirmSecretSaved(copy, declinedError, nonInteractiveError, io) {
|
|
27559
27587
|
const input = io?.input ?? process.stdin;
|
|
27560
27588
|
const output = io?.output ?? process.stderr;
|
|
27561
27589
|
const realStdin = !io && process.stdin.isTTY !== true;
|
|
27562
27590
|
if (realStdin) {
|
|
27563
|
-
throw new
|
|
27591
|
+
throw new nonInteractiveError();
|
|
27564
27592
|
}
|
|
27565
27593
|
const rl = promises$1.createInterface({ input, output });
|
|
27566
27594
|
try {
|
|
27567
27595
|
const answer = await rl.question(
|
|
27568
|
-
|
|
27596
|
+
`Have you saved the ${copy.promptLabel}? [y/N] `
|
|
27569
27597
|
);
|
|
27570
27598
|
const normalized = answer.trim().toLowerCase();
|
|
27571
27599
|
if (normalized !== "y" && normalized !== "yes") {
|
|
27572
|
-
throw new
|
|
27600
|
+
throw new declinedError();
|
|
27573
27601
|
}
|
|
27574
27602
|
} finally {
|
|
27575
27603
|
rl.close();
|
|
27576
27604
|
}
|
|
27577
27605
|
}
|
|
27578
|
-
async function
|
|
27606
|
+
async function discloseSecret(opts, copy, declinedError, nonInteractiveError) {
|
|
27579
27607
|
const mode = opts.mode ?? "interactive";
|
|
27580
|
-
const
|
|
27608
|
+
const writeOpts = {
|
|
27581
27609
|
storagePath: opts.storagePath,
|
|
27582
|
-
|
|
27583
|
-
|
|
27584
|
-
|
|
27585
|
-
|
|
27586
|
-
|
|
27587
|
-
|
|
27588
|
-
|
|
27589
|
-
opts.io?.output
|
|
27590
|
-
);
|
|
27610
|
+
secret: opts.secret,
|
|
27611
|
+
copy
|
|
27612
|
+
};
|
|
27613
|
+
if (opts.fortressId !== void 0) writeOpts.fortressId = opts.fortressId;
|
|
27614
|
+
if (opts.now !== void 0) writeOpts.now = opts.now;
|
|
27615
|
+
const fileResult = await writeSecretFile(writeOpts);
|
|
27616
|
+
printSecretBanner(opts.secret, fileResult.filePath, copy, opts.io?.output);
|
|
27591
27617
|
if (mode === "no-confirm" || mode === "stdio-server") {
|
|
27592
27618
|
return {
|
|
27593
27619
|
filePath: fileResult.filePath,
|
|
@@ -27595,17 +27621,72 @@ async function discloseRecoveryKey(opts) {
|
|
|
27595
27621
|
confirmed: false
|
|
27596
27622
|
};
|
|
27597
27623
|
}
|
|
27598
|
-
await
|
|
27624
|
+
await confirmSecretSaved(copy, declinedError, nonInteractiveError, opts.io);
|
|
27599
27625
|
return {
|
|
27600
27626
|
filePath: fileResult.filePath,
|
|
27601
27627
|
fileWritten: fileResult.written,
|
|
27602
27628
|
confirmed: true
|
|
27603
27629
|
};
|
|
27604
27630
|
}
|
|
27605
|
-
|
|
27631
|
+
async function discloseRecoveryKey(opts) {
|
|
27632
|
+
const internalOpts = {
|
|
27633
|
+
secret: opts.recoveryKey,
|
|
27634
|
+
storagePath: opts.storagePath
|
|
27635
|
+
};
|
|
27636
|
+
if (opts.fortressId !== void 0) internalOpts.fortressId = opts.fortressId;
|
|
27637
|
+
if (opts.mode !== void 0) internalOpts.mode = opts.mode;
|
|
27638
|
+
if (opts.now !== void 0) internalOpts.now = opts.now;
|
|
27639
|
+
if (opts.io !== void 0) internalOpts.io = opts.io;
|
|
27640
|
+
return discloseSecret(
|
|
27641
|
+
internalOpts,
|
|
27642
|
+
RECOVERY_KEY_COPY,
|
|
27643
|
+
RecoveryKeyConfirmationDeclinedError,
|
|
27644
|
+
RecoveryKeyConfirmationNonInteractiveError
|
|
27645
|
+
);
|
|
27646
|
+
}
|
|
27647
|
+
async function disclosePassphrase(opts) {
|
|
27648
|
+
const internalOpts = {
|
|
27649
|
+
secret: opts.passphrase,
|
|
27650
|
+
storagePath: opts.storagePath
|
|
27651
|
+
};
|
|
27652
|
+
if (opts.fortressId !== void 0) internalOpts.fortressId = opts.fortressId;
|
|
27653
|
+
if (opts.mode !== void 0) internalOpts.mode = opts.mode;
|
|
27654
|
+
if (opts.now !== void 0) internalOpts.now = opts.now;
|
|
27655
|
+
if (opts.io !== void 0) internalOpts.io = opts.io;
|
|
27656
|
+
return discloseSecret(
|
|
27657
|
+
internalOpts,
|
|
27658
|
+
PASSPHRASE_BACKUP_COPY,
|
|
27659
|
+
PassphraseConfirmationDeclinedError,
|
|
27660
|
+
PassphraseConfirmationNonInteractiveError
|
|
27661
|
+
);
|
|
27662
|
+
}
|
|
27663
|
+
var RECOVERY_KEY_FILENAME, PASSPHRASE_BACKUP_FILENAME, RECOVERY_KEY_COPY, PASSPHRASE_BACKUP_COPY, RecoveryKeyConfirmationDeclinedError, RecoveryKeyConfirmationNonInteractiveError, PassphraseConfirmationDeclinedError, PassphraseConfirmationNonInteractiveError;
|
|
27606
27664
|
var init_recovery_key_disclosure = __esm({
|
|
27607
27665
|
"src/cocoon/recovery-key-disclosure.ts"() {
|
|
27608
27666
|
RECOVERY_KEY_FILENAME = "recovery-key.txt";
|
|
27667
|
+
PASSPHRASE_BACKUP_FILENAME = "passphrase-backup.txt";
|
|
27668
|
+
RECOVERY_KEY_COPY = {
|
|
27669
|
+
fileName: RECOVERY_KEY_FILENAME,
|
|
27670
|
+
bannerHeader: "SANCTUARY: First Run, Recovery Key Generated",
|
|
27671
|
+
bannerSecretLabel: "Recovery Key",
|
|
27672
|
+
bannerSaveLine: "SAVE THIS KEY. It will not be shown again.",
|
|
27673
|
+
bannerLossLine: "Without it, your encrypted state is unrecoverable.",
|
|
27674
|
+
fileWarningHeader: "SANCTUARY RECOVERY KEY, DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.",
|
|
27675
|
+
fileSecretLabel: "Recovery key:",
|
|
27676
|
+
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",
|
|
27677
|
+
promptLabel: "recovery key"
|
|
27678
|
+
};
|
|
27679
|
+
PASSPHRASE_BACKUP_COPY = {
|
|
27680
|
+
fileName: PASSPHRASE_BACKUP_FILENAME,
|
|
27681
|
+
bannerHeader: "SANCTUARY: First Run, Passphrase Generated",
|
|
27682
|
+
bannerSecretLabel: "Passphrase",
|
|
27683
|
+
bannerSaveLine: "SAVE THIS PASSPHRASE. It will not be shown again.",
|
|
27684
|
+
bannerLossLine: "Without it, your encrypted state is unrecoverable.",
|
|
27685
|
+
fileWarningHeader: "SANCTUARY PASSPHRASE, DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.",
|
|
27686
|
+
fileSecretLabel: "Passphrase:",
|
|
27687
|
+
fileBody: "This file was created on first wrap when Sanctuary generated the passphrase.\nSanctuary will NOT regenerate this file on subsequent runs and will NOT display\nthe passphrase again. After moving this file off the host (encrypted backup,\npassword manager, paper safe), delete it from the fortress directory. Do NOT\nkeep it in the fortress; the keychain copy is recoverable only while the host\nand its OS keyring are intact.\n",
|
|
27688
|
+
promptLabel: "passphrase"
|
|
27689
|
+
};
|
|
27609
27690
|
RecoveryKeyConfirmationDeclinedError = class extends Error {
|
|
27610
27691
|
constructor() {
|
|
27611
27692
|
super(
|
|
@@ -27622,6 +27703,22 @@ var init_recovery_key_disclosure = __esm({
|
|
|
27622
27703
|
this.name = "RecoveryKeyConfirmationNonInteractiveError";
|
|
27623
27704
|
}
|
|
27624
27705
|
};
|
|
27706
|
+
PassphraseConfirmationDeclinedError = class extends Error {
|
|
27707
|
+
constructor() {
|
|
27708
|
+
super(
|
|
27709
|
+
"Passphrase confirmation declined. Save the passphrase (printed above and written to passphrase-backup.txt) before re-running wrap."
|
|
27710
|
+
);
|
|
27711
|
+
this.name = "PassphraseConfirmationDeclinedError";
|
|
27712
|
+
}
|
|
27713
|
+
};
|
|
27714
|
+
PassphraseConfirmationNonInteractiveError = class extends Error {
|
|
27715
|
+
constructor() {
|
|
27716
|
+
super(
|
|
27717
|
+
"Passphrase confirmation requires an interactive terminal. Re-run with --no-open for scripted use, or run from a TTY."
|
|
27718
|
+
);
|
|
27719
|
+
this.name = "PassphraseConfirmationNonInteractiveError";
|
|
27720
|
+
}
|
|
27721
|
+
};
|
|
27625
27722
|
}
|
|
27626
27723
|
});
|
|
27627
27724
|
|
|
@@ -30023,14 +30120,18 @@ async function startDashboardServer(options) {
|
|
|
30023
30120
|
}
|
|
30024
30121
|
}
|
|
30025
30122
|
};
|
|
30026
|
-
|
|
30027
|
-
|
|
30028
|
-
authToken: options.authToken,
|
|
30029
|
-
approvals: options.approvals,
|
|
30030
|
-
onEvent
|
|
30031
|
-
};
|
|
30123
|
+
let v11Bindings = null;
|
|
30124
|
+
let v11LoopbackAutoAuth = false;
|
|
30032
30125
|
const server = http.createServer(async (req, res) => {
|
|
30033
30126
|
try {
|
|
30127
|
+
const deps = {
|
|
30128
|
+
sources: options.sources,
|
|
30129
|
+
authToken: options.authToken,
|
|
30130
|
+
approvals: options.approvals,
|
|
30131
|
+
onEvent,
|
|
30132
|
+
v11Bindings,
|
|
30133
|
+
loopbackAutoAuth: v11LoopbackAutoAuth
|
|
30134
|
+
};
|
|
30034
30135
|
const served = await handleRequest(deps, req, res);
|
|
30035
30136
|
if (!served) {
|
|
30036
30137
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
@@ -30068,7 +30169,13 @@ async function startDashboardServer(options) {
|
|
|
30068
30169
|
publishActivity: (entry) => publish({ type: "activity", data: entry }),
|
|
30069
30170
|
publishApproval: (approval) => publish({ type: "approval", data: approval }),
|
|
30070
30171
|
publishInbox: (item) => publish({ type: "inbox", data: item }),
|
|
30071
|
-
publishAgentStatus: (snapshot) => publish({ type: "agent_status", data: snapshot })
|
|
30172
|
+
publishAgentStatus: (snapshot) => publish({ type: "agent_status", data: snapshot }),
|
|
30173
|
+
setV11Bindings: (bindings) => {
|
|
30174
|
+
v11Bindings = bindings;
|
|
30175
|
+
},
|
|
30176
|
+
setV11LoopbackAutoAuth: (enabled) => {
|
|
30177
|
+
v11LoopbackAutoAuth = enabled;
|
|
30178
|
+
}
|
|
30072
30179
|
};
|
|
30073
30180
|
}
|
|
30074
30181
|
var DEFAULT_PORT, DEFAULT_HOST;
|
|
@@ -31332,12 +31439,14 @@ async function runWrap(options, deps = {}) {
|
|
|
31332
31439
|
const storagePath = resolveStoragePath();
|
|
31333
31440
|
let passphraseLocation;
|
|
31334
31441
|
let passphraseSource;
|
|
31442
|
+
let passphraseValue;
|
|
31335
31443
|
if (options.passphrase) {
|
|
31336
31444
|
try {
|
|
31337
31445
|
const persist = deps.persistPassphrase ?? ((value) => persistUserProvidedPassphrase(value, { storagePath }));
|
|
31338
31446
|
const persisted = await persist(options.passphrase);
|
|
31339
31447
|
passphraseLocation = persisted.location;
|
|
31340
31448
|
passphraseSource = persisted.source;
|
|
31449
|
+
passphraseValue = options.passphrase;
|
|
31341
31450
|
console.error(
|
|
31342
31451
|
`
|
|
31343
31452
|
\u{1F510} Persisted user-supplied passphrase (${persisted.location}).`
|
|
@@ -31355,12 +31464,14 @@ async function runWrap(options, deps = {}) {
|
|
|
31355
31464
|
} else if (process.env.SANCTUARY_PASSPHRASE) {
|
|
31356
31465
|
passphraseLocation = "SANCTUARY_PASSPHRASE";
|
|
31357
31466
|
passphraseSource = "env";
|
|
31467
|
+
passphraseValue = process.env.SANCTUARY_PASSPHRASE;
|
|
31358
31468
|
} else {
|
|
31359
31469
|
try {
|
|
31360
31470
|
const resolve5 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
|
|
31361
31471
|
const resolved = await resolve5();
|
|
31362
31472
|
passphraseLocation = resolved.location;
|
|
31363
31473
|
passphraseSource = resolved.source;
|
|
31474
|
+
passphraseValue = resolved.value;
|
|
31364
31475
|
if (resolved.source === "generated") {
|
|
31365
31476
|
console.error(
|
|
31366
31477
|
`
|
|
@@ -31396,6 +31507,27 @@ async function runWrap(options, deps = {}) {
|
|
|
31396
31507
|
);
|
|
31397
31508
|
}
|
|
31398
31509
|
await promises.mkdir(storagePath, { recursive: true, mode: 448 });
|
|
31510
|
+
if (passphraseSource === "generated" && passphraseValue !== void 0) {
|
|
31511
|
+
try {
|
|
31512
|
+
await disclosePassphrase({
|
|
31513
|
+
passphrase: passphraseValue,
|
|
31514
|
+
storagePath,
|
|
31515
|
+
fortressId: fortressIdFromStoragePath(storagePath),
|
|
31516
|
+
// --no-open (CI / scripted) or non-TTY stdin both skip the prompt
|
|
31517
|
+
// the same way init's --no-confirm does. Operator who scripted the
|
|
31518
|
+
// call still gets the banner + the file; they will not see a hang.
|
|
31519
|
+
mode: options.noOpen || process.stdin.isTTY !== true ? "no-confirm" : "interactive"
|
|
31520
|
+
});
|
|
31521
|
+
} catch (err) {
|
|
31522
|
+
if (err instanceof PassphraseConfirmationDeclinedError || err instanceof PassphraseConfirmationNonInteractiveError) {
|
|
31523
|
+
console.error(`
|
|
31524
|
+
Sanctuary wrap: ${err.message}
|
|
31525
|
+
`);
|
|
31526
|
+
process.exit(2);
|
|
31527
|
+
}
|
|
31528
|
+
throw err;
|
|
31529
|
+
}
|
|
31530
|
+
}
|
|
31399
31531
|
const profile = createWrapProfile(upstreamServers);
|
|
31400
31532
|
const profilePath = path.join(storagePath, "cocoon-profile.json");
|
|
31401
31533
|
await promises.writeFile(profilePath, JSON.stringify(profile, null, 2), {
|
|
@@ -31418,6 +31550,13 @@ async function runWrap(options, deps = {}) {
|
|
|
31418
31550
|
if (process.env.SANCTUARY_DASHBOARD_ENABLED) {
|
|
31419
31551
|
sanctuaryEnv.SANCTUARY_DASHBOARD_ENABLED = process.env.SANCTUARY_DASHBOARD_ENABLED;
|
|
31420
31552
|
}
|
|
31553
|
+
if (options.fortress) {
|
|
31554
|
+
sanctuaryEnv.SANCTUARY_FORTRESS_PATH = path.resolve(options.fortress);
|
|
31555
|
+
} else if (process.env.SANCTUARY_FORTRESS_PATH) {
|
|
31556
|
+
sanctuaryEnv.SANCTUARY_FORTRESS_PATH = path.resolve(
|
|
31557
|
+
process.env.SANCTUARY_FORTRESS_PATH
|
|
31558
|
+
);
|
|
31559
|
+
}
|
|
31421
31560
|
const rewrite = deps.rewriteConfig ?? rewriteConfigForCocoon;
|
|
31422
31561
|
await rewrite(
|
|
31423
31562
|
agentConfig,
|
|
@@ -31445,6 +31584,40 @@ async function runWrap(options, deps = {}) {
|
|
|
31445
31584
|
authToken,
|
|
31446
31585
|
readPackageVersion()
|
|
31447
31586
|
);
|
|
31587
|
+
if (passphraseValue !== void 0) {
|
|
31588
|
+
try {
|
|
31589
|
+
const v11Storage = new FilesystemStorage(`${storagePath}/state`);
|
|
31590
|
+
let existingParams;
|
|
31591
|
+
try {
|
|
31592
|
+
const raw = await v11Storage.read("_meta", "key-params");
|
|
31593
|
+
if (raw) {
|
|
31594
|
+
existingParams = JSON.parse(bytesToString(raw));
|
|
31595
|
+
}
|
|
31596
|
+
} catch {
|
|
31597
|
+
}
|
|
31598
|
+
const derived = await deriveMasterKey(passphraseValue, existingParams);
|
|
31599
|
+
if (!existingParams) {
|
|
31600
|
+
await v11Storage.write(
|
|
31601
|
+
"_meta",
|
|
31602
|
+
"key-params",
|
|
31603
|
+
stringToBytes(JSON.stringify(derived.params))
|
|
31604
|
+
);
|
|
31605
|
+
}
|
|
31606
|
+
const wrapAuditLog = new AuditLog(v11Storage, derived.key);
|
|
31607
|
+
dashboard.setV11Bindings(
|
|
31608
|
+
buildV11Bindings({
|
|
31609
|
+
identityId: `fortress:${storagePath}`,
|
|
31610
|
+
fortressId: fortressIdFromStoragePath(storagePath),
|
|
31611
|
+
auditLog: wrapAuditLog
|
|
31612
|
+
})
|
|
31613
|
+
);
|
|
31614
|
+
dashboard.setV11LoopbackAutoAuth(true);
|
|
31615
|
+
} catch (err) {
|
|
31616
|
+
console.error(
|
|
31617
|
+
` Note: v1.1 dashboard surfaces unavailable on wrap URL (${err.message}). Run \`sanctuary dashboard\` to reach them.`
|
|
31618
|
+
);
|
|
31619
|
+
}
|
|
31620
|
+
}
|
|
31448
31621
|
const dashboardUrl = `${dashboard.url}?token=${authToken}`;
|
|
31449
31622
|
const webhookCallbackPortRaw = process.env.SANCTUARY_WEBHOOK_CALLBACK_PORT;
|
|
31450
31623
|
const webhookCallbackPort = webhookCallbackPortRaw ? parseInt(webhookCallbackPortRaw, 10) : void 0;
|
|
@@ -31807,9 +31980,15 @@ var init_cli2 = __esm({
|
|
|
31807
31980
|
init_config_reader();
|
|
31808
31981
|
init_passphrase();
|
|
31809
31982
|
init_dashboard2();
|
|
31983
|
+
init_wiring();
|
|
31984
|
+
init_filesystem();
|
|
31985
|
+
init_key_derivation();
|
|
31986
|
+
init_encoding();
|
|
31987
|
+
init_audit_log();
|
|
31810
31988
|
init_config();
|
|
31811
31989
|
init_paths();
|
|
31812
31990
|
init_runtime();
|
|
31991
|
+
init_recovery_key_disclosure();
|
|
31813
31992
|
COCOON_GOVERNOR_DEFAULTS = {
|
|
31814
31993
|
volume_limit: 200,
|
|
31815
31994
|
rate_limit_per_tool: 20,
|
|
@@ -35201,10 +35380,10 @@ __export(dashboard_standalone_exports, {
|
|
|
35201
35380
|
renderTenantDiscoveryHint: () => renderTenantDiscoveryHint,
|
|
35202
35381
|
startStandaloneDashboard: () => startStandaloneDashboard
|
|
35203
35382
|
});
|
|
35204
|
-
async function discoverableSubTenants(currentStoragePath) {
|
|
35383
|
+
async function discoverableSubTenants(currentStoragePath, discoveryOptions) {
|
|
35205
35384
|
let all;
|
|
35206
35385
|
try {
|
|
35207
|
-
all = await discoverTenants();
|
|
35386
|
+
all = await discoverTenants(discoveryOptions);
|
|
35208
35387
|
} catch {
|
|
35209
35388
|
return [];
|
|
35210
35389
|
}
|
|
@@ -35607,6 +35786,9 @@ var { version: PKG_VERSION4 } = require4("../package.json");
|
|
|
35607
35786
|
async function main() {
|
|
35608
35787
|
const args = process.argv.slice(2);
|
|
35609
35788
|
let passphrase = process.env.SANCTUARY_PASSPHRASE;
|
|
35789
|
+
if (process.env.SANCTUARY_FORTRESS_PATH && !process.env.SANCTUARY_STORAGE_PATH) {
|
|
35790
|
+
process.env.SANCTUARY_STORAGE_PATH = process.env.SANCTUARY_FORTRESS_PATH;
|
|
35791
|
+
}
|
|
35610
35792
|
if (args[0] === "dashboard") {
|
|
35611
35793
|
await runStandaloneDashboard(args.slice(1));
|
|
35612
35794
|
return;
|