@sanctuary-framework/mcp-server 1.2.12 → 1.2.13
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 +1735 -124
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1735 -124
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +671 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +388 -0
- package/dist/index.d.ts +388 -0
- package/dist/index.js +671 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -16707,8 +16707,30 @@ function crossHarnessSummary(details, sender) {
|
|
|
16707
16707
|
return `${sender} -> operator approval`;
|
|
16708
16708
|
}
|
|
16709
16709
|
var COORDINATION_VIEW_AUDIT_OPS = {
|
|
16710
|
+
/** v1.3 Omega-1: operator opened the chronological handoff list. */
|
|
16710
16711
|
VIEW_OPENED: "operator_coordination_view_opened",
|
|
16711
|
-
|
|
16712
|
+
/** v1.3 Omega-1: operator drilled into a single handoff for detail. */
|
|
16713
|
+
ENTRY_DRILLED: "operator_handoff_entry_drilled",
|
|
16714
|
+
/**
|
|
16715
|
+
* v1.3 Omega-3: operator opened the Workflows sibling-view (list of
|
|
16716
|
+
* multi-handoff workflows grouped by `workflow-grouper`). Mirrors
|
|
16717
|
+
* VIEW_OPENED's shape so the dashboard activity feed can group both
|
|
16718
|
+
* as "operator coordination surfaces."
|
|
16719
|
+
*/
|
|
16720
|
+
WORKFLOW_VIEW_OPENED: "operator_workflow_view_opened",
|
|
16721
|
+
/**
|
|
16722
|
+
* v1.3 Omega-3: operator drilled into a single workflow for its
|
|
16723
|
+
* timeline + member-handoffs detail. Mirrors ENTRY_DRILLED's shape.
|
|
16724
|
+
*/
|
|
16725
|
+
WORKFLOW_DRILLED: "operator_workflow_drilled",
|
|
16726
|
+
/**
|
|
16727
|
+
* v1.3 Omega-3: server-side state transition observed on a
|
|
16728
|
+
* workflow (e.g., in_progress -> stalled). Emitted by the route
|
|
16729
|
+
* layer after the state tracker diffs against its prior snapshot.
|
|
16730
|
+
* Distinct from the operator-action events above: this records what
|
|
16731
|
+
* the workflow itself is doing, not what the operator clicked.
|
|
16732
|
+
*/
|
|
16733
|
+
WORKFLOW_STATE_CHANGED: "coordination_workflow_state_changed"
|
|
16712
16734
|
};
|
|
16713
16735
|
|
|
16714
16736
|
// src/coordination/context-transfer-extractor.ts
|
|
@@ -16980,10 +17002,123 @@ function categoryFromPolicyRuleId(ruleId) {
|
|
|
16980
17002
|
var CONTEXT_TRANSFER_AUDIT_OPS = {
|
|
16981
17003
|
DECODED: "operator_handoff_context_transfer_decoded"
|
|
16982
17004
|
};
|
|
17005
|
+
var HEURISTIC_WINDOW_MS = 5 * 60 * 1e3;
|
|
17006
|
+
var STALL_THRESHOLD_MS = 2 * 60 * 60 * 1e3;
|
|
17007
|
+
var CYCLE_COMPLETION_MIN_HOPS = 2;
|
|
17008
|
+
function groupHandoffsIntoWorkflows(handoffs, opts) {
|
|
17009
|
+
if (handoffs.length === 0) return [];
|
|
17010
|
+
const now = opts?.now ?? /* @__PURE__ */ new Date();
|
|
17011
|
+
const linkedGroups = /* @__PURE__ */ new Map();
|
|
17012
|
+
const unlinked = [];
|
|
17013
|
+
for (const h of handoffs) {
|
|
17014
|
+
if (h.workflow_link !== null && h.workflow_link.length > 0) {
|
|
17015
|
+
let bucket = linkedGroups.get(h.workflow_link);
|
|
17016
|
+
if (!bucket) {
|
|
17017
|
+
bucket = [];
|
|
17018
|
+
linkedGroups.set(h.workflow_link, bucket);
|
|
17019
|
+
}
|
|
17020
|
+
bucket.push(h);
|
|
17021
|
+
} else {
|
|
17022
|
+
unlinked.push(h);
|
|
17023
|
+
}
|
|
17024
|
+
}
|
|
17025
|
+
const sortedUnlinked = [...unlinked].sort(
|
|
17026
|
+
(a, b) => a.observed_at < b.observed_at ? -1 : 1
|
|
17027
|
+
);
|
|
17028
|
+
const heuristicChains = [];
|
|
17029
|
+
for (const h of sortedUnlinked) {
|
|
17030
|
+
const joinedIdx = findExtendableChain(heuristicChains, h);
|
|
17031
|
+
if (joinedIdx !== null) {
|
|
17032
|
+
heuristicChains[joinedIdx].push(h);
|
|
17033
|
+
} else {
|
|
17034
|
+
heuristicChains.push([h]);
|
|
17035
|
+
}
|
|
17036
|
+
}
|
|
17037
|
+
const workflows = [];
|
|
17038
|
+
for (const members of linkedGroups.values()) {
|
|
17039
|
+
workflows.push(materialize(members, now));
|
|
17040
|
+
}
|
|
17041
|
+
for (const members of heuristicChains) {
|
|
17042
|
+
workflows.push(materialize(members, now));
|
|
17043
|
+
}
|
|
17044
|
+
workflows.sort(
|
|
17045
|
+
(a, b) => a.last_activity_at < b.last_activity_at ? 1 : -1
|
|
17046
|
+
);
|
|
17047
|
+
return workflows;
|
|
17048
|
+
}
|
|
17049
|
+
function determineWorkflowState(members, now) {
|
|
17050
|
+
if (members.length === 0) return "unknown";
|
|
17051
|
+
const sorted = [...members].sort(
|
|
17052
|
+
(a, b) => a.observed_at < b.observed_at ? -1 : 1
|
|
17053
|
+
);
|
|
17054
|
+
const last = sorted[sorted.length - 1];
|
|
17055
|
+
const root = sorted[0];
|
|
17056
|
+
const lastMs = Date.parse(last.observed_at);
|
|
17057
|
+
if (!Number.isFinite(lastMs)) return "unknown";
|
|
17058
|
+
if (last.target_agent_id === OPERATOR_PSEUDO_AGENT) {
|
|
17059
|
+
return "completed";
|
|
17060
|
+
}
|
|
17061
|
+
if (sorted.length > CYCLE_COMPLETION_MIN_HOPS && last.target_agent_id === root.source_agent_id) {
|
|
17062
|
+
return "completed";
|
|
17063
|
+
}
|
|
17064
|
+
const ageMs = now.getTime() - lastMs;
|
|
17065
|
+
if (ageMs > STALL_THRESHOLD_MS) {
|
|
17066
|
+
return "stalled";
|
|
17067
|
+
}
|
|
17068
|
+
return "in_progress";
|
|
17069
|
+
}
|
|
17070
|
+
function workflowIdFromRoot(rootEntryId) {
|
|
17071
|
+
return crypto.createHash("sha256").update(`workflow:${rootEntryId}`).digest("hex").slice(0, 32);
|
|
17072
|
+
}
|
|
17073
|
+
function findExtendableChain(chains, h) {
|
|
17074
|
+
const hMs = Date.parse(h.observed_at);
|
|
17075
|
+
if (!Number.isFinite(hMs)) return null;
|
|
17076
|
+
let bestIdx = null;
|
|
17077
|
+
let bestGapMs = Number.POSITIVE_INFINITY;
|
|
17078
|
+
for (let i = 0; i < chains.length; i += 1) {
|
|
17079
|
+
const chain = chains[i];
|
|
17080
|
+
const last = chain[chain.length - 1];
|
|
17081
|
+
const lastMs = Date.parse(last.observed_at);
|
|
17082
|
+
if (!Number.isFinite(lastMs)) continue;
|
|
17083
|
+
const gapMs = Math.abs(hMs - lastMs);
|
|
17084
|
+
if (gapMs > HEURISTIC_WINDOW_MS) continue;
|
|
17085
|
+
if (!sharesAgent(last, h)) continue;
|
|
17086
|
+
if (gapMs < bestGapMs) {
|
|
17087
|
+
bestGapMs = gapMs;
|
|
17088
|
+
bestIdx = i;
|
|
17089
|
+
}
|
|
17090
|
+
}
|
|
17091
|
+
return bestIdx;
|
|
17092
|
+
}
|
|
17093
|
+
function sharesAgent(a, b) {
|
|
17094
|
+
return a.source_agent_id === b.source_agent_id || a.source_agent_id === b.target_agent_id || a.target_agent_id === b.source_agent_id || a.target_agent_id === b.target_agent_id;
|
|
17095
|
+
}
|
|
17096
|
+
function materialize(members, now) {
|
|
17097
|
+
const sorted = [...members].sort(
|
|
17098
|
+
(a, b) => a.observed_at < b.observed_at ? -1 : 1
|
|
17099
|
+
);
|
|
17100
|
+
const root = sorted[0];
|
|
17101
|
+
const last = sorted[sorted.length - 1];
|
|
17102
|
+
const involved = /* @__PURE__ */ new Set();
|
|
17103
|
+
for (const h of sorted) {
|
|
17104
|
+
if (h.source_agent_id) involved.add(h.source_agent_id);
|
|
17105
|
+
if (h.target_agent_id) involved.add(h.target_agent_id);
|
|
17106
|
+
}
|
|
17107
|
+
return {
|
|
17108
|
+
workflow_id: workflowIdFromRoot(root.entry_id),
|
|
17109
|
+
root_handoff: root,
|
|
17110
|
+
member_handoffs: sorted,
|
|
17111
|
+
state: determineWorkflowState(sorted, now),
|
|
17112
|
+
started_at: root.observed_at,
|
|
17113
|
+
last_activity_at: last.observed_at,
|
|
17114
|
+
involved_agents: [...involved].sort()
|
|
17115
|
+
};
|
|
17116
|
+
}
|
|
16983
17117
|
|
|
16984
17118
|
// src/coordination/handoff-routes.ts
|
|
16985
17119
|
var COORDINATION_API_PREFIX = "/api/coordination";
|
|
16986
17120
|
var COORDINATION_HANDOFFS_PREFIX = "/api/coordination/handoffs";
|
|
17121
|
+
var COORDINATION_WORKFLOWS_PREFIX = "/api/coordination/workflows";
|
|
16987
17122
|
var COORDINATION_LIST_DEFAULT_LIMIT = 50;
|
|
16988
17123
|
var COORDINATION_LIST_MAX_LIMIT = 500;
|
|
16989
17124
|
var HandoffEventBridge = class {
|
|
@@ -17022,6 +17157,101 @@ function matchEntryRoute2(path) {
|
|
|
17022
17157
|
if (rest.includes("/")) return null;
|
|
17023
17158
|
return { entryId: decodeURIComponent(rest) };
|
|
17024
17159
|
}
|
|
17160
|
+
function matchWorkflowRoute(path) {
|
|
17161
|
+
const prefix = `${COORDINATION_WORKFLOWS_PREFIX}/`;
|
|
17162
|
+
if (!path.startsWith(prefix)) return null;
|
|
17163
|
+
const rest = path.slice(prefix.length);
|
|
17164
|
+
if (rest.length === 0 || rest === "stream") return null;
|
|
17165
|
+
if (rest.includes("/")) return null;
|
|
17166
|
+
return { workflowId: decodeURIComponent(rest) };
|
|
17167
|
+
}
|
|
17168
|
+
async function computeWorkflowsAndTrackTransitions(deps) {
|
|
17169
|
+
const handoffs = await deps.handoffLog.query({ limit: 500 });
|
|
17170
|
+
const workflows = groupHandoffsIntoWorkflows(handoffs, {
|
|
17171
|
+
...deps.now !== void 0 ? { now: deps.now() } : {}
|
|
17172
|
+
});
|
|
17173
|
+
const transitions = deps.workflowStateTracker ? deps.workflowStateTracker.observe(workflows) : [];
|
|
17174
|
+
for (const change of transitions) {
|
|
17175
|
+
deps.auditLog.append(
|
|
17176
|
+
"l2",
|
|
17177
|
+
COORDINATION_VIEW_AUDIT_OPS.WORKFLOW_STATE_CHANGED,
|
|
17178
|
+
deps.operatorId,
|
|
17179
|
+
{
|
|
17180
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17181
|
+
workflow_id: change.workflow_id,
|
|
17182
|
+
previous_state: change.previous_state,
|
|
17183
|
+
new_state: change.new_state
|
|
17184
|
+
}
|
|
17185
|
+
);
|
|
17186
|
+
}
|
|
17187
|
+
return { workflows, transitions };
|
|
17188
|
+
}
|
|
17189
|
+
function filterWorkflowList(workflows, opts) {
|
|
17190
|
+
let filtered = workflows;
|
|
17191
|
+
if (opts.state) {
|
|
17192
|
+
filtered = filtered.filter((w) => w.state === opts.state);
|
|
17193
|
+
}
|
|
17194
|
+
if (opts.agentId) {
|
|
17195
|
+
filtered = filtered.filter((w) => w.involved_agents.includes(opts.agentId));
|
|
17196
|
+
}
|
|
17197
|
+
if (opts.since) {
|
|
17198
|
+
filtered = filtered.filter((w) => w.last_activity_at >= opts.since);
|
|
17199
|
+
}
|
|
17200
|
+
return filtered.slice(0, opts.limit);
|
|
17201
|
+
}
|
|
17202
|
+
function isWorkflowState(value) {
|
|
17203
|
+
return value === "in_progress" || value === "completed" || value === "stalled" || value === "unknown";
|
|
17204
|
+
}
|
|
17205
|
+
async function handleWorkflowStream(deps, res) {
|
|
17206
|
+
res.writeHead(200, {
|
|
17207
|
+
"Content-Type": "text/event-stream",
|
|
17208
|
+
"Cache-Control": "no-cache, no-transform",
|
|
17209
|
+
Connection: "keep-alive",
|
|
17210
|
+
"X-Accel-Buffering": "no"
|
|
17211
|
+
});
|
|
17212
|
+
const initial = await computeWorkflowsAndTrackTransitions(deps);
|
|
17213
|
+
res.write(
|
|
17214
|
+
`event: workflow_snapshot
|
|
17215
|
+
data: ${JSON.stringify({ workflows: initial.workflows })}
|
|
17216
|
+
|
|
17217
|
+
`
|
|
17218
|
+
);
|
|
17219
|
+
if (initial.transitions.length > 0) {
|
|
17220
|
+
res.write(
|
|
17221
|
+
`event: workflow_state_changed
|
|
17222
|
+
data: ${JSON.stringify({ transitions: initial.transitions })}
|
|
17223
|
+
|
|
17224
|
+
`
|
|
17225
|
+
);
|
|
17226
|
+
}
|
|
17227
|
+
const unsubscribe = deps.events.subscribe(() => {
|
|
17228
|
+
void (async () => {
|
|
17229
|
+
try {
|
|
17230
|
+
const tick = await computeWorkflowsAndTrackTransitions(deps);
|
|
17231
|
+
res.write(
|
|
17232
|
+
`event: workflow_snapshot
|
|
17233
|
+
data: ${JSON.stringify({ workflows: tick.workflows })}
|
|
17234
|
+
|
|
17235
|
+
`
|
|
17236
|
+
);
|
|
17237
|
+
if (tick.transitions.length > 0) {
|
|
17238
|
+
res.write(
|
|
17239
|
+
`event: workflow_state_changed
|
|
17240
|
+
data: ${JSON.stringify({ transitions: tick.transitions })}
|
|
17241
|
+
|
|
17242
|
+
`
|
|
17243
|
+
);
|
|
17244
|
+
}
|
|
17245
|
+
} catch {
|
|
17246
|
+
}
|
|
17247
|
+
})();
|
|
17248
|
+
});
|
|
17249
|
+
const cleanup = () => {
|
|
17250
|
+
unsubscribe();
|
|
17251
|
+
};
|
|
17252
|
+
res.on("close", cleanup);
|
|
17253
|
+
res.on("error", cleanup);
|
|
17254
|
+
}
|
|
17025
17255
|
async function handleStream3(deps, res) {
|
|
17026
17256
|
res.writeHead(200, {
|
|
17027
17257
|
"Content-Type": "text/event-stream",
|
|
@@ -17105,6 +17335,67 @@ async function handleCoordinationRoute(deps, req, res) {
|
|
|
17105
17335
|
writeJSON6(res, 200, { ok: true, data: { entries } });
|
|
17106
17336
|
return true;
|
|
17107
17337
|
}
|
|
17338
|
+
if (method === "GET" && path === `${COORDINATION_WORKFLOWS_PREFIX}/stream`) {
|
|
17339
|
+
await handleWorkflowStream(deps, res);
|
|
17340
|
+
return true;
|
|
17341
|
+
}
|
|
17342
|
+
if (method === "GET" && path === COORDINATION_WORKFLOWS_PREFIX) {
|
|
17343
|
+
const limit = parseLimit4(
|
|
17344
|
+
url.searchParams.get("limit"),
|
|
17345
|
+
COORDINATION_LIST_DEFAULT_LIMIT,
|
|
17346
|
+
COORDINATION_LIST_MAX_LIMIT
|
|
17347
|
+
);
|
|
17348
|
+
const rawState = url.searchParams.get("state");
|
|
17349
|
+
const state = rawState && isWorkflowState(rawState) ? rawState : void 0;
|
|
17350
|
+
const since = url.searchParams.get("since") ?? void 0;
|
|
17351
|
+
const agentId = url.searchParams.get("agent_id") ?? void 0;
|
|
17352
|
+
const computed = await computeWorkflowsAndTrackTransitions(deps);
|
|
17353
|
+
const filtered = filterWorkflowList(computed.workflows, {
|
|
17354
|
+
...state !== void 0 ? { state } : {},
|
|
17355
|
+
...agentId !== void 0 ? { agentId } : {},
|
|
17356
|
+
...since !== void 0 ? { since } : {},
|
|
17357
|
+
limit
|
|
17358
|
+
});
|
|
17359
|
+
deps.auditLog.append(
|
|
17360
|
+
"l2",
|
|
17361
|
+
COORDINATION_VIEW_AUDIT_OPS.WORKFLOW_VIEW_OPENED,
|
|
17362
|
+
deps.operatorId,
|
|
17363
|
+
{
|
|
17364
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17365
|
+
result_count: filtered.length,
|
|
17366
|
+
...state !== void 0 ? { state } : {},
|
|
17367
|
+
...agentId !== void 0 ? { agent_id: agentId } : {},
|
|
17368
|
+
...since !== void 0 ? { since } : {}
|
|
17369
|
+
}
|
|
17370
|
+
);
|
|
17371
|
+
writeJSON6(res, 200, { ok: true, data: { workflows: filtered } });
|
|
17372
|
+
return true;
|
|
17373
|
+
}
|
|
17374
|
+
const workflowMatch = matchWorkflowRoute(path);
|
|
17375
|
+
if (method === "GET" && workflowMatch) {
|
|
17376
|
+
const computed = await computeWorkflowsAndTrackTransitions(deps);
|
|
17377
|
+
const wf = computed.workflows.find(
|
|
17378
|
+
(w) => w.workflow_id === workflowMatch.workflowId
|
|
17379
|
+
);
|
|
17380
|
+
if (!wf) {
|
|
17381
|
+
writeJSON6(res, 404, { ok: false, error: "not_found" });
|
|
17382
|
+
return true;
|
|
17383
|
+
}
|
|
17384
|
+
deps.auditLog.append(
|
|
17385
|
+
"l2",
|
|
17386
|
+
COORDINATION_VIEW_AUDIT_OPS.WORKFLOW_DRILLED,
|
|
17387
|
+
deps.operatorId,
|
|
17388
|
+
{
|
|
17389
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17390
|
+
workflow_id: wf.workflow_id,
|
|
17391
|
+
state: wf.state,
|
|
17392
|
+
member_count: wf.member_handoffs.length,
|
|
17393
|
+
involved_agent_count: wf.involved_agents.length
|
|
17394
|
+
}
|
|
17395
|
+
);
|
|
17396
|
+
writeJSON6(res, 200, { ok: true, data: { workflow: wf } });
|
|
17397
|
+
return true;
|
|
17398
|
+
}
|
|
17108
17399
|
const entryMatch = matchEntryRoute2(path);
|
|
17109
17400
|
if (method === "GET" && entryMatch) {
|
|
17110
17401
|
const detail = await deps.handoffLog.getEntry(entryMatch.entryId);
|
|
@@ -17246,6 +17537,8 @@ var DashboardApprovalChannel = class {
|
|
|
17246
17537
|
*/
|
|
17247
17538
|
handoffLog = null;
|
|
17248
17539
|
handoffEventBridge = null;
|
|
17540
|
+
handoffContextTransfer = null;
|
|
17541
|
+
workflowStateTracker = null;
|
|
17249
17542
|
handoffAuditLog = null;
|
|
17250
17543
|
handoffOperatorId = null;
|
|
17251
17544
|
constructor(config) {
|
|
@@ -17326,6 +17619,8 @@ var DashboardApprovalChannel = class {
|
|
|
17326
17619
|
this.handoffEventBridge = opts.eventBridge ?? null;
|
|
17327
17620
|
this.handoffAuditLog = opts.auditLog ?? null;
|
|
17328
17621
|
this.handoffOperatorId = opts.operatorId ?? null;
|
|
17622
|
+
this.handoffContextTransfer = opts.contextTransfer ?? null;
|
|
17623
|
+
this.workflowStateTracker = opts.workflowStateTracker ?? null;
|
|
17329
17624
|
}
|
|
17330
17625
|
/**
|
|
17331
17626
|
* v1.3 WP-V1.3-10 dispatch entry point. Called from `handleRequest`
|
|
@@ -17383,7 +17678,9 @@ var DashboardApprovalChannel = class {
|
|
|
17383
17678
|
handoffLog: this.handoffLog,
|
|
17384
17679
|
auditLog: this.handoffAuditLog,
|
|
17385
17680
|
operatorId: this.handoffOperatorId ?? this.identityManager?.getPrimaryIdentityId() ?? "operator_dashboard",
|
|
17386
|
-
events: this.handoffEventBridge
|
|
17681
|
+
events: this.handoffEventBridge,
|
|
17682
|
+
...this.handoffContextTransfer !== null ? { contextTransfer: this.handoffContextTransfer } : {},
|
|
17683
|
+
...this.workflowStateTracker !== null ? { workflowStateTracker: this.workflowStateTracker } : {}
|
|
17387
17684
|
},
|
|
17388
17685
|
req,
|
|
17389
17686
|
res
|
|
@@ -21916,6 +22213,70 @@ function classifierSpecificAuditOp(classifierId) {
|
|
|
21916
22213
|
return null;
|
|
21917
22214
|
}
|
|
21918
22215
|
|
|
22216
|
+
// src/coordination/workflow-state-tracker.ts
|
|
22217
|
+
var WorkflowStateTracker = class {
|
|
22218
|
+
states = /* @__PURE__ */ new Map();
|
|
22219
|
+
now;
|
|
22220
|
+
constructor(opts) {
|
|
22221
|
+
this.now = opts?.now ?? (() => /* @__PURE__ */ new Date());
|
|
22222
|
+
}
|
|
22223
|
+
/**
|
|
22224
|
+
* Diff the supplied workflow list against the last-observed states.
|
|
22225
|
+
* Returns the set of transitions detected this call; the tracker
|
|
22226
|
+
* mutates its internal map to reflect the new states.
|
|
22227
|
+
*
|
|
22228
|
+
* Transitions emitted:
|
|
22229
|
+
* - First observation of a workflow (`previous_state` is the
|
|
22230
|
+
* sentinel `unobserved`). Lets the route handler audit-emit
|
|
22231
|
+
* the initial state so the operator sees workflows as they
|
|
22232
|
+
* surface, not only when they change.
|
|
22233
|
+
* - Subsequent observation where `previous_state !== new_state`.
|
|
22234
|
+
*/
|
|
22235
|
+
observe(workflows) {
|
|
22236
|
+
const out = [];
|
|
22237
|
+
const observedAt = this.now().toISOString();
|
|
22238
|
+
for (const wf of workflows) {
|
|
22239
|
+
const prior = this.states.get(wf.workflow_id);
|
|
22240
|
+
if (prior === void 0) {
|
|
22241
|
+
out.push({
|
|
22242
|
+
workflow_id: wf.workflow_id,
|
|
22243
|
+
previous_state: "unobserved",
|
|
22244
|
+
new_state: wf.state,
|
|
22245
|
+
observed_at: observedAt
|
|
22246
|
+
});
|
|
22247
|
+
this.states.set(wf.workflow_id, wf.state);
|
|
22248
|
+
continue;
|
|
22249
|
+
}
|
|
22250
|
+
if (prior !== wf.state) {
|
|
22251
|
+
out.push({
|
|
22252
|
+
workflow_id: wf.workflow_id,
|
|
22253
|
+
previous_state: prior,
|
|
22254
|
+
new_state: wf.state,
|
|
22255
|
+
observed_at: observedAt
|
|
22256
|
+
});
|
|
22257
|
+
this.states.set(wf.workflow_id, wf.state);
|
|
22258
|
+
}
|
|
22259
|
+
}
|
|
22260
|
+
return out;
|
|
22261
|
+
}
|
|
22262
|
+
/**
|
|
22263
|
+
* Drop a workflow's recorded state. Surfaced for tests + future
|
|
22264
|
+
* "operator dismissed this workflow" affordance; not currently
|
|
22265
|
+
* called by the production wiring.
|
|
22266
|
+
*/
|
|
22267
|
+
forget(workflowId) {
|
|
22268
|
+
this.states.delete(workflowId);
|
|
22269
|
+
}
|
|
22270
|
+
/** Reset the tracker. Tests use this between runs. */
|
|
22271
|
+
reset() {
|
|
22272
|
+
this.states.clear();
|
|
22273
|
+
}
|
|
22274
|
+
/** Read-only view of the current snapshot. Useful for diagnostics. */
|
|
22275
|
+
snapshot() {
|
|
22276
|
+
return new Map(this.states);
|
|
22277
|
+
}
|
|
22278
|
+
};
|
|
22279
|
+
|
|
21919
22280
|
// src/sentinel/sentinel.ts
|
|
21920
22281
|
var Sentinel = class {
|
|
21921
22282
|
/**
|
|
@@ -25149,7 +25510,7 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
25149
25510
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25150
25511
|
const canonicalBytes = canonicalize2(outcome);
|
|
25151
25512
|
const canonicalString = new TextDecoder().decode(canonicalBytes);
|
|
25152
|
-
const
|
|
25513
|
+
const sha25612 = createCommitment(canonicalString);
|
|
25153
25514
|
let pedersenData;
|
|
25154
25515
|
if (includePedersen && Number.isInteger(outcome.rounds) && outcome.rounds >= 0) {
|
|
25155
25516
|
const pedersen = createPedersenCommitment(outcome.rounds);
|
|
@@ -25161,7 +25522,7 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
25161
25522
|
const commitmentPayload = {
|
|
25162
25523
|
bridge_commitment_id: commitmentId,
|
|
25163
25524
|
session_id: outcome.session_id,
|
|
25164
|
-
sha256_commitment:
|
|
25525
|
+
sha256_commitment: sha25612.commitment,
|
|
25165
25526
|
terms_hash: outcome.terms_hash,
|
|
25166
25527
|
committer_did: identity.did,
|
|
25167
25528
|
committed_at: now,
|
|
@@ -25172,8 +25533,8 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
25172
25533
|
return {
|
|
25173
25534
|
bridge_commitment_id: commitmentId,
|
|
25174
25535
|
session_id: outcome.session_id,
|
|
25175
|
-
sha256_commitment:
|
|
25176
|
-
blinding_factor:
|
|
25536
|
+
sha256_commitment: sha25612.commitment,
|
|
25537
|
+
blinding_factor: sha25612.blinding_factor,
|
|
25177
25538
|
committer_did: identity.did,
|
|
25178
25539
|
signature: toBase64url(signature),
|
|
25179
25540
|
pedersen_commitment: pedersenData,
|
|
@@ -39724,6 +40085,150 @@ var EXIT_BUNDLE_ARTIFACT_KINDS = [
|
|
|
39724
40085
|
"placeholder_vault_metadata"
|
|
39725
40086
|
];
|
|
39726
40087
|
|
|
40088
|
+
// src/recognition/did-web.ts
|
|
40089
|
+
init_encoding();
|
|
40090
|
+
init_hashing();
|
|
40091
|
+
var DEFAULT_TIMEOUT_MS4 = 5e3;
|
|
40092
|
+
var HOST_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/i;
|
|
40093
|
+
async function resolveDidWeb(did, opts) {
|
|
40094
|
+
const parsed = parseDidWeb(did);
|
|
40095
|
+
const url = didToUrl(parsed);
|
|
40096
|
+
if (!opts.allowed_hosts.includes(parsed.authority_host)) {
|
|
40097
|
+
return {
|
|
40098
|
+
ok: false,
|
|
40099
|
+
failure: "host_not_allowed",
|
|
40100
|
+
message: `did-web: authority_host '${parsed.authority_host}' is not in the operator's allowed_hosts allowlist; resolution refused (no-outbound-by-default)`,
|
|
40101
|
+
url
|
|
40102
|
+
};
|
|
40103
|
+
}
|
|
40104
|
+
const timeoutMs = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS4;
|
|
40105
|
+
const fetcher = opts.fetcher ?? defaultFetcher;
|
|
40106
|
+
const controller = new AbortController();
|
|
40107
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
40108
|
+
let response;
|
|
40109
|
+
try {
|
|
40110
|
+
response = await fetcher(url, { signal: controller.signal });
|
|
40111
|
+
} catch (err) {
|
|
40112
|
+
clearTimeout(timer);
|
|
40113
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40114
|
+
if (controller.signal.aborted) {
|
|
40115
|
+
return {
|
|
40116
|
+
ok: false,
|
|
40117
|
+
failure: "timeout",
|
|
40118
|
+
message: `did-web: resolution exceeded ${timeoutMs}ms`,
|
|
40119
|
+
url
|
|
40120
|
+
};
|
|
40121
|
+
}
|
|
40122
|
+
return {
|
|
40123
|
+
ok: false,
|
|
40124
|
+
failure: "fetch_failed",
|
|
40125
|
+
message: `did-web: fetch error: ${message}`,
|
|
40126
|
+
url
|
|
40127
|
+
};
|
|
40128
|
+
}
|
|
40129
|
+
clearTimeout(timer);
|
|
40130
|
+
if (response.status === 404) {
|
|
40131
|
+
return {
|
|
40132
|
+
ok: false,
|
|
40133
|
+
failure: "not_found",
|
|
40134
|
+
message: `did-web: 404 from authority host`,
|
|
40135
|
+
url
|
|
40136
|
+
};
|
|
40137
|
+
}
|
|
40138
|
+
if (!response.ok) {
|
|
40139
|
+
return {
|
|
40140
|
+
ok: false,
|
|
40141
|
+
failure: "fetch_failed",
|
|
40142
|
+
message: `did-web: authority host returned ${response.status}`,
|
|
40143
|
+
url
|
|
40144
|
+
};
|
|
40145
|
+
}
|
|
40146
|
+
let body;
|
|
40147
|
+
try {
|
|
40148
|
+
body = await response.json();
|
|
40149
|
+
} catch (err) {
|
|
40150
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40151
|
+
return {
|
|
40152
|
+
ok: false,
|
|
40153
|
+
failure: "invalid_json",
|
|
40154
|
+
message: `did-web: invalid JSON: ${message}`,
|
|
40155
|
+
url
|
|
40156
|
+
};
|
|
40157
|
+
}
|
|
40158
|
+
if (!isDidDocument(body, did)) {
|
|
40159
|
+
return {
|
|
40160
|
+
ok: false,
|
|
40161
|
+
failure: "invalid_json",
|
|
40162
|
+
message: `did-web: response body is not a valid DID Document for ${did}`,
|
|
40163
|
+
url
|
|
40164
|
+
};
|
|
40165
|
+
}
|
|
40166
|
+
if (opts.expected_public_key !== void 0) {
|
|
40167
|
+
const expectedX = toBase64url(opts.expected_public_key);
|
|
40168
|
+
const actualX = body.verificationMethod[0]?.publicKeyJwk.x;
|
|
40169
|
+
if (actualX !== expectedX) {
|
|
40170
|
+
return {
|
|
40171
|
+
ok: false,
|
|
40172
|
+
failure: "signature_mismatch",
|
|
40173
|
+
message: `did-web: verificationMethod public key does not match expected key`,
|
|
40174
|
+
url
|
|
40175
|
+
};
|
|
40176
|
+
}
|
|
40177
|
+
}
|
|
40178
|
+
return { ok: true, did_document: body, url };
|
|
40179
|
+
}
|
|
40180
|
+
function parseDidWeb(did) {
|
|
40181
|
+
if (!did.startsWith("did:web:")) {
|
|
40182
|
+
throw new Error(`did-web: '${did}' is not a did:web identifier`);
|
|
40183
|
+
}
|
|
40184
|
+
const rest = did.slice("did:web:".length);
|
|
40185
|
+
const segments = rest.split(":");
|
|
40186
|
+
const authorityHost = segments[0];
|
|
40187
|
+
if (!HOST_RE.test(authorityHost)) {
|
|
40188
|
+
throw new Error(`did-web: '${authorityHost}' is not a valid DNS host`);
|
|
40189
|
+
}
|
|
40190
|
+
const parsed = { authority_host: authorityHost };
|
|
40191
|
+
if (segments.length === 1) return parsed;
|
|
40192
|
+
if (segments.length === 5 && segments[1] === "fortress" && segments[3] === "agent") {
|
|
40193
|
+
parsed.fortress_id = segments[2];
|
|
40194
|
+
parsed.agent_label = segments[4];
|
|
40195
|
+
return parsed;
|
|
40196
|
+
}
|
|
40197
|
+
throw new Error(
|
|
40198
|
+
`did-web: '${did}' does not match the supported shapes (bare did:web:<host> or did:web:<host>:fortress:<fid>:agent:<alabel>)`
|
|
40199
|
+
);
|
|
40200
|
+
}
|
|
40201
|
+
function didToUrl(parsed) {
|
|
40202
|
+
if (parsed.fortress_id === void 0 || parsed.agent_label === void 0) {
|
|
40203
|
+
return `https://${parsed.authority_host}/.well-known/did.json`;
|
|
40204
|
+
}
|
|
40205
|
+
return `https://${parsed.authority_host}/fortress/${parsed.fortress_id}/agent/${parsed.agent_label}/did.json`;
|
|
40206
|
+
}
|
|
40207
|
+
function isDidDocument(value, expectedDid) {
|
|
40208
|
+
if (!value || typeof value !== "object") return false;
|
|
40209
|
+
const v = value;
|
|
40210
|
+
if (v["id"] !== expectedDid) return false;
|
|
40211
|
+
if (!Array.isArray(v["@context"])) return false;
|
|
40212
|
+
const vm = v["verificationMethod"];
|
|
40213
|
+
if (!Array.isArray(vm) || vm.length === 0) return false;
|
|
40214
|
+
const first = vm[0];
|
|
40215
|
+
if (!first || typeof first["id"] !== "string") return false;
|
|
40216
|
+
const jwk = first["publicKeyJwk"];
|
|
40217
|
+
if (!jwk || jwk["kty"] !== "OKP" || jwk["crv"] !== "Ed25519") return false;
|
|
40218
|
+
if (typeof jwk["x"] !== "string") return false;
|
|
40219
|
+
if (!Array.isArray(v["authentication"])) return false;
|
|
40220
|
+
if (!Array.isArray(v["assertionMethod"])) return false;
|
|
40221
|
+
return true;
|
|
40222
|
+
}
|
|
40223
|
+
async function defaultFetcher(url, init) {
|
|
40224
|
+
const response = await fetch(url, init);
|
|
40225
|
+
return {
|
|
40226
|
+
ok: response.ok,
|
|
40227
|
+
status: response.status,
|
|
40228
|
+
json: () => response.json()
|
|
40229
|
+
};
|
|
40230
|
+
}
|
|
40231
|
+
|
|
39727
40232
|
// src/exit/bundle.ts
|
|
39728
40233
|
init_hashing();
|
|
39729
40234
|
init_encoding();
|
|
@@ -40148,6 +40653,11 @@ async function verifyExitBundle(bundleDir, options = {}) {
|
|
|
40148
40653
|
|
|
40149
40654
|
// src/exit/bundle.ts
|
|
40150
40655
|
var ARTIFACT_DIR = "artifacts";
|
|
40656
|
+
var EXIT_BUNDLE_DID_WEB_AUDIT_OPS = {
|
|
40657
|
+
EXPORT_INCLUDED: "exit_bundle_did_web_export_included",
|
|
40658
|
+
IMPORT_VERIFIED: "exit_bundle_did_web_import_verified",
|
|
40659
|
+
AUTHORITY_HOST: "exit_bundle_did_web_authority_host"
|
|
40660
|
+
};
|
|
40151
40661
|
var EXIT_IMPORT_NAMESPACE = "_exit_imports";
|
|
40152
40662
|
var EXIT_PUBLIC_IDENTITIES_NAMESPACE = "_exit_public_identities";
|
|
40153
40663
|
var EXIT_AUDIT_RECEIPTS_NAMESPACE = "_exit_audit_receipts";
|
|
@@ -40439,6 +40949,7 @@ async function exportExitBundle(opts) {
|
|
|
40439
40949
|
"placeholder_vault_metadata"
|
|
40440
40950
|
)
|
|
40441
40951
|
);
|
|
40952
|
+
const didWebBinding = validateExportDidWeb(opts.didWeb);
|
|
40442
40953
|
const body = {
|
|
40443
40954
|
manifest_version: EXIT_BUNDLE_MANIFEST_VERSION,
|
|
40444
40955
|
exported_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -40446,7 +40957,8 @@ async function exportExitBundle(opts) {
|
|
|
40446
40957
|
identity_id: identity.identity_id,
|
|
40447
40958
|
fortress_id: identity.did,
|
|
40448
40959
|
fortress_master_pubkey: identity.public_key,
|
|
40449
|
-
did: identity.did
|
|
40960
|
+
did: identity.did,
|
|
40961
|
+
...didWebBinding !== void 0 ? { did_web: didWebBinding } : {}
|
|
40450
40962
|
},
|
|
40451
40963
|
source_sanctuary_version: opts.config?.version ?? SANCTUARY_VERSION,
|
|
40452
40964
|
artifacts,
|
|
@@ -40468,6 +40980,18 @@ async function exportExitBundle(opts) {
|
|
|
40468
40980
|
};
|
|
40469
40981
|
const manifestBytes = jsonBytes(manifest);
|
|
40470
40982
|
await promises.writeFile(path.join(bundleDir, "manifest.json"), manifestBytes, { mode: 384 });
|
|
40983
|
+
if (didWebBinding !== void 0) {
|
|
40984
|
+
opts.auditLog.append(
|
|
40985
|
+
"l1",
|
|
40986
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.EXPORT_INCLUDED,
|
|
40987
|
+
identity.identity_id,
|
|
40988
|
+
{
|
|
40989
|
+
approval_id: exportApprovalAuditId,
|
|
40990
|
+
identifier: didWebBinding.identifier,
|
|
40991
|
+
authority_host: didWebBinding.authority_host
|
|
40992
|
+
}
|
|
40993
|
+
);
|
|
40994
|
+
}
|
|
40471
40995
|
await opts.auditLog.flush();
|
|
40472
40996
|
return {
|
|
40473
40997
|
bundle_dir: bundleDir,
|
|
@@ -40479,6 +41003,30 @@ async function exportExitBundle(opts) {
|
|
|
40479
41003
|
]
|
|
40480
41004
|
};
|
|
40481
41005
|
}
|
|
41006
|
+
function validateExportDidWeb(binding) {
|
|
41007
|
+
if (binding === void 0) return void 0;
|
|
41008
|
+
if (!binding.identifier || typeof binding.identifier !== "string") {
|
|
41009
|
+
throw new Error(
|
|
41010
|
+
"exit-bundle: did_web.identifier must be a non-empty did:web URI"
|
|
41011
|
+
);
|
|
41012
|
+
}
|
|
41013
|
+
if (!binding.authority_host || typeof binding.authority_host !== "string") {
|
|
41014
|
+
throw new Error(
|
|
41015
|
+
"exit-bundle: did_web.authority_host must be a non-empty DNS host"
|
|
41016
|
+
);
|
|
41017
|
+
}
|
|
41018
|
+
const parsed = parseDidWeb(binding.identifier);
|
|
41019
|
+
if (parsed.authority_host.toLowerCase() !== binding.authority_host.toLowerCase()) {
|
|
41020
|
+
throw new Error(
|
|
41021
|
+
`exit-bundle: did_web.identifier authority host '${parsed.authority_host}' does not match did_web.authority_host '${binding.authority_host}'`
|
|
41022
|
+
);
|
|
41023
|
+
}
|
|
41024
|
+
return {
|
|
41025
|
+
identifier: binding.identifier,
|
|
41026
|
+
authority_host: binding.authority_host,
|
|
41027
|
+
...binding.published_at !== void 0 ? { published_at: binding.published_at } : {}
|
|
41028
|
+
};
|
|
41029
|
+
}
|
|
40482
41030
|
function publicKeysFromIdentityArtifact(identityArtifact) {
|
|
40483
41031
|
const pubkey = fromBase64url(identityArtifact.bundle.publicKey);
|
|
40484
41032
|
return {
|
|
@@ -40693,6 +41241,87 @@ async function importExitBundle(opts) {
|
|
|
40693
41241
|
};
|
|
40694
41242
|
}
|
|
40695
41243
|
const manifest = await readManifest(opts.bundleDir);
|
|
41244
|
+
const importWarnings = [];
|
|
41245
|
+
const manifestDidWeb = manifest.body.identity_binding.did_web;
|
|
41246
|
+
if (manifestDidWeb !== void 0 && !opts.skipDidWebVerify) {
|
|
41247
|
+
const expectedPublicKey = fromBase64url(
|
|
41248
|
+
manifest.body.identity_binding.fortress_master_pubkey
|
|
41249
|
+
);
|
|
41250
|
+
const resolveOpts = {
|
|
41251
|
+
allowed_hosts: opts.didWebAllowedHosts ?? [],
|
|
41252
|
+
expected_public_key: expectedPublicKey,
|
|
41253
|
+
...opts.didWebFetcher !== void 0 ? { fetcher: opts.didWebFetcher } : {},
|
|
41254
|
+
...opts.didWebTimeoutMs !== void 0 ? { timeout_ms: opts.didWebTimeoutMs } : {}
|
|
41255
|
+
};
|
|
41256
|
+
const resolution = await resolveDidWeb(
|
|
41257
|
+
manifestDidWeb.identifier,
|
|
41258
|
+
resolveOpts
|
|
41259
|
+
);
|
|
41260
|
+
const authorityHost = manifestDidWeb.authority_host;
|
|
41261
|
+
opts.auditLog.append(
|
|
41262
|
+
"l1",
|
|
41263
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.AUTHORITY_HOST,
|
|
41264
|
+
manifest.body.identity_binding.identity_id,
|
|
41265
|
+
{ authority_host: authorityHost, identifier: manifestDidWeb.identifier }
|
|
41266
|
+
);
|
|
41267
|
+
if (resolution.ok) {
|
|
41268
|
+
opts.auditLog.append(
|
|
41269
|
+
"l1",
|
|
41270
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41271
|
+
manifest.body.identity_binding.identity_id,
|
|
41272
|
+
{
|
|
41273
|
+
outcome: "success",
|
|
41274
|
+
identifier: manifestDidWeb.identifier,
|
|
41275
|
+
authority_host: authorityHost,
|
|
41276
|
+
resolved_url: resolution.url
|
|
41277
|
+
}
|
|
41278
|
+
);
|
|
41279
|
+
} else if (resolution.failure === "signature_mismatch") {
|
|
41280
|
+
opts.auditLog.append(
|
|
41281
|
+
"l1",
|
|
41282
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41283
|
+
manifest.body.identity_binding.identity_id,
|
|
41284
|
+
{
|
|
41285
|
+
outcome: "mismatch",
|
|
41286
|
+
identifier: manifestDidWeb.identifier,
|
|
41287
|
+
authority_host: authorityHost,
|
|
41288
|
+
resolved_url: resolution.url
|
|
41289
|
+
}
|
|
41290
|
+
);
|
|
41291
|
+
await opts.auditLog.flush();
|
|
41292
|
+
throw new ExitBundleImportError(
|
|
41293
|
+
"did_web_mismatch",
|
|
41294
|
+
`did:web cross-check failed: the DID Document at ${resolution.url} resolved successfully, but the verificationMethod public key did not match the manifest's claimed fortress_master_pubkey. The bundle's claimed origin (${manifestDidWeb.identifier}) is inconsistent with the published DID Document. To proceed anyway with the manifest signature alone, re-run import with --skip-did-web-verify.`
|
|
41295
|
+
);
|
|
41296
|
+
} else {
|
|
41297
|
+
opts.auditLog.append(
|
|
41298
|
+
"l1",
|
|
41299
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41300
|
+
manifest.body.identity_binding.identity_id,
|
|
41301
|
+
{
|
|
41302
|
+
outcome: "resolution_failure",
|
|
41303
|
+
failure: resolution.failure,
|
|
41304
|
+
identifier: manifestDidWeb.identifier,
|
|
41305
|
+
authority_host: authorityHost,
|
|
41306
|
+
resolved_url: resolution.url
|
|
41307
|
+
}
|
|
41308
|
+
);
|
|
41309
|
+
importWarnings.push(
|
|
41310
|
+
`did:web resolution failed (${resolution.failure}): ${resolution.message}. Import proceeded with manifest-signature verification alone; recognition-layer cross-check was skipped. Re-run with --did-web-allowed-host=<host> to enable resolution, or --skip-did-web-verify to skip deliberately.`
|
|
41311
|
+
);
|
|
41312
|
+
}
|
|
41313
|
+
} else if (manifestDidWeb !== void 0 && opts.skipDidWebVerify) {
|
|
41314
|
+
opts.auditLog.append(
|
|
41315
|
+
"l1",
|
|
41316
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41317
|
+
manifest.body.identity_binding.identity_id,
|
|
41318
|
+
{
|
|
41319
|
+
outcome: "skipped",
|
|
41320
|
+
identifier: manifestDidWeb.identifier,
|
|
41321
|
+
authority_host: manifestDidWeb.authority_host
|
|
41322
|
+
}
|
|
41323
|
+
);
|
|
41324
|
+
}
|
|
40696
41325
|
const identityArtifact = await loadExitArtifact(
|
|
40697
41326
|
opts.bundleDir,
|
|
40698
41327
|
manifest,
|
|
@@ -40757,7 +41386,7 @@ async function importExitBundle(opts) {
|
|
|
40757
41386
|
unverifiable_attestations: verification.reputation?.unverifiable_attestations ?? 0
|
|
40758
41387
|
},
|
|
40759
41388
|
staged_artifacts: [],
|
|
40760
|
-
warnings: verification.warnings,
|
|
41389
|
+
warnings: [...verification.warnings, ...importWarnings],
|
|
40761
41390
|
unsupported_artifacts: verification.unsupported_artifacts
|
|
40762
41391
|
};
|
|
40763
41392
|
}
|
|
@@ -40924,7 +41553,7 @@ async function importExitBundle(opts) {
|
|
|
40924
41553
|
state: stateResult,
|
|
40925
41554
|
reputation: reputationResult,
|
|
40926
41555
|
staged_artifacts: stagedArtifacts,
|
|
40927
|
-
warnings: verification.warnings,
|
|
41556
|
+
warnings: [...verification.warnings, ...importWarnings],
|
|
40928
41557
|
unsupported_artifacts: verification.unsupported_artifacts
|
|
40929
41558
|
};
|
|
40930
41559
|
}
|
|
@@ -41147,6 +41776,26 @@ ${policyErr.message}
|
|
|
41147
41776
|
}
|
|
41148
41777
|
throw policyErr;
|
|
41149
41778
|
}
|
|
41779
|
+
const includeDidWebFlag = flagValue(argv, "--include-did-web");
|
|
41780
|
+
const includeDidWebDisabled = includeDidWebFlag === "false";
|
|
41781
|
+
const didWebIdentifier = flagValue(argv, "--did-web");
|
|
41782
|
+
const didWebAuthorityHost = flagValue(argv, "--did-web-authority-host");
|
|
41783
|
+
const didWebPublishedAt = flagValue(argv, "--did-web-published-at");
|
|
41784
|
+
let exportDidWeb;
|
|
41785
|
+
if (!includeDidWebDisabled && didWebIdentifier !== void 0) {
|
|
41786
|
+
if (didWebAuthorityHost === void 0) {
|
|
41787
|
+
write(
|
|
41788
|
+
err,
|
|
41789
|
+
"Error: --did-web requires --did-web-authority-host=<host>\n"
|
|
41790
|
+
);
|
|
41791
|
+
return 2;
|
|
41792
|
+
}
|
|
41793
|
+
exportDidWeb = {
|
|
41794
|
+
identifier: didWebIdentifier,
|
|
41795
|
+
authority_host: didWebAuthorityHost,
|
|
41796
|
+
...didWebPublishedAt !== void 0 ? { published_at: didWebPublishedAt } : {}
|
|
41797
|
+
};
|
|
41798
|
+
}
|
|
41150
41799
|
const result = await exportExitBundle({
|
|
41151
41800
|
bundleDir: outDir,
|
|
41152
41801
|
storage: ctx.storage,
|
|
@@ -41158,7 +41807,8 @@ ${policyErr.message}
|
|
|
41158
41807
|
config,
|
|
41159
41808
|
stateStoragePath: ctx.stateStoragePath,
|
|
41160
41809
|
stateNamespaces: repeatedFlagValues(argv, "--state-namespace"),
|
|
41161
|
-
keySource: ctx.keySource
|
|
41810
|
+
keySource: ctx.keySource,
|
|
41811
|
+
...exportDidWeb !== void 0 ? { didWeb: exportDidWeb } : {}
|
|
41162
41812
|
});
|
|
41163
41813
|
if (json) write(out, JSON.stringify(result, null, 2) + "\n");
|
|
41164
41814
|
else {
|
|
@@ -41236,6 +41886,11 @@ ${policyErr.message}
|
|
|
41236
41886
|
write(err, "--conflict must be skip, overwrite, or version\n");
|
|
41237
41887
|
return 2;
|
|
41238
41888
|
}
|
|
41889
|
+
const didWebAllowedHosts = repeatedFlagValues(
|
|
41890
|
+
argv,
|
|
41891
|
+
"--did-web-allowed-host"
|
|
41892
|
+
);
|
|
41893
|
+
const skipDidWebVerify = hasFlag(argv, "--skip-did-web-verify");
|
|
41239
41894
|
let result;
|
|
41240
41895
|
try {
|
|
41241
41896
|
result = await importExitBundle({
|
|
@@ -41251,7 +41906,9 @@ ${policyErr.message}
|
|
|
41251
41906
|
conflictResolution: conflict,
|
|
41252
41907
|
sourcePassphrase: flagValue(argv, "--source-passphrase"),
|
|
41253
41908
|
sourceRecoveryKey: flagValue(argv, "--source-recovery-key"),
|
|
41254
|
-
destinationSignerIdentityId: flagValue(argv, "--destination-identity-id")
|
|
41909
|
+
destinationSignerIdentityId: flagValue(argv, "--destination-identity-id"),
|
|
41910
|
+
...didWebAllowedHosts.length > 0 ? { didWebAllowedHosts } : {},
|
|
41911
|
+
skipDidWebVerify
|
|
41255
41912
|
});
|
|
41256
41913
|
} catch (e) {
|
|
41257
41914
|
if (e instanceof InvalidExitBundleError) {
|
|
@@ -41988,12 +42645,14 @@ ${err.message}
|
|
|
41988
42645
|
fortressId: fortressIdForAggregator
|
|
41989
42646
|
});
|
|
41990
42647
|
const handoffEventBridge = new HandoffEventBridge();
|
|
42648
|
+
const workflowStateTracker = new WorkflowStateTracker();
|
|
41991
42649
|
if (dashboard) {
|
|
41992
42650
|
dashboard.setHandoffLog({
|
|
41993
42651
|
handoffLog,
|
|
41994
42652
|
eventBridge: handoffEventBridge,
|
|
41995
42653
|
auditLog,
|
|
41996
|
-
operatorId: aggregatorIdentityId
|
|
42654
|
+
operatorId: aggregatorIdentityId,
|
|
42655
|
+
workflowStateTracker
|
|
41997
42656
|
});
|
|
41998
42657
|
}
|
|
41999
42658
|
const policyTools = createPrincipalPolicyTools(policy, baseline, auditLog);
|