@ghostlytools/cli 1.1.2 → 1.1.4

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.
Files changed (32) hide show
  1. package/README.md +6 -6
  2. package/SKILL.md +1 -1
  3. package/dist/assets/argon2id-088uQfnH.js.map +1 -1
  4. package/dist/assets/bark_ffi_wasm_bg-EY2uc0Bx.js.map +1 -1
  5. package/dist/assets/bitcoindevkit_bg-B-iLWrmX.js.map +1 -1
  6. package/dist/assets/breez_sdk_spark_wasm_bg.wasm +0 -0
  7. package/dist/assets/fedimint_client_wasm_bg-BtHHlFpp.js.map +1 -1
  8. package/dist/assets/{hyperdhtRelay-CVLCuqVr.js → hyperdhtRelay-BoxPHp7v.js} +3 -3
  9. package/dist/assets/{hyperdhtRelay-CVLCuqVr.js.map → hyperdhtRelay-BoxPHp7v.js.map} +1 -1
  10. package/dist/assets/legacy_ciphers-JJOu0JXL.js.map +1 -1
  11. package/dist/assets/{mainline-CKFpwXcB.js → mainline-dkA94eh2.js} +2 -2
  12. package/dist/assets/{mainline-CKFpwXcB.js.map → mainline-dkA94eh2.js.map} +1 -1
  13. package/dist/assets/nacl-fast-CDhNCYHs.js.map +1 -1
  14. package/dist/assets/noble_curves-DDWn5AH0.js.map +1 -1
  15. package/dist/assets/noble_hashes-B7AZyFyS.js.map +1 -1
  16. package/dist/assets/openpgp-BBFr-zHQ.js.map +1 -1
  17. package/dist/assets/{server-vGoe9MET.js → server-CXFTDnFI.js} +285 -99
  18. package/dist/assets/server-CXFTDnFI.js.map +1 -0
  19. package/dist/assets/sha512-BYId-G-T.js.map +1 -1
  20. package/dist/assets/{src-CJdPTl7Q.js → src-K3NijMq-.js} +455 -87
  21. package/dist/assets/src-K3NijMq-.js.map +1 -0
  22. package/dist/assets/storage-Bya5b2Xt.js.map +1 -1
  23. package/dist/assets/tree-store-N362nuzZ.js.map +1 -1
  24. package/dist/assets/unbzip2-stream-B1C-JX9L.js.map +1 -1
  25. package/dist/assets/{web-OUQl4Q7L.js → web-DwgdhfcY.js} +16 -16
  26. package/dist/assets/{web-OUQl4Q7L.js.map → web-DwgdhfcY.js.map} +1 -1
  27. package/dist/ghostly.mjs +55 -40
  28. package/dist/ghostly.mjs.map +1 -1
  29. package/examples/call-echo.mjs +1 -1
  30. package/package.json +7 -7
  31. package/dist/assets/server-vGoe9MET.js.map +0 -1
  32. package/dist/assets/src-CJdPTl7Q.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { a as fileBytesOf, c as removeFileBytes, d as __exportAll, i as fileBytes, n as blobDigest } from "./fileBytes-DxiJHmTk.js";
2
- import { $a as filePartName, $i as BUTTON_ID, $n as freshHubs, $o as createChatInvite, $r as IdentityExchange, $s as randomBytes, $t as isPubkyKey, A as GROUP_EDIT_TEXT_BYTES, Aa as RTC_CONFIG, An as findEndpoint, Ao as readSetUpdate, Ar as newWakeToken, As as RelayTransport, At as encodeDidDhtPacket, Ba as LEGACY_SERVICES, Bi as GROUP_REACTION_FRAME, Bo as enrollCancelReason, Br as HOLD_LIMITS, Bs as createRelayPayload, Bt as USDT_PUBLIC_RPC, C as groupWakes, Ca as formatLocalTarget, Co as readHandoffHello, Cr as nextPinNumber, Cs as nextTurnPosition, D as GroupSession, Da as decodeControl, Dn as ENDPOINT, Do as sealHandoffPiece, Dr as WAKE_CALL_INTERVAL_MS, Ds as turnKeys, Dt as didDhtDocument, E as GROUP_READ_NOTE, Ea as LIMITS, En as decodeBolt11, Eo as readHandoffVerified, Er as readPin, Es as signTurnRelease, Fi as dhtEditId, Fn as INVITE_TAKEN, Fr as vapidKeysMatch, Fs as budgetRetryMs, Ft as ONCHAIN_PROVIDER, Ga as HANDOFF_HAVE, Gi as reactionEmoji, Gn as MESH_HUBS, Go as enrollProofFrame, Gr as CapsExchange, Gs as identityFromSeed, Gt as validatePaymentTarget, Ha as HANDOFF_ACK, Hi as ReactionWindow, Ho as enrollDoneFrame, Hr as HoldRefusedError, Ht as assertTokenUnits, Ia as parseCallSignal, Ii as parseLinkPreview, In as LIVENESS_PING_MS, Is as isDiscoveryBudgetError, It as PaymentPreflightError, Ja as HANDOFF_PAKE, Ji as validReactionNumber, Jn as pickMeshHubs, Jo as verifyEnrollProof, Jr as dialDescriptors, Js as verify, Jt as SPARK_PROVIDER, Ka as HANDOFF_HELLO, Ki as reactionIsNewer, Kn as mayBeHub, Ko as readEnrollGrant, Kr as DHT_TEXT_CAPABILITY, Ks as identityFromSeedB64, Kt as walletNetworkOf, Li as forwardedAgain, Ln as pairedMessageFrame, Lr as EDIT_CAPABILITY, Ls as withRequestOptions, Lt as SEPOLIA_TEST_USDT, M as parseCommunityEdit, Mi as EDIT_BUFFER_MS, Mn as parseCashuRequestPayload, Mo as setUpdateFrame, Mr as relayRequest, Ms as normalizeRelayUrl, Mt as signDidDhtPacket, N as mentionsMember, Ni as EDIT_RATE_WINDOW_MS, Nn as parseFedimintRequestPayload, No as signTombstone, Nr as wakeRequest, Nt as ETHEREUM_USDT, Oa as encodeControl, Oi as fileMessageText, On as cashuRequestPayload, Or as WakeLimiter, Os as turnName, Ot as didDhtFromPublicKey, P as groupName, Pi as RateWindow, Pn as GhostLink, Po as verifySetUpdate, Pr as checkPushEndpoint, Ps as traceLink, Pt as EVM_TEST_CHAINS, Qa as HANDOFF_VERIFIED, Qi as replySnippet, Qn as doorHubs, Qo as OwnInviteError, Qs as fromBase64Url, Ra as emptyLinkRecords, Ri as readForwarded, Rn as CarriedTransport, Rr as HOLD_CAPABILITY, Rs as PacketTooLargeError, Rt as SEPOLIA_TEST_USDT_FAUCET, S as groupWakeFrame, Sa as GhostlyHttpError, Si as safeBlobType, So as readHandoffHave, Sr as mayPin, Ss as firstDeviceSetSecret, Ta as webLocalFetch, Tn as engineText, To as readHandoffTurnFrame, Tr as pinNumberHolds, Ts as signTurnPacket, Ua as HANDOFF_CANCEL, Ui as nextReactionNumber, Un as groupSignalFrame, Uo as enrollGrantFrame, Ur as newMailbox, Ut as assertWholeSats, Va as serviceIdFromName, Vi as REACTION_LIMITS, Vo as enrollDigits, Vr as HoldKeys, Vt as WALLET_NETWORKS, Wa as HANDOFF_DONE, Wi as queueReaction, Wn as readGroupSignal, Wo as enrollHelloFrame, Wr as readManifest, Ws as createIdentity, Wt as formatPaymentAmount, Xa as HANDOFF_REQUEST, Xi as pairedReplyAuthor, Xn as beaconKeys, Xo as deviceInviteJoinerParams, Xs as sanitizeAvatar, Xt as sparkInvoiceDetails, Ya as HANDOFF_PIECE_BYTES, Yi as wireReaction, Yn as COMMUNITY_TOPOLOGY, Yo as createDeviceInvite, Yt as sparkAddressKind, Za as HANDOFF_TIMINGS, Zi as readReply, Zn as beaconRecords, Zo as readDeviceInvite, Zr as IDENTITY_REVOCATION_LABEL, Zs as bytesEqual, Zt as isBitcoinAddress, _ as knockRecords, _a as edgeParams, _i as peekDhtMailbox, _o as handoffVersions, _r as TypingSender, _s as TURN_SETTLE_MS, a as sealGroupTyping, aa as sameButtonText, ai as receivedIdentityStatus, ao as handoffContext, ar as pickHubs, as as ENROLL_CAPABILITY, ba as RELAY_POLL_INTERVALS, bi as parseImageMeta, bo as readHandoffCancel, bs as TURN_LAST_SEQUENCE, c as GROUP_READ_NOTE_COMMUNITY, ca as claimedTime, ci as PeerProofs, co as handoffDigest, cr as readBeaconHead, cs as deviceEchoNonce, d as encodeCommunityLink, da as shownTime, di as TRANSPORTS, do as handoffManifestFrames, dr as ChatFiles, ds as devicePingFrame, ec as toBase64Url, eo as handoffAttemptAllowed, er as lobbyKeys, es as decodeInviteCode, et as isAtprotoDid, fa as GROUP_MEMBER_CAP, fi as automaticTransport, fo as handoffOfferFrame, fr as FILE_LIMITS, fs as newDeviceSetSecret, g as knockIdentity, ga as verifyCommitSignature, gi as DHT_MESSAGE_TTL, go as handoffStreamKey, gs as TURN_RAISE_READ_TIMEOUT_MS, h as entryParams, ha as rosterHas, ho as handoffRetryAfter, i as openGroupTyping, ia as checkStatusCard, ii as newIdentityBinding, io as handoffCancelFrame, ir as newerHead, is as DEVICES_CAPABILITY, j as carryMentions, ji as parseVideoMeta, jn as isFederationId, jo as setAckFrame, jr as parseWakeFrame, js as currentRelays, ki as parseVoiceMeta, kn as fedimintRequestPayload, kr as deviceWakeRequest, ks as DEFAULT_RELAYS, kt as encodeDidDhtDeactivated, l as communityHasFrame, la as heardTime, li as emptyProofLedger, lo as handoffEphemeral, lr as readLobby, ls as deviceKeyZ32, m as encodeGroupEntryLink, ma as MEMBER_KEY, mi as irohRelayUrl, mo as handoffReleaseFrame, mr as transferStalled, ms as webCryptoSigner, n as GroupTypingBoard, na as STATUS_CARD_LIMITS, nc as utf8Decode, ni as identityRevocationValue, no as handoffAttemptSucceeded, nr as mergeBeacon, o as COMMUNITY_LIMITS, oa as statusCardText, oi as revokesIdentity, oo as handoffContextProof, or as rankHubs, os as HANDOFF_CAPABILITY, p as decodeGroupEntryLink, pi as irohDescriptor, po as handoffPakeFrame, pr as transferEnded, ps as seedSigner, qa as HANDOFF_OFFER, qi as readReaction, qn as meshHubs, qo as readEnrollHello, qr as capsDescriptors, qs as publicKeyFromZ32, qt as SPARK_NETWORKS, r as groupTypingEpoch, rc as utf8Encode, ri as identityStatement, ro as handoffBusyFrame, rr as mergeLobby, rs as inviteOwnership, s as CommunitySession, sa as arrivalKey, si as lastSharedWithMe, so as handoffDataFrame, sr as readBeacon, ss as deviceEchoFrame, tc as toZ32, ti as emptyIdentityLedger, to as handoffAttemptFailed, tr as lobbyRecords, ts as encodeInviteCode, tt as normalizeAtprotoHandle, u as decodeCommunityLink, ua as receivedTimestamp, ui as validExternalKey, uo as handoffHelloFrame, ur as shouldBeHub, us as deviceLinkPairing, v as mergeKnocks, va as DirectPathWatch, vi as DHT_FILE_CAPABILITY, vo as newHandoffId, vs as classifyTurnRead, w as parseGroupWakeFrame, wa as parseLocalTarget, wn as engineError, wo as readHandoffPake, wr as pinIsNewer, ws as readTurnPacket, x as GroupWakeLimiter, xa as presenceSeenAt, xo as readHandoffDone, xr as PIN_LIMITS, xs as TURN_MAX, y as readKnocks, yi as dhtFileId, yo as readHandoffAck, yr as typingActivity, ys as turnPutSummary, za as HTTP_SERVICE_PROTO, zi as COMMUNITY_REACTION_FRAME, zo as enrollCancelFrame, zr as HOLD_DATED_BACK_MS, zt as TEST_USDT_FAUCET_AMOUNT } from "./src-CJdPTl7Q.js";
2
+ import { $a as HANDOFF_VERIFIED, $i as replySnippet, $n as freshHubs, $o as OwnInviteError, $s as fromBase64Url, $t as isPubkyKey, A as GROUP_EDIT_TEXT_BYTES, Ai as parseVoiceMeta, An as findEndpoint, Ar as newWakeToken, As as DEFAULT_RELAYS, At as encodeDidDhtPacket, Ba as HTTP_SERVICE_PROTO, Bi as COMMUNITY_REACTION_FRAME, Bo as enrollCancelFrame, Br as HOLD_DATED_BACK_MS, Bt as USDT_PUBLIC_RPC, C as groupWakes, Ca as GhostlyHttpError, Ci as safeBlobType, Co as readHandoffHave, Cr as nextPinNumber, Cs as firstDeviceSetSecret, D as GroupSession, Da as LIMITS, Dn as ENDPOINT, Do as readHandoffVerified, Dr as WAKE_CALL_INTERVAL_MS, Ds as signTurnRelease, Dt as didDhtDocument, E as GROUP_READ_NOTE, Ea as webLocalFetch, En as decodeBolt11, Eo as readHandoffTurnFrame, Er as readPin, Es as signTurnPacket, Fi as RateWindow, Fn as INVITE_TAKEN, Fo as verifySetUpdate, Fr as vapidKeysMatch, Fs as traceLink, Ft as ONCHAIN_PROVIDER, Ga as HANDOFF_DONE, Gi as queueReaction, Gn as MESH_HUBS, Go as enrollHelloFrame, Gr as readManifest, Gs as createIdentity, Gt as validatePaymentTarget, Ha as serviceIdFromName, Hi as REACTION_LIMITS, Ho as enrollDigits, Hr as HoldKeys, Ht as assertTokenUnits, Ii as dhtEditId, In as LIVENESS_PING_MS, Is as budgetRetryMs, It as PaymentPreflightError, Ja as HANDOFF_OFFER, Ji as readReaction, Jn as pickMeshHubs, Jo as readEnrollHello, Jr as capsDescriptors, Js as publicKeyFromZ32, Jt as SPARK_PROVIDER, Ka as HANDOFF_HAVE, Ki as reactionEmoji, Kn as mayBeHub, Ko as enrollProofFrame, Kr as CapsExchange, Ks as identityFromSeed, Kt as walletNetworkOf, La as parseCallSignal, Li as parseLinkPreview, Ln as pairedMessageFrame, Lr as EDIT_CAPABILITY, Ls as isDiscoveryBudgetError, Lt as SEPOLIA_TEST_USDT, M as parseCommunityEdit, Mi as parseVideoMeta, Mn as parseCashuRequestPayload, Mo as setAckFrame, Mr as relayRequest, Ms as currentRelays, Mt as signDidDhtPacket, N as mentionsMember, Ni as EDIT_BUFFER_MS, Nn as parseFedimintRequestPayload, No as setUpdateFrame, Nr as wakeRequest, Ns as normalizeRelayUrl, Nt as ETHEREUM_USDT, Oa as decodeControl, On as cashuRequestPayload, Oo as sealHandoffPiece, Or as WakeLimiter, Os as turnKeys, Ot as didDhtFromPublicKey, P as groupName, Pi as EDIT_RATE_WINDOW_MS, Pn as GhostLink, Po as signTombstone, Pr as checkPushEndpoint, Pt as EVM_TEST_CHAINS, Qa as HANDOFF_TIMINGS, Qi as readReply, Qn as doorHubs, Qo as readDeviceInvite, Qr as IDENTITY_REVOCATION_LABEL, Qs as bytesEqual, Ri as forwardedAgain, Rn as CarriedTransport, Rr as UPGRADE_CAPABILITY, Rs as withRequestOptions, Rt as SEPOLIA_TEST_USDT_FAUCET, S as groupWakeFrame, Sa as presenceSeenAt, So as readHandoffDone, Sr as mayPin, Ss as TURN_MAX, Ta as parseLocalTarget, Tn as engineText, To as readHandoffPake, Tr as pinNumberHolds, Ts as readTurnPacket, Ua as HANDOFF_ACK, Ui as ReactionWindow, Un as groupSignalFrame, Uo as enrollDoneFrame, Ur as HoldRefusedError, Ut as assertWholeSats, Va as LEGACY_SERVICES, Vi as GROUP_REACTION_FRAME, Vo as enrollCancelReason, Vr as HOLD_LIMITS, Vs as createRelayPayload, Vt as WALLET_NETWORKS, Wa as HANDOFF_CANCEL, Wi as nextReactionNumber, Wn as readGroupSignal, Wo as enrollGrantFrame, Wr as newMailbox, Wt as formatPaymentAmount, Xa as HANDOFF_PIECE_BYTES, Xi as wireReaction, Xn as beaconKeys, Xo as createDeviceInvite, Xt as sparkInvoiceDetails, Ya as HANDOFF_PAKE, Yi as validReactionNumber, Yn as COMMUNITY_TOPOLOGY, Yo as verifyEnrollProof, Yr as dialDescriptors, Ys as verify, Yt as sparkAddressKind, Za as HANDOFF_REQUEST, Zi as pairedReplyAuthor, Zn as beaconRecords, Zo as deviceInviteJoinerParams, Zs as sanitizeAvatar, Zt as isBitcoinAddress, _ as knockRecords, _a as verifyCommitSignature, _i as DHT_MESSAGE_TTL, _o as handoffStreamKey, _r as TypingSender, _s as TURN_RAISE_READ_TIMEOUT_MS, a as sealGroupTyping, aa as checkStatusCard, ai as newIdentityBinding, ao as handoffCancelFrame, ar as pickHubs, as as DEVICES_CAPABILITY, bi as dhtFileId, bo as readHandoffAck, bs as turnPutSummary, c as GROUP_READ_NOTE_COMMUNITY, ca as arrivalKey, ci as lastSharedWithMe, co as handoffDataFrame, cr as readBeaconHead, cs as deviceEchoFrame, d as encodeCommunityLink, da as receivedTimestamp, di as validExternalKey, do as handoffHelloFrame, dr as ChatFiles, ds as deviceLinkPairing, ea as BUTTON_ID, ec as randomBytes, ei as IdentityExchange, eo as filePartName, er as lobbyKeys, es as createChatInvite, et as isAtprotoDid, fa as shownTime, fi as TRANSPORTS, fo as handoffManifestFrames, fr as FILE_LIMITS, fs as devicePingFrame, g as knockIdentity, ga as rosterHas, go as handoffRetryAfter, h as entryParams, ha as MEMBER_KEY, hi as irohRelayUrl, ho as handoffReleaseFrame, hs as webCryptoSigner, i as openGroupTyping, ic as utf8Encode, ii as identityStatement, io as handoffBusyFrame, ir as newerHead, is as inviteOwnership, j as carryMentions, ja as RTC_CONFIG, jn as isFederationId, jo as readSetUpdate, jr as parseWakeFrame, js as RelayTransport, ka as encodeControl, ki as fileMessageText, kn as fedimintRequestPayload, kr as deviceWakeRequest, ks as turnName, kt as encodeDidDhtDeactivated, l as communityHasFrame, la as claimedTime, li as PeerProofs, lo as handoffDigest, lr as readLobby, ls as deviceEchoNonce, m as encodeGroupEntryLink, mi as irohDescriptor, mo as handoffPakeFrame, mr as transferStalled, ms as seedSigner, n as GroupTypingBoard, nc as toZ32, ni as emptyIdentityLedger, no as handoffAttemptFailed, nr as mergeBeacon, ns as encodeInviteCode, o as COMMUNITY_LIMITS, oa as sameButtonText, oi as receivedIdentityStatus, oo as handoffContext, or as rankHubs, os as ENROLL_CAPABILITY, p as decodeGroupEntryLink, pa as GROUP_MEMBER_CAP, pi as automaticTransport, po as handoffOfferFrame, pr as transferEnded, ps as newDeviceSetSecret, qa as HANDOFF_HELLO, qi as reactionIsNewer, qn as meshHubs, qo as readEnrollGrant, qr as DHT_TEXT_CAPABILITY, qs as identityFromSeedB64, qt as SPARK_NETWORKS, r as groupTypingEpoch, ra as STATUS_CARD_LIMITS, rc as utf8Decode, ri as identityRevocationValue, ro as handoffAttemptSucceeded, rr as mergeLobby, s as CommunitySession, sa as statusCardText, si as revokesIdentity, so as handoffContextProof, sr as readBeacon, ss as HANDOFF_CAPABILITY, tc as toBase64Url, to as handoffAttemptAllowed, tr as lobbyRecords, ts as decodeInviteCode, tt as normalizeAtprotoHandle, u as decodeCommunityLink, ua as heardTime, ui as emptyProofLedger, uo as handoffEphemeral, ur as shouldBeHub, us as deviceKeyZ32, v as mergeKnocks, va as edgeParams, vi as peekDhtMailbox, vo as handoffVersions, vs as TURN_SETTLE_MS, w as parseGroupWakeFrame, wa as formatLocalTarget, wn as engineError, wo as readHandoffHello, wr as pinIsNewer, ws as nextTurnPosition, x as GroupWakeLimiter, xa as RELAY_POLL_INTERVALS, xi as parseImageMeta, xo as readHandoffCancel, xr as PIN_LIMITS, xs as TURN_LAST_SEQUENCE, y as readKnocks, ya as DirectPathWatch, yi as DHT_FILE_CAPABILITY, yo as newHandoffId, yr as typingActivity, ys as classifyTurnRead, za as emptyLinkRecords, zi as readForwarded, zr as HOLD_CAPABILITY, zs as PacketTooLargeError, zt as TEST_USDT_FAUCET_AMOUNT } from "./src-K3NijMq-.js";
3
3
  import { a as deleteDatabase, c as profileOpenFailure, d as wrap, i as databaseName, l as store, n as STORES$1, o as fileStore, r as clearProfileStores, s as openDb, t as CARD_INDEX, u as transact } from "./idb-Co2lObpU.js";
4
4
  import { $ as onAdaptersChanged, A as intentRepository, C as ProviderSources, Ct as FileAppender, D as requireTurn, Dt as streamStored, E as refuseAway, Et as storedSize, F as ModeGate, G as fakesLightning, H as BITCOIN_INVOICE_ON_TESTNET, I as WrongNetworkError, J as mintNetwork, K as isTestMint, L as networkLabel, M as sealSeed, N as unsealSeed, O as setAwayWallets, P as ModeChanged, Q as identityProviders, R as CASHU_MINT_SOURCE, S as lightningPaying, St as paymentWireId, T as awayFrom, Tt as removeStored, U as DEFAULT_MINTS, V as WALLET_TYPES, W as TEST_MINT, X as BACKUP_MEDIA_TYPE, Y as paysItsOwnInvoices, Z as MAX_BACKUP_BYTES, _ as normalizePhrase, _t as pairedWireReply, a as createFailure, at as SourceConfigError, b as breezDatabasesFor, bt as replyTo, c as paymentNetwork, ct as networkMode, d as networkReason, dt as resolveAtprotoDid, et as registeredLightningProviders, f as rpcHost, ft as setOwnDidSource, g as isRecoveryPhrase, gt as groupReply, h as cardsKey, ht as chosenResolver, i as assertConfirmedReal, it as PROVIDER_PLATFORMS, j as newDeviceKey, k as setSingleWriterGate, l as paymentNetworksOf, lt as redact, m as LightningCards, mt as verifyIdentity, n as loadFedimintSdk, nt as reserveAdapterIds, o as createTiming, ot as SourceUnreachableError, p as CASHU_CARD, pt as boundedIdentityFetch, q as isWorthlessMint, r as WALLET_NAMES, rt as NothingSpentError, s as crossNetwork, st as isNothingSpentError, t as fedimintDatabase, tt as registeredOnchainProviders, u as walletInstances, ut as assertPublicServer, v as breezDatabase, vt as receivedPairedReply, w as sourceKey, wt as readStored$1, x as dropBreezDatabase, xt as paymentAlias, y as breezDatabaseInUse, yt as replyRef, z as cashuMint } from "../ghostly.mjs";
5
5
  import { hkdf } from "@noble/hashes/hkdf.js";
@@ -190,6 +190,27 @@ var erc20 = new Interface([
190
190
  ]);
191
191
  var same$1 = (a, b) => a.toLowerCase() === b.toLowerCase();
192
192
  var CONFIRMATIONS = 2;
193
+ /** The JSON-RPC methods that only read: asked again once when they fail (see `UsdtAdapter.rpc`). */
194
+ var RETRIED_READS = /* @__PURE__ */ new Set([
195
+ "eth_chainId",
196
+ "eth_getCode",
197
+ "eth_call",
198
+ "eth_getBalance",
199
+ "eth_blockNumber",
200
+ "eth_getLogs",
201
+ "eth_getTransactionCount",
202
+ "eth_estimateGas",
203
+ "eth_getBlockByNumber",
204
+ "eth_maxPriorityFeePerGas",
205
+ "eth_gasPrice",
206
+ "eth_feeHistory",
207
+ "eth_getTransactionByHash",
208
+ "eth_getTransactionReceipt"
209
+ ]);
210
+ var RETRY_AFTER_MS = 1e3;
211
+ /** Failures that would fail the same way again at once: not retried. */
212
+ var FINAL = /* @__PURE__ */ new WeakSet();
213
+ var timedOut = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
193
214
  /**
194
215
  * The `provider` a request carries to the contact. Which RPC pays is the payer's own choice, so it names none of
195
216
  * this wallet's: a typed RPC URL may hold an API key (`/v3/<key>`), and every contact would read it. Older apps
@@ -259,7 +280,22 @@ var UsdtAdapter = class UsdtAdapter {
259
280
  adapter.account = await adapter.manager.getAccount(0);
260
281
  return adapter;
261
282
  }
283
+ /**
284
+ * A read that fails is asked again, once, a moment later: a public RPC drops a request now and then, and a chain that
285
+ * just made a block can refuse a read of "latest" it no longer holds. Without it a network switch, a restore or New
286
+ * gave up on the first such answer. Never a broadcast or anything else that writes, nor a request that did not answer
287
+ * in time (the person would wait twice as long), nor one the server refused as such (4xx).
288
+ */
262
289
  async rpc(method, params = []) {
290
+ try {
291
+ return await this.ask(method, params);
292
+ } catch (error) {
293
+ if (!RETRIED_READS.has(method) || FINAL.has(error)) throw error;
294
+ await new Promise((resolve) => setTimeout(resolve, RETRY_AFTER_MS));
295
+ return this.ask(method, params);
296
+ }
297
+ }
298
+ async ask(method, params) {
263
299
  let response;
264
300
  try {
265
301
  response = await fetch(this.config.provider, {
@@ -275,9 +311,15 @@ var UsdtAdapter = class UsdtAdapter {
275
311
  });
276
312
  } catch (error) {
277
313
  const reason = networkReason(error, rpcHost(this.config.provider));
278
- throw reason ? Object.assign(new Error(reason), { cause: error }) : error;
314
+ const thrown = reason ? Object.assign(new Error(reason), { cause: error }) : error;
315
+ if (timedOut(error) && thrown && typeof thrown === "object") FINAL.add(thrown);
316
+ throw thrown;
317
+ }
318
+ if (!response.ok) {
319
+ const error = engineError("usdtRpcUnavailable");
320
+ if (response.status !== 429 && response.status < 500) FINAL.add(error);
321
+ throw error;
279
322
  }
280
- if (!response.ok) throw engineError("usdtRpcUnavailable");
281
323
  const result = await response.json();
282
324
  if (result.error || !Object.prototype.hasOwnProperty.call(result, "result")) throw new Error("USDT RPC rejected the operation");
283
325
  return result.result;
@@ -1259,6 +1301,12 @@ var ArkadeAdapter = class ArkadeAdapter {
1259
1301
  };
1260
1302
  //#endregion
1261
1303
  //#region ../browser/src/engine/paymentAdapters/backup.ts
1304
+ /**
1305
+ * Wallet database versions a backup may carry. 3 is what SDK 0.4.74-0.4.76 made (Ghostly 1.1.3 and earlier);
1306
+ * 4 is SDK 0.4.78's, which adds the vtxos `scriptUnspent` index. An older one is restored at its own version
1307
+ * and the SDK upgrades it the first time the wallet opens. arkBackupSchema.test.ts fails when the SDK moves on.
1308
+ */
1309
+ var ARK_DB_VERSIONS = [3, 4];
1262
1310
  var allowed = /* @__PURE__ */ new Set([
1263
1311
  "vtxos",
1264
1312
  "utxos",
@@ -1315,7 +1363,7 @@ async function snapshotArkDatabase(walletId) {
1315
1363
  }
1316
1364
  /** Only imports into a freshly allocated database; never replaces an existing wallet. */
1317
1365
  async function restoreArkDatabase(walletId, snapshot) {
1318
- if (snapshot.version !== 3 || !Array.isArray(snapshot.stores) || snapshot.stores.length > allowed.size || new Set(snapshot.stores.map((s) => s.name)).size !== snapshot.stores.length || snapshot.stores.some((s) => !allowed.has(s.name) || !Array.isArray(s.values) || !Array.isArray(s.keys) || s.values.length !== s.keys.length)) throw new Error("Unsupported Ark backup schema");
1366
+ if (!ARK_DB_VERSIONS.includes(snapshot.version) || !Array.isArray(snapshot.stores) || snapshot.stores.length > allowed.size || new Set(snapshot.stores.map((s) => s.name)).size !== snapshot.stores.length || snapshot.stores.some((s) => !allowed.has(s.name) || !Array.isArray(s.values) || !Array.isArray(s.keys) || s.values.length !== s.keys.length)) throw new Error("Unsupported Ark backup schema");
1319
1367
  for (const name of [
1320
1368
  "vtxos",
1321
1369
  "utxos",
@@ -3330,7 +3378,7 @@ var loading$2;
3330
3378
  /** Loads the WebAssembly once, on first use: profiles that never pick Breez never fetch its 12.8 MB. */
3331
3379
  function loadBreezSdk() {
3332
3380
  return loading$2 ??= (async () => {
3333
- const breez = await import("./web-OUQl4Q7L.js");
3381
+ const breez = await import("./web-DwgdhfcY.js");
3334
3382
  await breez.default();
3335
3383
  return { async connect({ network, mnemonic, apiKey, storage }) {
3336
3384
  const config = breez.defaultConfig(network);
@@ -8432,6 +8480,16 @@ var REVOCATIONS = "identityRevocations";
8432
8480
  var REPUBLISH_MS$1 = 36e5;
8433
8481
  /** How often the contacts' proofs are looked up for a revocation (and once shortly after start). */
8434
8482
  var REVOCATION_CHECK_MS = 432e5;
8483
+ /**
8484
+ * After a contact withdraws a proof, how long to wait before each lookup for its revocation (WISP 300: removing a proof withdraws it from
8485
+ * every chat and revokes it). The notice goes out before the revocation is published, so not at once; each try backs
8486
+ * off, and the first that finds it stops the rest. The 12 h check covers it after that.
8487
+ */
8488
+ var WITHDRAWN_REVOCATION_TRIES_MS = [
8489
+ 2e4,
8490
+ 6e4,
8491
+ 18e4
8492
+ ];
8435
8493
  var DRAFT_TTL = 18e5;
8436
8494
  var MAX_DRAFTS$1 = 4;
8437
8495
  var MAX_PROOFS = 32;
@@ -8644,10 +8702,31 @@ var IdentityProofs = class {
8644
8702
  }
8645
8703
  for (const r of live) await this.publishRevocation(r).catch(() => {});
8646
8704
  }
8647
- /** Looks up every contact's current proofs for a revocation (the Pkarr record only, not the provider). */
8705
+ /**
8706
+ * Looks up the contacts' proofs for a revocation (the Pkarr record only, not the provider): the ones standing, and the
8707
+ * ones withdrawn, which a removal also revokes. Not a proof already revoked, nor one past its expiry: its owner
8708
+ * publishes the revocation only until then.
8709
+ */
8648
8710
  async checkRevocations() {
8649
8711
  if (!this.host.online()) return;
8650
- for (const linkId of this.host.linkIds()) for (const r of this.host.ledger(linkId)?.received ?? []) if (r.status === "verified" || r.status === "unconfirmed") await this.exchange(linkId)?.recheck(r.id, { revocationOnly: true }).catch(() => {});
8712
+ for (const linkId of this.host.linkIds()) for (const r of this.host.ledger(linkId)?.received ?? []) if (this.revocable(r)) await this.exchange(linkId)?.recheck(r.id, { revocationOnly: true }).catch(() => {});
8713
+ }
8714
+ revocable(r) {
8715
+ return r.status !== "revoked" && Math.min(r.binding.expiresAt, r.verified.expiresAt ?? Infinity) > now();
8716
+ }
8717
+ /** A proof the contact just withdrew: looked up for its revocation a few times (`WITHDRAWN_REVOCATION_TRIES_MS`). */
8718
+ afterWithdrawal(linkId, id, tries = WITHDRAWN_REVOCATION_TRIES_MS) {
8719
+ const [wait, ...rest] = tries;
8720
+ if (wait === void 0) return;
8721
+ const timer = setTimeout(() => {
8722
+ this.timers = this.timers.filter((t) => t !== timer);
8723
+ const r = this.host.ledger(linkId)?.received.find((x) => x.id === id);
8724
+ if (!r || r.status !== "withdrawn" || !this.revocable(r)) return;
8725
+ (async () => {
8726
+ if ((this.host.online() ? await this.exchange(linkId)?.recheck(id, { revocationOnly: true }).catch(() => void 0) : void 0)?.status !== "revoked") this.afterWithdrawal(linkId, id, rest);
8727
+ })();
8728
+ }, wait);
8729
+ this.timers.push(timer);
8651
8730
  }
8652
8731
  views() {
8653
8732
  return this.proofs.map((p) => this.viewOf(p));
@@ -8751,8 +8830,12 @@ var IdentityProofs = class {
8751
8830
  }));
8752
8831
  this.host.emit();
8753
8832
  }
8754
- frame(linkId, frame) {
8755
- return this.exchange(linkId)?.receive(frame) ?? Promise.resolve();
8833
+ async frame(linkId, frame) {
8834
+ const x = this.exchange(linkId);
8835
+ if (!x) return;
8836
+ const withdrawn = frame.t === "idp-withdraw" && typeof frame.id === "string" && this.host.ledger(linkId)?.received.some((r) => r.id === frame.id && r.status !== "withdrawn" && r.status !== "revoked");
8837
+ await x.receive(frame);
8838
+ if (withdrawn && this.host.ledger(linkId)?.received.some((r) => r.id === frame.id && r.status === "withdrawn")) this.afterWithdrawal(linkId, frame.id);
8756
8839
  }
8757
8840
  ready(linkId) {
8758
8841
  if (this.greeted.has(linkId) || !this.host.channel(linkId)) return;
@@ -10799,7 +10882,7 @@ function nostrPost(note) {
10799
10882
  }
10800
10883
  /**
10801
10884
  * Nostr: kind-1 notes and the kind-3 follow list of exactly the proven key, from the relays in the person's settings
10802
- * (never relays named in events, hints or NIP-65 lists: docs/wisps/3xx-nostr-social.md). Replies are left out; a page
10885
+ * (never relays named in events, hints or NIP-65 lists: docs/wisps/309-nostr-social.md). Replies are left out; a page
10803
10886
  * reads twice as many notes to find ten that are not. Followers cannot be listed without trusting a counting service.
10804
10887
  */
10805
10888
  var nostrActivity = {
@@ -13948,6 +14031,10 @@ var EnrollInviter = class extends Enrollment {
13948
14031
  onPeerSeen: () => {
13949
14032
  if (this.seen || this.over) return;
13950
14033
  this.seen = true;
14034
+ if (this.view.role === "inviter" && this.view.step === "waiting") this.show({
14035
+ ...this.view,
14036
+ seen: true
14037
+ });
13951
14038
  this.timer(this.options.timing?.proofMs ?? 12e4, () => {
13952
14039
  if (this.view.step === "waiting") this.fail("unreached");
13953
14040
  });
@@ -16374,6 +16461,9 @@ var DeviceLinks = class {
16374
16461
  const keeper = await this.turnKeeper();
16375
16462
  if (!keeper) return null;
16376
16463
  const outcome = await keeper.check(atStart);
16464
+ try {
16465
+ if (outcome) this.handoff?.turnRead?.(outcome);
16466
+ } catch {}
16377
16467
  await this.refresh();
16378
16468
  return outcome;
16379
16469
  }
@@ -16570,6 +16660,11 @@ var SHOWN_AFTER_RELOAD = /* @__PURE__ */ new Set([
16570
16660
  "cancelled"
16571
16661
  ]);
16572
16662
  var reportOf = (busy) => busy === null ? null : typeof busy === "string" ? { why: busy } : busy;
16663
+ /**
16664
+ * What `handoff-busy` tells the taker of a local reason (WISP 06: the requesting device "Shows why"). A call is said as
16665
+ * itself, so the person waits for it to end; the rest stay "handoff": which wallet keeps the profile is said on this device.
16666
+ */
16667
+ var wireWhy = (report) => report.why === "call" ? "call" : "handoff";
16573
16668
  var name = (record, key) => record.deviceSet.find((slot) => slot?.key === key)?.name ?? "";
16574
16669
  var slotOf = (record, key) => record.deviceSet.findIndex((slot) => slot?.key === key);
16575
16670
  /** One session of a handoff with the other device: its own X25519 key, the other's hello, and the stream key. */
@@ -16929,6 +17024,25 @@ var HandoffGiver = class {
16929
17024
  receive(from, frame) {
16930
17025
  return this.exclusive(() => this.handle(from, frame)).catch(() => {});
16931
17026
  }
17027
+ /**
17028
+ * A turn read on this standby found the record of `key` at `turn`: the device it released to took the turn, which is
17029
+ * what the `handoff-done` would have said. That frame goes once, as the taker reloads, and can be lost; without this
17030
+ * the screen said "Moving to …" for good, with no Use here, until that device offered the profile back.
17031
+ */
17032
+ turnTaken(key, turn) {
17033
+ return this.exclusive(async () => {
17034
+ if (this.stopped || this.phase !== "released" || key !== this.peer || turn <= this.turn) return;
17035
+ await this.taken();
17036
+ }).catch(() => {});
17037
+ }
17038
+ /** The release was taken: the handoff note goes, and this device is a plain standby. */
17039
+ async taken() {
17040
+ await this.dropBreez();
17041
+ await this.ports.records.amend({ handoff: void 0 });
17042
+ this.phase = "idle";
17043
+ this.peer = null;
17044
+ this.changed();
17045
+ }
16932
17046
  async handle(from, frame) {
16933
17047
  if (this.stopped) return;
16934
17048
  if (from === this.peer && frame.t.startsWith("handoff-")) this.heardAt = this.now();
@@ -16950,11 +17064,7 @@ var HandoffGiver = class {
16950
17064
  case HANDOFF_DONE:
16951
17065
  if (from !== this.peer || this.phase !== "released") return;
16952
17066
  if (frame.t === "handoff-done" ? readHandoffDone(frame) === null : readHandoffTurnFrame(frame) === null) return;
16953
- await this.dropBreez();
16954
- await this.ports.records.amend({ handoff: void 0 });
16955
- this.phase = "idle";
16956
- this.peer = null;
16957
- this.changed();
17067
+ await this.taken();
16958
17068
  return;
16959
17069
  case HANDOFF_CANCEL: {
16960
17070
  const why = readHandoffCancel(frame);
@@ -17015,7 +17125,7 @@ var HandoffGiver = class {
17015
17125
  this.failure = this.failWith(local);
17016
17126
  this.changed();
17017
17127
  }
17018
- return busy("handoff");
17128
+ return busy(wireWhy(local));
17019
17129
  }
17020
17130
  const self = await this.ownSelf();
17021
17131
  const versions = handoffVersions(self, session.peer);
@@ -17216,12 +17326,12 @@ var HandoffGiver = class {
17216
17326
  while (this.now() < deadline && reportOf(await this.ports.busy(taker))?.why === "payment") await new Promise((resolve) => setTimeout(resolve, 1e3));
17217
17327
  const still = reportOf(await this.ports.busy(taker));
17218
17328
  if (still) {
17219
- this.out(handoffBusyFrame("handoff", 30));
17329
+ this.out(handoffBusyFrame(wireWhy(still), 30));
17220
17330
  this.reset(still.why === "payment" ? "payment" : this.failWith(still));
17221
17331
  return;
17222
17332
  }
17223
17333
  } else if (why) {
17224
- this.out(handoffBusyFrame("handoff", 30));
17334
+ this.out(handoffBusyFrame(wireWhy(why), 30));
17225
17335
  this.reset(this.failWith(why));
17226
17336
  return;
17227
17337
  }
@@ -19095,7 +19205,7 @@ function canEdit(message) {
19095
19205
  }
19096
19206
  /** A group message's id: `<sender>:<epoch>:<seq>` in a private group, `<sender>:<epoch>:<commit>:<seq>` in a community. */
19097
19207
  var GROUP_MESSAGE_ID = /^[a-z0-9]{52}:\d+:(?:[0-9a-f]{16}:)?\d+$/;
19098
- /** A text of mine in a group (WISP 9xx § Edits): one the group carried under its id, not a payment, a note or an event line. */
19208
+ /** A text of mine in a group (WISP 902 § Edits): one the group carried under its id, not a payment, a note or an event line. */
19099
19209
  function canEditInGroup(message) {
19100
19210
  return message.sender === "me" && message.linkId.startsWith("group:") && GROUP_MESSAGE_ID.test(message.id) && !message.file && !message.paymentId && !message.event && !message.groupPay;
19101
19211
  }
@@ -19109,9 +19219,9 @@ function takesPeerEdit(message, text) {
19109
19219
  /**
19110
19220
  * The message after edit `seq`, made at `at`: the new text (and the preview and the status card that came with it, or
19111
19221
  * none: each belongs to its version), the version it replaces kept in the history. A text that did not change adds no
19112
- * version. A status card's update adds none either (WISP 4xx · Status Cards): it replaces the card in place, and its
19222
+ * version. A status card's update adds none either (WISP 405 · Status Cards): it replaces the card in place, and its
19113
19223
  * text is only the card's fallback, so a bot's thousands of updates keep one card and no trail of versions. Buttons
19114
- * (WISP 4xx · Message Buttons) are the exception: their text is the question people answer, so a new one keeps the
19224
+ * (WISP 406 · Message Buttons) are the exception: their text is the question people answer, so a new one keeps the
19115
19225
  * version it replaces, and only a change of the buttons alone (the answer marked, closed) adds none.
19116
19226
  */
19117
19227
  function withEdit(message, edit) {
@@ -19745,7 +19855,7 @@ var EditQueue = class {
19745
19855
  /** A card's updates from someone else are applied (stored, shown) at most once per this long, per message. */
19746
19856
  var CARD_APPLY_GAP_MS = 1e3;
19747
19857
  /**
19748
- * Received status card updates, paced per message (WISP 4xx · Status Cards): the first applies at once, the rest within
19858
+ * Received status card updates, paced per message (WISP 405 · Status Cards): the first applies at once, the rest within
19749
19859
  * `CARD_APPLY_GAP_MS` wait, and only the highest edit number of them applies when the time is up. A sender that
19750
19860
  * ignores its own pace costs this side one write and one render a second per card, whatever it sends.
19751
19861
  */
@@ -20771,8 +20881,8 @@ var HUB_CLOCK_SLACK_MS = COMMUNITY_TOPOLOGY.beaconEveryMs * 1.5;
20771
20881
  /** A hub's clock is remembered this long after its entry was last in the beacon. */
20772
20882
  var HUB_CLOCK_KEPT_MS = 6e5;
20773
20883
  /**
20774
- * Each hub's clock as its beacon entry showed it (WISP 9xx · Group Community § Topology, Clocks that differ; the mesh's
20775
- * hubs read their beacon the same way, WISP 9xx · Group Mesh § Hubs). Held against mine as it is, a hub whose clock is
20884
+ * Each hub's clock as its beacon entry showed it (WISP 903 · Group Community § Topology, Clocks that differ; the mesh's
20885
+ * hubs read their beacon the same way, WISP 902 · Group Mesh § Hubs). Held against mine as it is, a hub whose clock is
20776
20886
  * a minute and a half from mine never looks fresh (its entry is "stale", or "not come yet"): a member saw no hub and a
20777
20887
  * hub with such a clock was no hub to anyone. So a hub's clock is learned from its entry: when the entry moved between
20778
20888
  * two readings of mine at most `readGapMs` apart, it was written between them, and what it says the time was then,
@@ -21082,9 +21192,11 @@ var Communities = class {
21082
21192
  return !!id && this.host.linkReady(id, 2);
21083
21193
  };
21084
21194
  const hubs = freshHubs(this.hubs(live), this.now()).map((h) => h.key);
21195
+ const former = Object.entries(s.state.nicks).filter(([key]) => !rosterHas(s.roster, key));
21085
21196
  return [{
21086
21197
  ...base,
21087
21198
  name: s.name,
21199
+ ...former.length ? { formerNames: Object.fromEntries(former) } : {},
21088
21200
  status: s.status,
21089
21201
  statusReason: s.state.statusReason,
21090
21202
  epoch: s.epoch,
@@ -21223,14 +21335,14 @@ var Communities = class {
21223
21335
  messageId: result.id
21224
21336
  };
21225
21337
  }
21226
- /** An application frame to everyone in the group (WISP 9xx · Group Community § Payments). */
21338
+ /** An application frame to everyone in the group (WISP 903 · Group Community § Payments). */
21227
21339
  async sendApp(groupId, frame) {
21228
21340
  const live = this.live.get(groupId);
21229
21341
  if (!live) throw new Error("You are not in this group yet");
21230
21342
  const result = await live.session.sendApp(frame, this.host.myNick?.(), this.now());
21231
21343
  if ("error" in result) throw new Error(result.error);
21232
21344
  }
21233
- /** An edit of my message, through the group like any frame (WISP 9xx · Group Community § Edits). An error when it cannot go now. */
21345
+ /** An edit of my message, through the group like any frame (WISP 903 · Group Community § Edits). An error when it cannot go now. */
21234
21346
  async sendEdit(groupId, edit) {
21235
21347
  const live = this.live.get(groupId);
21236
21348
  if (!live) return "You are not in this group yet";
@@ -21635,7 +21747,7 @@ var Communities = class {
21635
21747
  }
21636
21748
  /**
21637
21749
  * The members this hub may keep edges with: `hubCapacity`, or on an app with a budget of connections (a Mac, WISP
21638
- * 9xx · Group Mesh § Hubs, Budget) what its other groups and 1:1 chats leave (`peerRoom`), less its edges to the
21750
+ * 902 · Group Mesh § Hubs, Budget) what its other groups and 1:1 chats leave (`peerRoom`), less its edges to the
21639
21751
  * other hubs and, for taking someone new (`entries`), its entry sessions.
21640
21752
  */
21641
21753
  capacity(groupId, live, now, entries = true) {
@@ -21672,7 +21784,7 @@ var Communities = class {
21672
21784
  return true;
21673
21785
  }
21674
21786
  /**
21675
- * The newest message frame I hold, for the beacon's head (WISP 9xx § Head): what a member's other profile on the
21787
+ * The newest message frame I hold, for the beacon's head (WISP 903 § Head): what a member's other profile on the
21676
21788
  * same device compares with what it took, to show that something new was said while it was not running.
21677
21789
  */
21678
21790
  newestFrame(live) {
@@ -22428,7 +22540,7 @@ var GONE_LINGER_MS = 15e3;
22428
22540
  */
22429
22541
  var HUB_CLOCK_READ_GAP_MS = 75e3;
22430
22542
  /**
22431
- * The hubs of the private groups this device is in (WISP 9xx · Group Mesh § Hubs): whether a group runs on hubs, who
22543
+ * The hubs of the private groups this device is in (WISP 902 · Group Mesh § Hubs): whether a group runs on hubs, who
22432
22544
  * they are (the community's sealed beacon and lobbies, under the epoch's rendezvous secret), whether I am one, which
22433
22545
  * edges that asks for, and passing frames on as a hub. `Groups` owns the sessions and the edges; this decides.
22434
22546
  */
@@ -22501,7 +22613,7 @@ var MeshHubs = class {
22501
22613
  this.live.delete(groupId);
22502
22614
  }
22503
22615
  /**
22504
- * The edges this app may keep in a group, within its budget of connections (WISP 9xx · Group Mesh § Hubs, Budget):
22616
+ * The edges this app may keep in a group, within its budget of connections (WISP 902 · Group Mesh § Hubs, Budget):
22505
22617
  * what the other groups' links do not hold, less what the groups I am a hub of still have to open. Undefined for an
22506
22618
  * app without a budget. What this group holds itself does not count: a hub that stepped down would fit again at once.
22507
22619
  */
@@ -22912,7 +23024,7 @@ var MeshHubs = class {
22912
23024
  }
22913
23025
  /**
22914
23026
  * As a hub, what the session took for the first time goes on to every other edge that is up: not back where it came
22915
- * from, nor to its author. Everyone else takes it as a frame handed on (WISP 9xx § Catch-up).
23027
+ * from, nor to its author. Everyone else takes it as a frame handed on (WISP 902 § Catch-up).
22916
23028
  */
22917
23029
  passOn(groupId, session, from, frames) {
22918
23030
  if (!this.live.get(groupId)?.hub || !frames.length) return 0;
@@ -22955,7 +23067,7 @@ function authorOf(frame) {
22955
23067
  //#endregion
22956
23068
  //#region ../browser/src/engine/groupTyping.ts
22957
23069
  /**
22958
- * Typing in private groups (WISP 9xx · Group Mesh § Typing): this side's word goes sealed on every open edge to a
23070
+ * Typing in private groups (WISP 902 · Group Mesh § Typing): this side's word goes sealed on every open edge to a
22959
23071
  * member, and each member's word is shown until its `stop`, its message, or 6 s without a new `start`. Never stored.
22960
23072
  * Community groups do not carry it yet (their frames are all stored messages).
22961
23073
  */
@@ -23022,7 +23134,7 @@ var GroupTypings = class {
23022
23134
  //#endregion
23023
23135
  //#region ../browser/src/engine/meshSignals.ts
23024
23136
  /**
23025
- * Edge signaling through members (WISP 9xx · Group Mesh § Signaling through members; `CarriedTransport` in core): which
23137
+ * Edge signaling through members (WISP 902 · Group Mesh § Signaling through members; `CarriedTransport` in core): which
23026
23138
  * links an edge's packet is handed to, what a member does with one that is for someone else, and what happens to one
23027
23139
  * that is for me. A packet is the edge's own Pkarr packet, signed under a key only its two ends derive: whoever
23028
23140
  * carries it sees a signed blob, as a relay does, and the edge checks it as it checks a relay's answer.
@@ -23395,7 +23507,7 @@ var HERE_PER_MINUTE = 4;
23395
23507
  * re-offer) slows in proportion, and an edge to someone away (read to notice them come back) by the square root:
23396
23508
  * at 32 members, every four minutes and every minute, so 31 edges cost about 8 reads a minute when everyone is here
23397
23509
  * and 32 when nobody is. Whoever reaches a member coming back first tells the others (`group-here`), who then look
23398
- * fast for it (WISP 9xx § Cost per member).
23510
+ * fast for it (WISP 902 § Cost per member).
23399
23511
  */
23400
23512
  function meshEdgeScale(members, connected = true) {
23401
23513
  const ratio = Math.max(1, (members - 1) / 8);
@@ -23489,15 +23601,15 @@ var Groups = class {
23489
23601
  communities;
23490
23602
  /** My messages and edits an edge of a private group took. */
23491
23603
  frames = new FramesTaken();
23492
- /** Hubs of the private groups past 16 members (WISP 9xx · Group Mesh § Hubs). */
23604
+ /** Hubs of the private groups past 16 members (WISP 902 · Group Mesh § Hubs). */
23493
23605
  hubs;
23494
23606
  /** Groups whose edges are being reconciled because their hubs changed: one at a time. */
23495
23607
  hubReconcile = /* @__PURE__ */ new Set();
23496
23608
  /** Groups a commit took me out of, and when: their edges close `REMOVED_LINGER_MS` later. */
23497
23609
  removedAt = /* @__PURE__ */ new Map();
23498
- /** Who is typing in each private group, and this side's word there (WISP 9xx · Group Mesh § Typing). */
23610
+ /** Who is typing in each private group, and this side's word there (WISP 902 · Group Mesh § Typing). */
23499
23611
  typings;
23500
- /** Edge signaling through members (WISP 9xx · Group Mesh § Signaling through members). */
23612
+ /** Edge signaling through members (WISP 902 · Group Mesh § Signaling through members). */
23501
23613
  signals;
23502
23614
  constructor(host, store = db, timings = ENTRY_TIMINGS, communityTimings = COMMUNITY_TIMINGS, random, hubTimings = MESH_HUB_TIMINGS) {
23503
23615
  this.host = host;
@@ -23583,6 +23695,7 @@ var Groups = class {
23583
23695
  isAdmin: false,
23584
23696
  members: [],
23585
23697
  canSend: false,
23698
+ ...group.formerNames ? { formerNames: group.formerNames } : {},
23586
23699
  invitation: {
23587
23700
  linkId: invitation.linkId,
23588
23701
  contact: this.host.contactName(invitation.linkId) ?? "",
@@ -23677,7 +23790,7 @@ var Groups = class {
23677
23790
  return this.isCommunity(groupId) ? this.communities.taken(groupId, key) : this.frames.count(groupId, key);
23678
23791
  }
23679
23792
  /**
23680
- * Says an edit of my message to the group (WISP 9xx § Edits): over the edges of a private group (to member `to`
23793
+ * Says an edit of my message to the group (WISP 902 § Edits): over the edges of a private group (to member `to`
23681
23794
  * alone, when given), through a community like any frame. An error when it cannot go now.
23682
23795
  */
23683
23796
  async sendEdit(groupId, edit, to) {
@@ -23762,7 +23875,7 @@ var Groups = class {
23762
23875
  }
23763
23876
  /**
23764
23877
  * Sends a text; `messageId` is the id it is kept under here (what history, replies and reactions name). `forwarded`:
23765
- * the hop count of a forwarded text (WISP 9xx § Forwards). `card`: a checked status card, the text its fallback.
23878
+ * the hop count of a forwarded text (WISP 902 § Forwards). `card`: a checked status card, the text its fallback.
23766
23879
  */
23767
23880
  async send(groupId, text, mentions = [], reply, forwarded, card) {
23768
23881
  if (this.isCommunity(groupId)) return card ? this.communities.send(groupId, text, mentions, reply, forwarded, card) : this.communities.send(groupId, text, mentions, reply, forwarded);
@@ -23776,7 +23889,7 @@ var Groups = class {
23776
23889
  };
23777
23890
  }
23778
23891
  /**
23779
- * This side is typing in a private group (with what it is doing), or stopped (WISP 9xx · Group Mesh § Typing).
23892
+ * This side is typing in a private group (with what it is doing), or stopped (WISP 902 · Group Mesh § Typing).
23780
23893
  * Nothing for a community: it does not carry typing yet.
23781
23894
  */
23782
23895
  setTyping(groupId, typing, activity) {
@@ -23995,7 +24108,7 @@ var Groups = class {
23995
24108
  }
23996
24109
  /**
23997
24110
  * The admin pins a member of a private group as a hub, excludes one from being a hub, or leaves it to the member's
23998
- * app (`null`): said in the group's metadata, which every member keeps (WISP 9xx · Group Mesh § Hubs).
24111
+ * app (`null`): said in the group's metadata, which every member keeps (WISP 902 · Group Mesh § Hubs).
23999
24112
  */
24000
24113
  async setHub(groupId, key, role) {
24001
24114
  if (this.isCommunity(groupId)) throw new Error("A community group chooses its hubs by itself");
@@ -24005,12 +24118,12 @@ var Groups = class {
24005
24118
  rotate(groupId) {
24006
24119
  return this.isCommunity(groupId) ? this.communities.rotate(groupId) : this.session(groupId).rotate();
24007
24120
  }
24008
- /** The admin sets or removes the group's picture; every member gets it over the edges (WISP 9xx § Metadata). */
24121
+ /** The admin sets or removes the group's picture; every member gets it over the edges (WISP 902 § Metadata). */
24009
24122
  async setPicture(groupId, picture) {
24010
24123
  if (this.isCommunity(groupId)) return this.communities.setPicture(groupId, picture);
24011
24124
  await this.session(groupId).setPicture(picture);
24012
24125
  }
24013
- /** The admin renames the group; every member gets the name over the edges, beside the picture (WISP 9xx § Metadata). */
24126
+ /** The admin renames the group; every member gets the name over the edges, beside the picture (WISP 902 § Metadata). */
24014
24127
  async rename(groupId, name) {
24015
24128
  if (this.isCommunity(groupId)) return this.communities.rename(groupId, name);
24016
24129
  await this.session(groupId).rename(name);
@@ -24173,7 +24286,7 @@ var Groups = class {
24173
24286
  }
24174
24287
  }
24175
24288
  /**
24176
- * Private groups past 16 members: their hubs (WISP 9xx · Group Mesh § Hubs), then their edges, when what the hubs
24289
+ * Private groups past 16 members: their hubs (WISP 902 · Group Mesh § Hubs), then their edges, when what the hubs
24177
24290
  * ask for is not what exists (a hub came or went, I became one, a member asked me).
24178
24291
  */
24179
24292
  async hubsTick(now) {
@@ -24224,7 +24337,7 @@ var Groups = class {
24224
24337
  return Object.entries(this.stored.get(groupId)?.contacts ?? {}).find(([, id]) => id === linkId)?.[0];
24225
24338
  }
24226
24339
  /**
24227
- * My packet for the edge to `to`, handed to links that reach that member or may pass it on (WISP 9xx · Group Mesh
24340
+ * My packet for the edge to `to`, handed to links that reach that member or may pass it on (WISP 902 · Group Mesh
24228
24341
  * § Signaling through members): how many took it, and whether one of them surely reaches that member. None in a
24229
24342
  * community, whose members keep no edges with each other.
24230
24343
  */
@@ -24380,6 +24493,10 @@ var Groups = class {
24380
24493
  if (existing?.state && existing.state.status === "active") return;
24381
24494
  if (existing?.invitation?.seedB64) return;
24382
24495
  if ([...this.stored.values()].filter((x) => x.invitation).length >= 32) return;
24496
+ const known = {
24497
+ ...existing?.formerNames,
24498
+ ...existing?.state?.nicks
24499
+ };
24383
24500
  const invitation = {
24384
24501
  id: g,
24385
24502
  createdAt: Date.now(),
@@ -24390,7 +24507,8 @@ var Groups = class {
24390
24507
  e: Number.isSafeInteger(frame.e) ? frame.e : 0,
24391
24508
  n: Number.isSafeInteger(frame.n) ? frame.n : 1,
24392
24509
  pieces: []
24393
- }
24510
+ },
24511
+ ...Object.keys(known).length ? { formerNames: known } : {}
24394
24512
  };
24395
24513
  if (existing) {
24396
24514
  this.sessions.delete(g);
@@ -24483,6 +24601,8 @@ var Groups = class {
24483
24601
  });
24484
24602
  await this.store.putGroup(member);
24485
24603
  if (!viaLink) await this.sessions.get(g).setNick(group.invitation.admin, this.host.contactName(linkId));
24604
+ const session = this.sessions.get(g);
24605
+ for (const [key, nick] of Object.entries(group.formerNames ?? {})) if (!session.state.nicks[key]) await session.setNick(key, nick);
24486
24606
  await this.event(g, "joined", `You joined. ${GROUP_READ_NOTE}`, Date.now(), joined.state.chain.length - 1);
24487
24607
  if (viaLink) traceJoin(g, "welcome.received");
24488
24608
  if (viaLink) {
@@ -24581,7 +24701,7 @@ var Groups = class {
24581
24701
  }
24582
24702
  /**
24583
24703
  * A reaction: from the member whose edge it came on (`group-react`), or signed by a member and passed on by a hub
24584
- * (`group-reacted`). As a hub, a signed one goes on to the other edges (WISP 9xx · Group Mesh § Hubs).
24704
+ * (`group-reacted`). As a hub, a signed one goes on to the other edges (WISP 902 · Group Mesh § Hubs).
24585
24705
  */
24586
24706
  async reaction(groupId, session, from, raw) {
24587
24707
  if (session.status !== "active" || raw.g !== groupId) return;
@@ -24992,7 +25112,7 @@ var Groups = class {
24992
25112
  //#endregion
24993
25113
  //#region ../browser/src/engine/groupEdges.ts
24994
25114
  /**
24995
- * An edge of a group as the group page shows it (WISP 9xx § Mesh): open means group frames flow, which
25115
+ * An edge of a group as the group page shows it (WISP 902 § Mesh): open means group frames flow, which
24996
25116
  * is what "reachable" means everywhere else in groups; what carries it; when the member was last heard.
24997
25117
  */
24998
25118
  function edgeView(live, now = Date.now(), noSlot = false) {
@@ -25016,7 +25136,7 @@ function edgeView(live, now = Date.now(), noSlot = false) {
25016
25136
  //#endregion
25017
25137
  //#region ../browser/src/engine/groupPayments.ts
25018
25138
  /**
25019
- * Payments in a group (WISP 9xx § Payments). The money, the invoice and the receipts travel only on the edge
25139
+ * Payments in a group (WISP 902 § Payments). The money, the invoice and the receipts travel only on the edge
25020
25140
  * between the two members, as they would on a 1:1 chat (the PaymentDesk does that part, with the edge's link as
25021
25141
  * the chat). What the rest of the group learns is a `group-pay` note, sent by each side about its own part over
25022
25142
  * every edge: who pays whom, how much, over what, and how it stands. A note is believed only from the member it
@@ -25335,7 +25455,7 @@ var GroupEdits = class {
25335
25455
  host;
25336
25456
  now;
25337
25457
  buffer;
25338
- /** Members' status card updates, applied at most once a second per message (WISP 4xx · Status Cards). */
25458
+ /** Members' status card updates, applied at most once a second per message (WISP 405 · Status Cards). */
25339
25459
  cards;
25340
25460
  sendPace = /* @__PURE__ */ new Map();
25341
25461
  receivePace = /* @__PURE__ */ new Map();
@@ -25351,7 +25471,7 @@ var GroupEdits = class {
25351
25471
  /**
25352
25472
  * Edits a text of mine in a group: the new text here at once, and to the group as soon as the pace allows. Mentions
25353
25473
  * the message had stay where their words still are; `added` are those picked while editing. `card`: the status card
25354
- * of the new version, already checked (WISP 4xx · Status Cards); a card message then takes `STATUS_CARD_LIMITS.edits`.
25474
+ * of the new version, already checked (WISP 405 · Status Cards); a card message then takes `STATUS_CARD_LIMITS.edits`.
25355
25475
  */
25356
25476
  async edit(groupId, messageId, raw, added = [], card) {
25357
25477
  const refuse = (error) => ({
@@ -25538,7 +25658,7 @@ var GroupEdits = class {
25538
25658
  //#endregion
25539
25659
  //#region ../browser/src/shared/payLinks.ts
25540
25660
  /**
25541
- * The desk's links to members of a community group (WISP 9xx · Group Community § Payments): payments with a member
25661
+ * The desk's links to members of a community group (WISP 903 · Group Community § Payments): payments with a member
25542
25662
  * go through the group, sealed to them, over `cpay:<group>:<member>`; a request to everyone over `cpay:<group>`.
25543
25663
  */
25544
25664
  var pairLinkId = (groupId, member) => `cpay:${groupId}:${member}`;
@@ -25554,7 +25674,7 @@ function parsePayLink(linkId) {
25554
25674
  //#endregion
25555
25675
  //#region ../browser/src/engine/communityPay.ts
25556
25676
  /**
25557
- * Payments in community groups (WISP 9xx · Group Community § Payments). Members have no edge to each other: what
25677
+ * Payments in community groups (WISP 903 · Group Community § Payments). Members have no edge to each other: what
25558
25678
  * two members say about a payment (`pay-req`, `pay-ask`, `pay`, `pay-res`) is sealed to the other one inside a frame
25559
25679
  * the whole group carries (`CommunitySession.sendPair`), so hubs relay it and keep it for a member who was away
25560
25680
  * without being able to read it. A request to the whole group, and its "paid", go to everyone as an application
@@ -26003,7 +26123,7 @@ var HOLD_FETCH_BYTES_PER_S = 65536;
26003
26123
  var holdFetchTimeoutMs = (max) => HOLD_FETCH_MS + Math.ceil(max / HOLD_FETCH_BYTES_PER_S) * 1e3;
26004
26124
  var hex$2 = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
26005
26125
  /**
26006
- * Store-and-forward for away contacts (WISP 4xx, `hold/1`), on both ends of every chat that turned it
26126
+ * Store-and-forward for away contacts (WISP 404, `hold/1`), on both ends of every chat that turned it
26007
26127
  * on: what this device sends while the contact is away is sealed and put in this device's own storage,
26008
26128
  * named in a manifest, and pointed at from a small record on the DHT; what the contact held for this
26009
26129
  * device is picked up from its pointer, checked and stored in order, and acknowledged on this device's
@@ -27513,7 +27633,7 @@ var ProfilePeek = class {
27513
27633
  };
27514
27634
  }
27515
27635
  /**
27516
- * A community group: its beacon, which hubs republish every 30 s with the newest frame they hold (WISP 9xx § Head).
27636
+ * A community group: its beacon, which hubs republish every 30 s with the newest frame they hold (WISP 903 § Head).
27517
27637
  * New when this profile has not taken that frame. Nothing is opened but the head.
27518
27638
  */
27519
27639
  async community(group, transport) {
@@ -27750,7 +27870,7 @@ var S3Store = class {
27750
27870
  }
27751
27871
  /**
27752
27872
  * A URL that reads one object with no credential until `seconds` from now: what an away contact is
27753
- * handed to pick up what was held for it (WISP 4xx). The secret key never leaves this device; the
27873
+ * handed to pick up what was held for it (WISP 404). The secret key never leaves this device; the
27754
27874
  * signature covers the exact object, method and lifetime, so the URL reads that object and nothing else.
27755
27875
  */
27756
27876
  async presign(name, seconds) {
@@ -28613,7 +28733,8 @@ var CashuWallet = class CashuWallet {
28613
28733
  let swap = (await this.swapsAt(mint)).find((s) => s.kind === "receive" && s.token === token);
28614
28734
  if (swap?.finished) return {
28615
28735
  amount: swap.tx?.amount ?? 0,
28616
- mint
28736
+ mint,
28737
+ fee: swap.tx?.fee ?? 0
28617
28738
  };
28618
28739
  if (!swap) {
28619
28740
  const preview = await (await this.wallet(mint)).prepareSwapToReceive(token);
@@ -28643,7 +28764,8 @@ var CashuWallet = class CashuWallet {
28643
28764
  this.events.onChange();
28644
28765
  return {
28645
28766
  amount: credited,
28646
- mint
28767
+ mint,
28768
+ fee: Math.max(0, faceValue - credited)
28647
28769
  };
28648
28770
  });
28649
28771
  }
@@ -29279,6 +29401,17 @@ var CashuWallet = class CashuWallet {
29279
29401
  * taken back.
29280
29402
  */
29281
29403
  var UNIT = "sat";
29404
+ var satsWord = (n) => `${n} sat${n === 1 ? "" : "s"}`;
29405
+ /**
29406
+ * A refused send's review, once its ecash came back: why, and exactly what came back. Taking ecash back is a swap at
29407
+ * the mint, which can keep a fee: "The sats came back" said nothing of it. Without `back` (Fedimint, or not known),
29408
+ * the plain sentence.
29409
+ */
29410
+ var refusedLine = (reason, back) => {
29411
+ const why = `Refused: ${reason.replace(/\.+$/, "")}.`;
29412
+ if (!back) return `${why} The sats came back.`;
29413
+ return back.fee > 0 ? `${why} ${satsWord(back.amount)} came back; the mint kept ${back.fee} as its fee.` : `${why} All ${satsWord(back.amount)} came back.`;
29414
+ };
29282
29415
  var mintLightning = (wallet, network) => ({
29283
29416
  createInvoice: (amount, paymentId) => wallet.receiveLightning(amount, paymentId, network),
29284
29417
  quote: (invoice) => wallet.quoteInvoice(invoice, network),
@@ -29752,7 +29885,7 @@ var PaymentDesk = class {
29752
29885
  return { paymentId: id };
29753
29886
  }
29754
29887
  /**
29755
- * A request to a whole group (WISP 9xx § Payments): one request, on one rail, sent to every member over their
29888
+ * A request to a whole group (WISP 902 § Payments): one request, on one rail, sent to every member over their
29756
29889
  * edge; the first payment that settles it wins. One rail only, because each enforces "once" by itself: a
29757
29890
  * Lightning invoice can be paid once, and ecash is checked here before it is redeemed (a later token is refused
29758
29891
  * unredeemed, so its payer takes it back). Two rails at once would let an invoice and a token both pay it.
@@ -29914,7 +30047,10 @@ var PaymentDesk = class {
29914
30047
  endpoint: [ENDPOINT.bolt11, request.invoice]
29915
30048
  }).catch(() => {});
29916
30049
  }
29917
- /** Takes back ecash the peer never redeemed. If they did redeem it, the mint says so and the payment is settled. */
30050
+ /**
30051
+ * Takes back ecash the peer never redeemed. If they did redeem it, the mint says so and the payment is settled.
30052
+ * Resolves to what came back of Cashu ecash (and the mint's fee for it); undefined when nothing did, or not Cashu.
30053
+ */
29918
30054
  reclaim(paymentId) {
29919
30055
  let running = this.reclaims.get(paymentId);
29920
30056
  if (!running) {
@@ -29945,7 +30081,7 @@ var PaymentDesk = class {
29945
30081
  }
29946
30082
  if (!payment?.token || payment.direction !== "out") throw new Error("Nothing to reclaim");
29947
30083
  try {
29948
- await this.wallet.receiveToken(payment.token, "reclaimed", payment.memo, { payment: () => ({
30084
+ const { amount, fee } = await this.wallet.receiveToken(payment.token, "reclaimed", payment.memo, { payment: () => ({
29949
30085
  ...this.current(payment),
29950
30086
  state: "reclaimed",
29951
30087
  token: void 0
@@ -29955,6 +30091,10 @@ var PaymentDesk = class {
29955
30091
  state: "reclaimed",
29956
30092
  token: void 0
29957
30093
  });
30094
+ return {
30095
+ amount,
30096
+ fee
30097
+ };
29958
30098
  } catch (error) {
29959
30099
  const message = error instanceof Error ? error.message : String(error);
29960
30100
  if (!/spent/i.test(message)) throw error;
@@ -29964,6 +30104,7 @@ var PaymentDesk = class {
29964
30104
  state: "settled",
29965
30105
  token: void 0
29966
30106
  });
30107
+ return;
29967
30108
  }
29968
30109
  }
29969
30110
  /** Asks this side made to pay, until the contact answers with a request (or two minutes pass). */
@@ -30052,7 +30193,7 @@ var PaymentDesk = class {
30052
30193
  endpoints: [...payment.invoice ? [[ENDPOINT.bolt11, payment.invoice]] : [], ...payment.mints?.length ? [[ENDPOINT.cashu, cashuRequestPayload(payment.mints)]] : []]
30053
30194
  };
30054
30195
  }
30055
- /** `held`: picked up from the contact's storage while it was away (WISP 4xx): only what this device allows counts, and only Cashu or Lightning. */
30196
+ /** `held`: picked up from the contact's storage while it was away (WISP 404): only what this device allows counts, and only Cashu or Lightning. */
30056
30197
  async onPaymentRequest(linkId, request, held = false) {
30057
30198
  if (this.find(linkId, request.id)?.linkId === linkId) return;
30058
30199
  if (held && (findEndpoint(request.endpoints, ENDPOINT.usdt) || findEndpoint(request.endpoints, ENDPOINT.arkade) || findEndpoint(request.endpoints, ENDPOINT.bark) || findEndpoint(request.endpoints, ENDPOINT.bitcoin) || findEndpoint(request.endpoints, ENDPOINT.fedimint) || findEndpoint(request.endpoints, ENDPOINT.spark))) return;
@@ -30379,8 +30520,8 @@ var PaymentDesk = class {
30379
30520
  ...payment,
30380
30521
  error: result.error ?? "The payment was refused"
30381
30522
  });
30382
- await this.reclaim(payment.id).catch(() => {});
30383
- if (this.current(payment).state === "reclaimed") await this.host.onReviewedPaymentRefused?.(payment.id, result.error ?? "The payment was refused");
30523
+ const back = await this.reclaim(payment.id).catch(() => void 0);
30524
+ if (this.current(payment).state === "reclaimed") await this.host.onReviewedPaymentRefused?.(payment.id, result.error ?? "The payment was refused", back);
30384
30525
  return;
30385
30526
  }
30386
30527
  Promise.resolve(this.host.onReviewedPaymentResult?.(payment.id)).catch(() => {});
@@ -32557,7 +32698,7 @@ var GhostlyNode = class GhostlyNode {
32557
32698
  editQueues = /* @__PURE__ */ new Map();
32558
32699
  /** The contacts' edits of messages not here yet. */
32559
32700
  editBuffer = new EditBuffer();
32560
- /** Received status card updates, applied at most once a second per message (WISP 4xx · Status Cards). */
32701
+ /** Received status card updates, applied at most once a second per message (WISP 405 · Status Cards). */
32561
32702
  cardEdits = new CardEditPacer();
32562
32703
  /** When each message's buttons were last pressed here (`pressButton`). */
32563
32704
  pressedAt = /* @__PURE__ */ new Map();
@@ -32565,7 +32706,7 @@ var GhostlyNode = class GhostlyNode {
32565
32706
  wakeLimiter = new WakeLimiter();
32566
32707
  /** Call wake-ups have their own, shorter limit: a call is rarer than a message and cannot wait five minutes. */
32567
32708
  callWakeLimiter = new WakeLimiter(WAKE_CALL_INTERVAL_MS);
32568
- /** Mentions in private groups: one per member per 5 minutes, four per group per minute (WISP 9xx · Group Mesh § Wake-up push). */
32709
+ /** Mentions in private groups: one per member per 5 minutes, four per group per minute (WISP 902 · Group Mesh § Wake-up push). */
32569
32710
  groupWakeLimiter = new GroupWakeLimiter();
32570
32711
  /** `group-wake` frames read per edge, a handful a minute. */
32571
32712
  groupWakeReceived = /* @__PURE__ */ new Map();
@@ -32603,7 +32744,7 @@ var GhostlyNode = class GhostlyNode {
32603
32744
  paymentTimer = null;
32604
32745
  /** Every minute, the reviews no Approve can send any more are cancelled (dropStaleReviews). */
32605
32746
  staleReviewTimer;
32606
- /** Group links: admins read knocks, joiners knock (WISP 9xx § Entry link). */
32747
+ /** Group links: admins read knocks, joiners knock (WISP 902 § Entry link). */
32607
32748
  groupEntryTimer = null;
32608
32749
  walletView = {
32609
32750
  mints: [],
@@ -32793,14 +32934,15 @@ var GhostlyNode = class GhostlyNode {
32793
32934
  onReviewedPaymentResult: async (id) => {
32794
32935
  await this.reconcilePayment({ id });
32795
32936
  },
32796
- onReviewedPaymentRefused: async (id, reason) => {
32937
+ onReviewedPaymentRefused: async (id, reason, back) => {
32797
32938
  const intent = await intentRepository.get(id);
32798
32939
  if (intent && ["submitted", "unknown"].includes(intent.review.state)) await intentRepository.put({
32799
32940
  ...intent,
32800
32941
  review: {
32801
32942
  ...intent.review,
32802
32943
  state: "failed",
32803
- error: `Refused: ${reason}. The sats came back.`
32944
+ error: refusedLine(reason, back),
32945
+ ...back ? { returned: back } : {}
32804
32946
  }
32805
32947
  });
32806
32948
  this.emitState();
@@ -32872,7 +33014,7 @@ var GhostlyNode = class GhostlyNode {
32872
33014
  await this.sparkWallets[network].adapter?.sync();
32873
33015
  }
32874
33016
  })));
32875
- /** Store-and-forward for away contacts (WISP 4xx): items sealed into this device's own storage, picked up from the contact's. */
33017
+ /** Store-and-forward for away contacts (WISP 404): items sealed into this device's own storage, picked up from the contact's. */
32876
33018
  holdStore = null;
32877
33019
  hold = new HoldEngine({
32878
33020
  transport: void 0,
@@ -33094,7 +33236,7 @@ var GhostlyNode = class GhostlyNode {
33094
33236
  publish: (seed, records) => this.transport.publish(identityFromSeed(seed), records),
33095
33237
  resolve: async (key) => (await this.transport.resolve(key))?.records ?? null
33096
33238
  });
33097
- /** The profile's did:dht: a key of its own, public, never tied to a chat (WISP 3xx-did-dht). */
33239
+ /** The profile's did:dht: a key of its own, public, never tied to a chat (WISP 310-did-dht). */
33098
33240
  did = new ProfileDid({
33099
33241
  online: () => this.networkOn,
33100
33242
  emit: () => this.emitState(),
@@ -33322,7 +33464,7 @@ var GhostlyNode = class GhostlyNode {
33322
33464
  communityPair: (groupId, sender, payload) => this.communityPay.receivePair(groupId, sender, payload)
33323
33465
  });
33324
33466
  /**
33325
- * Payments in community groups (WISP 9xx · Group Community § Payments): the desk pays and requests over a link
33467
+ * Payments in community groups (WISP 903 · Group Community § Payments): the desk pays and requests over a link
33326
33468
  * per member that sends through the group, sealed to that member; the group request goes to everyone at once.
33327
33469
  */
33328
33470
  communityPay = new CommunityPay({
@@ -33338,7 +33480,7 @@ var GhostlyNode = class GhostlyNode {
33338
33480
  emit: () => this.emitState()
33339
33481
  });
33340
33482
  /**
33341
- * Edits in groups (WISP 9xx § Edits): mine said to the group at the pace allowed, the members' on their messages.
33483
+ * Edits in groups (WISP 902 § Edits): mine said to the group at the pace allowed, the members' on their messages.
33342
33484
  * Never a new message: no sound, no unread.
33343
33485
  */
33344
33486
  groupEdits = new GroupEdits({
@@ -33374,7 +33516,7 @@ var GhostlyNode = class GhostlyNode {
33374
33516
  return this.outsideEdge(linkId) ? null : this.links.get(linkId)?.link ?? this.communityPay.link(linkId);
33375
33517
  }
33376
33518
  /**
33377
- * Payments in groups (WISP 9xx § Payments): the money goes over the edge to one member through the desk, like a
33519
+ * Payments in groups (WISP 902 § Payments): the money goes over the edge to one member through the desk, like a
33378
33520
  * chat's; what the group sees of it goes to every member as a `group-pay` note.
33379
33521
  */
33380
33522
  groupPayments = new GroupPayments({
@@ -35033,7 +35175,7 @@ var GhostlyNode = class GhostlyNode {
35033
35175
  this.transport.networkChanged?.();
35034
35176
  this.directPath.reset();
35035
35177
  }
35036
- for (const live of this.links.values()) live.link?.wake();
35178
+ for (const live of this.links.values()) live.link?.wake({ network: params.network });
35037
35179
  this.hold.wake();
35038
35180
  for (const network of WALLET_NETWORKS) {
35039
35181
  this.lightnings[network].wake();
@@ -35053,7 +35195,7 @@ var GhostlyNode = class GhostlyNode {
35053
35195
  }));
35054
35196
  }
35055
35197
  /**
35056
- * `card`: a bot's status card (WISP 4xx · Status Cards), checked here by the sender's rule; the text is its fallback,
35198
+ * `card`: a bot's status card (WISP 405 · Status Cards), checked here by the sender's rule; the text is its fallback,
35057
35199
  * written from the card when none is given. Only the headless runtime and SDKs send one: the app never offers it.
35058
35200
  */
35059
35201
  async sendMessage(params) {
@@ -35068,7 +35210,7 @@ var GhostlyNode = class GhostlyNode {
35068
35210
  error: "Nothing to send",
35069
35211
  refused: true
35070
35212
  };
35071
- const live = this.links.get(linkId);
35213
+ const live = await this.linkWhenAdded(linkId);
35072
35214
  const trimmed = text.trim();
35073
35215
  const bytes = new TextEncoder().encode(trimmed).length;
35074
35216
  if (live && !live.stored.profile && !(live.link?.isDataLinkOpen && trimmed.length <= LIMITS.maxChatMessageBytes / 4) && bytes > 500) return {
@@ -35293,7 +35435,7 @@ var GhostlyNode = class GhostlyNode {
35293
35435
  static dhtByChoice(live) {
35294
35436
  return live?.stored.deliveryMode === "dht" || live?.link?.dhtDelivery?.peerMode === "dht";
35295
35437
  }
35296
- /** A status card a caller asks to send, by the sender's rule (WISP 4xx · Status Cards), or why it cannot go. */
35438
+ /** A status card a caller asks to send, by the sender's rule (WISP 405 · Status Cards), or why it cannot go. */
35297
35439
  static cardToSend(raw) {
35298
35440
  const checked = checkStatusCard(raw);
35299
35441
  return "error" in checked ? `Status card: ${checked.error}` : checked.card;
@@ -35418,7 +35560,7 @@ var GhostlyNode = class GhostlyNode {
35418
35560
  return !!live?.caps?.peer?.capabilities.includes(EDIT_CAPABILITY);
35419
35561
  }
35420
35562
  /**
35421
- * A question of mine with buttons went on the DHT floor or into a hold, which carry its text alone (WISP 4xx · Message
35563
+ * A question of mine with buttons went on the DHT floor or into a hold, which carry its text alone (WISP 406 · Message
35422
35564
  * Buttons): its buttons are due to go again live. Once: a row that had them restored already stays so.
35423
35565
  */
35424
35566
  async buttonsWentBare(linkId, message) {
@@ -35426,7 +35568,7 @@ var GhostlyNode = class GhostlyNode {
35426
35568
  await db.patchMessage(linkId, message.id, (current) => current.sender === "me" && current.card?.kind === "buttons" && !current.buttonsRestore ? { buttonsRestore: "due" } : null);
35427
35569
  }
35428
35570
  /**
35429
- * Buttons the contact may not have (WISP 4xx · Message Buttons): a question whose text went on the DHT floor or into a
35571
+ * Buttons the contact may not have (WISP 406 · Message Buttons): a question whose text went on the DHT floor or into a
35430
35572
  * hold reached the contact as text alone, and a copy under the same id that comes live later is taken as the one
35431
35573
  * already there. Once the chat is live with an app that shows buttons (`buttons/1`) and takes edits, each such
35432
35574
  * question goes again as an edit of its buttons alone: the same text, so no version and no edit mark. Once per
@@ -35526,7 +35668,7 @@ var GhostlyNode = class GhostlyNode {
35526
35668
  }
35527
35669
  await this.outboxFor(linkId).transmit(messageId, { manual: true });
35528
35670
  }
35529
- /** The contact is away, and both sides chose to hold what is sent meanwhile (WISP 4xx). */
35671
+ /** The contact is away, and both sides chose to hold what is sent meanwhile (WISP 404). */
35530
35672
  holdingFor(live) {
35531
35673
  return !!live.stored.profile && !!live.link && !live.link.isDataLinkOpen && this.hold.canHold(live.stored.id);
35532
35674
  }
@@ -35875,7 +36017,7 @@ var GhostlyNode = class GhostlyNode {
35875
36017
  ...forwarded && { forwarded }
35876
36018
  };
35877
36019
  const wire = reply && pairedWireReply(reply);
35878
- const live = this.links.get(linkId);
36020
+ const live = this.linksAdding.has(linkId) ? await this.linkWhenAdded(linkId) : this.links.get(linkId);
35879
36021
  const fail = (error) => {
35880
36022
  const transfer = {
35881
36023
  state: "failed",
@@ -36634,7 +36776,7 @@ var GhostlyNode = class GhostlyNode {
36634
36776
  return this.groups.messages(groupId);
36635
36777
  }
36636
36778
  /**
36637
- * Presses a button of a message someone else sent to a chat or a group (`group:<id>`), WISP 4xx · Message Buttons:
36779
+ * Presses a button of a message someone else sent to a chat or a group (`group:<id>`), WISP 406 · Message Buttons:
36638
36780
  * a reply to it whose text is the button's label and whose `r` names the button, so the author learns who pressed
36639
36781
  * what, and an app without buttons reads an ordinary reply. Refused: my own message, a button it does not have,
36640
36782
  * buttons the author closed, a question I already answered with a `once` button, a second press within a second.
@@ -36725,7 +36867,7 @@ var GhostlyNode = class GhostlyNode {
36725
36867
  result.error ??= error;
36726
36868
  };
36727
36869
  const groupId = to.startsWith("group:") ? to.slice(6) : void 0;
36728
- const live = groupId ? void 0 : this.links.get(to);
36870
+ const live = groupId ? void 0 : await this.linkWhenAdded(to);
36729
36871
  if (groupId ? !this.membership(groupId) : !live || live.stored.group !== void 0) {
36730
36872
  note(groupId ? "You are not in this group" : "Chat not found");
36731
36873
  continue;
@@ -37099,7 +37241,7 @@ var GhostlyNode = class GhostlyNode {
37099
37241
  if (typing === true && this.settings.sendTyping !== false) live.link?.setTyping(true, typingActivity(kind, status));
37100
37242
  else live.link?.setTyping(false);
37101
37243
  }
37102
- /** WISP 9xx · Group Mesh § Typing: the same word in a private group, on its edges; a community says nothing yet. */
37244
+ /** WISP 902 · Group Mesh § Typing: the same word in a private group, on its edges; a community says nothing yet. */
37103
37245
  setGroupTyping({ groupId, typing, kind, status }) {
37104
37246
  if (typeof groupId !== "string") return;
37105
37247
  if (typing === true && this.settings.sendTyping !== false) this.groups.setTyping(groupId, true, typingActivity(kind, status));
@@ -37851,6 +37993,7 @@ var GhostlyNode = class GhostlyNode {
37851
37993
  const paying = params.target.method === "cashu" ? mintNetwork(params.target.provider) : walletNetworkOf(params.target.network);
37852
37994
  if (params.network && params.network !== paying) throw new Error(crossNetwork(params.network, paying));
37853
37995
  if (params.linkId) {
37996
+ await this.linkWhenAdded(params.linkId);
37854
37997
  const link = this.paymentLink(params.linkId);
37855
37998
  if (!link) throw new Error("The peer is offline");
37856
37999
  const payee = this.links.get(params.linkId)?.stored.peerPubKeyZ32 ?? parsePayLink(params.linkId)?.member ?? "";
@@ -37951,8 +38094,8 @@ var GhostlyNode = class GhostlyNode {
37951
38094
  }).catch(() => []);
37952
38095
  }
37953
38096
  /** `network`: the Cashu card of that network sends (its mints). `confirmedReal`: required on Mainnet. */
37954
- sendPayment(params) {
37955
- const live = this.links.get(params.linkId);
38097
+ async sendPayment(params) {
38098
+ const live = await this.linkWhenAdded(params.linkId);
37956
38099
  if (live && this.holdingFor(live)) throw new Error("Ecash is not held for an away contact. Send a request instead, or wait until they are back.");
37957
38100
  return this.desk.send({
37958
38101
  ...params,
@@ -37961,7 +38104,8 @@ var GhostlyNode = class GhostlyNode {
37961
38104
  }
37962
38105
  /** `network`: the card's network; the request is paid only by a wallet of that network. */
37963
38106
  /** `card`: the Lightning card the request's invoice comes from; absent, the network's default for receiving. */
37964
- requestPayment(params) {
38107
+ async requestPayment(params) {
38108
+ await this.linkWhenAdded(params.linkId);
37965
38109
  return this.desk.request({
37966
38110
  linkId: params.linkId,
37967
38111
  amount: params.amount,
@@ -37973,7 +38117,7 @@ var GhostlyNode = class GhostlyNode {
37973
38117
  ...params.rail === "cashu" || params.rail === "lightning" ? { rail: params.rail } : {}
37974
38118
  });
37975
38119
  }
37976
- /** A request any member of a group may pay, once (WISP 9xx § Payments). */
38120
+ /** A request any member of a group may pay, once (WISP 902 § Payments). */
37977
38121
  requestGroupPayment(params) {
37978
38122
  const group = this.groups.views().find((g) => g.id === params.groupId);
37979
38123
  if (group?.status !== "active") throw new Error("You are not in this group");
@@ -37984,8 +38128,9 @@ var GhostlyNode = class GhostlyNode {
37984
38128
  });
37985
38129
  }
37986
38130
  /** Paying on a card without a request (Ark, Bark, Spark, USDT, on-chain): the contact's app answers with one, on this card's network. */
37987
- askToPay(params) {
38131
+ async askToPay(params) {
37988
38132
  if (params.method !== "arkade" && params.method !== "usdt" && params.method !== "bark" && params.method !== "bitcoin" && params.method !== "fedimint" && params.method !== "spark") throw new Error("Only Ark, Bark, Spark, USDT, on-chain Bitcoin and Fedimint are paid this way");
38133
+ await this.linkWhenAdded(params.linkId);
37989
38134
  return this.desk.ask({
37990
38135
  ...params,
37991
38136
  network: this.net(params.network)
@@ -38621,10 +38766,21 @@ var GhostlyNode = class GhostlyNode {
38621
38766
  }
38622
38767
  }
38623
38768
  /**
38769
+ * Whether a device code may be used in this profile now, asked before the person presses Add (WISP 06 § User
38770
+ * experience): `ready`, or why not: `set` (this profile is already on several devices), `in-use` (it holds something,
38771
+ * `inUse`), `loading` (what it holds is not known yet). The page says up front that a new profile is made for the
38772
+ * code; `deviceEnrollJoin` still decides, and refuses as before.
38773
+ */
38774
+ async deviceEnrollReady() {
38775
+ if (this.starting) await this.starting;
38776
+ if (await readDeviceRecord(databaseName()).catch(() => null)) return "set";
38777
+ return this.inUse() ?? "ready";
38778
+ }
38779
+ /**
38624
38780
  * Whether this profile holds anything a person would lose if it became a standby here: a chat, a group, an identity,
38625
- * money or a payment, or a wallet with keys of its own. The wallets a new profile gets by itself (Mainnet Cashu and
38626
- * USDT, `walletSetup.ts`) count only once they hold, wait for or have moved money: otherwise no new install could
38627
- * join. `loading`: what they hold is not known yet (the engine is starting, or the USDT balance was not read).
38781
+ * money or a payment, or a wallet with keys of its own. The wallets a new profile gets by itself (Mainnet Cashu, with
38782
+ * the Cashu mints' Lightning card, and USDT, `walletSetup.ts`) count only once they hold, wait for or have moved
38783
+ * money: otherwise no new install could join. `loading`: what they hold is not known yet (the engine is starting, or the USDT balance was not read).
38628
38784
  */
38629
38785
  inUse() {
38630
38786
  if (this.links.size > 0 || this.groups.views().length > 0 || this.identities.views().length > 0) return "in-use";
@@ -38633,7 +38789,7 @@ var GhostlyNode = class GhostlyNode {
38633
38789
  const networks = [this.walletView, ...Object.values(this.walletView.networks ?? {})];
38634
38790
  const holds = (network) => !!network && (money(network.balance) || (network.history?.length ?? 0) > 0 || (network.awaiting?.length ?? 0) > 0 || money(network.setAside) || money(network.openSwaps) || money(network.swapsAmount) || money(network.unconfirmed) || !!network.usdt?.configured && (money(network.usdt.balance) || money(network.usdt.gasBalance)));
38635
38791
  if (networks.some(holds)) return "in-use";
38636
- const firstRun = (wallet) => wallet.network === "mainnet" && (wallet.type === "cashu" || wallet.type === "usdt");
38792
+ const firstRun = (wallet) => wallet.network === "mainnet" && (wallet.type === "cashu" || wallet.type === "usdt" || wallet.type === "lightning" && wallet.card === "cashu");
38637
38793
  if ((this.walletView.wallets ?? []).some((wallet) => !firstRun(wallet))) return "in-use";
38638
38794
  return networks.some((network) => !!network?.usdt?.configured && (network.usdt.locked || !network.usdt.read)) ? "loading" : null;
38639
38795
  }
@@ -38647,6 +38803,11 @@ var GhostlyNode = class GhostlyNode {
38647
38803
  }
38648
38804
  /** The giver of this active device: answers a pull, makes a push, runs pass 1 while this engine goes on. */
38649
38805
  handoffGiver = null;
38806
+ /** A call is on in one of the pages (`setCallOn`, told by the host for all of them): a handoff waits for it to end. */
38807
+ callOn = false;
38808
+ setCallOn({ on }) {
38809
+ this.callOn = on === true;
38810
+ }
38650
38811
  async startHandoff() {
38651
38812
  const host = handoffProfileHost(), links = this.deviceLinks;
38652
38813
  if (!host || !links || this.handoffGiver || this.options.singleDevice) return;
@@ -38733,6 +38894,7 @@ var GhostlyNode = class GhostlyNode {
38733
38894
  * or anything this build cannot judge. Null: the wallets move or stay home by their plan.
38734
38895
  */
38735
38896
  async handoffBusy(taker) {
38897
+ if (this.callOn) return { why: "call" };
38736
38898
  if (this.limitedMode || !this.walletsStarted) return { why: "loading" };
38737
38899
  await this.refreshWallet().catch(() => {});
38738
38900
  const { refusal } = await this.handoffPlan(taker);
@@ -38890,6 +39052,15 @@ var GhostlyNode = class GhostlyNode {
38890
39052
  async existingLink(id) {
38891
39053
  return await this.linksAdding.get(id) ?? id;
38892
39054
  }
39055
+ /**
39056
+ * The chat by its id, once it has started if it is still being added: the app may send to it by that id (a message,
39057
+ * a file, a forward, a payment) right after a reload, and that send waits for the chat rather than say "You are
39058
+ * offline" for that moment. A chat not being added is returned at once, offline or not; one whose adding failed is gone.
39059
+ */
39060
+ async linkWhenAdded(linkId) {
39061
+ await this.linksAdding.get(linkId)?.catch(() => {});
39062
+ return this.links.get(linkId);
39063
+ }
38893
39064
  addLink(params, inviteCode) {
38894
39065
  const id = identityFromSeedB64(params.seedB64).pubKeyZ32.slice(0, 16);
38895
39066
  const adding = this.addLinkNow(params, inviteCode).finally(() => {
@@ -39256,7 +39427,7 @@ var GhostlyNode = class GhostlyNode {
39256
39427
  if (native) this.ensureNativeEndpoints(linkId);
39257
39428
  }
39258
39429
  /**
39259
- * Whether a group's link (an edge, an entry session) runs native endpoints (WISP 9xx § Transports): where this app has
39430
+ * Whether a group's link (an edge, an entry session) runs native endpoints (WISP 902 § Transports): where this app has
39260
39431
  * no WebRTC (the Linux Desktop), and where the member's has none, as its packet said. Between two apps that have
39261
39432
  * WebRTC it runs none, as before: a group of eight would otherwise hold seven listeners per transport for nothing.
39262
39433
  */
@@ -39269,7 +39440,7 @@ var GhostlyNode = class GhostlyNode {
39269
39440
  * (`DirectEvidence`): a network that lets no direct connection through (a VPN, a firewall, on either side). WebRTC is
39270
39441
  * all an edge between two apps that have it runs, and there is no TURN server unless someone set one, so the edge
39271
39442
  * never went live, and the member stayed unreachable for as long as that network lasted. The edge starts again as on an
39272
- * app with no WebRTC (WISP 9xx § Transports): it runs its native endpoints and says so in its packet (`_tr`, with no
39443
+ * app with no WebRTC (WISP 902 § Transports): it runs its native endpoints and says so in its packet (`_tr`, with no
39273
39444
  * `webrtc/1`), the member's app starts its own for it as it does for a Linux Desktop, and the two meet over Iroh
39274
39445
  * through its relay. For this run of the app only.
39275
39446
  */
@@ -39318,6 +39489,8 @@ var GhostlyNode = class GhostlyNode {
39318
39489
  statusCardSupport: true,
39319
39490
  buttonsSupport: true,
39320
39491
  wakeSupport: true,
39492
+ upgradeSupport: true,
39493
+ peerUpgrades: () => !!live.caps?.peer?.capabilities.includes(UPGRADE_CAPABILITY),
39321
39494
  servicesSupport: this.options.servicesSupport ?? (this.options.platform ?? "web") !== "web",
39322
39495
  dht: stored.profile ? {
39323
39496
  state: stored.dhtDeliveryState,
@@ -39716,6 +39889,7 @@ var GhostlyNode = class GhostlyNode {
39716
39889
  DHT_FILE_CAPABILITY,
39717
39890
  ...live?.stored.hold?.enabled ? [HOLD_CAPABILITY] : [],
39718
39891
  "files/2",
39892
+ UPGRADE_CAPABILITY,
39719
39893
  ...GhostlyNode.testNoEdit() ? [] : [EDIT_CAPABILITY],
39720
39894
  ...cashu || lightning ? ["payments/1"] : [],
39721
39895
  ...cashu ? ["payments-cashu/1"] : [],
@@ -39772,7 +39946,7 @@ var GhostlyNode = class GhostlyNode {
39772
39946
  get nativeFactories() {
39773
39947
  const relay = this.relaysHyperdht ? this.hyperdhtRelay : "";
39774
39948
  return {
39775
- ...relay ? { "hyperdht/1": async (seedB64) => (await import("./hyperdhtRelay-CVLCuqVr.js")).createRelayedHyperEndpoint(seedB64, relay) } : {},
39949
+ ...relay ? { "hyperdht/1": async (seedB64) => (await import("./hyperdhtRelay-BoxPHp7v.js")).createRelayedHyperEndpoint(seedB64, relay) } : {},
39776
39950
  ...this.options.nativeTransports,
39777
39951
  ...this.ownIrohRelays ? { "iroh/1": (seedB64) => this.options.nativeTransports["iroh/1"](seedB64, this.settings.irohRelays?.length ? { relays: [...this.settings.irohRelays] } : {}) } : {},
39778
39952
  ...this.options.irohWeb ? { "iroh/1": (seedB64) => createIrohWebEndpoint(seedB64, { relays: this.irohRelays }) } : {}
@@ -40324,6 +40498,11 @@ var EngineServer = class {
40324
40498
  * it too: the extension's peer runs with no tab open, and a tab opened during the ring used to show no call.
40325
40499
  */
40326
40500
  offers = /* @__PURE__ */ new Map();
40501
+ /**
40502
+ * The clients a call is on in (`setCallOn`): the engine hears whether any is, so a handoff waits for every page's
40503
+ * call (WISP 06 § States and events). A client that goes away (a tab closed mid-call) is taken out with it.
40504
+ */
40505
+ onCall = /* @__PURE__ */ new Set();
40327
40506
  constructor(options = {}) {
40328
40507
  this.node = new GhostlyNode({
40329
40508
  onAttention: (event) => this.broadcast({
@@ -40393,6 +40572,7 @@ var EngineServer = class {
40393
40572
  this.clients.delete(client);
40394
40573
  this.histories.delete(client);
40395
40574
  if (this.clients.size === 0) this.node.setActiveLink({ linkId: null });
40575
+ if (this.onCall.delete(client)) this.node.setCallOn({ on: this.onCall.size > 0 });
40396
40576
  }
40397
40577
  async handle(client, request) {
40398
40578
  if (request?.kind !== "request") return;
@@ -40401,12 +40581,18 @@ var EngineServer = class {
40401
40581
  id: request.id
40402
40582
  };
40403
40583
  if (request.method === "setCallSignal") this.offers.delete(request.params?.linkId ?? "");
40584
+ let params = request.params;
40585
+ if (request.method === "setCallOn") {
40586
+ if (request.params?.on === true) this.onCall.add(client);
40587
+ else this.onCall.delete(client);
40588
+ params = { on: this.onCall.size > 0 };
40589
+ }
40404
40590
  try {
40405
40591
  await this.ready;
40406
40592
  const method = this.node[request.method];
40407
40593
  if (typeof method !== "function") throw new Error(`Unknown method: ${request.method}`);
40408
40594
  if (this.node.limited && !LIMITED_MODE_METHODS.has(String(request.method))) throw new Error(LIMITED_MODE_ERROR);
40409
- response.result = await method.call(this.node, request.params);
40595
+ response.result = await method.call(this.node, params);
40410
40596
  } catch (error) {
40411
40597
  response.error = error instanceof Error ? error.message : String(error);
40412
40598
  }
@@ -40493,4 +40679,4 @@ function signalKind(signal) {
40493
40679
  //#endregion
40494
40680
  export { EngineServer, hyperdhtRelayProblem as n, server_exports as t };
40495
40681
 
40496
- //# sourceMappingURL=server-vGoe9MET.js.map
40682
+ //# sourceMappingURL=server-CXFTDnFI.js.map