@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.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { mkdir, writeFile, readFile, stat, unlink, readdir, rm, chmod, access, constants, appendFile, lstat, copyFile, realpath } from 'fs/promises';
|
|
3
|
-
import { join, dirname,
|
|
3
|
+
import { join, dirname, resolve, isAbsolute, basename, sep } from 'path';
|
|
4
4
|
import { platform, homedir, userInfo, hostname } from 'os';
|
|
5
5
|
import { createRequire } from 'module';
|
|
6
6
|
import { createHash, randomBytes as randomBytes$1, randomUUID, createHmac } from 'crypto';
|
|
@@ -20,8 +20,8 @@ import { get, createServer as createServer$1 } from 'https';
|
|
|
20
20
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
21
21
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
22
22
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
23
|
-
import { createInterface
|
|
24
|
-
import { createInterface } from 'readline';
|
|
23
|
+
import { createInterface } from 'readline/promises';
|
|
24
|
+
import { createInterface as createInterface$1 } from 'readline';
|
|
25
25
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
26
26
|
|
|
27
27
|
var __defProp = Object.defineProperty;
|
|
@@ -11884,6 +11884,20 @@ async function handleRequest(deps, req, res) {
|
|
|
11884
11884
|
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
11885
11885
|
const method = (req.method ?? "GET").toUpperCase();
|
|
11886
11886
|
const path = url.pathname;
|
|
11887
|
+
if (deps.v11Bindings) {
|
|
11888
|
+
const handled = await dispatchV11Request(
|
|
11889
|
+
{
|
|
11890
|
+
bindings: deps.v11Bindings,
|
|
11891
|
+
...deps.authToken !== void 0 ? { authToken: deps.authToken } : {},
|
|
11892
|
+
loopbackAutoAuth: deps.loopbackAutoAuth ?? false
|
|
11893
|
+
},
|
|
11894
|
+
req,
|
|
11895
|
+
res,
|
|
11896
|
+
url,
|
|
11897
|
+
method
|
|
11898
|
+
);
|
|
11899
|
+
if (handled) return true;
|
|
11900
|
+
}
|
|
11887
11901
|
if (!isAuthorized(deps, req, url)) {
|
|
11888
11902
|
writeJSON(res, 401, { error: "unauthorized" });
|
|
11889
11903
|
return true;
|
|
@@ -12064,6 +12078,7 @@ var init_api = __esm({
|
|
|
12064
12078
|
init_registry();
|
|
12065
12079
|
init_init();
|
|
12066
12080
|
init_discovery();
|
|
12081
|
+
init_dispatch();
|
|
12067
12082
|
}
|
|
12068
12083
|
});
|
|
12069
12084
|
|
|
@@ -13599,6 +13614,53 @@ var init_v1_1 = __esm({
|
|
|
13599
13614
|
init_client();
|
|
13600
13615
|
}
|
|
13601
13616
|
});
|
|
13617
|
+
|
|
13618
|
+
// src/dashboard/v1_1/dispatch.ts
|
|
13619
|
+
async function dispatchV11Request(inputs, req, res, url, method) {
|
|
13620
|
+
const { bindings, authToken, loopbackAutoAuth } = inputs;
|
|
13621
|
+
if (method === "GET" && (url.pathname === "/v1.1" || url.pathname === "/v1.1/")) {
|
|
13622
|
+
return handleDashboardV11Route(
|
|
13623
|
+
{
|
|
13624
|
+
identityId: bindings.identityId,
|
|
13625
|
+
fortressId: bindings.fortressId,
|
|
13626
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
13627
|
+
},
|
|
13628
|
+
req,
|
|
13629
|
+
res
|
|
13630
|
+
);
|
|
13631
|
+
}
|
|
13632
|
+
if (url.pathname.startsWith("/api/hub/")) {
|
|
13633
|
+
const authConfig = {
|
|
13634
|
+
loopbackAutoAuth,
|
|
13635
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
13636
|
+
};
|
|
13637
|
+
return handleHubRoute(
|
|
13638
|
+
{ authConfig, service: bindings.hubService },
|
|
13639
|
+
req,
|
|
13640
|
+
res
|
|
13641
|
+
);
|
|
13642
|
+
}
|
|
13643
|
+
if (method === "GET" && url.pathname === "/api/identities") {
|
|
13644
|
+
const authConfig = {
|
|
13645
|
+
loopbackAutoAuth,
|
|
13646
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
13647
|
+
};
|
|
13648
|
+
const aliasReq = Object.create(req);
|
|
13649
|
+
aliasReq.url = "/api/hub/agents" + url.search;
|
|
13650
|
+
return handleHubRoute(
|
|
13651
|
+
{ authConfig, service: bindings.hubService },
|
|
13652
|
+
aliasReq,
|
|
13653
|
+
res
|
|
13654
|
+
);
|
|
13655
|
+
}
|
|
13656
|
+
return false;
|
|
13657
|
+
}
|
|
13658
|
+
var init_dispatch = __esm({
|
|
13659
|
+
"src/dashboard/v1_1/dispatch.ts"() {
|
|
13660
|
+
init_api_router();
|
|
13661
|
+
init_v1_1();
|
|
13662
|
+
}
|
|
13663
|
+
});
|
|
13602
13664
|
function isDashboardViewRoute(method, path) {
|
|
13603
13665
|
if (method !== "GET") return false;
|
|
13604
13666
|
return path === "/" || path === "/dashboard" || path === "/fortress" || path === "/events";
|
|
@@ -13611,8 +13673,7 @@ var init_dashboard = __esm({
|
|
|
13611
13673
|
init_dashboard_html();
|
|
13612
13674
|
init_fortress_view();
|
|
13613
13675
|
init_system_prompt_generator();
|
|
13614
|
-
|
|
13615
|
-
init_v1_1();
|
|
13676
|
+
init_dispatch();
|
|
13616
13677
|
SESSION_TTL_REMOTE_MS = 5 * 60 * 1e3;
|
|
13617
13678
|
SESSION_TTL_LOCAL_MS = 24 * 60 * 60 * 1e3;
|
|
13618
13679
|
MAX_SESSIONS = 1e3;
|
|
@@ -13734,45 +13795,17 @@ var init_dashboard = __esm({
|
|
|
13734
13795
|
*/
|
|
13735
13796
|
async dispatchV11(req, res, url, method) {
|
|
13736
13797
|
if (!this.v11Bindings) return false;
|
|
13737
|
-
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
}
|
|
13749
|
-
if (url.pathname.startsWith("/api/hub/")) {
|
|
13750
|
-
const authConfig = {
|
|
13751
|
-
loopbackAutoAuth: this._autoAuthLocalhost,
|
|
13752
|
-
...this.authToken !== void 0 ? { authToken: this.authToken } : {}
|
|
13753
|
-
};
|
|
13754
|
-
const handled = await handleHubRoute(
|
|
13755
|
-
{ authConfig, service: this.v11Bindings.hubService },
|
|
13756
|
-
req,
|
|
13757
|
-
res
|
|
13758
|
-
);
|
|
13759
|
-
return handled;
|
|
13760
|
-
}
|
|
13761
|
-
if (method === "GET" && url.pathname === "/api/identities") {
|
|
13762
|
-
const authConfig = {
|
|
13763
|
-
loopbackAutoAuth: this._autoAuthLocalhost,
|
|
13764
|
-
...this.authToken !== void 0 ? { authToken: this.authToken } : {}
|
|
13765
|
-
};
|
|
13766
|
-
const aliasReq = Object.create(req);
|
|
13767
|
-
aliasReq.url = "/api/hub/agents" + url.search;
|
|
13768
|
-
const handled = await handleHubRoute(
|
|
13769
|
-
{ authConfig, service: this.v11Bindings.hubService },
|
|
13770
|
-
aliasReq,
|
|
13771
|
-
res
|
|
13772
|
-
);
|
|
13773
|
-
return handled;
|
|
13774
|
-
}
|
|
13775
|
-
return false;
|
|
13798
|
+
return dispatchV11Request(
|
|
13799
|
+
{
|
|
13800
|
+
bindings: this.v11Bindings,
|
|
13801
|
+
...this.authToken !== void 0 ? { authToken: this.authToken } : {},
|
|
13802
|
+
loopbackAutoAuth: this._autoAuthLocalhost
|
|
13803
|
+
},
|
|
13804
|
+
req,
|
|
13805
|
+
res,
|
|
13806
|
+
url,
|
|
13807
|
+
method
|
|
13808
|
+
);
|
|
13776
13809
|
}
|
|
13777
13810
|
/**
|
|
13778
13811
|
* v0.10.2: enable (or disable) the loopback auto-auth fast path. See
|
|
@@ -27500,14 +27533,14 @@ var init_generator2 = __esm({
|
|
|
27500
27533
|
]);
|
|
27501
27534
|
}
|
|
27502
27535
|
});
|
|
27503
|
-
function
|
|
27536
|
+
function printSecretBanner(secret, filePath, copy, output = process.stderr) {
|
|
27504
27537
|
const lines = [
|
|
27505
|
-
|
|
27538
|
+
copy.bannerHeader,
|
|
27506
27539
|
"",
|
|
27507
|
-
|
|
27540
|
+
`${copy.bannerSecretLabel}: ${secret}`,
|
|
27508
27541
|
"",
|
|
27509
|
-
|
|
27510
|
-
|
|
27542
|
+
copy.bannerSaveLine,
|
|
27543
|
+
copy.bannerLossLine,
|
|
27511
27544
|
"",
|
|
27512
27545
|
"Plaintext copy written to:",
|
|
27513
27546
|
` ${filePath}`,
|
|
@@ -27526,8 +27559,8 @@ ${bottom}
|
|
|
27526
27559
|
|
|
27527
27560
|
`);
|
|
27528
27561
|
}
|
|
27529
|
-
async function
|
|
27530
|
-
const filePath = join(opts.storagePath,
|
|
27562
|
+
async function writeSecretFile(opts) {
|
|
27563
|
+
const filePath = join(opts.storagePath, opts.copy.fileName);
|
|
27531
27564
|
try {
|
|
27532
27565
|
await access(filePath, constants.F_OK);
|
|
27533
27566
|
return { filePath, written: false };
|
|
@@ -27537,54 +27570,47 @@ async function writeRecoveryKeyFile(opts) {
|
|
|
27537
27570
|
const now = (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
27538
27571
|
const fortressLine = opts.fortressId ? `Fortress: ${opts.fortressId}
|
|
27539
27572
|
` : "";
|
|
27540
|
-
const content =
|
|
27573
|
+
const content = `${opts.copy.fileWarningHeader}
|
|
27541
27574
|
Generated: ${now}
|
|
27542
27575
|
` + fortressLine + `
|
|
27543
|
-
|
|
27544
|
-
${opts.
|
|
27545
|
-
|
|
27546
|
-
|
|
27547
|
-
subsequent runs and will NOT display the key again. After moving this file off
|
|
27548
|
-
the host (encrypted backup, password manager, paper safe), delete it from the
|
|
27549
|
-
fortress directory. Do NOT keep it in the fortress; the recovery key bypasses
|
|
27550
|
-
the cocoon passphrase by design.
|
|
27551
|
-
`;
|
|
27576
|
+
${opts.copy.fileSecretLabel}
|
|
27577
|
+
${opts.secret}
|
|
27578
|
+
|
|
27579
|
+
` + opts.copy.fileBody;
|
|
27552
27580
|
await writeFile(filePath, content, { mode: 384 });
|
|
27553
27581
|
return { filePath, written: true };
|
|
27554
27582
|
}
|
|
27555
|
-
async function
|
|
27583
|
+
async function confirmSecretSaved(copy, declinedError, nonInteractiveError, io) {
|
|
27556
27584
|
const input = io?.input ?? process.stdin;
|
|
27557
27585
|
const output = io?.output ?? process.stderr;
|
|
27558
27586
|
const realStdin = !io && process.stdin.isTTY !== true;
|
|
27559
27587
|
if (realStdin) {
|
|
27560
|
-
throw new
|
|
27588
|
+
throw new nonInteractiveError();
|
|
27561
27589
|
}
|
|
27562
|
-
const rl = createInterface
|
|
27590
|
+
const rl = createInterface({ input, output });
|
|
27563
27591
|
try {
|
|
27564
27592
|
const answer = await rl.question(
|
|
27565
|
-
|
|
27593
|
+
`Have you saved the ${copy.promptLabel}? [y/N] `
|
|
27566
27594
|
);
|
|
27567
27595
|
const normalized = answer.trim().toLowerCase();
|
|
27568
27596
|
if (normalized !== "y" && normalized !== "yes") {
|
|
27569
|
-
throw new
|
|
27597
|
+
throw new declinedError();
|
|
27570
27598
|
}
|
|
27571
27599
|
} finally {
|
|
27572
27600
|
rl.close();
|
|
27573
27601
|
}
|
|
27574
27602
|
}
|
|
27575
|
-
async function
|
|
27603
|
+
async function discloseSecret(opts, copy, declinedError, nonInteractiveError) {
|
|
27576
27604
|
const mode = opts.mode ?? "interactive";
|
|
27577
|
-
const
|
|
27605
|
+
const writeOpts = {
|
|
27578
27606
|
storagePath: opts.storagePath,
|
|
27579
|
-
|
|
27580
|
-
|
|
27581
|
-
|
|
27582
|
-
|
|
27583
|
-
|
|
27584
|
-
|
|
27585
|
-
|
|
27586
|
-
opts.io?.output
|
|
27587
|
-
);
|
|
27607
|
+
secret: opts.secret,
|
|
27608
|
+
copy
|
|
27609
|
+
};
|
|
27610
|
+
if (opts.fortressId !== void 0) writeOpts.fortressId = opts.fortressId;
|
|
27611
|
+
if (opts.now !== void 0) writeOpts.now = opts.now;
|
|
27612
|
+
const fileResult = await writeSecretFile(writeOpts);
|
|
27613
|
+
printSecretBanner(opts.secret, fileResult.filePath, copy, opts.io?.output);
|
|
27588
27614
|
if (mode === "no-confirm" || mode === "stdio-server") {
|
|
27589
27615
|
return {
|
|
27590
27616
|
filePath: fileResult.filePath,
|
|
@@ -27592,17 +27618,72 @@ async function discloseRecoveryKey(opts) {
|
|
|
27592
27618
|
confirmed: false
|
|
27593
27619
|
};
|
|
27594
27620
|
}
|
|
27595
|
-
await
|
|
27621
|
+
await confirmSecretSaved(copy, declinedError, nonInteractiveError, opts.io);
|
|
27596
27622
|
return {
|
|
27597
27623
|
filePath: fileResult.filePath,
|
|
27598
27624
|
fileWritten: fileResult.written,
|
|
27599
27625
|
confirmed: true
|
|
27600
27626
|
};
|
|
27601
27627
|
}
|
|
27602
|
-
|
|
27628
|
+
async function discloseRecoveryKey(opts) {
|
|
27629
|
+
const internalOpts = {
|
|
27630
|
+
secret: opts.recoveryKey,
|
|
27631
|
+
storagePath: opts.storagePath
|
|
27632
|
+
};
|
|
27633
|
+
if (opts.fortressId !== void 0) internalOpts.fortressId = opts.fortressId;
|
|
27634
|
+
if (opts.mode !== void 0) internalOpts.mode = opts.mode;
|
|
27635
|
+
if (opts.now !== void 0) internalOpts.now = opts.now;
|
|
27636
|
+
if (opts.io !== void 0) internalOpts.io = opts.io;
|
|
27637
|
+
return discloseSecret(
|
|
27638
|
+
internalOpts,
|
|
27639
|
+
RECOVERY_KEY_COPY,
|
|
27640
|
+
RecoveryKeyConfirmationDeclinedError,
|
|
27641
|
+
RecoveryKeyConfirmationNonInteractiveError
|
|
27642
|
+
);
|
|
27643
|
+
}
|
|
27644
|
+
async function disclosePassphrase(opts) {
|
|
27645
|
+
const internalOpts = {
|
|
27646
|
+
secret: opts.passphrase,
|
|
27647
|
+
storagePath: opts.storagePath
|
|
27648
|
+
};
|
|
27649
|
+
if (opts.fortressId !== void 0) internalOpts.fortressId = opts.fortressId;
|
|
27650
|
+
if (opts.mode !== void 0) internalOpts.mode = opts.mode;
|
|
27651
|
+
if (opts.now !== void 0) internalOpts.now = opts.now;
|
|
27652
|
+
if (opts.io !== void 0) internalOpts.io = opts.io;
|
|
27653
|
+
return discloseSecret(
|
|
27654
|
+
internalOpts,
|
|
27655
|
+
PASSPHRASE_BACKUP_COPY,
|
|
27656
|
+
PassphraseConfirmationDeclinedError,
|
|
27657
|
+
PassphraseConfirmationNonInteractiveError
|
|
27658
|
+
);
|
|
27659
|
+
}
|
|
27660
|
+
var RECOVERY_KEY_FILENAME, PASSPHRASE_BACKUP_FILENAME, RECOVERY_KEY_COPY, PASSPHRASE_BACKUP_COPY, RecoveryKeyConfirmationDeclinedError, RecoveryKeyConfirmationNonInteractiveError, PassphraseConfirmationDeclinedError, PassphraseConfirmationNonInteractiveError;
|
|
27603
27661
|
var init_recovery_key_disclosure = __esm({
|
|
27604
27662
|
"src/cocoon/recovery-key-disclosure.ts"() {
|
|
27605
27663
|
RECOVERY_KEY_FILENAME = "recovery-key.txt";
|
|
27664
|
+
PASSPHRASE_BACKUP_FILENAME = "passphrase-backup.txt";
|
|
27665
|
+
RECOVERY_KEY_COPY = {
|
|
27666
|
+
fileName: RECOVERY_KEY_FILENAME,
|
|
27667
|
+
bannerHeader: "SANCTUARY: First Run, Recovery Key Generated",
|
|
27668
|
+
bannerSecretLabel: "Recovery Key",
|
|
27669
|
+
bannerSaveLine: "SAVE THIS KEY. It will not be shown again.",
|
|
27670
|
+
bannerLossLine: "Without it, your encrypted state is unrecoverable.",
|
|
27671
|
+
fileWarningHeader: "SANCTUARY RECOVERY KEY, DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.",
|
|
27672
|
+
fileSecretLabel: "Recovery key:",
|
|
27673
|
+
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",
|
|
27674
|
+
promptLabel: "recovery key"
|
|
27675
|
+
};
|
|
27676
|
+
PASSPHRASE_BACKUP_COPY = {
|
|
27677
|
+
fileName: PASSPHRASE_BACKUP_FILENAME,
|
|
27678
|
+
bannerHeader: "SANCTUARY: First Run, Passphrase Generated",
|
|
27679
|
+
bannerSecretLabel: "Passphrase",
|
|
27680
|
+
bannerSaveLine: "SAVE THIS PASSPHRASE. It will not be shown again.",
|
|
27681
|
+
bannerLossLine: "Without it, your encrypted state is unrecoverable.",
|
|
27682
|
+
fileWarningHeader: "SANCTUARY PASSPHRASE, DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.",
|
|
27683
|
+
fileSecretLabel: "Passphrase:",
|
|
27684
|
+
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",
|
|
27685
|
+
promptLabel: "passphrase"
|
|
27686
|
+
};
|
|
27606
27687
|
RecoveryKeyConfirmationDeclinedError = class extends Error {
|
|
27607
27688
|
constructor() {
|
|
27608
27689
|
super(
|
|
@@ -27619,6 +27700,22 @@ var init_recovery_key_disclosure = __esm({
|
|
|
27619
27700
|
this.name = "RecoveryKeyConfirmationNonInteractiveError";
|
|
27620
27701
|
}
|
|
27621
27702
|
};
|
|
27703
|
+
PassphraseConfirmationDeclinedError = class extends Error {
|
|
27704
|
+
constructor() {
|
|
27705
|
+
super(
|
|
27706
|
+
"Passphrase confirmation declined. Save the passphrase (printed above and written to passphrase-backup.txt) before re-running wrap."
|
|
27707
|
+
);
|
|
27708
|
+
this.name = "PassphraseConfirmationDeclinedError";
|
|
27709
|
+
}
|
|
27710
|
+
};
|
|
27711
|
+
PassphraseConfirmationNonInteractiveError = class extends Error {
|
|
27712
|
+
constructor() {
|
|
27713
|
+
super(
|
|
27714
|
+
"Passphrase confirmation requires an interactive terminal. Re-run with --no-open for scripted use, or run from a TTY."
|
|
27715
|
+
);
|
|
27716
|
+
this.name = "PassphraseConfirmationNonInteractiveError";
|
|
27717
|
+
}
|
|
27718
|
+
};
|
|
27622
27719
|
}
|
|
27623
27720
|
});
|
|
27624
27721
|
|
|
@@ -30020,14 +30117,18 @@ async function startDashboardServer(options) {
|
|
|
30020
30117
|
}
|
|
30021
30118
|
}
|
|
30022
30119
|
};
|
|
30023
|
-
|
|
30024
|
-
|
|
30025
|
-
authToken: options.authToken,
|
|
30026
|
-
approvals: options.approvals,
|
|
30027
|
-
onEvent
|
|
30028
|
-
};
|
|
30120
|
+
let v11Bindings = null;
|
|
30121
|
+
let v11LoopbackAutoAuth = false;
|
|
30029
30122
|
const server = createServer$2(async (req, res) => {
|
|
30030
30123
|
try {
|
|
30124
|
+
const deps = {
|
|
30125
|
+
sources: options.sources,
|
|
30126
|
+
authToken: options.authToken,
|
|
30127
|
+
approvals: options.approvals,
|
|
30128
|
+
onEvent,
|
|
30129
|
+
v11Bindings,
|
|
30130
|
+
loopbackAutoAuth: v11LoopbackAutoAuth
|
|
30131
|
+
};
|
|
30031
30132
|
const served = await handleRequest(deps, req, res);
|
|
30032
30133
|
if (!served) {
|
|
30033
30134
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
@@ -30065,7 +30166,13 @@ async function startDashboardServer(options) {
|
|
|
30065
30166
|
publishActivity: (entry) => publish({ type: "activity", data: entry }),
|
|
30066
30167
|
publishApproval: (approval) => publish({ type: "approval", data: approval }),
|
|
30067
30168
|
publishInbox: (item) => publish({ type: "inbox", data: item }),
|
|
30068
|
-
publishAgentStatus: (snapshot) => publish({ type: "agent_status", data: snapshot })
|
|
30169
|
+
publishAgentStatus: (snapshot) => publish({ type: "agent_status", data: snapshot }),
|
|
30170
|
+
setV11Bindings: (bindings) => {
|
|
30171
|
+
v11Bindings = bindings;
|
|
30172
|
+
},
|
|
30173
|
+
setV11LoopbackAutoAuth: (enabled) => {
|
|
30174
|
+
v11LoopbackAutoAuth = enabled;
|
|
30175
|
+
}
|
|
30069
30176
|
};
|
|
30070
30177
|
}
|
|
30071
30178
|
var DEFAULT_PORT, DEFAULT_HOST;
|
|
@@ -31329,12 +31436,14 @@ async function runWrap(options, deps = {}) {
|
|
|
31329
31436
|
const storagePath = resolveStoragePath();
|
|
31330
31437
|
let passphraseLocation;
|
|
31331
31438
|
let passphraseSource;
|
|
31439
|
+
let passphraseValue;
|
|
31332
31440
|
if (options.passphrase) {
|
|
31333
31441
|
try {
|
|
31334
31442
|
const persist = deps.persistPassphrase ?? ((value) => persistUserProvidedPassphrase(value, { storagePath }));
|
|
31335
31443
|
const persisted = await persist(options.passphrase);
|
|
31336
31444
|
passphraseLocation = persisted.location;
|
|
31337
31445
|
passphraseSource = persisted.source;
|
|
31446
|
+
passphraseValue = options.passphrase;
|
|
31338
31447
|
console.error(
|
|
31339
31448
|
`
|
|
31340
31449
|
\u{1F510} Persisted user-supplied passphrase (${persisted.location}).`
|
|
@@ -31352,12 +31461,14 @@ async function runWrap(options, deps = {}) {
|
|
|
31352
31461
|
} else if (process.env.SANCTUARY_PASSPHRASE) {
|
|
31353
31462
|
passphraseLocation = "SANCTUARY_PASSPHRASE";
|
|
31354
31463
|
passphraseSource = "env";
|
|
31464
|
+
passphraseValue = process.env.SANCTUARY_PASSPHRASE;
|
|
31355
31465
|
} else {
|
|
31356
31466
|
try {
|
|
31357
31467
|
const resolve5 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
|
|
31358
31468
|
const resolved = await resolve5();
|
|
31359
31469
|
passphraseLocation = resolved.location;
|
|
31360
31470
|
passphraseSource = resolved.source;
|
|
31471
|
+
passphraseValue = resolved.value;
|
|
31361
31472
|
if (resolved.source === "generated") {
|
|
31362
31473
|
console.error(
|
|
31363
31474
|
`
|
|
@@ -31393,6 +31504,27 @@ async function runWrap(options, deps = {}) {
|
|
|
31393
31504
|
);
|
|
31394
31505
|
}
|
|
31395
31506
|
await mkdir(storagePath, { recursive: true, mode: 448 });
|
|
31507
|
+
if (passphraseSource === "generated" && passphraseValue !== void 0) {
|
|
31508
|
+
try {
|
|
31509
|
+
await disclosePassphrase({
|
|
31510
|
+
passphrase: passphraseValue,
|
|
31511
|
+
storagePath,
|
|
31512
|
+
fortressId: fortressIdFromStoragePath(storagePath),
|
|
31513
|
+
// --no-open (CI / scripted) or non-TTY stdin both skip the prompt
|
|
31514
|
+
// the same way init's --no-confirm does. Operator who scripted the
|
|
31515
|
+
// call still gets the banner + the file; they will not see a hang.
|
|
31516
|
+
mode: options.noOpen || process.stdin.isTTY !== true ? "no-confirm" : "interactive"
|
|
31517
|
+
});
|
|
31518
|
+
} catch (err) {
|
|
31519
|
+
if (err instanceof PassphraseConfirmationDeclinedError || err instanceof PassphraseConfirmationNonInteractiveError) {
|
|
31520
|
+
console.error(`
|
|
31521
|
+
Sanctuary wrap: ${err.message}
|
|
31522
|
+
`);
|
|
31523
|
+
process.exit(2);
|
|
31524
|
+
}
|
|
31525
|
+
throw err;
|
|
31526
|
+
}
|
|
31527
|
+
}
|
|
31396
31528
|
const profile = createWrapProfile(upstreamServers);
|
|
31397
31529
|
const profilePath = join(storagePath, "cocoon-profile.json");
|
|
31398
31530
|
await writeFile(profilePath, JSON.stringify(profile, null, 2), {
|
|
@@ -31415,6 +31547,13 @@ async function runWrap(options, deps = {}) {
|
|
|
31415
31547
|
if (process.env.SANCTUARY_DASHBOARD_ENABLED) {
|
|
31416
31548
|
sanctuaryEnv.SANCTUARY_DASHBOARD_ENABLED = process.env.SANCTUARY_DASHBOARD_ENABLED;
|
|
31417
31549
|
}
|
|
31550
|
+
if (options.fortress) {
|
|
31551
|
+
sanctuaryEnv.SANCTUARY_FORTRESS_PATH = resolve(options.fortress);
|
|
31552
|
+
} else if (process.env.SANCTUARY_FORTRESS_PATH) {
|
|
31553
|
+
sanctuaryEnv.SANCTUARY_FORTRESS_PATH = resolve(
|
|
31554
|
+
process.env.SANCTUARY_FORTRESS_PATH
|
|
31555
|
+
);
|
|
31556
|
+
}
|
|
31418
31557
|
const rewrite = deps.rewriteConfig ?? rewriteConfigForCocoon;
|
|
31419
31558
|
await rewrite(
|
|
31420
31559
|
agentConfig,
|
|
@@ -31442,6 +31581,40 @@ async function runWrap(options, deps = {}) {
|
|
|
31442
31581
|
authToken,
|
|
31443
31582
|
readPackageVersion()
|
|
31444
31583
|
);
|
|
31584
|
+
if (passphraseValue !== void 0) {
|
|
31585
|
+
try {
|
|
31586
|
+
const v11Storage = new FilesystemStorage(`${storagePath}/state`);
|
|
31587
|
+
let existingParams;
|
|
31588
|
+
try {
|
|
31589
|
+
const raw = await v11Storage.read("_meta", "key-params");
|
|
31590
|
+
if (raw) {
|
|
31591
|
+
existingParams = JSON.parse(bytesToString(raw));
|
|
31592
|
+
}
|
|
31593
|
+
} catch {
|
|
31594
|
+
}
|
|
31595
|
+
const derived = await deriveMasterKey(passphraseValue, existingParams);
|
|
31596
|
+
if (!existingParams) {
|
|
31597
|
+
await v11Storage.write(
|
|
31598
|
+
"_meta",
|
|
31599
|
+
"key-params",
|
|
31600
|
+
stringToBytes(JSON.stringify(derived.params))
|
|
31601
|
+
);
|
|
31602
|
+
}
|
|
31603
|
+
const wrapAuditLog = new AuditLog(v11Storage, derived.key);
|
|
31604
|
+
dashboard.setV11Bindings(
|
|
31605
|
+
buildV11Bindings({
|
|
31606
|
+
identityId: `fortress:${storagePath}`,
|
|
31607
|
+
fortressId: fortressIdFromStoragePath(storagePath),
|
|
31608
|
+
auditLog: wrapAuditLog
|
|
31609
|
+
})
|
|
31610
|
+
);
|
|
31611
|
+
dashboard.setV11LoopbackAutoAuth(true);
|
|
31612
|
+
} catch (err) {
|
|
31613
|
+
console.error(
|
|
31614
|
+
` Note: v1.1 dashboard surfaces unavailable on wrap URL (${err.message}). Run \`sanctuary dashboard\` to reach them.`
|
|
31615
|
+
);
|
|
31616
|
+
}
|
|
31617
|
+
}
|
|
31445
31618
|
const dashboardUrl = `${dashboard.url}?token=${authToken}`;
|
|
31446
31619
|
const webhookCallbackPortRaw = process.env.SANCTUARY_WEBHOOK_CALLBACK_PORT;
|
|
31447
31620
|
const webhookCallbackPort = webhookCallbackPortRaw ? parseInt(webhookCallbackPortRaw, 10) : void 0;
|
|
@@ -31804,9 +31977,15 @@ var init_cli2 = __esm({
|
|
|
31804
31977
|
init_config_reader();
|
|
31805
31978
|
init_passphrase();
|
|
31806
31979
|
init_dashboard2();
|
|
31980
|
+
init_wiring();
|
|
31981
|
+
init_filesystem();
|
|
31982
|
+
init_key_derivation();
|
|
31983
|
+
init_encoding();
|
|
31984
|
+
init_audit_log();
|
|
31807
31985
|
init_config();
|
|
31808
31986
|
init_paths();
|
|
31809
31987
|
init_runtime();
|
|
31988
|
+
init_recovery_key_disclosure();
|
|
31810
31989
|
COCOON_GOVERNOR_DEFAULTS = {
|
|
31811
31990
|
volume_limit: 200,
|
|
31812
31991
|
rate_limit_per_tool: 20,
|
|
@@ -33809,7 +33988,7 @@ async function readValue(stdin, prompt2) {
|
|
|
33809
33988
|
}
|
|
33810
33989
|
async function readFirstLine(stdin) {
|
|
33811
33990
|
return new Promise((resolve5, reject) => {
|
|
33812
|
-
const rl = createInterface({ input: stdin });
|
|
33991
|
+
const rl = createInterface$1({ input: stdin });
|
|
33813
33992
|
let resolved = false;
|
|
33814
33993
|
const finish = (value) => {
|
|
33815
33994
|
if (resolved) return;
|
|
@@ -34835,7 +35014,7 @@ var init_reset_passphrase = __esm({
|
|
|
34835
35014
|
waiters = [];
|
|
34836
35015
|
closed = false;
|
|
34837
35016
|
constructor(stdin) {
|
|
34838
|
-
this.rl = createInterface({ input: stdin });
|
|
35017
|
+
this.rl = createInterface$1({ input: stdin });
|
|
34839
35018
|
this.rl.on("line", (line) => {
|
|
34840
35019
|
const w = this.waiters.shift();
|
|
34841
35020
|
if (w) {
|
|
@@ -35198,10 +35377,10 @@ __export(dashboard_standalone_exports, {
|
|
|
35198
35377
|
renderTenantDiscoveryHint: () => renderTenantDiscoveryHint,
|
|
35199
35378
|
startStandaloneDashboard: () => startStandaloneDashboard
|
|
35200
35379
|
});
|
|
35201
|
-
async function discoverableSubTenants(currentStoragePath) {
|
|
35380
|
+
async function discoverableSubTenants(currentStoragePath, discoveryOptions) {
|
|
35202
35381
|
let all;
|
|
35203
35382
|
try {
|
|
35204
|
-
all = await discoverTenants();
|
|
35383
|
+
all = await discoverTenants(discoveryOptions);
|
|
35205
35384
|
} catch {
|
|
35206
35385
|
return [];
|
|
35207
35386
|
}
|
|
@@ -35604,6 +35783,9 @@ var { version: PKG_VERSION4 } = require4("../package.json");
|
|
|
35604
35783
|
async function main() {
|
|
35605
35784
|
const args = process.argv.slice(2);
|
|
35606
35785
|
let passphrase = process.env.SANCTUARY_PASSPHRASE;
|
|
35786
|
+
if (process.env.SANCTUARY_FORTRESS_PATH && !process.env.SANCTUARY_STORAGE_PATH) {
|
|
35787
|
+
process.env.SANCTUARY_STORAGE_PATH = process.env.SANCTUARY_FORTRESS_PATH;
|
|
35788
|
+
}
|
|
35607
35789
|
if (args[0] === "dashboard") {
|
|
35608
35790
|
await runStandaloneDashboard(args.slice(1));
|
|
35609
35791
|
return;
|