wire-mesh-core 1.25.0 → 1.26.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.
@@ -475,7 +475,67 @@ async function acceptMeshSession(connection, identity, localDomains, options = {
475
475
  peerDeviceId
476
476
  };
477
477
  }
478
+ /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against applyManageRequest/applyFrame above). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
479
+ async function acceptDirectManageRequest(connection) {
480
+ const result = await connection.receive()[Symbol.asyncIterator]().next();
481
+ if (result.done === true || result.value.type !== "manage-request") return null;
482
+ const frame = result.value;
483
+ return {
484
+ requestId: frame["request-id"],
485
+ command: frame.command,
486
+ scope: frame.scope,
487
+ ...frame.token !== void 0 ? { token: frame.token } : {},
488
+ respond: async (outcome) => {
489
+ const response = {
490
+ type: "manage-response",
491
+ "request-id": frame["request-id"],
492
+ outcome
493
+ };
494
+ await connection.send(response);
495
+ await connection.close();
496
+ }
497
+ };
498
+ }
499
+ async function waitForDirectManageResponse(link, requestId) {
500
+ for await (const frame of link.receive()) if (frame.type === "manage-response" && frame["request-id"] === requestId) return frame.outcome;
501
+ throw new Error("connection closed before a response arrived");
502
+ }
503
+ /** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
504
+ async function sendDirectManageRequest(transport, address, command, scope, options = {}) {
505
+ const link = await transport.connect(address);
506
+ if (options.expectedPeerDeviceId !== void 0 && link.peerDeviceId !== void 0 && require_domain_device_id.deviceIdToHex(link.peerDeviceId) !== require_domain_device_id.deviceIdToHex(options.expectedPeerDeviceId)) {
507
+ await link.close();
508
+ throw new Error("direct connection's authenticated peer-device-id does not match the expected target -- refusing as a possible spoofing attempt");
509
+ }
510
+ const requestId = 0;
511
+ const frame = {
512
+ type: "manage-request",
513
+ "request-id": requestId,
514
+ command,
515
+ scope,
516
+ ...options.token !== void 0 ? { token: options.token } : {}
517
+ };
518
+ await link.send(frame);
519
+ const responsePromise = waitForDirectManageResponse(link, requestId);
520
+ let outcome;
521
+ if (options.timeoutMs === void 0) outcome = await responsePromise;
522
+ else {
523
+ outcome = await Promise.race([responsePromise, new Promise((resolve) => {
524
+ setTimeout(() => {
525
+ resolve({
526
+ result: "error",
527
+ code: "timeout"
528
+ });
529
+ }, options.timeoutMs);
530
+ })]);
531
+ responsePromise.catch(() => void 0);
532
+ }
533
+ await link.close();
534
+ return outcome;
535
+ }
478
536
  //#endregion
479
537
  exports.HANDSHAKE_TIMEOUT_MS = HANDSHAKE_TIMEOUT_MS;
538
+ exports.acceptDirectManageRequest = acceptDirectManageRequest;
480
539
  exports.acceptMeshSession = acceptMeshSession;
481
540
  exports.createMeshSession = createMeshSession;
541
+ exports.sendDirectManageRequest = sendDirectManageRequest;
@@ -108,4 +108,15 @@ export interface AcceptedMeshSessionOptions {
108
108
  }
109
109
  /** Wires an already-accepted Connection up as a full MeshSession, mirroring exactly what createMeshSession's own dial path does once a connection exists (send handshake, send self-advert, negotiate, consume frames) -- the wire-mesh#45 prerequisite agent-comms needs, since its peers both listen and dial rather than only ever dialing the way web-console's own console UI does. Reconnect does not apply here: if this connection drops, only the remote redialing and being accepted again produces a new connection, and therefore a new session -- there is nothing on this side to retry. */
110
110
  export declare function acceptMeshSession(connection: Readonly<Connection>, identity: Readonly<IdentityPort>, localDomains: readonly string[], options?: Readonly<AcceptedMeshSessionOptions>): Promise<AcceptedMeshSession>;
111
+ /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against applyManageRequest/applyFrame above). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
112
+ export declare function acceptDirectManageRequest(connection: Readonly<Connection>): Promise<IncomingManageRequest | null>;
113
+ export interface DirectManageRequestOptions {
114
+ /** Refuse the connection as a possible spoofing attempt if the transport's own authenticated Connection.peerDeviceId (never a value read off the wire -- see the Transport port's own doc comment) doesn't match this. Left unchecked when the transport gives no authenticated peerDeviceId at all (an unauthenticated transport, or a peer that presented no credential) -- the caller proceeds at its own risk in that case, exactly the same trust boundary Connection.peerDeviceId already documents for every other consumer of it. */
115
+ expectedPeerDeviceId?: DeviceId;
116
+ token?: CapabilityToken;
117
+ /** Omit to wait indefinitely, matching sendManageRequest's own default. */
118
+ timeoutMs?: number;
119
+ }
120
+ /** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
121
+ export declare function sendDirectManageRequest(transport: Readonly<Pick<Transport, "connect">>, address: string, command: ManageCommand, scope: Readonly<CapabilityScope>, options?: Readonly<DirectManageRequestOptions>): Promise<ManageOutcome>;
111
122
  //#endregion
@@ -108,4 +108,15 @@ export interface AcceptedMeshSessionOptions {
108
108
  }
109
109
  /** Wires an already-accepted Connection up as a full MeshSession, mirroring exactly what createMeshSession's own dial path does once a connection exists (send handshake, send self-advert, negotiate, consume frames) -- the wire-mesh#45 prerequisite agent-comms needs, since its peers both listen and dial rather than only ever dialing the way web-console's own console UI does. Reconnect does not apply here: if this connection drops, only the remote redialing and being accepted again produces a new connection, and therefore a new session -- there is nothing on this side to retry. */
110
110
  export declare function acceptMeshSession(connection: Readonly<Connection>, identity: Readonly<IdentityPort>, localDomains: readonly string[], options?: Readonly<AcceptedMeshSessionOptions>): Promise<AcceptedMeshSession>;
111
+ /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against applyManageRequest/applyFrame above). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
112
+ export declare function acceptDirectManageRequest(connection: Readonly<Connection>): Promise<IncomingManageRequest | null>;
113
+ export interface DirectManageRequestOptions {
114
+ /** Refuse the connection as a possible spoofing attempt if the transport's own authenticated Connection.peerDeviceId (never a value read off the wire -- see the Transport port's own doc comment) doesn't match this. Left unchecked when the transport gives no authenticated peerDeviceId at all (an unauthenticated transport, or a peer that presented no credential) -- the caller proceeds at its own risk in that case, exactly the same trust boundary Connection.peerDeviceId already documents for every other consumer of it. */
115
+ expectedPeerDeviceId?: DeviceId;
116
+ token?: CapabilityToken;
117
+ /** Omit to wait indefinitely, matching sendManageRequest's own default. */
118
+ timeoutMs?: number;
119
+ }
120
+ /** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
121
+ export declare function sendDirectManageRequest(transport: Readonly<Pick<Transport, "connect">>, address: string, command: ManageCommand, scope: Readonly<CapabilityScope>, options?: Readonly<DirectManageRequestOptions>): Promise<ManageOutcome>;
111
122
  //#endregion
@@ -474,5 +474,63 @@ async function acceptMeshSession(connection, identity, localDomains, options = {
474
474
  peerDeviceId
475
475
  };
476
476
  }
477
+ /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against applyManageRequest/applyFrame above). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
478
+ async function acceptDirectManageRequest(connection) {
479
+ const result = await connection.receive()[Symbol.asyncIterator]().next();
480
+ if (result.done === true || result.value.type !== "manage-request") return null;
481
+ const frame = result.value;
482
+ return {
483
+ requestId: frame["request-id"],
484
+ command: frame.command,
485
+ scope: frame.scope,
486
+ ...frame.token !== void 0 ? { token: frame.token } : {},
487
+ respond: async (outcome) => {
488
+ const response = {
489
+ type: "manage-response",
490
+ "request-id": frame["request-id"],
491
+ outcome
492
+ };
493
+ await connection.send(response);
494
+ await connection.close();
495
+ }
496
+ };
497
+ }
498
+ async function waitForDirectManageResponse(link, requestId) {
499
+ for await (const frame of link.receive()) if (frame.type === "manage-response" && frame["request-id"] === requestId) return frame.outcome;
500
+ throw new Error("connection closed before a response arrived");
501
+ }
502
+ /** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
503
+ async function sendDirectManageRequest(transport, address, command, scope, options = {}) {
504
+ const link = await transport.connect(address);
505
+ if (options.expectedPeerDeviceId !== void 0 && link.peerDeviceId !== void 0 && deviceIdToHex(link.peerDeviceId) !== deviceIdToHex(options.expectedPeerDeviceId)) {
506
+ await link.close();
507
+ throw new Error("direct connection's authenticated peer-device-id does not match the expected target -- refusing as a possible spoofing attempt");
508
+ }
509
+ const requestId = 0;
510
+ const frame = {
511
+ type: "manage-request",
512
+ "request-id": requestId,
513
+ command,
514
+ scope,
515
+ ...options.token !== void 0 ? { token: options.token } : {}
516
+ };
517
+ await link.send(frame);
518
+ const responsePromise = waitForDirectManageResponse(link, requestId);
519
+ let outcome;
520
+ if (options.timeoutMs === void 0) outcome = await responsePromise;
521
+ else {
522
+ outcome = await Promise.race([responsePromise, new Promise((resolve) => {
523
+ setTimeout(() => {
524
+ resolve({
525
+ result: "error",
526
+ code: "timeout"
527
+ });
528
+ }, options.timeoutMs);
529
+ })]);
530
+ responsePromise.catch(() => void 0);
531
+ }
532
+ await link.close();
533
+ return outcome;
534
+ }
477
535
  //#endregion
478
- export { HANDSHAKE_TIMEOUT_MS, acceptMeshSession, createMeshSession };
536
+ export { HANDSHAKE_TIMEOUT_MS, acceptDirectManageRequest, acceptMeshSession, createMeshSession, sendDirectManageRequest };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wire-mesh-core",
3
- "version": "1.25.0",
3
+ "version": "1.26.0",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",
6
6
  "repository": {