agent-comms 3.9.0 → 3.11.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/bridges/user/tui.js +2 -0
- package/dist/bridges/user/tui.js.map +1 -1
- package/dist/core/agent-registry.d.ts +3 -0
- package/dist/core/agent-registry.d.ts.map +1 -1
- package/dist/core/agent-registry.js +25 -14
- package/dist/core/agent-registry.js.map +1 -1
- package/dist/core/bind-retry.d.ts +10 -0
- package/dist/core/bind-retry.d.ts.map +1 -0
- package/dist/core/bind-retry.js +24 -0
- package/dist/core/bind-retry.js.map +1 -0
- package/dist/core/bridge-mesh.d.ts.map +1 -1
- package/dist/core/bridge-mesh.js +6 -0
- package/dist/core/bridge-mesh.js.map +1 -1
- package/dist/core/bridge.d.ts +7 -0
- package/dist/core/bridge.d.ts.map +1 -1
- package/dist/core/bridge.js +36 -0
- package/dist/core/bridge.js.map +1 -1
- package/dist/core/capability-ask.d.ts +51 -0
- package/dist/core/capability-ask.d.ts.map +1 -0
- package/dist/core/capability-ask.js +92 -0
- package/dist/core/capability-ask.js.map +1 -0
- package/dist/core/dm-token-verification.d.ts +28 -0
- package/dist/core/dm-token-verification.d.ts.map +1 -0
- package/dist/core/dm-token-verification.js +38 -0
- package/dist/core/dm-token-verification.js.map +1 -0
- package/dist/core/gossip-directory.d.ts +4 -1
- package/dist/core/gossip-directory.d.ts.map +1 -1
- package/dist/core/gossip-directory.js +11 -1
- package/dist/core/gossip-directory.js.map +1 -1
- package/dist/core/hub-forwarding.d.ts +13 -0
- package/dist/core/hub-forwarding.d.ts.map +1 -0
- package/dist/core/hub-forwarding.js +30 -0
- package/dist/core/hub-forwarding.js.map +1 -0
- package/dist/core/hub-session.d.ts +14 -4
- package/dist/core/hub-session.d.ts.map +1 -1
- package/dist/core/hub-session.js +44 -12
- package/dist/core/hub-session.js.map +1 -1
- package/dist/core/mcp-registry-retry.d.ts +1 -1
- package/dist/core/mcp-registry-retry.d.ts.map +1 -1
- package/dist/core/mcp-registry-retry.js +15 -2
- package/dist/core/mcp-registry-retry.js.map +1 -1
- package/dist/core/mesh-store-shared.d.ts +5 -1
- package/dist/core/mesh-store-shared.d.ts.map +1 -1
- package/dist/core/mesh-store-shared.js +7 -0
- package/dist/core/mesh-store-shared.js.map +1 -1
- package/dist/core/mesh-store.d.ts +32 -2
- package/dist/core/mesh-store.d.ts.map +1 -1
- package/dist/core/mesh-store.js +50 -4
- package/dist/core/mesh-store.js.map +1 -1
- package/dist/core/peer-lifecycle.d.ts +2 -0
- package/dist/core/peer-lifecycle.d.ts.map +1 -1
- package/dist/core/peer-lifecycle.js +17 -0
- package/dist/core/peer-lifecycle.js.map +1 -1
- package/dist/core/room-lifecycle.d.ts +12 -1
- package/dist/core/room-lifecycle.d.ts.map +1 -1
- package/dist/core/room-lifecycle.js +47 -3
- package/dist/core/room-lifecycle.js.map +1 -1
- package/dist/core/room-messaging.d.ts +2 -2
- package/dist/core/room-messaging.d.ts.map +1 -1
- package/dist/core/room-messaging.js +1 -1
- package/dist/core/room-messaging.js.map +1 -1
- package/dist/core/room-protocol.d.ts +1 -1
- package/dist/core/room-protocol.d.ts.map +1 -1
- package/dist/core/room-protocol.js +17 -1
- package/dist/core/room-protocol.js.map +1 -1
- package/dist/core/tool.d.ts +16 -0
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +44 -0
- package/dist/core/tool.js.map +1 -1
- package/dist/core/types.d.ts +38 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +21 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/user-identity.d.ts +10 -0
- package/dist/core/user-identity.d.ts.map +1 -1
- package/dist/core/user-identity.js +47 -9
- package/dist/core/user-identity.js.map +1 -1
- package/dist/core/wire-mesh-transport.d.ts +5 -3
- package/dist/core/wire-mesh-transport.d.ts.map +1 -1
- package/dist/core/wire-mesh-transport.js +25 -24
- package/dist/core/wire-mesh-transport.js.map +1 -1
- package/dist/test/agent-registry.test.js +56 -0
- package/dist/test/agent-registry.test.js.map +1 -1
- package/dist/test/bind-retry.test.d.ts +2 -0
- package/dist/test/bind-retry.test.d.ts.map +1 -0
- package/dist/test/bind-retry.test.js +42 -0
- package/dist/test/bind-retry.test.js.map +1 -0
- package/dist/test/capability-admission-tool-actions.test.d.ts +5 -0
- package/dist/test/capability-admission-tool-actions.test.d.ts.map +1 -0
- package/dist/test/capability-admission-tool-actions.test.js +198 -0
- package/dist/test/capability-admission-tool-actions.test.js.map +1 -0
- package/dist/test/capability-ask.test.d.ts +2 -0
- package/dist/test/capability-ask.test.d.ts.map +1 -0
- package/dist/test/capability-ask.test.js +300 -0
- package/dist/test/capability-ask.test.js.map +1 -0
- package/dist/test/delivery-engine-delivery.test.js +2 -0
- package/dist/test/delivery-engine-delivery.test.js.map +1 -1
- package/dist/test/delivery-engine-directed-notify.test.js +2 -0
- package/dist/test/delivery-engine-directed-notify.test.js.map +1 -1
- package/dist/test/delivery-engine.test.js +2 -0
- package/dist/test/delivery-engine.test.js.map +1 -1
- package/dist/test/dm-send-capability-admission.integration.test.d.ts +5 -0
- package/dist/test/dm-send-capability-admission.integration.test.d.ts.map +1 -0
- package/dist/test/dm-send-capability-admission.integration.test.js +90 -0
- package/dist/test/dm-send-capability-admission.integration.test.js.map +1 -0
- package/dist/test/dm-token-verification.test.d.ts +5 -0
- package/dist/test/dm-token-verification.test.d.ts.map +1 -0
- package/dist/test/dm-token-verification.test.js +164 -0
- package/dist/test/dm-token-verification.test.js.map +1 -0
- package/dist/test/gateway-forwarding.integration.test.d.ts +7 -0
- package/dist/test/gateway-forwarding.integration.test.d.ts.map +1 -0
- package/dist/test/gateway-forwarding.integration.test.js +185 -0
- package/dist/test/gateway-forwarding.integration.test.js.map +1 -0
- package/dist/test/identity-restart.integration.test.js +6 -0
- package/dist/test/identity-restart.integration.test.js.map +1 -1
- package/dist/test/mcp-registry-retry.test.js +16 -0
- package/dist/test/mcp-registry-retry.test.js.map +1 -1
- package/dist/test/mesh-e2e.integration.test.js +7 -1
- package/dist/test/mesh-e2e.integration.test.js.map +1 -1
- package/dist/test/mesh-smoke.runner.js +4 -1
- package/dist/test/mesh-smoke.runner.js.map +1 -1
- package/dist/test/mesh-store-orchestration.test.js +46 -2
- package/dist/test/mesh-store-orchestration.test.js.map +1 -1
- package/dist/test/peer-lifecycle.test.js +49 -2
- package/dist/test/peer-lifecycle.test.js.map +1 -1
- package/dist/test/room-join-admission.test.js +6 -0
- package/dist/test/room-join-admission.test.js.map +1 -1
- package/dist/test/room-lifecycle-membership.test.js +2 -0
- package/dist/test/room-lifecycle-membership.test.js.map +1 -1
- package/dist/test/room-lifecycle-remote.test.js +2 -0
- package/dist/test/room-lifecycle-remote.test.js.map +1 -1
- package/dist/test/room-lifecycle.test.js +2 -0
- package/dist/test/room-lifecycle.test.js.map +1 -1
- package/dist/test/room-messaging-durable-send.test.js +3 -1
- package/dist/test/room-messaging-durable-send.test.js.map +1 -1
- package/dist/test/room-messaging.test.js +25 -6
- package/dist/test/room-messaging.test.js.map +1 -1
- package/dist/test/room-protocol-admission.test.js +2 -0
- package/dist/test/room-protocol-admission.test.js.map +1 -1
- package/dist/test/room-protocol-notify.test.js +2 -0
- package/dist/test/room-protocol-notify.test.js.map +1 -1
- package/dist/test/room-protocol.test.js +2 -0
- package/dist/test/room-protocol.test.js.map +1 -1
- package/dist/test/room-send-retry.integration.test.js +6 -0
- package/dist/test/room-send-retry.integration.test.js.map +1 -1
- package/dist/test/test-transport.d.ts +1 -1
- package/dist/test/test-transport.d.ts.map +1 -1
- package/dist/test/test-transport.js +10 -2
- package/dist/test/test-transport.js.map +1 -1
- package/dist/test/user-identity.test.js +62 -1
- package/dist/test/user-identity.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CapabilityAskAdmission — the ask tier surfaced at the tool layer (agent-comms#165): the generic, capability-agnostic counterpart to RoomProtocol's own pendingRoomJoins and ConnectionApproval's own pendingInboundConnections, built directly on wire-mesh-core's createCapabilityRequestHandler (the ask primitive agent-comms#164's own bubble-up module already builds on top of, wire-mesh#78). Three tiers exist at the tool layer for whether an action gets a capability token: allow (a held token already answers canGrant), deny (canGrant says no and resolveBubbleUpRoute finds no route to bubble the ask up to either), and ask -- a capability-request held open pending a human decision, which is what this module gives a name and a surface to. A capability-gated verb that lands in that third tier registers a handler built by createAskHandler below instead of failing outright: the request is held open exactly as core/room's own room.join admission holds one open, surfaced to the owning agent as a capability_request delivery event a human can act on (the approval prompt), and the outcome (acceptCapabilityRequest/rejectCapabilityRequest) resolves the held request in place, minting the granted token on acceptance -- the same accept/reject/list shape room_accept/room_reject/room_pending and mesh_accept/mesh_reject/mesh_pending already establish in tool.ts, generalised the way the issue's own framing asks for ("mirror its shape for capability requests generally"). With only one user-principal identity per machine today (agent-comms#160/#161), "the right device or principal" the issue's own framing anticipates routing an ask to is currently always this device -- the same simplification core/room's own single-decision-maker admission already makes; createAskHandler is the integration point a future capability-gated verb (e.g. agent-comms#162's dm:send) registers against its own session to get a ready `(incoming) => Promise<void>` handler backed by this admission's live pending state.
|
|
3
|
+
*/
|
|
4
|
+
import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
|
|
5
|
+
import { createCapabilityRequestHandler } from "wire-mesh-core/domain/capability-request";
|
|
6
|
+
import { nanoid } from "./nanoid.js";
|
|
7
|
+
import { CommsError } from "./store.js";
|
|
8
|
+
export class CapabilityAskAdmission {
|
|
9
|
+
deps;
|
|
10
|
+
pending = new Map();
|
|
11
|
+
constructor(deps) {
|
|
12
|
+
this.deps = deps;
|
|
13
|
+
}
|
|
14
|
+
/** Builds a manage-request handler for one capability's incoming capability-requests that never decides on its own: every not-malformed, not-yet-expired ask is held open and surfaced as a capability_request delivery event, exactly like handleConnectionRequest already does for an inbound mesh connection. */
|
|
15
|
+
createAskHandler(options) {
|
|
16
|
+
return createCapabilityRequestHandler({
|
|
17
|
+
capability: options.capability,
|
|
18
|
+
identity: options.identity,
|
|
19
|
+
clock: options.clock,
|
|
20
|
+
bearerDevice: options.bearerDevice,
|
|
21
|
+
timeoutMs: options.timeoutMs,
|
|
22
|
+
onRequest: (event) => {
|
|
23
|
+
this.handleCapabilityRequest(options.capability, event);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
handleCapabilityRequest(capability, event) {
|
|
28
|
+
const requestId = nanoid();
|
|
29
|
+
this.pending.set(requestId, {
|
|
30
|
+
capability,
|
|
31
|
+
scope: event.scope,
|
|
32
|
+
requesterDevice: event.requesterDevice,
|
|
33
|
+
decide: event.decide,
|
|
34
|
+
});
|
|
35
|
+
const deliveryEvent = {
|
|
36
|
+
type: "capability_request",
|
|
37
|
+
requestId,
|
|
38
|
+
capability,
|
|
39
|
+
scopeKind: event.scope.kind,
|
|
40
|
+
...(event.scope.path !== undefined
|
|
41
|
+
? { scopePath: event.scope.path }
|
|
42
|
+
: {}),
|
|
43
|
+
requesterDevice: deviceIdToHex(event.requesterDevice),
|
|
44
|
+
};
|
|
45
|
+
const peerId = this.deps.getPeerId();
|
|
46
|
+
this.deps.queueDelivery(peerId, deliveryEvent);
|
|
47
|
+
const onDelivery = this.deps.getOnDelivery();
|
|
48
|
+
if (onDelivery) {
|
|
49
|
+
void onDelivery(peerId, deliveryEvent);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Every capability-request currently held open awaiting a human decision. */
|
|
53
|
+
listPendingCapabilityRequests() {
|
|
54
|
+
return [...this.pending.entries()].map(([requestId, ask]) => ({
|
|
55
|
+
requestId,
|
|
56
|
+
capability: ask.capability,
|
|
57
|
+
scopeKind: ask.scope.kind,
|
|
58
|
+
...(ask.scope.path !== undefined ? { scopePath: ask.scope.path } : {}),
|
|
59
|
+
requesterDevice: deviceIdToHex(ask.requesterDevice),
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
/** Approves a pending capability-request, resolving its held decide() with an accept -- wire-mesh-core's own createCapabilityRequestHandler mints the granted token and responds on this admission's behalf. `capability`, when given, grants something narrower than what was originally asked for (the primitive's own CapabilityGrantDecision allows this); absent, the request's own originally-asked-for capability is granted unchanged. */
|
|
63
|
+
async acceptCapabilityRequest(requestId, options) {
|
|
64
|
+
const pending = this.requirePending(requestId);
|
|
65
|
+
this.pending.delete(requestId);
|
|
66
|
+
await pending.decide({
|
|
67
|
+
kind: "accept",
|
|
68
|
+
capability: options.capability ?? pending.capability,
|
|
69
|
+
expires: options.expires,
|
|
70
|
+
...(options.delegationsRemaining !== undefined
|
|
71
|
+
? { delegationsRemaining: options.delegationsRemaining }
|
|
72
|
+
: {}),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** Denies a pending capability-request, optionally with a reason surfaced to the requester in the resulting manage-error's own message field. */
|
|
76
|
+
async rejectCapabilityRequest(requestId, reason) {
|
|
77
|
+
const pending = this.requirePending(requestId);
|
|
78
|
+
this.pending.delete(requestId);
|
|
79
|
+
await pending.decide({
|
|
80
|
+
kind: "reject",
|
|
81
|
+
...(reason !== undefined ? { reason } : {}),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
requirePending(requestId) {
|
|
85
|
+
const pending = this.pending.get(requestId);
|
|
86
|
+
if (pending === undefined) {
|
|
87
|
+
throw new CommsError(`No pending capability request ${requestId}`, "NOT_PENDING");
|
|
88
|
+
}
|
|
89
|
+
return pending;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=capability-ask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-ask.js","sourceRoot":"","sources":["../../src/core/capability-ask.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receiver-side verification of a `dm:send` capability token (agent-comms#162): the receiver-controlled admission list a user principal issues, parallel to room-token-verification.ts's own `room:member` obligations but rooted at the user principal's device-id (user-identity.ts) rather than a room's own owner -- a `dm:send` grant admits a bearer device into THIS user's communication scope across every bridge slot sharing that principal, not into one specific room path. Layered on verifyCapabilityToken the same way verifyRoomToken is: obligations 2/4/5 (bearer match, ordinary token-claims checks, delegations-remaining narrowing) live there already; this module adds the dm:send-specific obligations -- the presented token must actually carry the dm:send capability (not merely happen to share a scope shape), its scope must name this exact user principal, and its delegation chain must root at that same user principal (never at the bearer itself, or self-issued authority would let any sender simply mint its own admission).
|
|
3
|
+
*/
|
|
4
|
+
import { type TokenVerdictReason, type VerifyCapabilityTokenOptions } from "wire-mesh-core/domain/tokens";
|
|
5
|
+
import type { CapabilityToken, DeviceId, TokenClaims } from "wire-mesh-core/generated/protocol";
|
|
6
|
+
/** The one capability a user principal issues to admit a device into its own DM-communication scope (agent-comms#162) -- checked receiver-side wherever an unsolicited DM contact is admitted, the way room-token-verification.ts's ROOM_MEMBER_CAPABILITY gates every ordinary room-membership verb. */
|
|
7
|
+
export declare const DM_SEND_CAPABILITY = "dm:send";
|
|
8
|
+
/** The scope kind a dm:send grant's own scope always carries -- the resource being granted is "communication with this user principal", named by that principal's own device-id, never a specific bridge slot's peer-id, since a dm:send grant admits a bearer into the user's whole communication scope rather than one particular device. */
|
|
9
|
+
export declare const DM_SEND_SCOPE_KIND = "user";
|
|
10
|
+
export type DmSendTokenVerdictReason = TokenVerdictReason | "wrong_capability" | "wrong_scope_kind" | "wrong_scope_path" | "wrong_chain_root";
|
|
11
|
+
export type DmSendTokenVerdict = {
|
|
12
|
+
ok: true;
|
|
13
|
+
claims: TokenClaims;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
reason: DmSendTokenVerdictReason;
|
|
17
|
+
};
|
|
18
|
+
export interface VerifyDmSendTokenOptions extends Omit<VerifyCapabilityTokenOptions, "expectedBearer"> {
|
|
19
|
+
/** The peer identity actually authenticated on the arriving connection -- the requester presenting this token as its own authority to contact userPrincipalDeviceId, never a relay-asserted or gossip-derived value. Mandatory here, matching verifyRoomToken's own mandatory expectedBearer: every dm:send check gates a specific counterparty, unlike verifyCapabilityToken's own optional field for a caller presenting a token to authorise itself. */
|
|
20
|
+
expectedBearer: DeviceId;
|
|
21
|
+
/** This receiving node's own user-principal device-id (user-identity.ts's loadOrCreateUserIdentity), the only issuer a dm:send grant may validly root at. */
|
|
22
|
+
userPrincipalDeviceId: DeviceId;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Verifies a `dm:send` capability token against the receiver's own user-principal identity: the token must carry the dm:send capability, its scope must be kind "user" with path equal to userPrincipalDeviceId exactly, and its delegation chain must root at userPrincipalDeviceId itself -- a self-issued or third-party-issued token naming the right scope by coincidence still fails here, since rootIssuer is checked independently of scope.path.
|
|
26
|
+
*/
|
|
27
|
+
export declare function verifyDmSendToken(token: CapabilityToken, options: Readonly<VerifyDmSendTokenOptions>): Promise<DmSendTokenVerdict>;
|
|
28
|
+
//# sourceMappingURL=dm-token-verification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dm-token-verification.d.ts","sourceRoot":"","sources":["../../src/core/dm-token-verification.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,4BAA4B,EAClC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EACV,eAAe,EACf,QAAQ,EACR,WAAW,EACZ,MAAM,mCAAmC,CAAC;AAE3C,ySAAyS;AACzS,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAE5C,+UAA+U;AAC/U,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC,MAAM,MAAM,wBAAwB,GAChC,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACjC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,wBAAwB,CAAA;CAAE,CAAC;AAEpD,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CACpD,4BAA4B,EAC5B,gBAAgB,CACjB;IACC,2bAA2b;IAC3b,cAAc,EAAE,QAAQ,CAAC;IACzB,6JAA6J;IAC7J,qBAAqB,EAAE,QAAQ,CAAC;CACjC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,QAAQ,CAAC,wBAAwB,CAAC,GAC1C,OAAO,CAAC,kBAAkB,CAAC,CA0B7B"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receiver-side verification of a `dm:send` capability token (agent-comms#162): the receiver-controlled admission list a user principal issues, parallel to room-token-verification.ts's own `room:member` obligations but rooted at the user principal's device-id (user-identity.ts) rather than a room's own owner -- a `dm:send` grant admits a bearer device into THIS user's communication scope across every bridge slot sharing that principal, not into one specific room path. Layered on verifyCapabilityToken the same way verifyRoomToken is: obligations 2/4/5 (bearer match, ordinary token-claims checks, delegations-remaining narrowing) live there already; this module adds the dm:send-specific obligations -- the presented token must actually carry the dm:send capability (not merely happen to share a scope shape), its scope must name this exact user principal, and its delegation chain must root at that same user principal (never at the bearer itself, or self-issued authority would let any sender simply mint its own admission).
|
|
3
|
+
*/
|
|
4
|
+
import { verifyCapabilityToken, } from "wire-mesh-core/domain/tokens";
|
|
5
|
+
import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
|
|
6
|
+
/** The one capability a user principal issues to admit a device into its own DM-communication scope (agent-comms#162) -- checked receiver-side wherever an unsolicited DM contact is admitted, the way room-token-verification.ts's ROOM_MEMBER_CAPABILITY gates every ordinary room-membership verb. */
|
|
7
|
+
export const DM_SEND_CAPABILITY = "dm:send";
|
|
8
|
+
/** The scope kind a dm:send grant's own scope always carries -- the resource being granted is "communication with this user principal", named by that principal's own device-id, never a specific bridge slot's peer-id, since a dm:send grant admits a bearer into the user's whole communication scope rather than one particular device. */
|
|
9
|
+
export const DM_SEND_SCOPE_KIND = "user";
|
|
10
|
+
/**
|
|
11
|
+
* Verifies a `dm:send` capability token against the receiver's own user-principal identity: the token must carry the dm:send capability, its scope must be kind "user" with path equal to userPrincipalDeviceId exactly, and its delegation chain must root at userPrincipalDeviceId itself -- a self-issued or third-party-issued token naming the right scope by coincidence still fails here, since rootIssuer is checked independently of scope.path.
|
|
12
|
+
*/
|
|
13
|
+
export async function verifyDmSendToken(token, options) {
|
|
14
|
+
const verdict = await verifyCapabilityToken(token, {
|
|
15
|
+
identity: options.identity,
|
|
16
|
+
clock: options.clock,
|
|
17
|
+
revocation: options.revocation,
|
|
18
|
+
expectedBearer: options.expectedBearer,
|
|
19
|
+
});
|
|
20
|
+
if (!verdict.ok) {
|
|
21
|
+
return verdict;
|
|
22
|
+
}
|
|
23
|
+
if (verdict.claims.capability !== DM_SEND_CAPABILITY) {
|
|
24
|
+
return { ok: false, reason: "wrong_capability" };
|
|
25
|
+
}
|
|
26
|
+
if (verdict.claims.scope.kind !== DM_SEND_SCOPE_KIND) {
|
|
27
|
+
return { ok: false, reason: "wrong_scope_kind" };
|
|
28
|
+
}
|
|
29
|
+
const expectedPath = deviceIdToHex(options.userPrincipalDeviceId);
|
|
30
|
+
if (verdict.claims.scope.path !== expectedPath) {
|
|
31
|
+
return { ok: false, reason: "wrong_scope_path" };
|
|
32
|
+
}
|
|
33
|
+
if (deviceIdToHex(verdict.rootIssuer) !== expectedPath) {
|
|
34
|
+
return { ok: false, reason: "wrong_chain_root" };
|
|
35
|
+
}
|
|
36
|
+
return { ok: true, claims: verdict.claims };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=dm-token-verification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dm-token-verification.js","sourceRoot":"","sources":["../../src/core/dm-token-verification.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,qBAAqB,GAGtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAOhE,ySAAyS;AACzS,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,CAAC;AAE5C,+UAA+U;AAC/U,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAuBzC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAsB,EACtB,OAA2C;IAE3C,MAAM,OAAO,GAAG,MAAM,qBAAqB,CAAC,KAAK,EAAE;QACjD,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,cAAc,EAAE,OAAO,CAAC,cAAc;KACvC,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,CAAC,UAAU,KAAK,kBAAkB,EAAE,CAAC;QACrD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACnD,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;QACrD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACnD,CAAC;IACD,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAClE,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAC/C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACnD,CAAC;IACD,IAAI,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,YAAY,EAAE,CAAC;QACvD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACnD,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;AAC9C,CAAC"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* gossip-directory — the mesh-wide gossip-directory aggregation WireMeshTransport's own listKnownDevices reads from, plus per-event directory lookups (a specific peer's presence advert). Split out purely to keep wire-mesh-transport.ts under the repo's max-lines cap, the same reason connection-approval.ts, room-router.ts, hub-session.ts, and peer-lifecycle.ts were each split from their own owning file.
|
|
3
3
|
*/
|
|
4
4
|
import type { DirectoryEntry } from "wire-mesh-core/domain/mesh-session";
|
|
5
5
|
import type { PeerAdvert } from "wire-mesh-core/generated/protocol";
|
|
6
|
+
import { AgentStatus } from "./types.js";
|
|
6
7
|
/** Merges one session event's own directory into the mesh-wide knownDevices view (mutated in place), keeping the newer advert (by snapshot-seconds) whenever a device-id is already known from an earlier event or a different session. */
|
|
7
8
|
export declare function mergeKnownDevices(knownDevices: Map<string, PeerAdvert>, directory: readonly DirectoryEntry[]): void;
|
|
9
|
+
/** Reads a presence extension from one specific device's own gossiped self-advert, if this event's directory carries a fresh one for exactly that device-id -- never for any other device-id a multi-hop directory might mention, since only the session's own authenticated peer's advert is that session's business to report. Returns undefined for a missing presence/status key, or a value that isn't a recognised AgentStatus -- an advert simply not participating in this convention, not an error (the same verifier obligation peer-advert's own open extension tail is documented under). */
|
|
10
|
+
export declare function findPresenceAdvert(deviceIdHex: string, directory: readonly DirectoryEntry[]): AgentStatus | undefined;
|
|
8
11
|
//# sourceMappingURL=gossip-directory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gossip-directory.d.ts","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"gossip-directory.d.ts","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,2OAA2O;AAC3O,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACrC,SAAS,EAAE,SAAS,cAAc,EAAE,GACnC,IAAI,CAWN;AAED,ykBAAykB;AACzkB,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,SAAS,cAAc,EAAE,GACnC,WAAW,GAAG,SAAS,CAOzB"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* gossip-directory — the mesh-wide gossip-directory aggregation WireMeshTransport's own listKnownDevices reads from, plus per-event directory lookups (a specific peer's presence advert). Split out purely to keep wire-mesh-transport.ts under the repo's max-lines cap, the same reason connection-approval.ts, room-router.ts, hub-session.ts, and peer-lifecycle.ts were each split from their own owning file.
|
|
3
3
|
*/
|
|
4
4
|
import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
|
|
5
|
+
import { AgentStatus } from "./types.js";
|
|
6
|
+
import { PRESENCE_GOSSIP_KEY } from "./wire-mesh-transport.js";
|
|
5
7
|
/** Merges one session event's own directory into the mesh-wide knownDevices view (mutated in place), keeping the newer advert (by snapshot-seconds) whenever a device-id is already known from an earlier event or a different session. */
|
|
6
8
|
export function mergeKnownDevices(knownDevices, directory) {
|
|
7
9
|
for (const entry of directory) {
|
|
@@ -13,4 +15,12 @@ export function mergeKnownDevices(knownDevices, directory) {
|
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
}
|
|
18
|
+
/** Reads a presence extension from one specific device's own gossiped self-advert, if this event's directory carries a fresh one for exactly that device-id -- never for any other device-id a multi-hop directory might mention, since only the session's own authenticated peer's advert is that session's business to report. Returns undefined for a missing presence/status key, or a value that isn't a recognised AgentStatus -- an advert simply not participating in this convention, not an error (the same verifier obligation peer-advert's own open extension tail is documented under). */
|
|
19
|
+
export function findPresenceAdvert(deviceIdHex, directory) {
|
|
20
|
+
const entry = directory.find((candidate) => deviceIdToHex(candidate.device) === deviceIdHex);
|
|
21
|
+
if (entry === undefined)
|
|
22
|
+
return undefined;
|
|
23
|
+
const status = entry.advert[PRESENCE_GOSSIP_KEY];
|
|
24
|
+
return AgentStatus.is(status) ? status : undefined;
|
|
25
|
+
}
|
|
16
26
|
//# sourceMappingURL=gossip-directory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gossip-directory.js","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"gossip-directory.js","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGhE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,2OAA2O;AAC3O,MAAM,UAAU,iBAAiB,CAC/B,YAAqC,EACrC,SAAoC;IAEpC,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC/C,IACE,QAAQ,KAAK,SAAS;YACtB,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,QAAQ,CAAC,kBAAkB,CAAC,EAChE,CAAC;YACD,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;AACH,CAAC;AAED,ykBAAykB;AACzkB,MAAM,UAAU,kBAAkB,CAChC,WAAmB,EACnB,SAAoC;IAEpC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAC1B,CAAC,SAAS,EAAE,EAAE,CAAC,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,WAAW,CAC/D,CAAC;IACF,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,MAAM,GAAY,KAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC1D,OAAO,WAAW,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hub-forwarding -- agent-comms#155's gateway forwarding, split from wire-mesh-transport.ts under the repo's max-lines cap. Outbound: filters a directory of gossiped local devices down to the ones eligible for cross-machine advertisement (an agent/self-bearing entry, meaning only ever a "visible" agent -- see AGENT_SELF_GOSSIP_KEY's own doc for why no separate visibility check is needed here) and pushes them onto a connected HubSession, best-effort. Local-to-remote routing: falls a room-domain request back through the hub's own relay-connect/relay-data pairing when its target isn't a local peer session.
|
|
3
|
+
*/
|
|
4
|
+
import type { CapabilityScope, CapabilityToken, ManageCommand, PeerAdvert } from "wire-mesh-core/generated/protocol";
|
|
5
|
+
import type { DirectoryEntry, ManageOutcome } from "wire-mesh-core/domain/mesh-session";
|
|
6
|
+
import type { HubSession } from "./hub-session.js";
|
|
7
|
+
/** Forwards every directory entry carrying an agent/self extension onto hub, if hub currently holds a live connection -- a no-op otherwise, which is every local peer except whichever one is currently the gateway. Called both from WireMeshTransport's own watchForDisconnect (as each local peer session's directory changes) and from connectHub's own initial catch-up push, so this side's already-known local devices reach the hub immediately on taking over the gateway role rather than waiting for their own next periodic gossip tick. Forwards unconditionally on every call (no caching or dedup against a previous call), mirroring relay-hub.ts's own "forward every gossip frame, as received" philosophy: an advert's own snapshot-seconds/addresses are meant to keep propagating as a liveness heartbeat, so suppressing a "duplicate" would silently stop legitimate freshness updates from reaching remote gateways. A forward that fails is reported via onError and swallowed, matching every other best-effort gossip send in this codebase. */
|
|
8
|
+
export declare function forwardAdvertsToHub(hub: Readonly<Pick<HubSession, "isConnected" | "advertiseDevices">>, directory: readonly DirectoryEntry[], onError: ((error: Error) => void) | undefined): void;
|
|
9
|
+
/** Catches the hub up with every local device already known at the moment this side becomes the gateway (connectHub's own trailing call, right after hub.connect resolves) -- without this, a device whose own last gossip arrived before this coordinator took over the gateway role would never be (re-)advertised until its own next periodic gossip tick (hub-side state is rebuilt from scratch on every takeover, per coordinator-gateway.ts's own class doc). Reuses forwardAdvertsToHub's own eligibility filter, so only ever a visible, agent/self-bearing device is pushed, exactly as an ordinary directory-change forward would. */
|
|
10
|
+
export declare function pushHubCatchUp(hub: Readonly<Pick<HubSession, "isConnected" | "advertiseDevices">>, knownDevices: ReadonlyMap<string, Readonly<PeerAdvert>>, onError: ((error: Error) => void) | undefined): void;
|
|
11
|
+
/** Routes a room-domain request through the hub's relay-connect/relay-data pairing when memberId isn't a local peer session -- WireMeshTransport.sendRoomRequest's own fallback, since the member may be a remote agent reachable only via this machine's gateway connection (agent-comms#155's local-to-remote leg). Resolves the same not_connected outcome sendRoomRequest already returned before the hub existed at all when this side isn't currently the gateway. */
|
|
12
|
+
export declare function routeRoomRequestViaHub(hub: Readonly<Pick<HubSession, "isConnected" | "sendRoomRequest">>, memberId: string, command: ManageCommand, scope: Readonly<CapabilityScope>, token?: CapabilityToken): Promise<ManageOutcome>;
|
|
13
|
+
//# sourceMappingURL=hub-forwarding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-forwarding.d.ts","sourceRoot":"","sources":["../../src/core/hub-forwarding.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,aAAa,EACb,UAAU,EACX,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACd,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,2gCAA2gC;AAC3gC,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,kBAAkB,CAAC,CAAC,EACnE,SAAS,EAAE,SAAS,cAAc,EAAE,EACpC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,GAC5C,IAAI,CASN;AAED,inBAAinB;AACjnB,wBAAgB,cAAc,CAC5B,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,kBAAkB,CAAC,CAAC,EACnE,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,EACvD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,GAC5C,IAAI,CAMN;AAED,4cAA4c;AAC5c,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,iBAAiB,CAAC,CAAC,EAClE,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,EAChC,KAAK,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,aAAa,CAAC,CAGxB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hub-forwarding -- agent-comms#155's gateway forwarding, split from wire-mesh-transport.ts under the repo's max-lines cap. Outbound: filters a directory of gossiped local devices down to the ones eligible for cross-machine advertisement (an agent/self-bearing entry, meaning only ever a "visible" agent -- see AGENT_SELF_GOSSIP_KEY's own doc for why no separate visibility check is needed here) and pushes them onto a connected HubSession, best-effort. Local-to-remote routing: falls a room-domain request back through the hub's own relay-connect/relay-data pairing when its target isn't a local peer session.
|
|
3
|
+
*/
|
|
4
|
+
import { AGENT_SELF_GOSSIP_KEY } from "./wire-mesh-transport.js";
|
|
5
|
+
/** Forwards every directory entry carrying an agent/self extension onto hub, if hub currently holds a live connection -- a no-op otherwise, which is every local peer except whichever one is currently the gateway. Called both from WireMeshTransport's own watchForDisconnect (as each local peer session's directory changes) and from connectHub's own initial catch-up push, so this side's already-known local devices reach the hub immediately on taking over the gateway role rather than waiting for their own next periodic gossip tick. Forwards unconditionally on every call (no caching or dedup against a previous call), mirroring relay-hub.ts's own "forward every gossip frame, as received" philosophy: an advert's own snapshot-seconds/addresses are meant to keep propagating as a liveness heartbeat, so suppressing a "duplicate" would silently stop legitimate freshness updates from reaching remote gateways. A forward that fails is reported via onError and swallowed, matching every other best-effort gossip send in this codebase. */
|
|
6
|
+
export function forwardAdvertsToHub(hub, directory, onError) {
|
|
7
|
+
if (!hub.isConnected)
|
|
8
|
+
return;
|
|
9
|
+
const eligible = directory.filter((entry) => entry.advert[AGENT_SELF_GOSSIP_KEY] !== undefined);
|
|
10
|
+
if (eligible.length === 0)
|
|
11
|
+
return;
|
|
12
|
+
hub.advertiseDevices(eligible).catch((error) => {
|
|
13
|
+
onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/** Catches the hub up with every local device already known at the moment this side becomes the gateway (connectHub's own trailing call, right after hub.connect resolves) -- without this, a device whose own last gossip arrived before this coordinator took over the gateway role would never be (re-)advertised until its own next periodic gossip tick (hub-side state is rebuilt from scratch on every takeover, per coordinator-gateway.ts's own class doc). Reuses forwardAdvertsToHub's own eligibility filter, so only ever a visible, agent/self-bearing device is pushed, exactly as an ordinary directory-change forward would. */
|
|
17
|
+
export function pushHubCatchUp(hub, knownDevices, onError) {
|
|
18
|
+
const catchUp = [...knownDevices.values()].map((advert) => ({
|
|
19
|
+
device: advert.device,
|
|
20
|
+
advert,
|
|
21
|
+
}));
|
|
22
|
+
forwardAdvertsToHub(hub, catchUp, onError);
|
|
23
|
+
}
|
|
24
|
+
/** Routes a room-domain request through the hub's relay-connect/relay-data pairing when memberId isn't a local peer session -- WireMeshTransport.sendRoomRequest's own fallback, since the member may be a remote agent reachable only via this machine's gateway connection (agent-comms#155's local-to-remote leg). Resolves the same not_connected outcome sendRoomRequest already returned before the hub existed at all when this side isn't currently the gateway. */
|
|
25
|
+
export async function routeRoomRequestViaHub(hub, memberId, command, scope, token) {
|
|
26
|
+
if (!hub.isConnected)
|
|
27
|
+
return { result: "error", code: "not_connected" };
|
|
28
|
+
return hub.sendRoomRequest(memberId, command, scope, token);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=hub-forwarding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-forwarding.js","sourceRoot":"","sources":["../../src/core/hub-forwarding.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,2gCAA2gC;AAC3gC,MAAM,UAAU,mBAAmB,CACjC,GAAmE,EACnE,SAAoC,EACpC,OAA6C;IAE7C,IAAI,CAAC,GAAG,CAAC,WAAW;QAAE,OAAO;IAC7B,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAC/B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAAC,KAAK,SAAS,CAC7D,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAClC,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACtD,OAAO,EAAE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,inBAAinB;AACjnB,MAAM,UAAU,cAAc,CAC5B,GAAmE,EACnE,YAAuD,EACvD,OAA6C;IAE7C,MAAM,OAAO,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC1D,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM;KACP,CAAC,CAAC,CAAC;IACJ,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,4cAA4c;AAC5c,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,GAAkE,EAClE,QAAgB,EAChB,OAAsB,EACtB,KAAgC,EAChC,KAAuB;IAEvB,IAAI,CAAC,GAAG,CAAC,WAAW;QAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IACxE,OAAO,GAAG,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HubSession -- the relay-hub connection mode (agent-comms#151), split from wire-mesh-transport.ts under the max-lines cap the same way connection-approval.ts and its siblings were. A hub connection (wss://mesh.exadev.io/) is a RELAY, not a coordinator: no connect_request/introduce approval applies. The session's own self-advert is forwarded by the hub to every other connected agent and the hub answers with a catch-up of everyone already there, so peers discover each other purely through gossip. Messages ride relay-connect pairings -- sendManageRequest's own targetDevice routing, which the session layer wraps as relay-data to exactly that device.
|
|
3
3
|
*/
|
|
4
|
-
import { type AcceptedMeshSession } from "wire-mesh-core/domain/mesh-session";
|
|
5
|
-
import type { Frame } from "wire-mesh-core/generated/protocol";
|
|
4
|
+
import { type AcceptedMeshSession, type DirectoryEntry, type IncomingManageRequest, type ManageOutcome } from "wire-mesh-core/domain/mesh-session";
|
|
5
|
+
import type { CapabilityScope, CapabilityToken, Frame, ManageCommand } from "wire-mesh-core/generated/protocol";
|
|
6
6
|
import type { IdentityPort } from "wire-mesh-core/ports/identity";
|
|
7
7
|
import type { Connection } from "wire-mesh-core/ports/transport";
|
|
8
|
-
import type { TransportEvents } from "./transport.js";
|
|
8
|
+
import type { ConnectionHandle, TransportEvents } from "./transport.js";
|
|
9
9
|
import type { MeshMessage } from "./wire-protocol.js";
|
|
10
10
|
export interface HubSessionDeps {
|
|
11
11
|
/** Resolves this node's own identity port once ready. */
|
|
@@ -19,10 +19,16 @@ export interface HubSessionDeps {
|
|
|
19
19
|
onFrame: (connection: Readonly<Connection>, frame: Readonly<Frame>) => void | Promise<void>;
|
|
20
20
|
/** Tracks the session for shutdown -- every session the transport ever creates, always. */
|
|
21
21
|
trackForShutdown: (session: AcceptedMeshSession) => void;
|
|
22
|
+
/** Fires with every remote (non-self) directory entry the hub's own gossip/catch-up surfaces, every time the session's directory changes (agent-comms#155's own remote-directory-merge leg) -- the transport merges these into its own mesh-wide knownDevices the same way it already merges a local peer session's directory, so a hub-learned agent surfaces in listAgents/getAgent with no separate lookup path. */
|
|
23
|
+
onDirectory: (entries: readonly DirectoryEntry[]) => void;
|
|
24
|
+
/** Dispatches an already-received, non-legacy-frame request (a real core/room verb: room.send, room.notify, room.join, ...) to WireMeshTransport's own roomRouter, the identical dispatch a local peer session's own drainSession already uses -- the outbound half of agent-comms#155's "remote to local" routing leg. Deferred the same lazy-`this`-capture way DeliveryEngine's own sendRoomRequestToMember closure is, since roomRouter is constructed after this class's own instance in WireMeshTransport's constructor. */
|
|
25
|
+
handleRoomRequest: (request: IncomingManageRequest, handle: Readonly<ConnectionHandle>) => Promise<void>;
|
|
22
26
|
}
|
|
23
27
|
export declare class HubSession {
|
|
24
28
|
private readonly deps;
|
|
25
29
|
private session;
|
|
30
|
+
/** The raw connection underlying `session` -- MeshSession's own sendGossipUpdate only ever advertises this side's own single device, so advertising OTHER (local mesh) devices onto the hub (agent-comms#155's outbound leg) needs a raw gossip frame sent directly, bypassing that per-session single-self-advert limit. */
|
|
31
|
+
private connection;
|
|
26
32
|
private readonly hubPeersKnown;
|
|
27
33
|
constructor(deps: Readonly<HubSessionDeps>);
|
|
28
34
|
/** This node's own device-id, hex-encoded -- the identifier hub peers address it by. */
|
|
@@ -35,10 +41,14 @@ export declare class HubSession {
|
|
|
35
41
|
peers(): readonly string[];
|
|
36
42
|
/** Dials the hub and participates as a peer (see the class doc for the discovery and routing model). */
|
|
37
43
|
connect(url: string): Promise<void>;
|
|
38
|
-
/** Dispatches inbound relayed manage-requests: each is handled with a handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests), so
|
|
44
|
+
/** Dispatches inbound relayed manage-requests: each is handled with a handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests), so every downstream consumer sees the true origin, never the hub -- the same discipline extends to a real room-domain verb (agent-comms#155's "remote to local" leg) as it already applied to the legacy opaque-frame path. A legacy FRAME_VERB carrying state_sync/state_update is dropped before ever reaching onMessage/applyPatch -- see isStateMutatingMessage's own doc for why: the hub has no per-peer admission control yet (that lands in agent-comms#156), so accepting one from an arbitrary hub peer would let it directly patch this side's mesh state (a security review finding on agent-comms#169). A real room-domain verb (room.send, room.join, room.notify, ...) carries no equivalent risk -- it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over -- so it is dispatched to the same roomVerbHandlers a local peer session's own drainSession uses, via handleRoomRequest, against THIS side's own local mesh state. Known limitation, inherited from wire-mesh-core's own session layer rather than something agent-comms can fix here: a session tracks at most one active relay pairing per remote device (mesh-session.ts's own single relayPeerDevice slot), so a request relayed here is dispatched as "addressed to this gateway's own agent" unconditionally -- there is no target-device disambiguation available to route it on to a DIFFERENT local peer this gateway also advertises. Forwarding this gateway's own agent's traffic is therefore correct; a remote request genuinely meant for another local peer behind this same gateway is not yet distinguishable from one meant for this gateway's own agent. */
|
|
39
45
|
private consume;
|
|
40
46
|
private watchDisconnect;
|
|
41
47
|
/** Sends one message to a hub-discovered peer through the hub's relay pairing. */
|
|
42
48
|
sendToPeer(peerDeviceHex: string, message: MeshMessage): Promise<void>;
|
|
49
|
+
/** Gossips a raw `gossip` frame carrying every given entry's own advert onto the hub, over the raw connection rather than sendGossipUpdate (which can only ever advertise this side's own single device) -- the outbound leg of agent-comms#155's gateway forwarding. relay-hub.ts registers each advert's own `device` field against the CONNECTION it arrives on, so every entry passed here becomes reachable, cross-machine, as "via this gateway" -- callers are responsible for only ever passing entries that are actually meant to be advertised (WireMeshTransport filters to local, visible-agent-bearing entries before calling this). Throws if this side isn't currently connected to a hub, matching sendToPeer's own contract -- callers gate on isConnected first. */
|
|
50
|
+
advertiseDevices(entries: readonly DirectoryEntry[]): Promise<void>;
|
|
51
|
+
/** Sends a real core/room manage-request to a specific hub-reachable peer, through the hub's relay-connect/relay-data pairing -- the local-to-remote leg of agent-comms#155's routing, mirroring MeshTransport.sendRoomRequest's own not_connected/outcome contract so WireMeshTransport can fall back to this uniformly when memberId isn't a local peer session. Bounded by HUB_ROOM_REQUEST_TIMEOUT_MS (see its own doc) since an unreachable target-device is silently dropped by the hub with no error frame, unlike a local session's own connection-level failure. */
|
|
52
|
+
sendRoomRequest(peerDeviceHex: string, command: ManageCommand, scope: Readonly<CapabilityScope>, token?: CapabilityToken): Promise<ManageOutcome>;
|
|
43
53
|
}
|
|
44
54
|
//# sourceMappingURL=hub-session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub-session.d.ts","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,KAAK,mBAAmB,
|
|
1
|
+
{"version":3,"file":"hub-session.d.ts","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EACnB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,KAAK,EACL,aAAa,EACd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAatD,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,aAAa,EAAE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/C,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IAClC,yHAAyH;IACzH,cAAc,EAAE,MAAM,OAAO,CAAC;IAC9B,wEAAwE;IACxE,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,sIAAsI;IACtI,OAAO,EAAE,CACP,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAChC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,KACnB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,2FAA2F;IAC3F,gBAAgB,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACzD,uZAAuZ;IACvZ,WAAW,EAAE,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,KAAK,IAAI,CAAC;IAC1D,kgBAAkgB;IAClgB,iBAAiB,EAAE,CACjB,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,KAC/B,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,qBAAa,UAAU;IAMT,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,OAAO,CAAkC;IACjD,6TAA6T;IAC7T,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;gBAEtB,IAAI,EAAE,QAAQ,CAAC,cAAc,CAAC;IAE3D,wFAAwF;IAClF,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAKrC,kOAAkO;IAClO,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,mKAAmK;IAC7J,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,qFAAqF;IACrF,KAAK,IAAI,SAAS,MAAM,EAAE;IAI1B,wGAAwG;IAClG,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCzC,oyDAAoyD;IACpyD,OAAO,CAAC,OAAO;YAuBD,eAAe;IAU7B,kFAAkF;IAC5E,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5E,svBAAsvB;IAChvB,gBAAgB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAWzE,6iBAA6iB;IACviB,eAAe,CACnB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,EAChC,KAAK,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,aAAa,CAAC;CAa1B"}
|
package/dist/core/hub-session.js
CHANGED
|
@@ -5,10 +5,14 @@ import { acceptMeshSession, } from "wire-mesh-core/domain/mesh-session";
|
|
|
5
5
|
import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
|
|
6
6
|
import { extractMessage } from "./room-router.js";
|
|
7
7
|
import { connectWsUrl } from "./ws-dial.js";
|
|
8
|
-
import { buildCommand, DOMAIN, FRAME_SCOPE } from "./wire-mesh-transport.js";
|
|
8
|
+
import { buildCommand, DOMAIN, FRAME_SCOPE, FRAME_VERB, } from "./wire-mesh-transport.js";
|
|
9
|
+
/** How long a room-domain request routed through the hub's relay-connect/relay-data pairing waits for a response before giving up. Unlike an ordinary local peer session, a relay-connect naming an unknown target-device is silently dropped by the hub (spec/relay-hub's own documented behaviour -- no error frame exists for "no such device"), so a request to a device that turns out not to be reachable via any gateway would otherwise hang forever rather than surfacing as a normal "not reachable" outcome. */
|
|
10
|
+
const HUB_ROOM_REQUEST_TIMEOUT_MS = 15_000;
|
|
9
11
|
export class HubSession {
|
|
10
12
|
deps;
|
|
11
13
|
session;
|
|
14
|
+
/** The raw connection underlying `session` -- MeshSession's own sendGossipUpdate only ever advertises this side's own single device, so advertising OTHER (local mesh) devices onto the hub (agent-comms#155's outbound leg) needs a raw gossip frame sent directly, bypassing that per-session single-self-advert limit. */
|
|
15
|
+
connection;
|
|
12
16
|
hubPeersKnown = new Set();
|
|
13
17
|
constructor(deps) {
|
|
14
18
|
this.deps = deps;
|
|
@@ -28,6 +32,7 @@ export class HubSession {
|
|
|
28
32
|
if (session === undefined)
|
|
29
33
|
return;
|
|
30
34
|
this.session = undefined;
|
|
35
|
+
this.connection = undefined;
|
|
31
36
|
await session.close();
|
|
32
37
|
}
|
|
33
38
|
/** The device ids (hex) of peers discovered through the hub's gossiped directory. */
|
|
@@ -51,18 +56,20 @@ export class HubSession {
|
|
|
51
56
|
return;
|
|
52
57
|
}
|
|
53
58
|
this.session = session;
|
|
59
|
+
this.connection = connection;
|
|
54
60
|
this.deps.trackForShutdown(session);
|
|
55
|
-
// Merge the hub's directory (its catch-up arrives as the first session events) and keep refreshing it on every subsequent one.
|
|
61
|
+
// Merge the hub's directory (its catch-up arrives as the first session events) and keep refreshing it on every subsequent one. Every remote entry is also surfaced via onDirectory, so the transport can merge it into its own mesh-wide knownDevices (agent-comms#155's remote-directory-merge leg).
|
|
56
62
|
void (async () => {
|
|
63
|
+
const ownHex = deviceIdToHex(identity.deviceId);
|
|
57
64
|
for await (const event of session.events) {
|
|
58
65
|
if (this.deps.isShuttingDown())
|
|
59
66
|
break;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.hubPeersKnown.add(hex);
|
|
64
|
-
}
|
|
67
|
+
const remoteEntries = event.directory.filter((entry) => deviceIdToHex(entry.device) !== ownHex);
|
|
68
|
+
for (const entry of remoteEntries) {
|
|
69
|
+
this.hubPeersKnown.add(deviceIdToHex(entry.device));
|
|
65
70
|
}
|
|
71
|
+
if (remoteEntries.length > 0)
|
|
72
|
+
this.deps.onDirectory(remoteEntries);
|
|
66
73
|
if (event.state.status === "closed")
|
|
67
74
|
break;
|
|
68
75
|
}
|
|
@@ -72,7 +79,7 @@ export class HubSession {
|
|
|
72
79
|
await this.watchDisconnect(session);
|
|
73
80
|
})();
|
|
74
81
|
}
|
|
75
|
-
/** Dispatches inbound relayed manage-requests: each is handled with a handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests), so
|
|
82
|
+
/** Dispatches inbound relayed manage-requests: each is handled with a handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests), so every downstream consumer sees the true origin, never the hub -- the same discipline extends to a real room-domain verb (agent-comms#155's "remote to local" leg) as it already applied to the legacy opaque-frame path. A legacy FRAME_VERB carrying state_sync/state_update is dropped before ever reaching onMessage/applyPatch -- see isStateMutatingMessage's own doc for why: the hub has no per-peer admission control yet (that lands in agent-comms#156), so accepting one from an arbitrary hub peer would let it directly patch this side's mesh state (a security review finding on agent-comms#169). A real room-domain verb (room.send, room.join, room.notify, ...) carries no equivalent risk -- it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over -- so it is dispatched to the same roomVerbHandlers a local peer session's own drainSession uses, via handleRoomRequest, against THIS side's own local mesh state. Known limitation, inherited from wire-mesh-core's own session layer rather than something agent-comms can fix here: a session tracks at most one active relay pairing per remote device (mesh-session.ts's own single relayPeerDevice slot), so a request relayed here is dispatched as "addressed to this gateway's own agent" unconditionally -- there is no target-device disambiguation available to route it on to a DIFFERENT local peer this gateway also advertises. Forwarding this gateway's own agent's traffic is therefore correct; a remote request genuinely meant for another local peer behind this same gateway is not yet distinguishable from one meant for this gateway's own agent. */
|
|
76
83
|
consume(session) {
|
|
77
84
|
void (async () => {
|
|
78
85
|
for await (const request of session.incomingManageRequests) {
|
|
@@ -82,11 +89,16 @@ export class HubSession {
|
|
|
82
89
|
? deviceIdToHex(request.fromDevice)
|
|
83
90
|
: "hub-peer";
|
|
84
91
|
this.hubPeersKnown.add(senderHex);
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
|
|
92
|
+
const handle = { id: senderHex };
|
|
93
|
+
if (request.command.verb === FRAME_VERB) {
|
|
94
|
+
const message = extractMessage(request.command);
|
|
95
|
+
if (message !== undefined && !isStateMutatingMessage(message)) {
|
|
96
|
+
this.deps.events.onMessage(handle, message);
|
|
97
|
+
}
|
|
98
|
+
await request.respond({ result: "ok" }).catch(() => undefined);
|
|
99
|
+
continue;
|
|
88
100
|
}
|
|
89
|
-
await
|
|
101
|
+
await this.deps.handleRoomRequest(request, handle);
|
|
90
102
|
}
|
|
91
103
|
})();
|
|
92
104
|
}
|
|
@@ -97,6 +109,7 @@ export class HubSession {
|
|
|
97
109
|
}
|
|
98
110
|
if (this.session === session) {
|
|
99
111
|
this.session = undefined;
|
|
112
|
+
this.connection = undefined;
|
|
100
113
|
}
|
|
101
114
|
}
|
|
102
115
|
/** Sends one message to a hub-discovered peer through the hub's relay pairing. */
|
|
@@ -107,6 +120,25 @@ export class HubSession {
|
|
|
107
120
|
}
|
|
108
121
|
await session.sendManageRequest(buildCommand(message), FRAME_SCOPE, hexToBytes(peerDeviceHex));
|
|
109
122
|
}
|
|
123
|
+
/** Gossips a raw `gossip` frame carrying every given entry's own advert onto the hub, over the raw connection rather than sendGossipUpdate (which can only ever advertise this side's own single device) -- the outbound leg of agent-comms#155's gateway forwarding. relay-hub.ts registers each advert's own `device` field against the CONNECTION it arrives on, so every entry passed here becomes reachable, cross-machine, as "via this gateway" -- callers are responsible for only ever passing entries that are actually meant to be advertised (WireMeshTransport filters to local, visible-agent-bearing entries before calling this). Throws if this side isn't currently connected to a hub, matching sendToPeer's own contract -- callers gate on isConnected first. */
|
|
124
|
+
async advertiseDevices(entries) {
|
|
125
|
+
const connection = this.connection;
|
|
126
|
+
if (connection === undefined) {
|
|
127
|
+
throw new Error("not connected to a hub");
|
|
128
|
+
}
|
|
129
|
+
await connection.send({
|
|
130
|
+
type: "gossip",
|
|
131
|
+
peers: entries.map((entry) => entry.advert),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/** Sends a real core/room manage-request to a specific hub-reachable peer, through the hub's relay-connect/relay-data pairing -- the local-to-remote leg of agent-comms#155's routing, mirroring MeshTransport.sendRoomRequest's own not_connected/outcome contract so WireMeshTransport can fall back to this uniformly when memberId isn't a local peer session. Bounded by HUB_ROOM_REQUEST_TIMEOUT_MS (see its own doc) since an unreachable target-device is silently dropped by the hub with no error frame, unlike a local session's own connection-level failure. */
|
|
135
|
+
async sendRoomRequest(peerDeviceHex, command, scope, token) {
|
|
136
|
+
const session = this.session;
|
|
137
|
+
if (session === undefined) {
|
|
138
|
+
return { result: "error", code: "not_connected" };
|
|
139
|
+
}
|
|
140
|
+
return session.sendManageRequest(command, scope, hexToBytes(peerDeviceHex), token, HUB_ROOM_REQUEST_TIMEOUT_MS);
|
|
141
|
+
}
|
|
110
142
|
}
|
|
111
143
|
function hexToBytes(hex) {
|
|
112
144
|
const bytes = new Uint8Array(new ArrayBuffer(hex.length / 2));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub-session.js","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,iBAAiB,
|
|
1
|
+
{"version":3,"file":"hub-session.js","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,6 @@ export declare const RETRY_BACKOFF_MULTIPLIER = 2;
|
|
|
4
4
|
export declare const MAX_TOTAL_RETRY_MS: number;
|
|
5
5
|
/** The next delay in the doubling-with-cap backoff schedule, given the delay just used. */
|
|
6
6
|
export declare function nextRetryDelayMs(previousDelayMs: number): number;
|
|
7
|
-
/** Recognises a publish failure worth retrying (npm propagation lag,
|
|
7
|
+
/** Recognises a publish failure worth retrying (npm propagation lag, a transient 5xx from the registry's own infrastructure, a connection-level failure that never reached the registry, or a login JWT that expired mid-retry-window) as opposed to any other failure -- a genuinely invalid publish request or authorisation failure, for instance, which retrying can never fix. */
|
|
8
8
|
export declare function isRetryablePublishFailure(output: string): boolean;
|
|
9
9
|
//# sourceMappingURL=mcp-registry-retry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-registry-retry.d.ts","sourceRoot":"","sources":["../../src/core/mcp-registry-retry.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAC7C,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAC1C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAI1C,eAAO,MAAM,kBAAkB,QAC+B,CAAC;AAE/D,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAKhE;
|
|
1
|
+
{"version":3,"file":"mcp-registry-retry.d.ts","sourceRoot":"","sources":["../../src/core/mcp-registry-retry.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAC7C,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAC1C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAI1C,eAAO,MAAM,kBAAkB,QAC+B,CAAC;AAE/D,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAKhE;AAgCD,uXAAuX;AACvX,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAOjE"}
|
|
@@ -23,8 +23,21 @@ const SERVER_ERROR_STATUS_PATTERN = /server returned status 5\d{2}\b/;
|
|
|
23
23
|
function isRetryableServerError(output) {
|
|
24
24
|
return SERVER_ERROR_STATUS_PATTERN.test(output);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
// Confirmed as a third retryable failure mode (ExaDev/agent-comms, 2026-09-17, issue #180, run 35249771867, v3.9.0): the registry request never got as far as an HTTP response at all, a raw connection-level failure from mcp-publisher's own Go HTTP client (a DNS/dial/timeout/reset at the TCP layer), which matches neither the propagation-lag text nor the 5xx status pattern above since there is no status line to match against. This is transient infrastructure the same way a 5xx is: the request itself may well have been valid, retrying it can plausibly succeed.
|
|
27
|
+
const CONNECTION_ERROR_PATTERN = /\b(?:dial tcp|i\/o timeout|connection refused|connection reset|no such host)\b/;
|
|
28
|
+
function isConnectionError(output) {
|
|
29
|
+
return CONNECTION_ERROR_PATTERN.test(output);
|
|
30
|
+
}
|
|
31
|
+
// Confirmed as a fourth retryable failure mode (ExaDev/agent-comms, 2026-09-17, issue #180, run 35239013253, v3.9.0): mcp-publisher logs in with `login github-oidc` once before the retry loop starts, and a retry attempt late enough in the MAX_TOTAL_RETRY_MS budget can present a JWT that expired in the meantime, producing a 401 that no amount of retrying the same token will ever fix. It's retryable only because the caller re-runs the login step before every retry attempt (see scripts/publish-mcp-registry.ts), which mints a fresh token, so this classifier deliberately matches only the specific "expired" 401 body, not a 401 in general, since a genuine authorisation failure (wrong scope, revoked credential) would 401 again regardless of how fresh the token is.
|
|
32
|
+
const EXPIRED_JWT_STATUS_PATTERN = /server returned status 401\b/;
|
|
33
|
+
function isExpiredJwt(output) {
|
|
34
|
+
return EXPIRED_JWT_STATUS_PATTERN.test(output) && output.includes("expired");
|
|
35
|
+
}
|
|
36
|
+
/** Recognises a publish failure worth retrying (npm propagation lag, a transient 5xx from the registry's own infrastructure, a connection-level failure that never reached the registry, or a login JWT that expired mid-retry-window) as opposed to any other failure -- a genuinely invalid publish request or authorisation failure, for instance, which retrying can never fix. */
|
|
27
37
|
export function isRetryablePublishFailure(output) {
|
|
28
|
-
return isNpmPropagationLag(output) ||
|
|
38
|
+
return (isNpmPropagationLag(output) ||
|
|
39
|
+
isRetryableServerError(output) ||
|
|
40
|
+
isConnectionError(output) ||
|
|
41
|
+
isExpiredJwt(output));
|
|
29
42
|
}
|
|
30
43
|
//# sourceMappingURL=mcp-registry-retry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-registry-retry.js","sourceRoot":"","sources":["../../src/core/mcp-registry-retry.ts"],"names":[],"mappings":"AAAA,mgBAAmgB;AAEngB,sSAAsS;AACtS,EAAE;AACF,6bAA6b;AAC7b,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAC1C,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAC1C,MAAM,uBAAuB,GAAG,EAAE,CAAC;AACnC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,aAAa,GAAG,IAAI,CAAC;AAC3B,MAAM,CAAC,MAAM,kBAAkB,GAC7B,uBAAuB,GAAG,kBAAkB,GAAG,aAAa,CAAC;AAE/D,2FAA2F;AAC3F,MAAM,UAAU,gBAAgB,CAAC,eAAuB;IACtD,OAAO,IAAI,CAAC,GAAG,CACb,eAAe,GAAG,wBAAwB,EAC1C,kBAAkB,CACnB,CAAC;AACJ,CAAC;AAED,kLAAkL;AAClL,SAAS,mBAAmB,CAAC,MAAc;IACzC,OAAO,CACL,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;QAChC,MAAM,CAAC,QAAQ,CAAC,6CAA6C,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED,yxBAAyxB;AACzxB,MAAM,2BAA2B,GAAG,iCAAiC,CAAC;AAEtE,SAAS,sBAAsB,CAAC,MAAc;IAC5C,OAAO,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"mcp-registry-retry.js","sourceRoot":"","sources":["../../src/core/mcp-registry-retry.ts"],"names":[],"mappings":"AAAA,mgBAAmgB;AAEngB,sSAAsS;AACtS,EAAE;AACF,6bAA6b;AAC7b,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAC1C,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAC1C,MAAM,uBAAuB,GAAG,EAAE,CAAC;AACnC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,aAAa,GAAG,IAAI,CAAC;AAC3B,MAAM,CAAC,MAAM,kBAAkB,GAC7B,uBAAuB,GAAG,kBAAkB,GAAG,aAAa,CAAC;AAE/D,2FAA2F;AAC3F,MAAM,UAAU,gBAAgB,CAAC,eAAuB;IACtD,OAAO,IAAI,CAAC,GAAG,CACb,eAAe,GAAG,wBAAwB,EAC1C,kBAAkB,CACnB,CAAC;AACJ,CAAC;AAED,kLAAkL;AAClL,SAAS,mBAAmB,CAAC,MAAc;IACzC,OAAO,CACL,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;QAChC,MAAM,CAAC,QAAQ,CAAC,6CAA6C,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED,yxBAAyxB;AACzxB,MAAM,2BAA2B,GAAG,iCAAiC,CAAC;AAEtE,SAAS,sBAAsB,CAAC,MAAc;IAC5C,OAAO,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,mjBAAmjB;AACnjB,MAAM,wBAAwB,GAC5B,gFAAgF,CAAC;AAEnF,SAAS,iBAAiB,CAAC,MAAc;IACvC,OAAO,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,+vBAA+vB;AAC/vB,MAAM,0BAA0B,GAAG,8BAA8B,CAAC;AAElE,SAAS,YAAY,CAAC,MAAc;IAClC,OAAO,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAC/E,CAAC;AAED,uXAAuX;AACvX,MAAM,UAAU,yBAAyB,CAAC,MAAc;IACtD,OAAO,CACL,mBAAmB,CAAC,MAAM,CAAC;QAC3B,sBAAsB,CAAC,MAAM,CAAC;QAC9B,iBAAiB,CAAC,MAAM,CAAC;QACzB,YAAY,CAAC,MAAM,CAAC,CACrB,CAAC;AACJ,CAAC"}
|