@sanctuary-framework/mcp-server 1.2.2 → 1.2.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 +444 -89
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +444 -89
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +161 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +162 -37
- package/dist/index.js.map +1 -1
- package/package.json +17 -16
package/dist/cli.js
CHANGED
|
@@ -4421,13 +4421,23 @@ function parseScalar(value) {
|
|
|
4421
4421
|
return value.replace(/^["']|["']$/g, "");
|
|
4422
4422
|
}
|
|
4423
4423
|
function validatePolicy(raw) {
|
|
4424
|
+
if (!("tier1_always_approve" in raw)) {
|
|
4425
|
+
throw new Error(
|
|
4426
|
+
"Policy file must include 'tier1_always_approve' as an explicit list (use [] for empty). Remove specific entries instead of removing the whole key."
|
|
4427
|
+
);
|
|
4428
|
+
}
|
|
4429
|
+
if (!("approval_channel" in raw)) {
|
|
4430
|
+
throw new Error(
|
|
4431
|
+
"Policy file must include 'approval_channel' as an explicit object (use {} for defaults). Remove specific entries instead of removing the whole key."
|
|
4432
|
+
);
|
|
4433
|
+
}
|
|
4424
4434
|
const userTier3 = raw.tier3_always_allow ?? [];
|
|
4425
4435
|
const mergedTier3 = [
|
|
4426
4436
|
.../* @__PURE__ */ new Set([...userTier3, ...DEFAULT_POLICY.tier3_always_allow])
|
|
4427
4437
|
];
|
|
4428
4438
|
return {
|
|
4429
4439
|
version: raw.version ?? 1,
|
|
4430
|
-
tier1_always_approve: raw.tier1_always_approve
|
|
4440
|
+
tier1_always_approve: raw.tier1_always_approve,
|
|
4431
4441
|
tier2_anomaly: {
|
|
4432
4442
|
...DEFAULT_TIER2,
|
|
4433
4443
|
...raw.tier2_anomaly ?? {}
|
|
@@ -4448,6 +4458,11 @@ function generateDefaultPolicyYaml() {
|
|
|
4448
4458
|
# This file controls what your agent can do without asking.
|
|
4449
4459
|
# Edit this file directly. Your agent cannot modify it.
|
|
4450
4460
|
# Changes take effect on server restart.
|
|
4461
|
+
#
|
|
4462
|
+
# Required keys (must be present; use [] or {} for empty):
|
|
4463
|
+
# tier1_always_approve, approval_channel
|
|
4464
|
+
# Optional keys (omit to use defaults; new defaults merge automatically):
|
|
4465
|
+
# tier2_anomaly, tier3_always_allow
|
|
4451
4466
|
|
|
4452
4467
|
version: 1
|
|
4453
4468
|
|
|
@@ -11567,6 +11582,7 @@ __export(passphrase_exports, {
|
|
|
11567
11582
|
getOrCreatePassphrase: () => getOrCreatePassphrase,
|
|
11568
11583
|
isOsKeyringLocation: () => isOsKeyringLocation,
|
|
11569
11584
|
keychainServiceFor: () => keychainServiceFor,
|
|
11585
|
+
legacyKeychainServiceFor: () => legacyKeychainServiceFor,
|
|
11570
11586
|
persistUserProvidedPassphrase: () => persistUserProvidedPassphrase,
|
|
11571
11587
|
readStoredPassphrase: () => readStoredPassphrase
|
|
11572
11588
|
});
|
|
@@ -11580,16 +11596,29 @@ async function getOrCreatePassphrase(opts = {}) {
|
|
|
11580
11596
|
const plat = opts.platformOverride ?? platform();
|
|
11581
11597
|
const exec2 = opts.exec ?? defaultExec;
|
|
11582
11598
|
const derive = opts.deriveMachineKey ?? deriveMachineKey;
|
|
11599
|
+
const legacyService = legacyKeychainServiceFor(storagePath, home);
|
|
11583
11600
|
if (plat === "darwin") {
|
|
11584
11601
|
const fromKc = await readFromKeychain(exec2, service);
|
|
11585
11602
|
if (fromKc) {
|
|
11586
11603
|
return { value: fromKc, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
|
|
11587
11604
|
}
|
|
11605
|
+
if (legacyService !== service) {
|
|
11606
|
+
const fromLegacy = await readFromKeychain(exec2, legacyService);
|
|
11607
|
+
if (fromLegacy) {
|
|
11608
|
+
return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
|
|
11609
|
+
}
|
|
11610
|
+
}
|
|
11588
11611
|
} else if (plat === "linux") {
|
|
11589
11612
|
const fromSs = await readFromSecretService(exec2, service);
|
|
11590
11613
|
if (fromSs) {
|
|
11591
11614
|
return { value: fromSs, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
|
|
11592
11615
|
}
|
|
11616
|
+
if (legacyService !== service) {
|
|
11617
|
+
const fromLegacy = await readFromSecretService(exec2, legacyService);
|
|
11618
|
+
if (fromLegacy) {
|
|
11619
|
+
return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
|
|
11620
|
+
}
|
|
11621
|
+
}
|
|
11593
11622
|
}
|
|
11594
11623
|
const fallback = fallbackFilePath(home, storagePath);
|
|
11595
11624
|
const fromFile = await readFromFallbackFile(fallback, home, derive);
|
|
@@ -11622,6 +11651,7 @@ async function readStoredPassphrase(opts = {}) {
|
|
|
11622
11651
|
const home = opts.home ?? homedir();
|
|
11623
11652
|
const storagePath = opts.storagePath ?? resolveStoragePath(process.env, home);
|
|
11624
11653
|
const service = keychainServiceFor(storagePath, home);
|
|
11654
|
+
const legacyService = legacyKeychainServiceFor(storagePath, home);
|
|
11625
11655
|
const plat = opts.platformOverride ?? platform();
|
|
11626
11656
|
const exec2 = opts.exec ?? defaultExec;
|
|
11627
11657
|
const derive = opts.deriveMachineKey ?? deriveMachineKey;
|
|
@@ -11630,11 +11660,23 @@ async function readStoredPassphrase(opts = {}) {
|
|
|
11630
11660
|
if (fromKc) {
|
|
11631
11661
|
return { value: fromKc, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
|
|
11632
11662
|
}
|
|
11663
|
+
if (legacyService !== service) {
|
|
11664
|
+
const fromLegacy = await readFromKeychain(exec2, legacyService);
|
|
11665
|
+
if (fromLegacy) {
|
|
11666
|
+
return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
|
|
11667
|
+
}
|
|
11668
|
+
}
|
|
11633
11669
|
} else if (plat === "linux") {
|
|
11634
11670
|
const fromSs = await readFromSecretService(exec2, service);
|
|
11635
11671
|
if (fromSs) {
|
|
11636
11672
|
return { value: fromSs, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
|
|
11637
11673
|
}
|
|
11674
|
+
if (legacyService !== service) {
|
|
11675
|
+
const fromLegacy = await readFromSecretService(exec2, legacyService);
|
|
11676
|
+
if (fromLegacy) {
|
|
11677
|
+
return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
|
|
11678
|
+
}
|
|
11679
|
+
}
|
|
11638
11680
|
}
|
|
11639
11681
|
const fallback = fallbackFilePath(home, storagePath);
|
|
11640
11682
|
const fromFile = await readFromFallbackFile(fallback, home, derive);
|
|
@@ -11715,9 +11757,18 @@ async function writeToKeychain(value, exec2, service = KEYCHAIN_SERVICE_DEFAULT)
|
|
|
11715
11757
|
}
|
|
11716
11758
|
}
|
|
11717
11759
|
function keychainServiceFor(storagePath, home = homedir()) {
|
|
11718
|
-
const defaultPath = join(home, DEFAULT_STORAGE_DIR);
|
|
11719
|
-
|
|
11720
|
-
|
|
11760
|
+
const defaultPath = resolve(join(home, DEFAULT_STORAGE_DIR));
|
|
11761
|
+
const canonicalStorage = resolve(storagePath);
|
|
11762
|
+
if (canonicalStorage === defaultPath) return KEYCHAIN_SERVICE_DEFAULT;
|
|
11763
|
+
const digest = sha256(Buffer.from(canonicalStorage, "utf-8"));
|
|
11764
|
+
const suffix = Buffer.from(digest).toString("hex").slice(0, 16);
|
|
11765
|
+
return `${KEYCHAIN_SERVICE_DEFAULT}-${suffix}`;
|
|
11766
|
+
}
|
|
11767
|
+
function legacyKeychainServiceFor(storagePath, home = homedir()) {
|
|
11768
|
+
const defaultPath = resolve(join(home, DEFAULT_STORAGE_DIR));
|
|
11769
|
+
const canonicalStorage = resolve(storagePath);
|
|
11770
|
+
if (canonicalStorage === defaultPath) return KEYCHAIN_SERVICE_DEFAULT;
|
|
11771
|
+
const digest = sha256(Buffer.from(canonicalStorage, "utf-8"));
|
|
11721
11772
|
const suffix = Buffer.from(digest).toString("hex").slice(0, 12);
|
|
11722
11773
|
return `${KEYCHAIN_SERVICE_DEFAULT}-${suffix}`;
|
|
11723
11774
|
}
|
|
@@ -11804,7 +11855,7 @@ function deriveMachineKey(home) {
|
|
|
11804
11855
|
return hkdf(sha256, material, void 0, "sanctuary-passphrase-v1", 32);
|
|
11805
11856
|
}
|
|
11806
11857
|
async function defaultExec(cmd, args, input) {
|
|
11807
|
-
return new Promise((
|
|
11858
|
+
return new Promise((resolve8, reject) => {
|
|
11808
11859
|
const child = spawn(cmd, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
11809
11860
|
let stdout = "";
|
|
11810
11861
|
let stderr = "";
|
|
@@ -11815,7 +11866,7 @@ async function defaultExec(cmd, args, input) {
|
|
|
11815
11866
|
stderr += d.toString();
|
|
11816
11867
|
});
|
|
11817
11868
|
child.on("error", reject);
|
|
11818
|
-
child.on("close", (code) =>
|
|
11869
|
+
child.on("close", (code) => resolve8({ stdout, stderr, code }));
|
|
11819
11870
|
if (input !== void 0) {
|
|
11820
11871
|
child.stdin.write(input);
|
|
11821
11872
|
}
|
|
@@ -12009,7 +12060,7 @@ async function discoverTenants(options = {}) {
|
|
|
12009
12060
|
for (const child of children) {
|
|
12010
12061
|
const childPath = join(root, child);
|
|
12011
12062
|
if (child.startsWith(".")) continue;
|
|
12012
|
-
if (child === "state" || child === "backup" || child === "config") continue;
|
|
12063
|
+
if (child === "state" || child === "backup" || child === "config" || child === "default") continue;
|
|
12013
12064
|
const s = await stat(childPath).catch(() => null);
|
|
12014
12065
|
if (!s || !s.isDirectory()) continue;
|
|
12015
12066
|
const desc = await describeTenant(child, childPath, home);
|
|
@@ -12021,6 +12072,17 @@ async function discoverTenants(options = {}) {
|
|
|
12021
12072
|
const desc = await describeTenant(basename(extra), extra, home);
|
|
12022
12073
|
if (desc) tenants.push(desc);
|
|
12023
12074
|
}
|
|
12075
|
+
const seen = /* @__PURE__ */ new Map();
|
|
12076
|
+
for (const t of tenants) {
|
|
12077
|
+
seen.set(t.name, (seen.get(t.name) ?? 0) + 1);
|
|
12078
|
+
}
|
|
12079
|
+
for (const [name, count] of seen) {
|
|
12080
|
+
if (count > 1) {
|
|
12081
|
+
console.error(
|
|
12082
|
+
`[sanctuary] warning: ${count} tenants share the name "${name}". Use --tenant with a unique name or storage path to disambiguate.`
|
|
12083
|
+
);
|
|
12084
|
+
}
|
|
12085
|
+
}
|
|
12024
12086
|
tenants.sort((a, b) => {
|
|
12025
12087
|
if (a.name === "default") return -1;
|
|
12026
12088
|
if (b.name === "default") return 1;
|
|
@@ -16819,6 +16881,8 @@ var init_intelligence_api_router = __esm({
|
|
|
16819
16881
|
this.code = code;
|
|
16820
16882
|
this.name = "IntelligenceRouterError";
|
|
16821
16883
|
}
|
|
16884
|
+
statusCode;
|
|
16885
|
+
code;
|
|
16822
16886
|
};
|
|
16823
16887
|
}
|
|
16824
16888
|
});
|
|
@@ -17095,7 +17159,7 @@ var init_dashboard = __esm({
|
|
|
17095
17159
|
server = createServer$2(handler);
|
|
17096
17160
|
}
|
|
17097
17161
|
this.httpServer = server;
|
|
17098
|
-
return new Promise((
|
|
17162
|
+
return new Promise((resolve8, reject) => {
|
|
17099
17163
|
const protocol = this.useTLS ? "https" : "http";
|
|
17100
17164
|
const baseUrl = `${protocol}://${this.config.host}:${this.config.port}`;
|
|
17101
17165
|
server.listen(this.config.port, this.config.host, () => {
|
|
@@ -17120,7 +17184,7 @@ var init_dashboard = __esm({
|
|
|
17120
17184
|
if (shouldAutoOpen) {
|
|
17121
17185
|
this.openInBrowser(sessionUrl);
|
|
17122
17186
|
}
|
|
17123
|
-
|
|
17187
|
+
resolve8();
|
|
17124
17188
|
});
|
|
17125
17189
|
server.on("error", (err) => {
|
|
17126
17190
|
if (err.code === "EADDRINUSE") {
|
|
@@ -17166,8 +17230,8 @@ var init_dashboard = __esm({
|
|
|
17166
17230
|
}
|
|
17167
17231
|
this.rateLimits.clear();
|
|
17168
17232
|
if (this.httpServer) {
|
|
17169
|
-
return new Promise((
|
|
17170
|
-
this.httpServer.close(() =>
|
|
17233
|
+
return new Promise((resolve8) => {
|
|
17234
|
+
this.httpServer.close(() => resolve8());
|
|
17171
17235
|
});
|
|
17172
17236
|
}
|
|
17173
17237
|
}
|
|
@@ -17181,7 +17245,7 @@ var init_dashboard = __esm({
|
|
|
17181
17245
|
`[Sanctuary] Approval required: ${request.operation} (Tier ${request.tier}) \u2014 open dashboard to respond
|
|
17182
17246
|
`
|
|
17183
17247
|
);
|
|
17184
|
-
return new Promise((
|
|
17248
|
+
return new Promise((resolve8) => {
|
|
17185
17249
|
const timer = setTimeout(() => {
|
|
17186
17250
|
this.pending.delete(id);
|
|
17187
17251
|
const response = {
|
|
@@ -17195,12 +17259,12 @@ var init_dashboard = __esm({
|
|
|
17195
17259
|
decision: response.decision,
|
|
17196
17260
|
decided_by: "timeout"
|
|
17197
17261
|
});
|
|
17198
|
-
|
|
17262
|
+
resolve8(response);
|
|
17199
17263
|
}, this.config.timeout_seconds * 1e3);
|
|
17200
17264
|
const pending = {
|
|
17201
17265
|
id,
|
|
17202
17266
|
request,
|
|
17203
|
-
resolve:
|
|
17267
|
+
resolve: resolve8,
|
|
17204
17268
|
timer,
|
|
17205
17269
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
17206
17270
|
};
|
|
@@ -18066,7 +18130,7 @@ var init_webhook = __esm({
|
|
|
18066
18130
|
* Start the callback listener server.
|
|
18067
18131
|
*/
|
|
18068
18132
|
async start() {
|
|
18069
|
-
return new Promise((
|
|
18133
|
+
return new Promise((resolve8, reject) => {
|
|
18070
18134
|
this.callbackServer = createServer$2(
|
|
18071
18135
|
(req, res) => this.handleCallback(req, res)
|
|
18072
18136
|
);
|
|
@@ -18081,7 +18145,7 @@ var init_webhook = __esm({
|
|
|
18081
18145
|
|
|
18082
18146
|
`
|
|
18083
18147
|
);
|
|
18084
|
-
|
|
18148
|
+
resolve8();
|
|
18085
18149
|
}
|
|
18086
18150
|
);
|
|
18087
18151
|
this.callbackServer.on("error", reject);
|
|
@@ -18101,8 +18165,8 @@ var init_webhook = __esm({
|
|
|
18101
18165
|
}
|
|
18102
18166
|
this.pending.clear();
|
|
18103
18167
|
if (this.callbackServer) {
|
|
18104
|
-
return new Promise((
|
|
18105
|
-
this.callbackServer.close(() =>
|
|
18168
|
+
return new Promise((resolve8) => {
|
|
18169
|
+
this.callbackServer.close(() => resolve8());
|
|
18106
18170
|
});
|
|
18107
18171
|
}
|
|
18108
18172
|
}
|
|
@@ -18115,7 +18179,7 @@ var init_webhook = __esm({
|
|
|
18115
18179
|
`[Sanctuary] Webhook approval sent: ${request.operation} (Tier ${request.tier}) \u2014 awaiting callback
|
|
18116
18180
|
`
|
|
18117
18181
|
);
|
|
18118
|
-
return new Promise((
|
|
18182
|
+
return new Promise((resolve8) => {
|
|
18119
18183
|
const timer = setTimeout(() => {
|
|
18120
18184
|
this.pending.delete(id);
|
|
18121
18185
|
const response = {
|
|
@@ -18124,12 +18188,12 @@ var init_webhook = __esm({
|
|
|
18124
18188
|
decided_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18125
18189
|
decided_by: "timeout"
|
|
18126
18190
|
};
|
|
18127
|
-
|
|
18191
|
+
resolve8(response);
|
|
18128
18192
|
}, this.config.timeout_seconds * 1e3);
|
|
18129
18193
|
const pending = {
|
|
18130
18194
|
id,
|
|
18131
18195
|
request,
|
|
18132
|
-
resolve:
|
|
18196
|
+
resolve: resolve8,
|
|
18133
18197
|
timer,
|
|
18134
18198
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
18135
18199
|
};
|
|
@@ -19422,12 +19486,25 @@ var init_injection_detector = __esm({
|
|
|
19422
19486
|
}
|
|
19423
19487
|
});
|
|
19424
19488
|
|
|
19489
|
+
// src/principal-policy/deny-vocabulary.ts
|
|
19490
|
+
var AGENT_VISIBLE_DENY_REASONS;
|
|
19491
|
+
var init_deny_vocabulary = __esm({
|
|
19492
|
+
"src/principal-policy/deny-vocabulary.ts"() {
|
|
19493
|
+
AGENT_VISIBLE_DENY_REASONS = {
|
|
19494
|
+
REQUIRES_APPROVAL: "operation requires operator approval",
|
|
19495
|
+
NOT_PERMITTED: "operation not permitted",
|
|
19496
|
+
REQUIRES_OPERATOR: "operation requires operator action"
|
|
19497
|
+
};
|
|
19498
|
+
}
|
|
19499
|
+
});
|
|
19500
|
+
|
|
19425
19501
|
// src/principal-policy/gate.ts
|
|
19426
19502
|
var ApprovalGate;
|
|
19427
19503
|
var init_gate = __esm({
|
|
19428
19504
|
"src/principal-policy/gate.ts"() {
|
|
19429
19505
|
init_loader();
|
|
19430
19506
|
init_injection_detector();
|
|
19507
|
+
init_deny_vocabulary();
|
|
19431
19508
|
ApprovalGate = class {
|
|
19432
19509
|
policy;
|
|
19433
19510
|
baseline;
|
|
@@ -19479,10 +19556,16 @@ var init_gate = __esm({
|
|
|
19479
19556
|
});
|
|
19480
19557
|
}
|
|
19481
19558
|
if (injectionResult.recommendation === "block") {
|
|
19559
|
+
this.auditLog.append("l2", `gate_injection_block:${operation}`, "system", {
|
|
19560
|
+
tier: 1,
|
|
19561
|
+
operation,
|
|
19562
|
+
injection_confidence: injectionResult.confidence,
|
|
19563
|
+
signal_count: injectionResult.signals.length
|
|
19564
|
+
});
|
|
19482
19565
|
return {
|
|
19483
19566
|
allowed: false,
|
|
19484
19567
|
tier: 1,
|
|
19485
|
-
reason:
|
|
19568
|
+
reason: AGENT_VISIBLE_DENY_REASONS.NOT_PERMITTED,
|
|
19486
19569
|
approval_required: false
|
|
19487
19570
|
};
|
|
19488
19571
|
}
|
|
@@ -19559,7 +19642,7 @@ var init_gate = __esm({
|
|
|
19559
19642
|
this.auditLog.append("l2", `gate_unclassified:${operation}`, "system", {
|
|
19560
19643
|
tier: 1,
|
|
19561
19644
|
operation,
|
|
19562
|
-
warning: "Operation is not classified in any policy tier
|
|
19645
|
+
warning: "Operation is not classified in any policy tier, defaulting to Tier 1 (require approval)"
|
|
19563
19646
|
});
|
|
19564
19647
|
return this.requestApproval(
|
|
19565
19648
|
operation,
|
|
@@ -19688,7 +19771,7 @@ var init_gate = __esm({
|
|
|
19688
19771
|
return {
|
|
19689
19772
|
allowed: response.decision === "approve",
|
|
19690
19773
|
tier,
|
|
19691
|
-
reason: response.decision === "approve" ? `Approved by ${response.decided_by}` :
|
|
19774
|
+
reason: response.decision === "approve" ? `Approved by ${response.decided_by}` : AGENT_VISIBLE_DENY_REASONS.REQUIRES_APPROVAL,
|
|
19692
19775
|
approval_required: true,
|
|
19693
19776
|
approval_response: response
|
|
19694
19777
|
};
|
|
@@ -20277,7 +20360,11 @@ var init_tools5 = __esm({
|
|
|
20277
20360
|
|
|
20278
20361
|
// src/handshake/protocol.ts
|
|
20279
20362
|
function generateNonce() {
|
|
20280
|
-
|
|
20363
|
+
const nonce = randomBytes(32);
|
|
20364
|
+
if (!nonce || nonce.length !== 32) {
|
|
20365
|
+
throw new Error("Nonce generation failed: randomBytes returned unexpected length");
|
|
20366
|
+
}
|
|
20367
|
+
return toBase64url(nonce);
|
|
20281
20368
|
}
|
|
20282
20369
|
function initiateHandshake(ourSHR) {
|
|
20283
20370
|
const nonce = generateNonce();
|
|
@@ -20388,6 +20475,18 @@ function completeHandshake(response, session, identityManager, masterKey, identi
|
|
|
20388
20475
|
return { completion, result };
|
|
20389
20476
|
}
|
|
20390
20477
|
function verifyCompletion(completion, session) {
|
|
20478
|
+
if (completion.protocol_version !== "1.0") {
|
|
20479
|
+
return {
|
|
20480
|
+
counterparty_id: "unknown",
|
|
20481
|
+
counterparty_shr: session.our_shr,
|
|
20482
|
+
verified: false,
|
|
20483
|
+
sovereignty_level: "unverified",
|
|
20484
|
+
trust_tier: "unverified",
|
|
20485
|
+
completed_at: completion.completed_at,
|
|
20486
|
+
expires_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20487
|
+
errors: [`Unsupported protocol version: ${completion.protocol_version}`]
|
|
20488
|
+
};
|
|
20489
|
+
}
|
|
20391
20490
|
const errors = [];
|
|
20392
20491
|
if (!session.their_shr) {
|
|
20393
20492
|
return {
|
|
@@ -22674,6 +22773,9 @@ Inspect the file and either correct the JSON or delete it manually before re-run
|
|
|
22674
22773
|
this.cause = cause;
|
|
22675
22774
|
this.name = "ResetHistoryMalformedError";
|
|
22676
22775
|
}
|
|
22776
|
+
markerPath;
|
|
22777
|
+
lineNumber;
|
|
22778
|
+
cause;
|
|
22677
22779
|
};
|
|
22678
22780
|
}
|
|
22679
22781
|
});
|
|
@@ -24832,7 +24934,7 @@ async function runOpenAIPrivacyFilter(text, config) {
|
|
|
24832
24934
|
return parsed;
|
|
24833
24935
|
}
|
|
24834
24936
|
function runCommand(command, input, timeoutMs) {
|
|
24835
|
-
return new Promise((
|
|
24937
|
+
return new Promise((resolve8, reject) => {
|
|
24836
24938
|
const child = spawn(command, [], {
|
|
24837
24939
|
stdio: ["pipe", "pipe", "pipe"],
|
|
24838
24940
|
shell: false
|
|
@@ -24863,7 +24965,7 @@ function runCommand(command, input, timeoutMs) {
|
|
|
24863
24965
|
));
|
|
24864
24966
|
return;
|
|
24865
24967
|
}
|
|
24866
|
-
|
|
24968
|
+
resolve8(stdout);
|
|
24867
24969
|
});
|
|
24868
24970
|
child.stdin.end(input);
|
|
24869
24971
|
});
|
|
@@ -26739,13 +26841,13 @@ var init_proxy_router = __esm({
|
|
|
26739
26841
|
* Call an upstream tool with a timeout.
|
|
26740
26842
|
*/
|
|
26741
26843
|
async callWithTimeout(serverName, toolName, args, timeoutMs) {
|
|
26742
|
-
return new Promise((
|
|
26844
|
+
return new Promise((resolve8, reject) => {
|
|
26743
26845
|
const timer = setTimeout(() => {
|
|
26744
26846
|
reject(new Error(`Upstream tool call timed out after ${timeoutMs}ms`));
|
|
26745
26847
|
}, timeoutMs);
|
|
26746
26848
|
this.clientManager.callTool(serverName, toolName, args).then((result) => {
|
|
26747
26849
|
clearTimeout(timer);
|
|
26748
|
-
|
|
26850
|
+
resolve8(result);
|
|
26749
26851
|
}).catch((err) => {
|
|
26750
26852
|
clearTimeout(timer);
|
|
26751
26853
|
reject(err);
|
|
@@ -32081,7 +32183,7 @@ function makeEventId(prefix) {
|
|
|
32081
32183
|
function hashOf(input) {
|
|
32082
32184
|
return hashToString(sha256(stringToBytes(input)));
|
|
32083
32185
|
}
|
|
32084
|
-
var DEFAULT_CONCIERGE_MAX_TOKENS, OperatorChatService;
|
|
32186
|
+
var DEFAULT_CONCIERGE_MAX_TOKENS, SANCTUARY_DOMAIN_REFERENCE, OperatorChatService;
|
|
32085
32187
|
var init_operator_chat_service = __esm({
|
|
32086
32188
|
"src/chat/operator-chat-service.ts"() {
|
|
32087
32189
|
init_hashing();
|
|
@@ -32089,6 +32191,33 @@ var init_operator_chat_service = __esm({
|
|
|
32089
32191
|
init_operator_chat_audit_events();
|
|
32090
32192
|
init_operator_chat_types();
|
|
32091
32193
|
DEFAULT_CONCIERGE_MAX_TOKENS = 512;
|
|
32194
|
+
SANCTUARY_DOMAIN_REFERENCE = `Castle Architecture (four enforcement layers):
|
|
32195
|
+
1. Castle Wall: OS-boundary egress filter enforced at the kernel level. Blocks unauthorized outbound calls even from prompt-injected agents.
|
|
32196
|
+
2. Sentinels: internal observation via process introspection. Surfaces anomalies to the operator; does not enforce.
|
|
32197
|
+
3. Charter (Cooperative MCP): the sovereignty surface for compliant agents. Policy gates, approval tiers, audit logging, and encrypted state all live here.
|
|
32198
|
+
4. Heralds: Concordia receipts and Verascore reputation. Cross-fortress accountability after an action completes.
|
|
32199
|
+
|
|
32200
|
+
Five channel templates (canonical names):
|
|
32201
|
+
- request-approve-act: agent proposes an action, operator approves or denies before execution.
|
|
32202
|
+
- read-then-report: agent reads outputs from a data source and reports summaries to the operator.
|
|
32203
|
+
- scheduled-digest: agent runs on a schedule and delivers a periodic digest.
|
|
32204
|
+
- plan-draft-only: agent drafts plans; operator reviews before any execution step.
|
|
32205
|
+
- fortress-relay: agent relays messages between fortresses under operator-scoped policy.
|
|
32206
|
+
|
|
32207
|
+
Four canonical policy slots:
|
|
32208
|
+
- memory: governs what the agent may persist and retrieve from encrypted state.
|
|
32209
|
+
- credentials: governs access to secrets, API keys, and tokens held in the broker.
|
|
32210
|
+
- plans: governs the agent's ability to create, modify, or execute plans.
|
|
32211
|
+
- outputs: governs what the agent may emit to external surfaces (files, APIs, messages).
|
|
32212
|
+
|
|
32213
|
+
Key concepts:
|
|
32214
|
+
- Fortress: the operator-owned sovereignty harness. All state is encrypted at rest under the cocoon.
|
|
32215
|
+
- Cocoon: master-key-wrapped storage derived from the operator's passphrase via Argon2id.
|
|
32216
|
+
- Identity: Ed25519 keypair with a DID, owned by the operator. Private keys never leave the cocoon.
|
|
32217
|
+
- Audit log: append-only encrypted blobs, sequential, recording every gate decision and tool call.
|
|
32218
|
+
- Wrapped agent: any agent runtime that connects to Sanctuary as an MCP client. Tier A (native), Tier B (adapter-wrapped), Tier C (escape hatch).
|
|
32219
|
+
|
|
32220
|
+
Note: this is a static reference block (v1.2.x). Dynamic context injection (live template list, policy schema) ships in v1.3.`;
|
|
32092
32221
|
OperatorChatService = class {
|
|
32093
32222
|
store;
|
|
32094
32223
|
auditLog;
|
|
@@ -32233,6 +32362,9 @@ var init_operator_chat_service = __esm({
|
|
|
32233
32362
|
* than nested structures. Format:
|
|
32234
32363
|
*
|
|
32235
32364
|
* ```
|
|
32365
|
+
* ## Sanctuary reference
|
|
32366
|
+
* <static domain reference block>
|
|
32367
|
+
*
|
|
32236
32368
|
* ## Recent activity
|
|
32237
32369
|
* <recentActivity output>
|
|
32238
32370
|
*
|
|
@@ -32244,15 +32376,28 @@ var init_operator_chat_service = __esm({
|
|
|
32244
32376
|
* ```
|
|
32245
32377
|
*/
|
|
32246
32378
|
async assembleConciergeContext() {
|
|
32379
|
+
const ref = `## Sanctuary reference
|
|
32380
|
+
${SANCTUARY_DOMAIN_REFERENCE}`;
|
|
32247
32381
|
if (!this.contextProviders) {
|
|
32248
|
-
return
|
|
32382
|
+
return `${ref}
|
|
32383
|
+
|
|
32384
|
+
## Recent activity
|
|
32385
|
+
(no providers wired)
|
|
32386
|
+
|
|
32387
|
+
## Wrapped agents
|
|
32388
|
+
(no providers wired)
|
|
32389
|
+
|
|
32390
|
+
## Open inbox
|
|
32391
|
+
(no providers wired)`;
|
|
32249
32392
|
}
|
|
32250
32393
|
const [activity, agents, inbox] = await Promise.all([
|
|
32251
32394
|
this.contextProviders.recentActivity(),
|
|
32252
32395
|
this.contextProviders.agentInventory(),
|
|
32253
32396
|
this.contextProviders.openInbox()
|
|
32254
32397
|
]);
|
|
32255
|
-
return
|
|
32398
|
+
return `${ref}
|
|
32399
|
+
|
|
32400
|
+
## Recent activity
|
|
32256
32401
|
${activity}
|
|
32257
32402
|
|
|
32258
32403
|
## Wrapped agents
|
|
@@ -35284,6 +35429,9 @@ async function importExitBundle(opts) {
|
|
|
35284
35429
|
reputationArtifact?.json ?? null,
|
|
35285
35430
|
manifest
|
|
35286
35431
|
);
|
|
35432
|
+
if (!conflicts.public_identity_exists && identityArtifact?.json && opts.identityManager.getPrimaryIdentityId() !== null && opts.identityManager.getPrimaryIdentityId() !== identityArtifact.json.bundle.identity_id) {
|
|
35433
|
+
conflicts.public_identity_exists = true;
|
|
35434
|
+
}
|
|
35287
35435
|
if (!opts.activate) {
|
|
35288
35436
|
return {
|
|
35289
35437
|
verified: true,
|
|
@@ -35310,7 +35458,7 @@ async function importExitBundle(opts) {
|
|
|
35310
35458
|
if (conflicts.public_identity_exists && !opts.forceRebind) {
|
|
35311
35459
|
throw new ExitBundleImportError(
|
|
35312
35460
|
"IDENTITY_OVERWRITE_REFUSED",
|
|
35313
|
-
"Importing this bundle would overwrite an existing fortress public identity. Pass forceRebind: true (CLI: --force-rebind) to confirm explicit replacement."
|
|
35461
|
+
"Importing this exit bundle would overwrite an existing fortress public identity (either the same identity already imported, or a different identity is currently active). Pass forceRebind: true (CLI: --force-rebind) to confirm explicit replacement."
|
|
35314
35462
|
);
|
|
35315
35463
|
}
|
|
35316
35464
|
if (conflicts.public_identity_exists && opts.forceRebind && identityArtifact) {
|
|
@@ -35700,6 +35848,26 @@ async function runExitCommand(args) {
|
|
|
35700
35848
|
write(err, "Usage: sanctuary exit import <dir> [--activate]\n");
|
|
35701
35849
|
return 2;
|
|
35702
35850
|
}
|
|
35851
|
+
const bundleRoot = resolve(dir);
|
|
35852
|
+
try {
|
|
35853
|
+
await access(bundleRoot);
|
|
35854
|
+
} catch {
|
|
35855
|
+
write(err, `Error: bundle directory not found: ${bundleRoot}
|
|
35856
|
+
`);
|
|
35857
|
+
return 1;
|
|
35858
|
+
}
|
|
35859
|
+
const manifestPath = join(bundleRoot, "manifest.json");
|
|
35860
|
+
try {
|
|
35861
|
+
const raw = await readFile(manifestPath, "utf8");
|
|
35862
|
+
JSON.parse(raw);
|
|
35863
|
+
} catch {
|
|
35864
|
+
write(
|
|
35865
|
+
err,
|
|
35866
|
+
`Error: bundle manifest missing or malformed at ${manifestPath}
|
|
35867
|
+
`
|
|
35868
|
+
);
|
|
35869
|
+
return 1;
|
|
35870
|
+
}
|
|
35703
35871
|
const activate = hasFlag(argv, "--activate");
|
|
35704
35872
|
const forceRebind = hasFlag(argv, "--force-rebind");
|
|
35705
35873
|
const acceptUnverifiableAttestations = hasFlag(
|
|
@@ -35873,11 +36041,11 @@ async function startDashboardServer(options) {
|
|
|
35873
36041
|
}
|
|
35874
36042
|
}
|
|
35875
36043
|
});
|
|
35876
|
-
await new Promise((
|
|
36044
|
+
await new Promise((resolve8, reject) => {
|
|
35877
36045
|
server.once("error", reject);
|
|
35878
36046
|
server.listen(port, host, () => {
|
|
35879
36047
|
server.off("error", reject);
|
|
35880
|
-
|
|
36048
|
+
resolve8();
|
|
35881
36049
|
});
|
|
35882
36050
|
});
|
|
35883
36051
|
const actualPort = (() => {
|
|
@@ -35890,8 +36058,8 @@ async function startDashboardServer(options) {
|
|
|
35890
36058
|
url,
|
|
35891
36059
|
port: actualPort,
|
|
35892
36060
|
host,
|
|
35893
|
-
stop: () => new Promise((
|
|
35894
|
-
server.close((err) => err ? reject(err) :
|
|
36061
|
+
stop: () => new Promise((resolve8, reject) => {
|
|
36062
|
+
server.close((err) => err ? reject(err) : resolve8());
|
|
35895
36063
|
}),
|
|
35896
36064
|
publish,
|
|
35897
36065
|
publishActivity: (entry) => publish({ type: "activity", data: entry }),
|
|
@@ -36564,7 +36732,7 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
|
|
|
36564
36732
|
clientManager.configure(enabledServers).catch((err) => {
|
|
36565
36733
|
console.error(`[Sanctuary] Failed to configure upstream servers: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
36566
36734
|
});
|
|
36567
|
-
await new Promise((
|
|
36735
|
+
await new Promise((resolve8) => setTimeout(resolve8, 2e3));
|
|
36568
36736
|
const proxiedTools = proxyRouter.getProxiedTools();
|
|
36569
36737
|
if (proxiedTools.length > 0) {
|
|
36570
36738
|
allTools.push(...proxiedTools);
|
|
@@ -37403,8 +37571,8 @@ async function runWrap(options, deps = {}) {
|
|
|
37403
37571
|
passphraseValue = process.env.SANCTUARY_PASSPHRASE;
|
|
37404
37572
|
} else {
|
|
37405
37573
|
try {
|
|
37406
|
-
const
|
|
37407
|
-
const resolved = await
|
|
37574
|
+
const resolve8 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
|
|
37575
|
+
const resolved = await resolve8();
|
|
37408
37576
|
passphraseLocation = resolved.location;
|
|
37409
37577
|
passphraseSource = resolved.source;
|
|
37410
37578
|
passphraseValue = resolved.value;
|
|
@@ -37759,12 +37927,12 @@ async function defaultOpenBrowser(url) {
|
|
|
37759
37927
|
cmd = "xdg-open";
|
|
37760
37928
|
args = [url];
|
|
37761
37929
|
}
|
|
37762
|
-
await new Promise((
|
|
37930
|
+
await new Promise((resolve8) => {
|
|
37763
37931
|
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
37764
|
-
child.on("error", () =>
|
|
37932
|
+
child.on("error", () => resolve8());
|
|
37765
37933
|
child.on("spawn", () => {
|
|
37766
37934
|
child.unref();
|
|
37767
|
-
|
|
37935
|
+
resolve8();
|
|
37768
37936
|
});
|
|
37769
37937
|
});
|
|
37770
37938
|
}
|
|
@@ -38733,32 +38901,32 @@ endstream`;
|
|
|
38733
38901
|
const offsets = new Array(totalObjects + 1).fill(0);
|
|
38734
38902
|
const chunks = [];
|
|
38735
38903
|
let bytePos = 0;
|
|
38736
|
-
const
|
|
38904
|
+
const write3 = (s) => {
|
|
38737
38905
|
const buf = Buffer.from(s, "latin1");
|
|
38738
38906
|
chunks.push(buf);
|
|
38739
38907
|
bytePos += buf.length;
|
|
38740
38908
|
};
|
|
38741
|
-
|
|
38909
|
+
write3("%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
|
|
38742
38910
|
for (let i = 1; i <= totalObjects; i++) {
|
|
38743
38911
|
offsets[i] = bytePos;
|
|
38744
|
-
|
|
38912
|
+
write3(`${i} 0 obj
|
|
38745
38913
|
${objectBodies[i]}
|
|
38746
38914
|
endobj
|
|
38747
38915
|
`);
|
|
38748
38916
|
}
|
|
38749
38917
|
const xrefPos = bytePos;
|
|
38750
|
-
|
|
38918
|
+
write3(`xref
|
|
38751
38919
|
0 ${totalObjects + 1}
|
|
38752
38920
|
`);
|
|
38753
|
-
|
|
38921
|
+
write3("0000000000 65535 f \n");
|
|
38754
38922
|
for (let i = 1; i <= totalObjects; i++) {
|
|
38755
|
-
|
|
38923
|
+
write3(`${offsets[i].toString().padStart(10, "0")} 00000 n
|
|
38756
38924
|
`);
|
|
38757
38925
|
}
|
|
38758
|
-
|
|
38926
|
+
write3(`trailer
|
|
38759
38927
|
<< /Size ${totalObjects + 1} /Root 1 0 R >>
|
|
38760
38928
|
`);
|
|
38761
|
-
|
|
38929
|
+
write3(`startxref
|
|
38762
38930
|
${xrefPos}
|
|
38763
38931
|
%%EOF
|
|
38764
38932
|
`);
|
|
@@ -39093,7 +39261,7 @@ var init_backend_interface = __esm({
|
|
|
39093
39261
|
}
|
|
39094
39262
|
});
|
|
39095
39263
|
async function runSecurity(args, input) {
|
|
39096
|
-
return new Promise((
|
|
39264
|
+
return new Promise((resolve8, reject) => {
|
|
39097
39265
|
const child = spawn(SECURITY_BIN, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
39098
39266
|
let stdout = "";
|
|
39099
39267
|
let stderr = "";
|
|
@@ -39115,7 +39283,7 @@ async function runSecurity(args, input) {
|
|
|
39115
39283
|
reject(err);
|
|
39116
39284
|
});
|
|
39117
39285
|
child.on("close", (code) => {
|
|
39118
|
-
|
|
39286
|
+
resolve8({ stdout, stderr, code: code ?? -1 });
|
|
39119
39287
|
});
|
|
39120
39288
|
if (input !== void 0) {
|
|
39121
39289
|
child.stdin.write(input);
|
|
@@ -40191,7 +40359,7 @@ async function readValue(stdin, prompt2) {
|
|
|
40191
40359
|
return await readFirstLine(stdin);
|
|
40192
40360
|
}
|
|
40193
40361
|
async function readFirstLine(stdin) {
|
|
40194
|
-
return new Promise((
|
|
40362
|
+
return new Promise((resolve8, reject) => {
|
|
40195
40363
|
const rl = createInterface$1({ input: stdin });
|
|
40196
40364
|
let resolved = false;
|
|
40197
40365
|
const finish = (value) => {
|
|
@@ -40202,7 +40370,7 @@ async function readFirstLine(stdin) {
|
|
|
40202
40370
|
rl.close();
|
|
40203
40371
|
} catch {
|
|
40204
40372
|
}
|
|
40205
|
-
|
|
40373
|
+
resolve8(value);
|
|
40206
40374
|
};
|
|
40207
40375
|
const deadline = setTimeout(() => {
|
|
40208
40376
|
finish("");
|
|
@@ -40221,7 +40389,7 @@ async function promptSilently(stdin, prompt2) {
|
|
|
40221
40389
|
process.stderr.write(`${prompt2}: `);
|
|
40222
40390
|
stdin.setRawMode?.(true);
|
|
40223
40391
|
stdin.resume();
|
|
40224
|
-
return await new Promise((
|
|
40392
|
+
return await new Promise((resolve8) => {
|
|
40225
40393
|
let buf = "";
|
|
40226
40394
|
const onData = (chunk) => {
|
|
40227
40395
|
const s = chunk.toString("utf8");
|
|
@@ -40231,7 +40399,7 @@ async function promptSilently(stdin, prompt2) {
|
|
|
40231
40399
|
stdin.pause();
|
|
40232
40400
|
stdin.off("data", onData);
|
|
40233
40401
|
process.stderr.write("\n");
|
|
40234
|
-
|
|
40402
|
+
resolve8(buf);
|
|
40235
40403
|
return;
|
|
40236
40404
|
}
|
|
40237
40405
|
if (ch === "") {
|
|
@@ -40498,13 +40666,179 @@ var init_cli4 = __esm({
|
|
|
40498
40666
|
init_discovery();
|
|
40499
40667
|
}
|
|
40500
40668
|
});
|
|
40669
|
+
|
|
40670
|
+
// src/cli/identity.ts
|
|
40671
|
+
var identity_exports2 = {};
|
|
40672
|
+
__export(identity_exports2, {
|
|
40673
|
+
runIdentityCommand: () => runIdentityCommand
|
|
40674
|
+
});
|
|
40675
|
+
function write2(stream, text) {
|
|
40676
|
+
stream.write(text);
|
|
40677
|
+
}
|
|
40678
|
+
function flagValue2(argv, name) {
|
|
40679
|
+
const index = argv.indexOf(name);
|
|
40680
|
+
if (index === -1) return void 0;
|
|
40681
|
+
return argv[index + 1];
|
|
40682
|
+
}
|
|
40683
|
+
function hasFlag2(argv, name) {
|
|
40684
|
+
return argv.includes(name);
|
|
40685
|
+
}
|
|
40686
|
+
function printUsage3(out) {
|
|
40687
|
+
write2(
|
|
40688
|
+
out,
|
|
40689
|
+
`Usage: sanctuary identity <command> [options]
|
|
40690
|
+
|
|
40691
|
+
Commands:
|
|
40692
|
+
show Print the active identity (DID, identity_id, public key).
|
|
40693
|
+
|
|
40694
|
+
Options:
|
|
40695
|
+
--fortress <path> Override the storage path.
|
|
40696
|
+
--passphrase <val> Passphrase for master-key derivation.
|
|
40697
|
+
--json Output as JSON.
|
|
40698
|
+
--help, -h Show this help.
|
|
40699
|
+
|
|
40700
|
+
Environment variables:
|
|
40701
|
+
SANCTUARY_PASSPHRASE Key derivation passphrase.
|
|
40702
|
+
SANCTUARY_STORAGE_PATH State directory (default: ~/.sanctuary).
|
|
40703
|
+
SANCTUARY_FORTRESS_PATH Operator-friendly alias for STORAGE_PATH.
|
|
40704
|
+
SANCTUARY_RECOVERY_KEY Recovery key (alternative to passphrase).
|
|
40705
|
+
|
|
40706
|
+
Identity data is encrypted at rest. A passphrase or recovery key is
|
|
40707
|
+
required to decrypt and display identity information.
|
|
40708
|
+
`
|
|
40709
|
+
);
|
|
40710
|
+
}
|
|
40711
|
+
async function runIdentityCommand(args) {
|
|
40712
|
+
const argv = args.argv;
|
|
40713
|
+
const out = args.out ?? process.stdout;
|
|
40714
|
+
const err = args.err ?? process.stderr;
|
|
40715
|
+
const env = args.env ?? process.env;
|
|
40716
|
+
if (argv.length === 0 || hasFlag2(argv, "--help") || hasFlag2(argv, "-h")) {
|
|
40717
|
+
printUsage3(out);
|
|
40718
|
+
return 0;
|
|
40719
|
+
}
|
|
40720
|
+
const command = argv[0];
|
|
40721
|
+
if (command === "show") {
|
|
40722
|
+
return await cmdShow(argv.slice(1), out, err, env);
|
|
40723
|
+
}
|
|
40724
|
+
write2(err, `Unknown identity command: ${command}
|
|
40725
|
+
`);
|
|
40726
|
+
write2(err, `Run "sanctuary identity --help" for usage.
|
|
40727
|
+
`);
|
|
40728
|
+
return 2;
|
|
40729
|
+
}
|
|
40730
|
+
async function cmdShow(argv, out, err, env) {
|
|
40731
|
+
const json = hasFlag2(argv, "--json");
|
|
40732
|
+
const fortressFlag = flagValue2(argv, "--fortress");
|
|
40733
|
+
if (fortressFlag) {
|
|
40734
|
+
process.env.SANCTUARY_STORAGE_PATH = fortressFlag;
|
|
40735
|
+
}
|
|
40736
|
+
const passphrase = flagValue2(argv, "--passphrase") ?? env.SANCTUARY_PASSPHRASE;
|
|
40737
|
+
const recoveryKey = env.SANCTUARY_RECOVERY_KEY;
|
|
40738
|
+
if (!passphrase && !recoveryKey) {
|
|
40739
|
+
write2(
|
|
40740
|
+
err,
|
|
40741
|
+
"Error: sanctuary identity show requires SANCTUARY_PASSPHRASE, --passphrase, or SANCTUARY_RECOVERY_KEY.\n"
|
|
40742
|
+
);
|
|
40743
|
+
return 1;
|
|
40744
|
+
}
|
|
40745
|
+
try {
|
|
40746
|
+
const config = await loadConfig();
|
|
40747
|
+
await mkdir(config.storage_path, { recursive: true, mode: 448 });
|
|
40748
|
+
const stateStoragePath = join(config.storage_path, "state");
|
|
40749
|
+
const storage = new FilesystemStorage(stateStoragePath);
|
|
40750
|
+
let masterKey;
|
|
40751
|
+
if (passphrase) {
|
|
40752
|
+
let existingParams;
|
|
40753
|
+
const raw = await storage.read("_meta", "key-params");
|
|
40754
|
+
if (raw)
|
|
40755
|
+
existingParams = JSON.parse(bytesToString(raw));
|
|
40756
|
+
const derived = await deriveMasterKey(passphrase, existingParams);
|
|
40757
|
+
masterKey = derived.key;
|
|
40758
|
+
} else {
|
|
40759
|
+
masterKey = fromBase64url(recoveryKey);
|
|
40760
|
+
if (masterKey.length !== 32) {
|
|
40761
|
+
write2(err, "Error: SANCTUARY_RECOVERY_KEY must decode to 32 bytes.\n");
|
|
40762
|
+
return 1;
|
|
40763
|
+
}
|
|
40764
|
+
}
|
|
40765
|
+
const identityManager = new IdentityManager(storage, masterKey);
|
|
40766
|
+
const loadResult = await identityManager.load();
|
|
40767
|
+
if (loadResult.loaded === 0) {
|
|
40768
|
+
write2(
|
|
40769
|
+
err,
|
|
40770
|
+
loadResult.total > 0 ? "Error: identity files found but none could be decrypted. Wrong passphrase?\n" : "No identities found in this fortress.\n"
|
|
40771
|
+
);
|
|
40772
|
+
return 1;
|
|
40773
|
+
}
|
|
40774
|
+
const primary = identityManager.getDefault();
|
|
40775
|
+
if (!primary) {
|
|
40776
|
+
write2(err, "No primary identity set.\n");
|
|
40777
|
+
return 1;
|
|
40778
|
+
}
|
|
40779
|
+
if (json) {
|
|
40780
|
+
write2(
|
|
40781
|
+
out,
|
|
40782
|
+
JSON.stringify(
|
|
40783
|
+
{
|
|
40784
|
+
identity_id: primary.identity_id,
|
|
40785
|
+
did: primary.did,
|
|
40786
|
+
public_key: primary.public_key,
|
|
40787
|
+
label: primary.label,
|
|
40788
|
+
key_type: primary.key_type,
|
|
40789
|
+
created_at: primary.created_at,
|
|
40790
|
+
storage_path: config.storage_path,
|
|
40791
|
+
total_identities: loadResult.loaded
|
|
40792
|
+
},
|
|
40793
|
+
null,
|
|
40794
|
+
2
|
|
40795
|
+
) + "\n"
|
|
40796
|
+
);
|
|
40797
|
+
} else {
|
|
40798
|
+
write2(out, `identity_id: ${primary.identity_id}
|
|
40799
|
+
`);
|
|
40800
|
+
write2(out, `did: ${primary.did}
|
|
40801
|
+
`);
|
|
40802
|
+
write2(out, `public_key: ${primary.public_key}
|
|
40803
|
+
`);
|
|
40804
|
+
write2(out, `label: ${primary.label}
|
|
40805
|
+
`);
|
|
40806
|
+
write2(out, `key_type: ${primary.key_type}
|
|
40807
|
+
`);
|
|
40808
|
+
write2(out, `created_at: ${primary.created_at}
|
|
40809
|
+
`);
|
|
40810
|
+
write2(out, `storage_path: ${config.storage_path}
|
|
40811
|
+
`);
|
|
40812
|
+
write2(out, `total_identities: ${loadResult.loaded}
|
|
40813
|
+
`);
|
|
40814
|
+
}
|
|
40815
|
+
return 0;
|
|
40816
|
+
} catch (error) {
|
|
40817
|
+
write2(
|
|
40818
|
+
err,
|
|
40819
|
+
error instanceof Error ? `Error: ${error.message}
|
|
40820
|
+
` : `Error: ${String(error)}
|
|
40821
|
+
`
|
|
40822
|
+
);
|
|
40823
|
+
return 1;
|
|
40824
|
+
}
|
|
40825
|
+
}
|
|
40826
|
+
var init_identity2 = __esm({
|
|
40827
|
+
"src/cli/identity.ts"() {
|
|
40828
|
+
init_filesystem();
|
|
40829
|
+
init_tools();
|
|
40830
|
+
init_key_derivation();
|
|
40831
|
+
init_encoding();
|
|
40832
|
+
init_config();
|
|
40833
|
+
}
|
|
40834
|
+
});
|
|
40501
40835
|
async function probeTenantDashboard(tenant, options = {}) {
|
|
40502
40836
|
const rt = tenant.runtime;
|
|
40503
40837
|
if (!rt) {
|
|
40504
40838
|
return { running: false, status: null, reason: "no runtime.json" };
|
|
40505
40839
|
}
|
|
40506
40840
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS4;
|
|
40507
|
-
return await new Promise((
|
|
40841
|
+
return await new Promise((resolve8) => {
|
|
40508
40842
|
const req = get$1(
|
|
40509
40843
|
{
|
|
40510
40844
|
host: rt.dashboard_host,
|
|
@@ -40516,9 +40850,9 @@ async function probeTenantDashboard(tenant, options = {}) {
|
|
|
40516
40850
|
res.resume();
|
|
40517
40851
|
const status = res.statusCode ?? 0;
|
|
40518
40852
|
if (status > 0 && status < 500) {
|
|
40519
|
-
|
|
40853
|
+
resolve8({ running: true, status, reason: null });
|
|
40520
40854
|
} else {
|
|
40521
|
-
|
|
40855
|
+
resolve8({
|
|
40522
40856
|
running: false,
|
|
40523
40857
|
status,
|
|
40524
40858
|
reason: `dashboard returned ${status}`
|
|
@@ -40528,10 +40862,10 @@ async function probeTenantDashboard(tenant, options = {}) {
|
|
|
40528
40862
|
);
|
|
40529
40863
|
req.on("timeout", () => {
|
|
40530
40864
|
req.destroy();
|
|
40531
|
-
|
|
40865
|
+
resolve8({ running: false, status: null, reason: "timeout" });
|
|
40532
40866
|
});
|
|
40533
40867
|
req.on("error", (err) => {
|
|
40534
|
-
|
|
40868
|
+
resolve8({
|
|
40535
40869
|
running: false,
|
|
40536
40870
|
status: null,
|
|
40537
40871
|
reason: err.code ?? err.message
|
|
@@ -40563,10 +40897,17 @@ function resolveCtx(args) {
|
|
|
40563
40897
|
};
|
|
40564
40898
|
}
|
|
40565
40899
|
async function runAgentsCommand(args) {
|
|
40900
|
+
const fortressIdx = args.argv.indexOf("--fortress");
|
|
40901
|
+
if (fortressIdx !== -1 && args.argv[fortressIdx + 1]) {
|
|
40902
|
+
args = { ...args, root: args.argv[fortressIdx + 1] };
|
|
40903
|
+
const filtered = [...args.argv];
|
|
40904
|
+
filtered.splice(fortressIdx, 2);
|
|
40905
|
+
args = { ...args, argv: filtered };
|
|
40906
|
+
}
|
|
40566
40907
|
const ctx = resolveCtx(args);
|
|
40567
40908
|
const [sub, ...rest] = args.argv;
|
|
40568
40909
|
if (!sub || sub === "--help" || sub === "-h" || sub === "help") {
|
|
40569
|
-
|
|
40910
|
+
printUsage4(ctx.out);
|
|
40570
40911
|
return 0;
|
|
40571
40912
|
}
|
|
40572
40913
|
try {
|
|
@@ -40574,13 +40915,13 @@ async function runAgentsCommand(args) {
|
|
|
40574
40915
|
case "list":
|
|
40575
40916
|
return await cmdList3(rest, ctx);
|
|
40576
40917
|
case "show":
|
|
40577
|
-
return await
|
|
40918
|
+
return await cmdShow2(rest, ctx);
|
|
40578
40919
|
case "status":
|
|
40579
40920
|
return await cmdStatus(rest, ctx);
|
|
40580
40921
|
default:
|
|
40581
40922
|
ctx.err.write(`Unknown subcommand: ${sub}
|
|
40582
40923
|
`);
|
|
40583
|
-
|
|
40924
|
+
printUsage4(ctx.err);
|
|
40584
40925
|
return 2;
|
|
40585
40926
|
}
|
|
40586
40927
|
} catch (e) {
|
|
@@ -40590,13 +40931,17 @@ async function runAgentsCommand(args) {
|
|
|
40590
40931
|
return 1;
|
|
40591
40932
|
}
|
|
40592
40933
|
}
|
|
40593
|
-
function
|
|
40934
|
+
function printUsage4(s) {
|
|
40594
40935
|
s.write(`Usage: sanctuary agents <command> [flags]
|
|
40595
40936
|
|
|
40596
40937
|
list [--json] List every tenant visible on this host.
|
|
40597
40938
|
show <tenant> [--json] Show details for one tenant.
|
|
40598
40939
|
status [--json] One-line-per-tenant running/stopped summary.
|
|
40599
40940
|
|
|
40941
|
+
Options:
|
|
40942
|
+
--fortress <path> Scope discovery to a specific storage path
|
|
40943
|
+
instead of scanning ~/.sanctuary.
|
|
40944
|
+
|
|
40600
40945
|
Tenants are discovered by scanning ~/.sanctuary and any storage paths in
|
|
40601
40946
|
SANCTUARY_AGENTS_EXTRA_PATHS or ~/.sanctuary/agents-extra.json. Tenant
|
|
40602
40947
|
creation is done via \`sanctuary wrap\` with SANCTUARY_STORAGE_PATH set.
|
|
@@ -40680,7 +41025,7 @@ async function cmdList3(argv, ctx) {
|
|
|
40680
41025
|
}
|
|
40681
41026
|
return 0;
|
|
40682
41027
|
}
|
|
40683
|
-
async function
|
|
41028
|
+
async function cmdShow2(argv, ctx) {
|
|
40684
41029
|
const positional = argv.find((a) => !a.startsWith("--"));
|
|
40685
41030
|
if (!positional) {
|
|
40686
41031
|
ctx.err.write("Missing tenant. Usage: sanctuary agents show <tenant>\n");
|
|
@@ -40831,10 +41176,10 @@ async function runResetPassphraseCommand(args) {
|
|
|
40831
41176
|
const plat = args.platformOverride ?? process.platform;
|
|
40832
41177
|
const parsed = parseArgs2(args.argv);
|
|
40833
41178
|
if (parsed.help) {
|
|
40834
|
-
|
|
41179
|
+
printUsage5(out);
|
|
40835
41180
|
return 0;
|
|
40836
41181
|
}
|
|
40837
|
-
const storagePath = parsed.storage ?? args.storagePath ?? resolveStoragePath(process.env, home);
|
|
41182
|
+
const storagePath = parsed.storage ?? parsed.fortress ?? args.storagePath ?? resolveStoragePath(process.env, home);
|
|
40838
41183
|
out.write(banner(storagePath));
|
|
40839
41184
|
const runtimeFile = join(storagePath, "runtime.json");
|
|
40840
41185
|
if (await fileExists4(runtimeFile)) {
|
|
@@ -40891,13 +41236,15 @@ function parseArgs2(argv) {
|
|
|
40891
41236
|
out.mode = v;
|
|
40892
41237
|
} else if (a === "--storage" && argv[i + 1]) {
|
|
40893
41238
|
out.storage = argv[++i];
|
|
41239
|
+
} else if (a === "--fortress" && argv[i + 1]) {
|
|
41240
|
+
out.fortress = argv[++i];
|
|
40894
41241
|
} else if (a && a.startsWith("--")) {
|
|
40895
41242
|
throw new Error(`Unknown flag: ${a}`);
|
|
40896
41243
|
}
|
|
40897
41244
|
}
|
|
40898
41245
|
return out;
|
|
40899
41246
|
}
|
|
40900
|
-
function
|
|
41247
|
+
function printUsage5(out) {
|
|
40901
41248
|
out.write(`
|
|
40902
41249
|
Usage: sanctuary reset-passphrase [options]
|
|
40903
41250
|
|
|
@@ -40920,9 +41267,9 @@ Recover a fortress whose passphrase has been lost or corrupted. Three modes:
|
|
|
40920
41267
|
|
|
40921
41268
|
Options:
|
|
40922
41269
|
--mode <shares|guardian|nuke> Pick a path non-interactively.
|
|
40923
|
-
--
|
|
40924
|
-
|
|
40925
|
-
|
|
41270
|
+
--fortress <path> Override the fortress storage path.
|
|
41271
|
+
Consistent with "sanctuary wrap --fortress".
|
|
41272
|
+
--storage <path> Alias for --fortress.
|
|
40926
41273
|
--help, -h Show this help.
|
|
40927
41274
|
|
|
40928
41275
|
Without --mode, the command surveys which paths are operationally available
|
|
@@ -41193,7 +41540,7 @@ async function prompt(lines, err, question) {
|
|
|
41193
41540
|
return await lines.next();
|
|
41194
41541
|
}
|
|
41195
41542
|
async function defaultExec2(cmd, args) {
|
|
41196
|
-
return await new Promise((
|
|
41543
|
+
return await new Promise((resolve8, reject) => {
|
|
41197
41544
|
const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
41198
41545
|
let stdout = "";
|
|
41199
41546
|
let stderr = "";
|
|
@@ -41204,7 +41551,7 @@ async function defaultExec2(cmd, args) {
|
|
|
41204
41551
|
stderr += d.toString();
|
|
41205
41552
|
});
|
|
41206
41553
|
child.on("error", reject);
|
|
41207
|
-
child.on("close", (code) =>
|
|
41554
|
+
child.on("close", (code) => resolve8({ stdout, stderr, code }));
|
|
41208
41555
|
});
|
|
41209
41556
|
}
|
|
41210
41557
|
var LineReader;
|
|
@@ -41240,8 +41587,8 @@ var init_reset_passphrase = __esm({
|
|
|
41240
41587
|
return Promise.resolve(this.queue.shift());
|
|
41241
41588
|
}
|
|
41242
41589
|
if (this.closed) return Promise.resolve("");
|
|
41243
|
-
return new Promise((
|
|
41244
|
-
this.waiters.push(
|
|
41590
|
+
return new Promise((resolve8) => {
|
|
41591
|
+
this.waiters.push(resolve8);
|
|
41245
41592
|
});
|
|
41246
41593
|
}
|
|
41247
41594
|
close() {
|
|
@@ -41640,11 +41987,11 @@ async function startMultiDashboardServer(options = {}) {
|
|
|
41640
41987
|
}
|
|
41641
41988
|
}
|
|
41642
41989
|
});
|
|
41643
|
-
await new Promise((
|
|
41990
|
+
await new Promise((resolve8, reject) => {
|
|
41644
41991
|
server.once("error", reject);
|
|
41645
41992
|
server.listen(port, host, () => {
|
|
41646
41993
|
server.off("error", reject);
|
|
41647
|
-
|
|
41994
|
+
resolve8();
|
|
41648
41995
|
});
|
|
41649
41996
|
});
|
|
41650
41997
|
const addr = server.address();
|
|
@@ -41653,8 +42000,8 @@ async function startMultiDashboardServer(options = {}) {
|
|
|
41653
42000
|
url: `http://${host}:${actualPort}`,
|
|
41654
42001
|
port: actualPort,
|
|
41655
42002
|
host,
|
|
41656
|
-
stop: () => new Promise((
|
|
41657
|
-
server.close((err) => err ? reject(err) :
|
|
42003
|
+
stop: () => new Promise((resolve8, reject) => {
|
|
42004
|
+
server.close((err) => err ? reject(err) : resolve8());
|
|
41658
42005
|
})
|
|
41659
42006
|
};
|
|
41660
42007
|
}
|
|
@@ -42054,7 +42401,7 @@ function formatUpdateMessage(current, latest) {
|
|
|
42054
42401
|
return `[Sanctuary] Update available: ${current} \u2192 ${latest}. Run: npx @sanctuary-framework/mcp-server@latest`;
|
|
42055
42402
|
}
|
|
42056
42403
|
function fetchLatestVersion(currentVersion) {
|
|
42057
|
-
return new Promise((
|
|
42404
|
+
return new Promise((resolve8) => {
|
|
42058
42405
|
const req = get(
|
|
42059
42406
|
REGISTRY_URL,
|
|
42060
42407
|
{
|
|
@@ -42064,7 +42411,7 @@ function fetchLatestVersion(currentVersion) {
|
|
|
42064
42411
|
(res) => {
|
|
42065
42412
|
if (res.statusCode !== 200) {
|
|
42066
42413
|
res.resume();
|
|
42067
|
-
|
|
42414
|
+
resolve8(null);
|
|
42068
42415
|
return;
|
|
42069
42416
|
}
|
|
42070
42417
|
let data = "";
|
|
@@ -42073,7 +42420,7 @@ function fetchLatestVersion(currentVersion) {
|
|
|
42073
42420
|
data += chunk;
|
|
42074
42421
|
if (data.length > 32768) {
|
|
42075
42422
|
res.destroy();
|
|
42076
|
-
|
|
42423
|
+
resolve8(null);
|
|
42077
42424
|
}
|
|
42078
42425
|
});
|
|
42079
42426
|
res.on("end", () => {
|
|
@@ -42081,20 +42428,20 @@ function fetchLatestVersion(currentVersion) {
|
|
|
42081
42428
|
const json = JSON.parse(data);
|
|
42082
42429
|
const latest = json.version;
|
|
42083
42430
|
if (typeof latest === "string" && isNewerVersion(currentVersion, latest)) {
|
|
42084
|
-
|
|
42431
|
+
resolve8(latest);
|
|
42085
42432
|
} else {
|
|
42086
|
-
|
|
42433
|
+
resolve8(null);
|
|
42087
42434
|
}
|
|
42088
42435
|
} catch {
|
|
42089
|
-
|
|
42436
|
+
resolve8(null);
|
|
42090
42437
|
}
|
|
42091
42438
|
});
|
|
42092
42439
|
}
|
|
42093
42440
|
);
|
|
42094
|
-
req.on("error", () =>
|
|
42441
|
+
req.on("error", () => resolve8(null));
|
|
42095
42442
|
req.on("timeout", () => {
|
|
42096
42443
|
req.destroy();
|
|
42097
|
-
|
|
42444
|
+
resolve8(null);
|
|
42098
42445
|
});
|
|
42099
42446
|
});
|
|
42100
42447
|
}
|
|
@@ -42172,6 +42519,11 @@ async function main() {
|
|
|
42172
42519
|
const code = await runTemplateCommand2({ argv: args.slice(1) });
|
|
42173
42520
|
process.exit(code);
|
|
42174
42521
|
}
|
|
42522
|
+
if (args[0] === "identity") {
|
|
42523
|
+
const { runIdentityCommand: runIdentityCommand2 } = await Promise.resolve().then(() => (init_identity2(), identity_exports2));
|
|
42524
|
+
const code = await runIdentityCommand2({ argv: args.slice(1) });
|
|
42525
|
+
process.exit(code);
|
|
42526
|
+
}
|
|
42175
42527
|
if (args[0] === "agents") {
|
|
42176
42528
|
const { runAgentsCommand: runAgentsCommand2 } = await Promise.resolve().then(() => (init_agents(), agents_exports));
|
|
42177
42529
|
const code = await runAgentsCommand2({ argv: args.slice(1) });
|
|
@@ -42393,6 +42745,9 @@ Subcommands:
|
|
|
42393
42745
|
Use "sanctuary dashboard --help" for options.
|
|
42394
42746
|
Pass --multi to render the multi-tenant overview.
|
|
42395
42747
|
|
|
42748
|
+
identity Inspect the active identity (DID, public key).
|
|
42749
|
+
Use "sanctuary identity --help" for options.
|
|
42750
|
+
|
|
42396
42751
|
template Manage policy templates (list, init).
|
|
42397
42752
|
Use "sanctuary template --help" for options.
|
|
42398
42753
|
|