@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.js
CHANGED
|
@@ -16700,8 +16700,30 @@ function crossHarnessSummary(details, sender) {
|
|
|
16700
16700
|
return `${sender} -> operator approval`;
|
|
16701
16701
|
}
|
|
16702
16702
|
var COORDINATION_VIEW_AUDIT_OPS = {
|
|
16703
|
+
/** v1.3 Omega-1: operator opened the chronological handoff list. */
|
|
16703
16704
|
VIEW_OPENED: "operator_coordination_view_opened",
|
|
16704
|
-
|
|
16705
|
+
/** v1.3 Omega-1: operator drilled into a single handoff for detail. */
|
|
16706
|
+
ENTRY_DRILLED: "operator_handoff_entry_drilled",
|
|
16707
|
+
/**
|
|
16708
|
+
* v1.3 Omega-3: operator opened the Workflows sibling-view (list of
|
|
16709
|
+
* multi-handoff workflows grouped by `workflow-grouper`). Mirrors
|
|
16710
|
+
* VIEW_OPENED's shape so the dashboard activity feed can group both
|
|
16711
|
+
* as "operator coordination surfaces."
|
|
16712
|
+
*/
|
|
16713
|
+
WORKFLOW_VIEW_OPENED: "operator_workflow_view_opened",
|
|
16714
|
+
/**
|
|
16715
|
+
* v1.3 Omega-3: operator drilled into a single workflow for its
|
|
16716
|
+
* timeline + member-handoffs detail. Mirrors ENTRY_DRILLED's shape.
|
|
16717
|
+
*/
|
|
16718
|
+
WORKFLOW_DRILLED: "operator_workflow_drilled",
|
|
16719
|
+
/**
|
|
16720
|
+
* v1.3 Omega-3: server-side state transition observed on a
|
|
16721
|
+
* workflow (e.g., in_progress -> stalled). Emitted by the route
|
|
16722
|
+
* layer after the state tracker diffs against its prior snapshot.
|
|
16723
|
+
* Distinct from the operator-action events above: this records what
|
|
16724
|
+
* the workflow itself is doing, not what the operator clicked.
|
|
16725
|
+
*/
|
|
16726
|
+
WORKFLOW_STATE_CHANGED: "coordination_workflow_state_changed"
|
|
16705
16727
|
};
|
|
16706
16728
|
|
|
16707
16729
|
// src/coordination/context-transfer-extractor.ts
|
|
@@ -16973,10 +16995,123 @@ function categoryFromPolicyRuleId(ruleId) {
|
|
|
16973
16995
|
var CONTEXT_TRANSFER_AUDIT_OPS = {
|
|
16974
16996
|
DECODED: "operator_handoff_context_transfer_decoded"
|
|
16975
16997
|
};
|
|
16998
|
+
var HEURISTIC_WINDOW_MS = 5 * 60 * 1e3;
|
|
16999
|
+
var STALL_THRESHOLD_MS = 2 * 60 * 60 * 1e3;
|
|
17000
|
+
var CYCLE_COMPLETION_MIN_HOPS = 2;
|
|
17001
|
+
function groupHandoffsIntoWorkflows(handoffs, opts) {
|
|
17002
|
+
if (handoffs.length === 0) return [];
|
|
17003
|
+
const now = opts?.now ?? /* @__PURE__ */ new Date();
|
|
17004
|
+
const linkedGroups = /* @__PURE__ */ new Map();
|
|
17005
|
+
const unlinked = [];
|
|
17006
|
+
for (const h of handoffs) {
|
|
17007
|
+
if (h.workflow_link !== null && h.workflow_link.length > 0) {
|
|
17008
|
+
let bucket = linkedGroups.get(h.workflow_link);
|
|
17009
|
+
if (!bucket) {
|
|
17010
|
+
bucket = [];
|
|
17011
|
+
linkedGroups.set(h.workflow_link, bucket);
|
|
17012
|
+
}
|
|
17013
|
+
bucket.push(h);
|
|
17014
|
+
} else {
|
|
17015
|
+
unlinked.push(h);
|
|
17016
|
+
}
|
|
17017
|
+
}
|
|
17018
|
+
const sortedUnlinked = [...unlinked].sort(
|
|
17019
|
+
(a, b) => a.observed_at < b.observed_at ? -1 : 1
|
|
17020
|
+
);
|
|
17021
|
+
const heuristicChains = [];
|
|
17022
|
+
for (const h of sortedUnlinked) {
|
|
17023
|
+
const joinedIdx = findExtendableChain(heuristicChains, h);
|
|
17024
|
+
if (joinedIdx !== null) {
|
|
17025
|
+
heuristicChains[joinedIdx].push(h);
|
|
17026
|
+
} else {
|
|
17027
|
+
heuristicChains.push([h]);
|
|
17028
|
+
}
|
|
17029
|
+
}
|
|
17030
|
+
const workflows = [];
|
|
17031
|
+
for (const members of linkedGroups.values()) {
|
|
17032
|
+
workflows.push(materialize(members, now));
|
|
17033
|
+
}
|
|
17034
|
+
for (const members of heuristicChains) {
|
|
17035
|
+
workflows.push(materialize(members, now));
|
|
17036
|
+
}
|
|
17037
|
+
workflows.sort(
|
|
17038
|
+
(a, b) => a.last_activity_at < b.last_activity_at ? 1 : -1
|
|
17039
|
+
);
|
|
17040
|
+
return workflows;
|
|
17041
|
+
}
|
|
17042
|
+
function determineWorkflowState(members, now) {
|
|
17043
|
+
if (members.length === 0) return "unknown";
|
|
17044
|
+
const sorted = [...members].sort(
|
|
17045
|
+
(a, b) => a.observed_at < b.observed_at ? -1 : 1
|
|
17046
|
+
);
|
|
17047
|
+
const last = sorted[sorted.length - 1];
|
|
17048
|
+
const root = sorted[0];
|
|
17049
|
+
const lastMs = Date.parse(last.observed_at);
|
|
17050
|
+
if (!Number.isFinite(lastMs)) return "unknown";
|
|
17051
|
+
if (last.target_agent_id === OPERATOR_PSEUDO_AGENT) {
|
|
17052
|
+
return "completed";
|
|
17053
|
+
}
|
|
17054
|
+
if (sorted.length > CYCLE_COMPLETION_MIN_HOPS && last.target_agent_id === root.source_agent_id) {
|
|
17055
|
+
return "completed";
|
|
17056
|
+
}
|
|
17057
|
+
const ageMs = now.getTime() - lastMs;
|
|
17058
|
+
if (ageMs > STALL_THRESHOLD_MS) {
|
|
17059
|
+
return "stalled";
|
|
17060
|
+
}
|
|
17061
|
+
return "in_progress";
|
|
17062
|
+
}
|
|
17063
|
+
function workflowIdFromRoot(rootEntryId) {
|
|
17064
|
+
return createHash("sha256").update(`workflow:${rootEntryId}`).digest("hex").slice(0, 32);
|
|
17065
|
+
}
|
|
17066
|
+
function findExtendableChain(chains, h) {
|
|
17067
|
+
const hMs = Date.parse(h.observed_at);
|
|
17068
|
+
if (!Number.isFinite(hMs)) return null;
|
|
17069
|
+
let bestIdx = null;
|
|
17070
|
+
let bestGapMs = Number.POSITIVE_INFINITY;
|
|
17071
|
+
for (let i = 0; i < chains.length; i += 1) {
|
|
17072
|
+
const chain = chains[i];
|
|
17073
|
+
const last = chain[chain.length - 1];
|
|
17074
|
+
const lastMs = Date.parse(last.observed_at);
|
|
17075
|
+
if (!Number.isFinite(lastMs)) continue;
|
|
17076
|
+
const gapMs = Math.abs(hMs - lastMs);
|
|
17077
|
+
if (gapMs > HEURISTIC_WINDOW_MS) continue;
|
|
17078
|
+
if (!sharesAgent(last, h)) continue;
|
|
17079
|
+
if (gapMs < bestGapMs) {
|
|
17080
|
+
bestGapMs = gapMs;
|
|
17081
|
+
bestIdx = i;
|
|
17082
|
+
}
|
|
17083
|
+
}
|
|
17084
|
+
return bestIdx;
|
|
17085
|
+
}
|
|
17086
|
+
function sharesAgent(a, b) {
|
|
17087
|
+
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;
|
|
17088
|
+
}
|
|
17089
|
+
function materialize(members, now) {
|
|
17090
|
+
const sorted = [...members].sort(
|
|
17091
|
+
(a, b) => a.observed_at < b.observed_at ? -1 : 1
|
|
17092
|
+
);
|
|
17093
|
+
const root = sorted[0];
|
|
17094
|
+
const last = sorted[sorted.length - 1];
|
|
17095
|
+
const involved = /* @__PURE__ */ new Set();
|
|
17096
|
+
for (const h of sorted) {
|
|
17097
|
+
if (h.source_agent_id) involved.add(h.source_agent_id);
|
|
17098
|
+
if (h.target_agent_id) involved.add(h.target_agent_id);
|
|
17099
|
+
}
|
|
17100
|
+
return {
|
|
17101
|
+
workflow_id: workflowIdFromRoot(root.entry_id),
|
|
17102
|
+
root_handoff: root,
|
|
17103
|
+
member_handoffs: sorted,
|
|
17104
|
+
state: determineWorkflowState(sorted, now),
|
|
17105
|
+
started_at: root.observed_at,
|
|
17106
|
+
last_activity_at: last.observed_at,
|
|
17107
|
+
involved_agents: [...involved].sort()
|
|
17108
|
+
};
|
|
17109
|
+
}
|
|
16976
17110
|
|
|
16977
17111
|
// src/coordination/handoff-routes.ts
|
|
16978
17112
|
var COORDINATION_API_PREFIX = "/api/coordination";
|
|
16979
17113
|
var COORDINATION_HANDOFFS_PREFIX = "/api/coordination/handoffs";
|
|
17114
|
+
var COORDINATION_WORKFLOWS_PREFIX = "/api/coordination/workflows";
|
|
16980
17115
|
var COORDINATION_LIST_DEFAULT_LIMIT = 50;
|
|
16981
17116
|
var COORDINATION_LIST_MAX_LIMIT = 500;
|
|
16982
17117
|
var HandoffEventBridge = class {
|
|
@@ -17015,6 +17150,101 @@ function matchEntryRoute2(path) {
|
|
|
17015
17150
|
if (rest.includes("/")) return null;
|
|
17016
17151
|
return { entryId: decodeURIComponent(rest) };
|
|
17017
17152
|
}
|
|
17153
|
+
function matchWorkflowRoute(path) {
|
|
17154
|
+
const prefix = `${COORDINATION_WORKFLOWS_PREFIX}/`;
|
|
17155
|
+
if (!path.startsWith(prefix)) return null;
|
|
17156
|
+
const rest = path.slice(prefix.length);
|
|
17157
|
+
if (rest.length === 0 || rest === "stream") return null;
|
|
17158
|
+
if (rest.includes("/")) return null;
|
|
17159
|
+
return { workflowId: decodeURIComponent(rest) };
|
|
17160
|
+
}
|
|
17161
|
+
async function computeWorkflowsAndTrackTransitions(deps) {
|
|
17162
|
+
const handoffs = await deps.handoffLog.query({ limit: 500 });
|
|
17163
|
+
const workflows = groupHandoffsIntoWorkflows(handoffs, {
|
|
17164
|
+
...deps.now !== void 0 ? { now: deps.now() } : {}
|
|
17165
|
+
});
|
|
17166
|
+
const transitions = deps.workflowStateTracker ? deps.workflowStateTracker.observe(workflows) : [];
|
|
17167
|
+
for (const change of transitions) {
|
|
17168
|
+
deps.auditLog.append(
|
|
17169
|
+
"l2",
|
|
17170
|
+
COORDINATION_VIEW_AUDIT_OPS.WORKFLOW_STATE_CHANGED,
|
|
17171
|
+
deps.operatorId,
|
|
17172
|
+
{
|
|
17173
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17174
|
+
workflow_id: change.workflow_id,
|
|
17175
|
+
previous_state: change.previous_state,
|
|
17176
|
+
new_state: change.new_state
|
|
17177
|
+
}
|
|
17178
|
+
);
|
|
17179
|
+
}
|
|
17180
|
+
return { workflows, transitions };
|
|
17181
|
+
}
|
|
17182
|
+
function filterWorkflowList(workflows, opts) {
|
|
17183
|
+
let filtered = workflows;
|
|
17184
|
+
if (opts.state) {
|
|
17185
|
+
filtered = filtered.filter((w) => w.state === opts.state);
|
|
17186
|
+
}
|
|
17187
|
+
if (opts.agentId) {
|
|
17188
|
+
filtered = filtered.filter((w) => w.involved_agents.includes(opts.agentId));
|
|
17189
|
+
}
|
|
17190
|
+
if (opts.since) {
|
|
17191
|
+
filtered = filtered.filter((w) => w.last_activity_at >= opts.since);
|
|
17192
|
+
}
|
|
17193
|
+
return filtered.slice(0, opts.limit);
|
|
17194
|
+
}
|
|
17195
|
+
function isWorkflowState(value) {
|
|
17196
|
+
return value === "in_progress" || value === "completed" || value === "stalled" || value === "unknown";
|
|
17197
|
+
}
|
|
17198
|
+
async function handleWorkflowStream(deps, res) {
|
|
17199
|
+
res.writeHead(200, {
|
|
17200
|
+
"Content-Type": "text/event-stream",
|
|
17201
|
+
"Cache-Control": "no-cache, no-transform",
|
|
17202
|
+
Connection: "keep-alive",
|
|
17203
|
+
"X-Accel-Buffering": "no"
|
|
17204
|
+
});
|
|
17205
|
+
const initial = await computeWorkflowsAndTrackTransitions(deps);
|
|
17206
|
+
res.write(
|
|
17207
|
+
`event: workflow_snapshot
|
|
17208
|
+
data: ${JSON.stringify({ workflows: initial.workflows })}
|
|
17209
|
+
|
|
17210
|
+
`
|
|
17211
|
+
);
|
|
17212
|
+
if (initial.transitions.length > 0) {
|
|
17213
|
+
res.write(
|
|
17214
|
+
`event: workflow_state_changed
|
|
17215
|
+
data: ${JSON.stringify({ transitions: initial.transitions })}
|
|
17216
|
+
|
|
17217
|
+
`
|
|
17218
|
+
);
|
|
17219
|
+
}
|
|
17220
|
+
const unsubscribe = deps.events.subscribe(() => {
|
|
17221
|
+
void (async () => {
|
|
17222
|
+
try {
|
|
17223
|
+
const tick = await computeWorkflowsAndTrackTransitions(deps);
|
|
17224
|
+
res.write(
|
|
17225
|
+
`event: workflow_snapshot
|
|
17226
|
+
data: ${JSON.stringify({ workflows: tick.workflows })}
|
|
17227
|
+
|
|
17228
|
+
`
|
|
17229
|
+
);
|
|
17230
|
+
if (tick.transitions.length > 0) {
|
|
17231
|
+
res.write(
|
|
17232
|
+
`event: workflow_state_changed
|
|
17233
|
+
data: ${JSON.stringify({ transitions: tick.transitions })}
|
|
17234
|
+
|
|
17235
|
+
`
|
|
17236
|
+
);
|
|
17237
|
+
}
|
|
17238
|
+
} catch {
|
|
17239
|
+
}
|
|
17240
|
+
})();
|
|
17241
|
+
});
|
|
17242
|
+
const cleanup = () => {
|
|
17243
|
+
unsubscribe();
|
|
17244
|
+
};
|
|
17245
|
+
res.on("close", cleanup);
|
|
17246
|
+
res.on("error", cleanup);
|
|
17247
|
+
}
|
|
17018
17248
|
async function handleStream3(deps, res) {
|
|
17019
17249
|
res.writeHead(200, {
|
|
17020
17250
|
"Content-Type": "text/event-stream",
|
|
@@ -17098,6 +17328,67 @@ async function handleCoordinationRoute(deps, req, res) {
|
|
|
17098
17328
|
writeJSON6(res, 200, { ok: true, data: { entries } });
|
|
17099
17329
|
return true;
|
|
17100
17330
|
}
|
|
17331
|
+
if (method === "GET" && path === `${COORDINATION_WORKFLOWS_PREFIX}/stream`) {
|
|
17332
|
+
await handleWorkflowStream(deps, res);
|
|
17333
|
+
return true;
|
|
17334
|
+
}
|
|
17335
|
+
if (method === "GET" && path === COORDINATION_WORKFLOWS_PREFIX) {
|
|
17336
|
+
const limit = parseLimit4(
|
|
17337
|
+
url.searchParams.get("limit"),
|
|
17338
|
+
COORDINATION_LIST_DEFAULT_LIMIT,
|
|
17339
|
+
COORDINATION_LIST_MAX_LIMIT
|
|
17340
|
+
);
|
|
17341
|
+
const rawState = url.searchParams.get("state");
|
|
17342
|
+
const state = rawState && isWorkflowState(rawState) ? rawState : void 0;
|
|
17343
|
+
const since = url.searchParams.get("since") ?? void 0;
|
|
17344
|
+
const agentId = url.searchParams.get("agent_id") ?? void 0;
|
|
17345
|
+
const computed = await computeWorkflowsAndTrackTransitions(deps);
|
|
17346
|
+
const filtered = filterWorkflowList(computed.workflows, {
|
|
17347
|
+
...state !== void 0 ? { state } : {},
|
|
17348
|
+
...agentId !== void 0 ? { agentId } : {},
|
|
17349
|
+
...since !== void 0 ? { since } : {},
|
|
17350
|
+
limit
|
|
17351
|
+
});
|
|
17352
|
+
deps.auditLog.append(
|
|
17353
|
+
"l2",
|
|
17354
|
+
COORDINATION_VIEW_AUDIT_OPS.WORKFLOW_VIEW_OPENED,
|
|
17355
|
+
deps.operatorId,
|
|
17356
|
+
{
|
|
17357
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17358
|
+
result_count: filtered.length,
|
|
17359
|
+
...state !== void 0 ? { state } : {},
|
|
17360
|
+
...agentId !== void 0 ? { agent_id: agentId } : {},
|
|
17361
|
+
...since !== void 0 ? { since } : {}
|
|
17362
|
+
}
|
|
17363
|
+
);
|
|
17364
|
+
writeJSON6(res, 200, { ok: true, data: { workflows: filtered } });
|
|
17365
|
+
return true;
|
|
17366
|
+
}
|
|
17367
|
+
const workflowMatch = matchWorkflowRoute(path);
|
|
17368
|
+
if (method === "GET" && workflowMatch) {
|
|
17369
|
+
const computed = await computeWorkflowsAndTrackTransitions(deps);
|
|
17370
|
+
const wf = computed.workflows.find(
|
|
17371
|
+
(w) => w.workflow_id === workflowMatch.workflowId
|
|
17372
|
+
);
|
|
17373
|
+
if (!wf) {
|
|
17374
|
+
writeJSON6(res, 404, { ok: false, error: "not_found" });
|
|
17375
|
+
return true;
|
|
17376
|
+
}
|
|
17377
|
+
deps.auditLog.append(
|
|
17378
|
+
"l2",
|
|
17379
|
+
COORDINATION_VIEW_AUDIT_OPS.WORKFLOW_DRILLED,
|
|
17380
|
+
deps.operatorId,
|
|
17381
|
+
{
|
|
17382
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17383
|
+
workflow_id: wf.workflow_id,
|
|
17384
|
+
state: wf.state,
|
|
17385
|
+
member_count: wf.member_handoffs.length,
|
|
17386
|
+
involved_agent_count: wf.involved_agents.length
|
|
17387
|
+
}
|
|
17388
|
+
);
|
|
17389
|
+
writeJSON6(res, 200, { ok: true, data: { workflow: wf } });
|
|
17390
|
+
return true;
|
|
17391
|
+
}
|
|
17101
17392
|
const entryMatch = matchEntryRoute2(path);
|
|
17102
17393
|
if (method === "GET" && entryMatch) {
|
|
17103
17394
|
const detail = await deps.handoffLog.getEntry(entryMatch.entryId);
|
|
@@ -17239,6 +17530,8 @@ var DashboardApprovalChannel = class {
|
|
|
17239
17530
|
*/
|
|
17240
17531
|
handoffLog = null;
|
|
17241
17532
|
handoffEventBridge = null;
|
|
17533
|
+
handoffContextTransfer = null;
|
|
17534
|
+
workflowStateTracker = null;
|
|
17242
17535
|
handoffAuditLog = null;
|
|
17243
17536
|
handoffOperatorId = null;
|
|
17244
17537
|
constructor(config) {
|
|
@@ -17319,6 +17612,8 @@ var DashboardApprovalChannel = class {
|
|
|
17319
17612
|
this.handoffEventBridge = opts.eventBridge ?? null;
|
|
17320
17613
|
this.handoffAuditLog = opts.auditLog ?? null;
|
|
17321
17614
|
this.handoffOperatorId = opts.operatorId ?? null;
|
|
17615
|
+
this.handoffContextTransfer = opts.contextTransfer ?? null;
|
|
17616
|
+
this.workflowStateTracker = opts.workflowStateTracker ?? null;
|
|
17322
17617
|
}
|
|
17323
17618
|
/**
|
|
17324
17619
|
* v1.3 WP-V1.3-10 dispatch entry point. Called from `handleRequest`
|
|
@@ -17376,7 +17671,9 @@ var DashboardApprovalChannel = class {
|
|
|
17376
17671
|
handoffLog: this.handoffLog,
|
|
17377
17672
|
auditLog: this.handoffAuditLog,
|
|
17378
17673
|
operatorId: this.handoffOperatorId ?? this.identityManager?.getPrimaryIdentityId() ?? "operator_dashboard",
|
|
17379
|
-
events: this.handoffEventBridge
|
|
17674
|
+
events: this.handoffEventBridge,
|
|
17675
|
+
...this.handoffContextTransfer !== null ? { contextTransfer: this.handoffContextTransfer } : {},
|
|
17676
|
+
...this.workflowStateTracker !== null ? { workflowStateTracker: this.workflowStateTracker } : {}
|
|
17380
17677
|
},
|
|
17381
17678
|
req,
|
|
17382
17679
|
res
|
|
@@ -21909,6 +22206,70 @@ function classifierSpecificAuditOp(classifierId) {
|
|
|
21909
22206
|
return null;
|
|
21910
22207
|
}
|
|
21911
22208
|
|
|
22209
|
+
// src/coordination/workflow-state-tracker.ts
|
|
22210
|
+
var WorkflowStateTracker = class {
|
|
22211
|
+
states = /* @__PURE__ */ new Map();
|
|
22212
|
+
now;
|
|
22213
|
+
constructor(opts) {
|
|
22214
|
+
this.now = opts?.now ?? (() => /* @__PURE__ */ new Date());
|
|
22215
|
+
}
|
|
22216
|
+
/**
|
|
22217
|
+
* Diff the supplied workflow list against the last-observed states.
|
|
22218
|
+
* Returns the set of transitions detected this call; the tracker
|
|
22219
|
+
* mutates its internal map to reflect the new states.
|
|
22220
|
+
*
|
|
22221
|
+
* Transitions emitted:
|
|
22222
|
+
* - First observation of a workflow (`previous_state` is the
|
|
22223
|
+
* sentinel `unobserved`). Lets the route handler audit-emit
|
|
22224
|
+
* the initial state so the operator sees workflows as they
|
|
22225
|
+
* surface, not only when they change.
|
|
22226
|
+
* - Subsequent observation where `previous_state !== new_state`.
|
|
22227
|
+
*/
|
|
22228
|
+
observe(workflows) {
|
|
22229
|
+
const out = [];
|
|
22230
|
+
const observedAt = this.now().toISOString();
|
|
22231
|
+
for (const wf of workflows) {
|
|
22232
|
+
const prior = this.states.get(wf.workflow_id);
|
|
22233
|
+
if (prior === void 0) {
|
|
22234
|
+
out.push({
|
|
22235
|
+
workflow_id: wf.workflow_id,
|
|
22236
|
+
previous_state: "unobserved",
|
|
22237
|
+
new_state: wf.state,
|
|
22238
|
+
observed_at: observedAt
|
|
22239
|
+
});
|
|
22240
|
+
this.states.set(wf.workflow_id, wf.state);
|
|
22241
|
+
continue;
|
|
22242
|
+
}
|
|
22243
|
+
if (prior !== wf.state) {
|
|
22244
|
+
out.push({
|
|
22245
|
+
workflow_id: wf.workflow_id,
|
|
22246
|
+
previous_state: prior,
|
|
22247
|
+
new_state: wf.state,
|
|
22248
|
+
observed_at: observedAt
|
|
22249
|
+
});
|
|
22250
|
+
this.states.set(wf.workflow_id, wf.state);
|
|
22251
|
+
}
|
|
22252
|
+
}
|
|
22253
|
+
return out;
|
|
22254
|
+
}
|
|
22255
|
+
/**
|
|
22256
|
+
* Drop a workflow's recorded state. Surfaced for tests + future
|
|
22257
|
+
* "operator dismissed this workflow" affordance; not currently
|
|
22258
|
+
* called by the production wiring.
|
|
22259
|
+
*/
|
|
22260
|
+
forget(workflowId) {
|
|
22261
|
+
this.states.delete(workflowId);
|
|
22262
|
+
}
|
|
22263
|
+
/** Reset the tracker. Tests use this between runs. */
|
|
22264
|
+
reset() {
|
|
22265
|
+
this.states.clear();
|
|
22266
|
+
}
|
|
22267
|
+
/** Read-only view of the current snapshot. Useful for diagnostics. */
|
|
22268
|
+
snapshot() {
|
|
22269
|
+
return new Map(this.states);
|
|
22270
|
+
}
|
|
22271
|
+
};
|
|
22272
|
+
|
|
21912
22273
|
// src/sentinel/sentinel.ts
|
|
21913
22274
|
var Sentinel = class {
|
|
21914
22275
|
/**
|
|
@@ -25142,7 +25503,7 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
25142
25503
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25143
25504
|
const canonicalBytes = canonicalize2(outcome);
|
|
25144
25505
|
const canonicalString = new TextDecoder().decode(canonicalBytes);
|
|
25145
|
-
const
|
|
25506
|
+
const sha25612 = createCommitment(canonicalString);
|
|
25146
25507
|
let pedersenData;
|
|
25147
25508
|
if (includePedersen && Number.isInteger(outcome.rounds) && outcome.rounds >= 0) {
|
|
25148
25509
|
const pedersen = createPedersenCommitment(outcome.rounds);
|
|
@@ -25154,7 +25515,7 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
25154
25515
|
const commitmentPayload = {
|
|
25155
25516
|
bridge_commitment_id: commitmentId,
|
|
25156
25517
|
session_id: outcome.session_id,
|
|
25157
|
-
sha256_commitment:
|
|
25518
|
+
sha256_commitment: sha25612.commitment,
|
|
25158
25519
|
terms_hash: outcome.terms_hash,
|
|
25159
25520
|
committer_did: identity.did,
|
|
25160
25521
|
committed_at: now,
|
|
@@ -25165,8 +25526,8 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
25165
25526
|
return {
|
|
25166
25527
|
bridge_commitment_id: commitmentId,
|
|
25167
25528
|
session_id: outcome.session_id,
|
|
25168
|
-
sha256_commitment:
|
|
25169
|
-
blinding_factor:
|
|
25529
|
+
sha256_commitment: sha25612.commitment,
|
|
25530
|
+
blinding_factor: sha25612.blinding_factor,
|
|
25170
25531
|
committer_did: identity.did,
|
|
25171
25532
|
signature: toBase64url(signature),
|
|
25172
25533
|
pedersen_commitment: pedersenData,
|
|
@@ -39717,6 +40078,150 @@ var EXIT_BUNDLE_ARTIFACT_KINDS = [
|
|
|
39717
40078
|
"placeholder_vault_metadata"
|
|
39718
40079
|
];
|
|
39719
40080
|
|
|
40081
|
+
// src/recognition/did-web.ts
|
|
40082
|
+
init_encoding();
|
|
40083
|
+
init_hashing();
|
|
40084
|
+
var DEFAULT_TIMEOUT_MS4 = 5e3;
|
|
40085
|
+
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;
|
|
40086
|
+
async function resolveDidWeb(did, opts) {
|
|
40087
|
+
const parsed = parseDidWeb(did);
|
|
40088
|
+
const url = didToUrl(parsed);
|
|
40089
|
+
if (!opts.allowed_hosts.includes(parsed.authority_host)) {
|
|
40090
|
+
return {
|
|
40091
|
+
ok: false,
|
|
40092
|
+
failure: "host_not_allowed",
|
|
40093
|
+
message: `did-web: authority_host '${parsed.authority_host}' is not in the operator's allowed_hosts allowlist; resolution refused (no-outbound-by-default)`,
|
|
40094
|
+
url
|
|
40095
|
+
};
|
|
40096
|
+
}
|
|
40097
|
+
const timeoutMs = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS4;
|
|
40098
|
+
const fetcher = opts.fetcher ?? defaultFetcher;
|
|
40099
|
+
const controller = new AbortController();
|
|
40100
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
40101
|
+
let response;
|
|
40102
|
+
try {
|
|
40103
|
+
response = await fetcher(url, { signal: controller.signal });
|
|
40104
|
+
} catch (err) {
|
|
40105
|
+
clearTimeout(timer);
|
|
40106
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40107
|
+
if (controller.signal.aborted) {
|
|
40108
|
+
return {
|
|
40109
|
+
ok: false,
|
|
40110
|
+
failure: "timeout",
|
|
40111
|
+
message: `did-web: resolution exceeded ${timeoutMs}ms`,
|
|
40112
|
+
url
|
|
40113
|
+
};
|
|
40114
|
+
}
|
|
40115
|
+
return {
|
|
40116
|
+
ok: false,
|
|
40117
|
+
failure: "fetch_failed",
|
|
40118
|
+
message: `did-web: fetch error: ${message}`,
|
|
40119
|
+
url
|
|
40120
|
+
};
|
|
40121
|
+
}
|
|
40122
|
+
clearTimeout(timer);
|
|
40123
|
+
if (response.status === 404) {
|
|
40124
|
+
return {
|
|
40125
|
+
ok: false,
|
|
40126
|
+
failure: "not_found",
|
|
40127
|
+
message: `did-web: 404 from authority host`,
|
|
40128
|
+
url
|
|
40129
|
+
};
|
|
40130
|
+
}
|
|
40131
|
+
if (!response.ok) {
|
|
40132
|
+
return {
|
|
40133
|
+
ok: false,
|
|
40134
|
+
failure: "fetch_failed",
|
|
40135
|
+
message: `did-web: authority host returned ${response.status}`,
|
|
40136
|
+
url
|
|
40137
|
+
};
|
|
40138
|
+
}
|
|
40139
|
+
let body;
|
|
40140
|
+
try {
|
|
40141
|
+
body = await response.json();
|
|
40142
|
+
} catch (err) {
|
|
40143
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40144
|
+
return {
|
|
40145
|
+
ok: false,
|
|
40146
|
+
failure: "invalid_json",
|
|
40147
|
+
message: `did-web: invalid JSON: ${message}`,
|
|
40148
|
+
url
|
|
40149
|
+
};
|
|
40150
|
+
}
|
|
40151
|
+
if (!isDidDocument(body, did)) {
|
|
40152
|
+
return {
|
|
40153
|
+
ok: false,
|
|
40154
|
+
failure: "invalid_json",
|
|
40155
|
+
message: `did-web: response body is not a valid DID Document for ${did}`,
|
|
40156
|
+
url
|
|
40157
|
+
};
|
|
40158
|
+
}
|
|
40159
|
+
if (opts.expected_public_key !== void 0) {
|
|
40160
|
+
const expectedX = toBase64url(opts.expected_public_key);
|
|
40161
|
+
const actualX = body.verificationMethod[0]?.publicKeyJwk.x;
|
|
40162
|
+
if (actualX !== expectedX) {
|
|
40163
|
+
return {
|
|
40164
|
+
ok: false,
|
|
40165
|
+
failure: "signature_mismatch",
|
|
40166
|
+
message: `did-web: verificationMethod public key does not match expected key`,
|
|
40167
|
+
url
|
|
40168
|
+
};
|
|
40169
|
+
}
|
|
40170
|
+
}
|
|
40171
|
+
return { ok: true, did_document: body, url };
|
|
40172
|
+
}
|
|
40173
|
+
function parseDidWeb(did) {
|
|
40174
|
+
if (!did.startsWith("did:web:")) {
|
|
40175
|
+
throw new Error(`did-web: '${did}' is not a did:web identifier`);
|
|
40176
|
+
}
|
|
40177
|
+
const rest = did.slice("did:web:".length);
|
|
40178
|
+
const segments = rest.split(":");
|
|
40179
|
+
const authorityHost = segments[0];
|
|
40180
|
+
if (!HOST_RE.test(authorityHost)) {
|
|
40181
|
+
throw new Error(`did-web: '${authorityHost}' is not a valid DNS host`);
|
|
40182
|
+
}
|
|
40183
|
+
const parsed = { authority_host: authorityHost };
|
|
40184
|
+
if (segments.length === 1) return parsed;
|
|
40185
|
+
if (segments.length === 5 && segments[1] === "fortress" && segments[3] === "agent") {
|
|
40186
|
+
parsed.fortress_id = segments[2];
|
|
40187
|
+
parsed.agent_label = segments[4];
|
|
40188
|
+
return parsed;
|
|
40189
|
+
}
|
|
40190
|
+
throw new Error(
|
|
40191
|
+
`did-web: '${did}' does not match the supported shapes (bare did:web:<host> or did:web:<host>:fortress:<fid>:agent:<alabel>)`
|
|
40192
|
+
);
|
|
40193
|
+
}
|
|
40194
|
+
function didToUrl(parsed) {
|
|
40195
|
+
if (parsed.fortress_id === void 0 || parsed.agent_label === void 0) {
|
|
40196
|
+
return `https://${parsed.authority_host}/.well-known/did.json`;
|
|
40197
|
+
}
|
|
40198
|
+
return `https://${parsed.authority_host}/fortress/${parsed.fortress_id}/agent/${parsed.agent_label}/did.json`;
|
|
40199
|
+
}
|
|
40200
|
+
function isDidDocument(value, expectedDid) {
|
|
40201
|
+
if (!value || typeof value !== "object") return false;
|
|
40202
|
+
const v = value;
|
|
40203
|
+
if (v["id"] !== expectedDid) return false;
|
|
40204
|
+
if (!Array.isArray(v["@context"])) return false;
|
|
40205
|
+
const vm = v["verificationMethod"];
|
|
40206
|
+
if (!Array.isArray(vm) || vm.length === 0) return false;
|
|
40207
|
+
const first = vm[0];
|
|
40208
|
+
if (!first || typeof first["id"] !== "string") return false;
|
|
40209
|
+
const jwk = first["publicKeyJwk"];
|
|
40210
|
+
if (!jwk || jwk["kty"] !== "OKP" || jwk["crv"] !== "Ed25519") return false;
|
|
40211
|
+
if (typeof jwk["x"] !== "string") return false;
|
|
40212
|
+
if (!Array.isArray(v["authentication"])) return false;
|
|
40213
|
+
if (!Array.isArray(v["assertionMethod"])) return false;
|
|
40214
|
+
return true;
|
|
40215
|
+
}
|
|
40216
|
+
async function defaultFetcher(url, init) {
|
|
40217
|
+
const response = await fetch(url, init);
|
|
40218
|
+
return {
|
|
40219
|
+
ok: response.ok,
|
|
40220
|
+
status: response.status,
|
|
40221
|
+
json: () => response.json()
|
|
40222
|
+
};
|
|
40223
|
+
}
|
|
40224
|
+
|
|
39720
40225
|
// src/exit/bundle.ts
|
|
39721
40226
|
init_hashing();
|
|
39722
40227
|
init_encoding();
|
|
@@ -40141,6 +40646,11 @@ async function verifyExitBundle(bundleDir, options = {}) {
|
|
|
40141
40646
|
|
|
40142
40647
|
// src/exit/bundle.ts
|
|
40143
40648
|
var ARTIFACT_DIR = "artifacts";
|
|
40649
|
+
var EXIT_BUNDLE_DID_WEB_AUDIT_OPS = {
|
|
40650
|
+
EXPORT_INCLUDED: "exit_bundle_did_web_export_included",
|
|
40651
|
+
IMPORT_VERIFIED: "exit_bundle_did_web_import_verified",
|
|
40652
|
+
AUTHORITY_HOST: "exit_bundle_did_web_authority_host"
|
|
40653
|
+
};
|
|
40144
40654
|
var EXIT_IMPORT_NAMESPACE = "_exit_imports";
|
|
40145
40655
|
var EXIT_PUBLIC_IDENTITIES_NAMESPACE = "_exit_public_identities";
|
|
40146
40656
|
var EXIT_AUDIT_RECEIPTS_NAMESPACE = "_exit_audit_receipts";
|
|
@@ -40432,6 +40942,7 @@ async function exportExitBundle(opts) {
|
|
|
40432
40942
|
"placeholder_vault_metadata"
|
|
40433
40943
|
)
|
|
40434
40944
|
);
|
|
40945
|
+
const didWebBinding = validateExportDidWeb(opts.didWeb);
|
|
40435
40946
|
const body = {
|
|
40436
40947
|
manifest_version: EXIT_BUNDLE_MANIFEST_VERSION,
|
|
40437
40948
|
exported_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -40439,7 +40950,8 @@ async function exportExitBundle(opts) {
|
|
|
40439
40950
|
identity_id: identity.identity_id,
|
|
40440
40951
|
fortress_id: identity.did,
|
|
40441
40952
|
fortress_master_pubkey: identity.public_key,
|
|
40442
|
-
did: identity.did
|
|
40953
|
+
did: identity.did,
|
|
40954
|
+
...didWebBinding !== void 0 ? { did_web: didWebBinding } : {}
|
|
40443
40955
|
},
|
|
40444
40956
|
source_sanctuary_version: opts.config?.version ?? SANCTUARY_VERSION,
|
|
40445
40957
|
artifacts,
|
|
@@ -40461,6 +40973,18 @@ async function exportExitBundle(opts) {
|
|
|
40461
40973
|
};
|
|
40462
40974
|
const manifestBytes = jsonBytes(manifest);
|
|
40463
40975
|
await writeFile(join(bundleDir, "manifest.json"), manifestBytes, { mode: 384 });
|
|
40976
|
+
if (didWebBinding !== void 0) {
|
|
40977
|
+
opts.auditLog.append(
|
|
40978
|
+
"l1",
|
|
40979
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.EXPORT_INCLUDED,
|
|
40980
|
+
identity.identity_id,
|
|
40981
|
+
{
|
|
40982
|
+
approval_id: exportApprovalAuditId,
|
|
40983
|
+
identifier: didWebBinding.identifier,
|
|
40984
|
+
authority_host: didWebBinding.authority_host
|
|
40985
|
+
}
|
|
40986
|
+
);
|
|
40987
|
+
}
|
|
40464
40988
|
await opts.auditLog.flush();
|
|
40465
40989
|
return {
|
|
40466
40990
|
bundle_dir: bundleDir,
|
|
@@ -40472,6 +40996,30 @@ async function exportExitBundle(opts) {
|
|
|
40472
40996
|
]
|
|
40473
40997
|
};
|
|
40474
40998
|
}
|
|
40999
|
+
function validateExportDidWeb(binding) {
|
|
41000
|
+
if (binding === void 0) return void 0;
|
|
41001
|
+
if (!binding.identifier || typeof binding.identifier !== "string") {
|
|
41002
|
+
throw new Error(
|
|
41003
|
+
"exit-bundle: did_web.identifier must be a non-empty did:web URI"
|
|
41004
|
+
);
|
|
41005
|
+
}
|
|
41006
|
+
if (!binding.authority_host || typeof binding.authority_host !== "string") {
|
|
41007
|
+
throw new Error(
|
|
41008
|
+
"exit-bundle: did_web.authority_host must be a non-empty DNS host"
|
|
41009
|
+
);
|
|
41010
|
+
}
|
|
41011
|
+
const parsed = parseDidWeb(binding.identifier);
|
|
41012
|
+
if (parsed.authority_host.toLowerCase() !== binding.authority_host.toLowerCase()) {
|
|
41013
|
+
throw new Error(
|
|
41014
|
+
`exit-bundle: did_web.identifier authority host '${parsed.authority_host}' does not match did_web.authority_host '${binding.authority_host}'`
|
|
41015
|
+
);
|
|
41016
|
+
}
|
|
41017
|
+
return {
|
|
41018
|
+
identifier: binding.identifier,
|
|
41019
|
+
authority_host: binding.authority_host,
|
|
41020
|
+
...binding.published_at !== void 0 ? { published_at: binding.published_at } : {}
|
|
41021
|
+
};
|
|
41022
|
+
}
|
|
40475
41023
|
function publicKeysFromIdentityArtifact(identityArtifact) {
|
|
40476
41024
|
const pubkey = fromBase64url(identityArtifact.bundle.publicKey);
|
|
40477
41025
|
return {
|
|
@@ -40686,6 +41234,87 @@ async function importExitBundle(opts) {
|
|
|
40686
41234
|
};
|
|
40687
41235
|
}
|
|
40688
41236
|
const manifest = await readManifest(opts.bundleDir);
|
|
41237
|
+
const importWarnings = [];
|
|
41238
|
+
const manifestDidWeb = manifest.body.identity_binding.did_web;
|
|
41239
|
+
if (manifestDidWeb !== void 0 && !opts.skipDidWebVerify) {
|
|
41240
|
+
const expectedPublicKey = fromBase64url(
|
|
41241
|
+
manifest.body.identity_binding.fortress_master_pubkey
|
|
41242
|
+
);
|
|
41243
|
+
const resolveOpts = {
|
|
41244
|
+
allowed_hosts: opts.didWebAllowedHosts ?? [],
|
|
41245
|
+
expected_public_key: expectedPublicKey,
|
|
41246
|
+
...opts.didWebFetcher !== void 0 ? { fetcher: opts.didWebFetcher } : {},
|
|
41247
|
+
...opts.didWebTimeoutMs !== void 0 ? { timeout_ms: opts.didWebTimeoutMs } : {}
|
|
41248
|
+
};
|
|
41249
|
+
const resolution = await resolveDidWeb(
|
|
41250
|
+
manifestDidWeb.identifier,
|
|
41251
|
+
resolveOpts
|
|
41252
|
+
);
|
|
41253
|
+
const authorityHost = manifestDidWeb.authority_host;
|
|
41254
|
+
opts.auditLog.append(
|
|
41255
|
+
"l1",
|
|
41256
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.AUTHORITY_HOST,
|
|
41257
|
+
manifest.body.identity_binding.identity_id,
|
|
41258
|
+
{ authority_host: authorityHost, identifier: manifestDidWeb.identifier }
|
|
41259
|
+
);
|
|
41260
|
+
if (resolution.ok) {
|
|
41261
|
+
opts.auditLog.append(
|
|
41262
|
+
"l1",
|
|
41263
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41264
|
+
manifest.body.identity_binding.identity_id,
|
|
41265
|
+
{
|
|
41266
|
+
outcome: "success",
|
|
41267
|
+
identifier: manifestDidWeb.identifier,
|
|
41268
|
+
authority_host: authorityHost,
|
|
41269
|
+
resolved_url: resolution.url
|
|
41270
|
+
}
|
|
41271
|
+
);
|
|
41272
|
+
} else if (resolution.failure === "signature_mismatch") {
|
|
41273
|
+
opts.auditLog.append(
|
|
41274
|
+
"l1",
|
|
41275
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41276
|
+
manifest.body.identity_binding.identity_id,
|
|
41277
|
+
{
|
|
41278
|
+
outcome: "mismatch",
|
|
41279
|
+
identifier: manifestDidWeb.identifier,
|
|
41280
|
+
authority_host: authorityHost,
|
|
41281
|
+
resolved_url: resolution.url
|
|
41282
|
+
}
|
|
41283
|
+
);
|
|
41284
|
+
await opts.auditLog.flush();
|
|
41285
|
+
throw new ExitBundleImportError(
|
|
41286
|
+
"did_web_mismatch",
|
|
41287
|
+
`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.`
|
|
41288
|
+
);
|
|
41289
|
+
} else {
|
|
41290
|
+
opts.auditLog.append(
|
|
41291
|
+
"l1",
|
|
41292
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41293
|
+
manifest.body.identity_binding.identity_id,
|
|
41294
|
+
{
|
|
41295
|
+
outcome: "resolution_failure",
|
|
41296
|
+
failure: resolution.failure,
|
|
41297
|
+
identifier: manifestDidWeb.identifier,
|
|
41298
|
+
authority_host: authorityHost,
|
|
41299
|
+
resolved_url: resolution.url
|
|
41300
|
+
}
|
|
41301
|
+
);
|
|
41302
|
+
importWarnings.push(
|
|
41303
|
+
`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.`
|
|
41304
|
+
);
|
|
41305
|
+
}
|
|
41306
|
+
} else if (manifestDidWeb !== void 0 && opts.skipDidWebVerify) {
|
|
41307
|
+
opts.auditLog.append(
|
|
41308
|
+
"l1",
|
|
41309
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41310
|
+
manifest.body.identity_binding.identity_id,
|
|
41311
|
+
{
|
|
41312
|
+
outcome: "skipped",
|
|
41313
|
+
identifier: manifestDidWeb.identifier,
|
|
41314
|
+
authority_host: manifestDidWeb.authority_host
|
|
41315
|
+
}
|
|
41316
|
+
);
|
|
41317
|
+
}
|
|
40689
41318
|
const identityArtifact = await loadExitArtifact(
|
|
40690
41319
|
opts.bundleDir,
|
|
40691
41320
|
manifest,
|
|
@@ -40750,7 +41379,7 @@ async function importExitBundle(opts) {
|
|
|
40750
41379
|
unverifiable_attestations: verification.reputation?.unverifiable_attestations ?? 0
|
|
40751
41380
|
},
|
|
40752
41381
|
staged_artifacts: [],
|
|
40753
|
-
warnings: verification.warnings,
|
|
41382
|
+
warnings: [...verification.warnings, ...importWarnings],
|
|
40754
41383
|
unsupported_artifacts: verification.unsupported_artifacts
|
|
40755
41384
|
};
|
|
40756
41385
|
}
|
|
@@ -40917,7 +41546,7 @@ async function importExitBundle(opts) {
|
|
|
40917
41546
|
state: stateResult,
|
|
40918
41547
|
reputation: reputationResult,
|
|
40919
41548
|
staged_artifacts: stagedArtifacts,
|
|
40920
|
-
warnings: verification.warnings,
|
|
41549
|
+
warnings: [...verification.warnings, ...importWarnings],
|
|
40921
41550
|
unsupported_artifacts: verification.unsupported_artifacts
|
|
40922
41551
|
};
|
|
40923
41552
|
}
|
|
@@ -41140,6 +41769,26 @@ ${policyErr.message}
|
|
|
41140
41769
|
}
|
|
41141
41770
|
throw policyErr;
|
|
41142
41771
|
}
|
|
41772
|
+
const includeDidWebFlag = flagValue(argv, "--include-did-web");
|
|
41773
|
+
const includeDidWebDisabled = includeDidWebFlag === "false";
|
|
41774
|
+
const didWebIdentifier = flagValue(argv, "--did-web");
|
|
41775
|
+
const didWebAuthorityHost = flagValue(argv, "--did-web-authority-host");
|
|
41776
|
+
const didWebPublishedAt = flagValue(argv, "--did-web-published-at");
|
|
41777
|
+
let exportDidWeb;
|
|
41778
|
+
if (!includeDidWebDisabled && didWebIdentifier !== void 0) {
|
|
41779
|
+
if (didWebAuthorityHost === void 0) {
|
|
41780
|
+
write(
|
|
41781
|
+
err,
|
|
41782
|
+
"Error: --did-web requires --did-web-authority-host=<host>\n"
|
|
41783
|
+
);
|
|
41784
|
+
return 2;
|
|
41785
|
+
}
|
|
41786
|
+
exportDidWeb = {
|
|
41787
|
+
identifier: didWebIdentifier,
|
|
41788
|
+
authority_host: didWebAuthorityHost,
|
|
41789
|
+
...didWebPublishedAt !== void 0 ? { published_at: didWebPublishedAt } : {}
|
|
41790
|
+
};
|
|
41791
|
+
}
|
|
41143
41792
|
const result = await exportExitBundle({
|
|
41144
41793
|
bundleDir: outDir,
|
|
41145
41794
|
storage: ctx.storage,
|
|
@@ -41151,7 +41800,8 @@ ${policyErr.message}
|
|
|
41151
41800
|
config,
|
|
41152
41801
|
stateStoragePath: ctx.stateStoragePath,
|
|
41153
41802
|
stateNamespaces: repeatedFlagValues(argv, "--state-namespace"),
|
|
41154
|
-
keySource: ctx.keySource
|
|
41803
|
+
keySource: ctx.keySource,
|
|
41804
|
+
...exportDidWeb !== void 0 ? { didWeb: exportDidWeb } : {}
|
|
41155
41805
|
});
|
|
41156
41806
|
if (json) write(out, JSON.stringify(result, null, 2) + "\n");
|
|
41157
41807
|
else {
|
|
@@ -41229,6 +41879,11 @@ ${policyErr.message}
|
|
|
41229
41879
|
write(err, "--conflict must be skip, overwrite, or version\n");
|
|
41230
41880
|
return 2;
|
|
41231
41881
|
}
|
|
41882
|
+
const didWebAllowedHosts = repeatedFlagValues(
|
|
41883
|
+
argv,
|
|
41884
|
+
"--did-web-allowed-host"
|
|
41885
|
+
);
|
|
41886
|
+
const skipDidWebVerify = hasFlag(argv, "--skip-did-web-verify");
|
|
41232
41887
|
let result;
|
|
41233
41888
|
try {
|
|
41234
41889
|
result = await importExitBundle({
|
|
@@ -41244,7 +41899,9 @@ ${policyErr.message}
|
|
|
41244
41899
|
conflictResolution: conflict,
|
|
41245
41900
|
sourcePassphrase: flagValue(argv, "--source-passphrase"),
|
|
41246
41901
|
sourceRecoveryKey: flagValue(argv, "--source-recovery-key"),
|
|
41247
|
-
destinationSignerIdentityId: flagValue(argv, "--destination-identity-id")
|
|
41902
|
+
destinationSignerIdentityId: flagValue(argv, "--destination-identity-id"),
|
|
41903
|
+
...didWebAllowedHosts.length > 0 ? { didWebAllowedHosts } : {},
|
|
41904
|
+
skipDidWebVerify
|
|
41248
41905
|
});
|
|
41249
41906
|
} catch (e) {
|
|
41250
41907
|
if (e instanceof InvalidExitBundleError) {
|
|
@@ -41981,12 +42638,14 @@ ${err.message}
|
|
|
41981
42638
|
fortressId: fortressIdForAggregator
|
|
41982
42639
|
});
|
|
41983
42640
|
const handoffEventBridge = new HandoffEventBridge();
|
|
42641
|
+
const workflowStateTracker = new WorkflowStateTracker();
|
|
41984
42642
|
if (dashboard) {
|
|
41985
42643
|
dashboard.setHandoffLog({
|
|
41986
42644
|
handoffLog,
|
|
41987
42645
|
eventBridge: handoffEventBridge,
|
|
41988
42646
|
auditLog,
|
|
41989
|
-
operatorId: aggregatorIdentityId
|
|
42647
|
+
operatorId: aggregatorIdentityId,
|
|
42648
|
+
workflowStateTracker
|
|
41990
42649
|
});
|
|
41991
42650
|
}
|
|
41992
42651
|
const policyTools = createPrincipalPolicyTools(policy, baseline, auditLog);
|