@ghostlytools/cli 1.1.5 → 1.1.6

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.
@@ -1,8 +1,8 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { $a as parseLocalTarget, $i as parseLinkPreview, $o as readHandoffPake, $r as EDIT_CAPABILITY, $s as readTurnPacket, $t as TEST_USDT_FAUCET_AMOUNT, A as knockIdentity, Ac as toZ32, Ao as handoffAttemptFailed, As as encodeInviteCode, B as GROUP_READ_NOTE, Bn as decodeBolt11, Bo as handoffManifestFrames, Br as readPin, Bs as devicePingFrame, C as communityHasFrame, Ca as statusCardText, Cc as verify, Ci as revokesIdentity, Co as HANDOFF_PAKE, Cr as readLobby, Cs as verifyEnrollProof, D as decodeGroupEntryLink, Da as receivedTimestamp, Dc as fromBase64Url, Di as validExternalKey, Do as HANDOFF_VERIFIED, Dr as transferEnded, Ds as OwnInviteError, Ea as heardTime, Ec as bytesEqual, Ei as emptyProofLedger, Eo as HANDOFF_TIMINGS, Er as FILE_LIMITS, Es as readDeviceInvite, F as GroupWakeLimiter, Fa as isAppRef, Fi as DHT_FILE_CAPABILITY, Fo as handoffContextProof, Fr as PIN_LIMITS, Fs as HANDOFF_CAPABILITY, G as carryMentions, Ga as verifyCommitSignature, Gi as fileMessageText, Gn as isFederationId, Go as handoffStreamKey, Gr as parseWakeFrame, Gs as TURN_RAISE_READ_TIMEOUT_MS, Hn as cashuRequestPayload, Ho as handoffPakeFrame, Hr as WakeLimiter, Hs as seedSigner, Ht as didDhtFromPublicKey, I as groupWakeFrame, Ia as readAppRevocations, Ii as dhtFileId, Io as handoffDataFrame, Ir as mayPin, Is as deviceEchoFrame, J as groupName, Ji as parseVideoMeta, Jn as GhostLink, Jo as readHandoffAck, Jr as checkPushEndpoint, Js as turnPutSummary, Jt as EVM_TEST_CHAINS, K as parseCommunityEdit, Ka as edgeParams, Ki as parseVoiceMeta, Kn as parseCashuRequestPayload, Ko as handoffVersions, Kr as relayRequest, Ks as TURN_SETTLE_MS, Kt as signDidDhtPacket, L as groupWakes, Li as parseImageMeta, Lo as handoffDigest, Lr as nextPinNumber, Ls as deviceEchoNonce, M as mergeKnocks, Ma as appRef, Mc as utf8Encode, Mo as handoffBusyFrame, Ms as inviteOwnership, N as readKnocks, Ni as DHT_MESSAGE_TTL, No as handoffCancelFrame, Nr as typingActivity, Ns as DEVICES_CAPABILITY, O as encodeGroupEntryLink, Oa as shownTime, Oc as randomBytes, Oi as TRANSPORTS, Oo as filePartName, Or as transferStalled, Os as createChatInvite, Pa as isAppKey, Pi as peekDhtMailbox, Po as handoffContext, Ps as ENROLL_CAPABILITY, Qa as formatLocalTarget, Qi as dhtEditId, Qn as CarriedTransport, Qo as readHandoffHello, Qs as nextTurnPosition, Qt as SEPOLIA_TEST_USDT_FAUCET, R as parseGroupWakeFrame, Rn as engineError, Ro as handoffEphemeral, Rr as pinIsNewer, Rs as deviceKeyZ32, S as GROUP_READ_NOTE_COMMUNITY, Sa as sameButtonText, Sc as publicKeyFromZ32, Si as receivedIdentityStatus, So as HANDOFF_OFFER, Sr as readBeaconHead, Ss as readEnrollHello, T as encodeCommunityLink, Ta as claimedTime, Tc as sanitizeAvatar, Ti as PeerProofs, To as HANDOFF_REQUEST, Tr as ChatFiles, Ts as deviceInviteJoinerParams, Ua as MEMBER_KEY, Un as fedimintRequestPayload, Uo as handoffReleaseFrame, Ur as deviceWakeRequest, Us as webCryptoSigner, Ut as encodeDidDhtDeactivated, V as GroupSession, Va as GROUP_MEMBER_CAP, Vn as ENDPOINT, Vo as handoffOfferFrame, Vr as WAKE_CALL_INTERVAL_MS, Vs as newDeviceSetSecret, Vt as didDhtDocument, W as GROUP_EDIT_TEXT_BYTES, Wa as rosterHas, Wn as findEndpoint, Wo as handoffRetryAfter, Wr as newWakeToken, Wt as encodeDidDhtPacket, Xa as presenceSeenAt, Xi as EDIT_RATE_WINDOW_MS, Xn as LIVENESS_PING_MS, Xo as readHandoffDone, Xr as chatAppId, Xs as TURN_MAX, Xt as PaymentPreflightError, Ya as RELAY_POLL_INTERVALS, Yi as EDIT_BUFFER_MS, Yn as INVITE_TAKEN, Yo as readHandoffCancel, Yr as vapidKeysMatch, Ys as TURN_LAST_SEQUENCE, Yt as ONCHAIN_PROVIDER, Za as GhostlyHttpError, Zi as RateWindow, Zn as pairedMessageFrame, Zo as readHandoffHave, Zr as isAppVersion, Zs as firstDeviceSetSecret, Zt as SEPOLIA_TEST_USDT, _ as groupTypingEpoch, _o as HANDOFF_ACK, _r as mergeLobby, _s as enrollDoneFrame, aa as ReactionWindow, ac as RelayTransport, ai as HoldRefusedError, an as validatePaymentTarget, ao as RTC_CONFIG, ar as MESH_HUBS, as as readSetUpdate, b as COMMUNITY_LIMITS, bc as identityFromSeed, bi as identityStatement, bo as HANDOFF_HAVE, br as rankHubs, bs as enrollProofFrame, c as APP_BUNDLE_LIMITS, ca as reactionEmoji, ci as CapsExchange, cn as SPARK_PROVIDER, cr as pickMeshHubs, cs as signTombstone, da as validReactionNumber, dc as isDiscoveryBudgetError, di as dialDescriptors, dn as isBitcoinAddress, do as parseCallSignal, dr as beaconRecords, ea as forwardedAgain, ec as signTurnPacket, ei as UPGRADE_CAPABILITY, en as USDT_PUBLIC_RPC, eo as webLocalFetch, es as readHandoffTurnFrame, fa as wireReaction, fc as withRequestOptions, fr as doorHubs, g as GroupTypingBoard, ga as BUTTON_ID, gi as IdentityExchange, go as serviceIdFromName, gr as mergeBeacon, gs as enrollDigits, ha as replySnippet, hc as createRelayPayload, ho as LEGACY_SERVICES, hr as lobbyRecords, hs as enrollCancelReason, ht as normalizeAtprotoHandle, i as planAppUpdate, ia as REACTION_LIMITS, ic as DEFAULT_RELAYS, ii as HoldKeys, in as formatPaymentAmount, ir as readGroupSignal, j as knockRecords, ja as appFingerprint, jc as utf8Decode, jo as handoffAttemptSucceeded, jr as TypingSender, k as entryParams, kc as toBase64Url, ki as automaticTransport, ko as handoffAttemptAllowed, ks as decodeInviteCode, l as APP_BUNDLE_MAGIC, la as reactionIsNewer, lc as traceLink, li as DHT_TEXT_CAPABILITY, ln as sparkAddressKind, lr as COMMUNITY_TOPOLOGY, ls as verifySetUpdate, m as readAppBundle, ma as readReply, mi as IDENTITY_REVOCATION_LABEL, mo as HTTP_SERVICE_PROTO, mr as lobbyKeys, ms as enrollCancelFrame, mt as isAtprotoDid, n as appUpdateDecision, na as COMMUNITY_REACTION_FRAME, nc as turnKeys, ni as HOLD_DATED_BACK_MS, nn as assertTokenUnits, no as decodeControl, ns as sealHandoffPiece, o as readAppStore, oa as nextReactionNumber, oc as currentRelays, oi as newMailbox, on as walletNetworkOf, or as mayBeHub, os as setAckFrame, p as isAppUrl, pa as pairedReplyAuthor, pc as PacketTooLargeError, pn as isPubkyKey, po as emptyLinkRecords, pr as freshHubs, q as mentionsMember, qa as DirectPathWatch, qn as parseFedimintRequestPayload, qo as newHandoffId, qr as wakeRequest, qs as classifyTurnRead, qt as ETHEREUM_USDT, r as checkAppBeforeRun, ra as GROUP_REACTION_FRAME, rc as turnName, ri as HOLD_LIMITS, rn as assertWholeSats, ro as encodeControl, rr as groupSignalFrame, sa as queueReaction, sc as normalizeRelayUrl, si as readManifest, sn as SPARK_NETWORKS, sr as meshHubs, ss as setUpdateFrame, t as appStoreDecision, ta as readForwarded, tc as signTurnRelease, ti as HOLD_CAPABILITY, tn as WALLET_NETWORKS, to as LIMITS, ts as readHandoffVerified, u as appDigest, ua as readReaction, uc as budgetRetryMs, ui as capsDescriptors, un as sparkInvoiceDetails, ur as beaconKeys, v as openGroupTyping, vi as emptyIdentityLedger, vo as HANDOFF_CANCEL, vr as newerHead, vs as enrollGrantFrame, w as decodeCommunityLink, wa as arrivalKey, wi as lastSharedWithMe, wo as HANDOFF_PIECE_BYTES, wr as shouldBeHub, ws as createDeviceInvite, x as CommunitySession, xa as checkStatusCard, xc as identityFromSeedB64, xi as newIdentityBinding, xo as HANDOFF_HELLO, xr as readBeacon, xs as readEnrollGrant, y as sealGroupTyping, ya as STATUS_CARD_LIMITS, yc as createIdentity, yi as identityRevocationValue, yo as HANDOFF_DONE, yr as pickHubs, ys as enrollHelloFrame, za as canonicalJson, zi as safeBlobType, zn as engineText, zo as handoffHelloFrame, zr as pinNumberHolds, zs as deviceLinkPairing } from "./src-C6qQYrAH.js";
2
+ import { $i as formatFileSize, $n as GROUP_FILE_LIMITS, $o as handoffEphemeral, $r as WAKE_CALL_INTERVAL_MS, $s as deviceKeyZ32, $t as SEPOLIA_TEST_USDT_FAUCET, A as entryParams, Aa as replySnippet, Ac as createRelayPayload, Ai as IdentityExchange, Ao as LEGACY_SERVICES, Ar as newerHead, As as enrollCancelReason, Ba as claimedTime, Bc as sanitizeAvatar, Bi as emptyProofLedger, Bn as engineText, Bo as HANDOFF_REQUEST, Br as transferStalled, Bs as deviceInviteJoinerParams, C as GROUP_READ_NOTE_COMMUNITY, Ca as reactionEmoji, Ci as DHT_TEXT_CAPABILITY, Cr as beaconRecords, Cs as signTombstone, Da as wireReaction, Dc as withRequestOptions, Dr as lobbyRecords, E as encodeCommunityLink, Ea as validReactionNumber, Ec as isDiscoveryBudgetError, Eo as parseCallSignal, Er as lobbyKeys, Fc as identityFromSeed, Fi as newIdentityBinding, Fo as HANDOFF_HAVE, Fr as readLobby, Fs as enrollProofFrame, G as GROUP_EDIT_TEXT_BYTES, Gc as toZ32, Gn as findEndpoint, Go as handoffAttemptFailed, Gr as typingActivity, Gs as encodeInviteCode, Gt as encodeDidDhtPacket, H as GroupSession, Ha as receivedTimestamp, Hc as fromBase64Url, Hi as TRANSPORTS, Hn as ENDPOINT, Ho as HANDOFF_VERIFIED, Hs as OwnInviteError, Ht as didDhtDocument, I as GroupWakeLimiter, Ia as checkStatusCard, Ic as identityFromSeedB64, Ii as receivedIdentityStatus, Io as HANDOFF_HELLO, Ir as shouldBeHub, Is as readEnrollGrant, J as mentionsMember, Ji as peekDhtMailbox, Jn as parseFedimintRequestPayload, Jo as handoffCancelFrame, Jr as mayPin, Js as DEVICES_CAPABILITY, Jt as ETHEREUM_USDT, K as carryMentions, Ka as appFingerprint, Kc as utf8Decode, Kn as isFederationId, Ko as handoffAttemptSucceeded, L as groupWakeFrame, La as sameButtonText, Lc as publicKeyFromZ32, Li as revokesIdentity, Lo as HANDOFF_OFFER, Lr as ChatFiles, Ls as readEnrollHello, M as knockRecords, Mi as emptyIdentityLedger, Mo as HANDOFF_ACK, Mr as rankHubs, Ms as enrollDoneFrame, N as mergeKnocks, Ni as identityRevocationValue, No as HANDOFF_CANCEL, Nr as readBeacon, Ns as enrollGrantFrame, O as decodeGroupEntryLink, Oa as pairedReplyAuthor, Oc as PacketTooLargeError, Oi as IDENTITY_REVOCATION_LABEL, Oo as emptyLinkRecords, Or as mergeBeacon, P as readKnocks, Pa as STATUS_CARD_LIMITS, Pc as createIdentity, Pi as identityStatement, Po as HANDOFF_DONE, Pr as readBeaconHead, Ps as enrollHelloFrame, Qn as pairedMessageFrame, Qo as handoffDigest, Qr as readPin, Qs as deviceEchoNonce, Qt as SEPOLIA_TEST_USDT, R as groupWakes, Ra as statusCardText, Rc as verify, Ri as lastSharedWithMe, Ro as HANDOFF_PAKE, Rr as FILE_LIMITS, Rs as verifyEnrollProof, S as CommunitySession, Sa as queueReaction, Sc as normalizeRelayUrl, Si as CapsExchange, Sr as beaconKeys, Ss as setUpdateFrame, T as decodeCommunityLink, Ta as readReaction, Tc as budgetRetryMs, Ti as dialDescriptors, Tr as freshHubs, Ua as shownTime, Uc as randomBytes, Ui as automaticTransport, Un as cashuRequestPayload, Uo as filePartName, Ur as TypingSender, Us as createChatInvite, Ut as didDhtFromPublicKey, V as GROUP_READ_NOTE, Va as heardTime, Vc as bytesEqual, Vi as validExternalKey, Vn as decodeBolt11, Vo as HANDOFF_TIMINGS, Vs as readDeviceInvite, Wc as toBase64Url, Wn as fedimintRequestPayload, Wo as handoffAttemptAllowed, Ws as decodeInviteCode, Wt as encodeDidDhtDeactivated, Xa as isAppRef, Xi as dhtFileId, Xn as INVITE_TAKEN, Xo as handoffContextProof, Xr as pinIsNewer, Xs as HANDOFF_CAPABILITY, Xt as ONCHAIN_PROVIDER, Y as groupName, Ya as isAppKey, Yi as DHT_FILE_CAPABILITY, Yn as GhostLink, Yo as handoffContext, Yr as nextPinNumber, Ys as ENROLL_CAPABILITY, Yt as EVM_TEST_CHAINS, Za as readAppRevocations, Zi as parseImageMeta, Zn as LIVENESS_PING_MS, Zo as handoffDataFrame, Zr as pinNumberHolds, Zs as deviceEchoFrame, Zt as PaymentPreflightError, _ as GroupTypingBoard, _a as COMMUNITY_REACTION_FRAME, _c as turnKeys, _i as HOLD_LIMITS, _o as decodeControl, _r as MESH_HUBS, _s as sealHandoffPiece, ai as wakeRequest, an as formatPaymentAmount, ao as rosterHas, as as handoffRetryAfter, b as sealGroupTyping, ba as ReactionWindow, bc as RelayTransport, bi as newMailbox, bo as RTC_CONFIG, br as pickMeshHubs, bs as readSetUpdate, c as APP_BUNDLE_LIMITS, cc as classifyTurnRead, ci as chatAppId, cn as SPARK_NETWORKS, co as DirectPathWatch, cs as newHandoffId, d as appViewOf, da as EDIT_RATE_WINDOW_MS, dc as TURN_MAX, di as BUTTONS_CAPABILITY, dn as sparkInvoiceDetails, do as presenceSeenAt, ds as readHandoffDone, ea as safeBlobType, ec as deviceLinkPairing, ei as WakeLimiter, en as TEST_USDT_FAUCET_AMOUNT, eo as canonicalJson, es as handoffHelloFrame, fa as RateWindow, fc as firstDeviceSetSecret, fi as EDIT_CAPABILITY, fn as isBitcoinAddress, fo as GhostlyHttpError, fs as readHandoffHave, ga as readForwarded, gc as signTurnRelease, gi as HOLD_DATED_BACK_MS, go as LIMITS, gr as readGroupSignal, gs as readHandoffVerified, gt as normalizeAtprotoHandle, h as readAppBundle, ha as forwardedAgain, hc as signTurnPacket, hi as HOLD_CAPABILITY, ho as webLocalFetch, hr as groupSignalFrame, hs as readHandoffTurnFrame, ht as isAtprotoDid, i as planAppUpdate, ic as webCryptoSigner, ii as relayRequest, in as assertWholeSats, io as MEMBER_KEY, ir as groupFileFallback, is as handoffReleaseFrame, j as knockIdentity, ja as BUTTON_ID, jo as serviceIdFromName, jr as pickHubs, js as enrollDigits, k as encodeGroupEntryLink, ka as readReply, ko as HTTP_SERVICE_PROTO, kr as mergeLobby, ks as enrollCancelFrame, l as APP_BUNDLE_MAGIC, la as parseVideoMeta, lc as turnPutSummary, li as isAppVersion, ln as SPARK_PROVIDER, ls as readHandoffAck, m as isAppUrl, ma as parseLinkPreview, mc as readTurnPacket, mi as UPGRADE_CAPABILITY, mn as isPubkyKey, mo as parseLocalTarget, ms as readHandoffPake, n as appUpdateDecision, nc as newDeviceSetSecret, ni as newWakeToken, nn as WALLET_NETWORKS, no as GROUP_MEMBER_CAP, ns as handoffOfferFrame, o as readAppStore, oa as fileMessageText, oc as TURN_RAISE_READ_TIMEOUT_MS, oi as checkPushEndpoint, on as validatePaymentTarget, oo as verifyCommitSignature, or as readGroupFileMeta, os as handoffStreamKey, pa as dhtEditId, pc as nextTurnPosition, pi as STATUS_CARD_CAPABILITY, po as formatLocalTarget, ps as readHandoffHello, q as parseCommunityEdit, qa as appRef, qc as utf8Encode, qi as DHT_MESSAGE_TTL, qn as parseCashuRequestPayload, qo as handoffBusyFrame, qr as PIN_LIMITS, qs as inviteOwnership, qt as signDidDhtPacket, r as checkAppBeforeRun, rc as seedSigner, ri as parseWakeFrame, rn as assertTokenUnits, rs as handoffPakeFrame, sa as parseVoiceMeta, sc as TURN_SETTLE_MS, si as vapidKeysMatch, sn as walletNetworkOf, so as edgeParams, ss as handoffVersions, t as appStoreDecision, tc as devicePingFrame, ti as deviceWakeRequest, tn as USDT_PUBLIC_RPC, ts as handoffManifestFrames, u as appDigest, ua as EDIT_BUFFER_MS, uc as TURN_LAST_SEQUENCE, ui as APPS_CAPABILITY, un as sparkAddressKind, uo as RELAY_POLL_INTERVALS, ur as CarriedTransport, us as readHandoffCancel, v as groupTypingEpoch, va as GROUP_REACTION_FRAME, vc as turnName, vi as HoldKeys, vo as encodeControl, vr as mayBeHub, w as communityHasFrame, wa as reactionIsNewer, wc as traceLink, wi as capsDescriptors, wr as doorHubs, ws as verifySetUpdate, x as COMMUNITY_LIMITS, xa as nextReactionNumber, xc as currentRelays, xi as readManifest, xr as COMMUNITY_TOPOLOGY, xs as setAckFrame, y as openGroupTyping, ya as REACTION_LIMITS, yc as DEFAULT_RELAYS, yi as HoldRefusedError, yr as meshHubs, z as parseGroupWakeFrame, za as arrivalKey, zi as PeerProofs, zn as engineError, zo as HANDOFF_PIECE_BYTES, zr as transferEnded, zs as createDeviceInvite } from "./src-DtxVo2Rm.js";
3
3
  import { a as clearProfileStores, c as fileStore, d as store, f as transact, i as STORES$1, l as openDb, n as APP_STORAGE_SCOPE_INDEX, o as databaseName, p as wrap, r as CARD_INDEX, s as deleteDatabase, t as APP_STORAGE_CHAT_INDEX, u as profileOpenFailure } from "./idb-BIdus0WX.js";
4
4
  import { a as fileBytesOf, c as removeFileBytes, i as fileBytes, l as FILE_BYTES_STEP, n as blobDigest } from "./fileBytes-3AQ2tJzW.js";
5
- import { $ as BACKUP_MEDIA_TYPE, A as requireTurn, At as streamStored, B as networkLabel, C as breezDatabasesFor, Ct as replyTo, D as sourceKey, Dt as readStored$1, E as ProviderSources, Et as FileAppender, F as sealSeed, G as BITCOIN_INVOICE_ON_TESTNET, H as cashuMint, I as unsealSeed, J as fakesLightning, K as DEFAULT_MINTS, L as ModeChanged, M as setSingleWriterGate, N as intentRepository, O as awayFrom, Ot as removeStored, P as newDeviceKey, Q as paysItsOwnInvoices, R as ModeGate, S as breezDatabaseInUse, St as replyRef, T as lightningPaying, Tt as paymentWireId, V as CASHU_MINT_SOURCE, W as WALLET_TYPES, X as isWorthlessMint, Y as isTestMint, Z as mintNetwork, _ as LightningCards, _t as verifyIdentity, a as irohRelayProblem, at as reserveAdapterIds, b as normalizePhrase, bt as pairedWireReply, c as createFailure, ct as SourceConfigError, d as paymentNetwork, dt as networkMode, et as MAX_BACKUP_BYTES, f as paymentNetworksOf, ft as redact, g as CASHU_CARD, gt as boundedIdentityFetch, h as rpcHost, ht as setOwnDidSource, i as createIrohWebEndpoint, it as registeredOnchainProviders, j as setAwayWallets, k as refuseAway, kt as storedSize, l as createTiming, lt as SourceUnreachableError, m as networkReason, mt as resolveAtprotoDid, n as loadFedimintSdk, nt as onAdaptersChanged, o as WALLET_NAMES, ot as NothingSpentError, p as walletInstances, pt as assertPublicServer, q as TEST_MINT, r as DEFAULT_IROH_RELAYS, rt as registeredLightningProviders, s as assertConfirmedReal, st as PROVIDER_PLATFORMS, t as fedimintDatabase, tt as identityProviders, u as crossNetwork, ut as isNothingSpentError, v as cardsKey, vt as chosenResolver, w as dropBreezDatabase, wt as paymentAlias, x as breezDatabase, xt as receivedPairedReply, y as isRecoveryPhrase, yt as groupReply, z as WrongNetworkError } from "../ghostly.mjs";
5
+ import { $ as BACKUP_MEDIA_TYPE, A as requireTurn, At as paymentWireId, B as networkLabel, C as breezDatabasesFor, Ct as storedGroupFiles, D as sourceKey, Dt as replyRef, E as ProviderSources, Et as receivedPairedReply, F as sealSeed, Ft as streamStored, G as BITCOIN_INVOICE_ON_TESTNET, H as cashuMint, I as unsealSeed, J as fakesLightning, K as DEFAULT_MINTS, L as ModeChanged, M as setSingleWriterGate, Mt as readStored$1, N as intentRepository, Nt as removeStored, O as awayFrom, Ot as replyTo, P as newDeviceKey, Pt as storedSize, Q as paysItsOwnInvoices, R as ModeGate, S as breezDatabaseInUse, St as groupOfFile, T as lightningPaying, Tt as pairedWireReply, V as CASHU_MINT_SOURCE, W as WALLET_TYPES, X as isWorthlessMint, Y as isTestMint, Z as mintNetwork, _ as LightningCards, _t as verifyIdentity, a as irohRelayProblem, at as reserveAdapterIds, b as normalizePhrase, bt as groupFileId, c as createFailure, ct as SourceConfigError, d as paymentNetwork, dt as networkMode, et as MAX_BACKUP_BYTES, f as paymentNetworksOf, ft as redact, g as CASHU_CARD, gt as boundedIdentityFetch, h as rpcHost, ht as setOwnDidSource, i as createIrohWebEndpoint, it as registeredOnchainProviders, j as setAwayWallets, jt as FileAppender, k as refuseAway, kt as paymentAlias, l as createTiming, lt as SourceUnreachableError, m as networkReason, mt as resolveAtprotoDid, n as loadFedimintSdk, nt as onAdaptersChanged, o as WALLET_NAMES, ot as NothingSpentError, p as walletInstances, pt as assertPublicServer, q as TEST_MINT, r as DEFAULT_IROH_RELAYS, rt as registeredLightningProviders, s as assertConfirmedReal, st as PROVIDER_PLATFORMS, t as fedimintDatabase, tt as identityProviders, u as crossNetwork, ut as isNothingSpentError, v as cardsKey, vt as chosenResolver, w as dropBreezDatabase, wt as groupReply, x as breezDatabase, xt as groupFileText, y as isRecoveryPhrase, yt as GroupFileDesk, z as WrongNetworkError } from "../ghostly.mjs";
6
6
  import { n as traceJoin } from "./joinTrace-CO8mlhNO.js";
7
7
  import { hkdf } from "@noble/hashes/hkdf.js";
8
8
  import { sha256 } from "@noble/hashes/sha2.js";
@@ -12685,6 +12685,7 @@ var Apps = class {
12685
12685
  version: app.manifest.version,
12686
12686
  title: app.manifest.title,
12687
12687
  permissions: [...app.permissions],
12688
+ view: appViewOf(bundle.manifest),
12688
12689
  entry: utf8Decode(entry)
12689
12690
  };
12690
12691
  }
@@ -12710,6 +12711,7 @@ var Apps = class {
12710
12711
  sequence: app.sequence,
12711
12712
  digest: app.digest,
12712
12713
  permissions: [...app.permissions],
12714
+ view: appViewOf(manifest),
12713
12715
  from: app.from,
12714
12716
  icon: manifest.files.some((f) => f.path === "icon.png"),
12715
12717
  installedAt: app.installedAt,
@@ -21635,14 +21637,17 @@ var db = {
21635
21637
  }
21636
21638
  },
21637
21639
  /**
21638
- * Every message of this profile that carries a task or a routine card, from every chat and group, oldest first (time,
21640
+ * Every message of this profile that carries a task, a routine or a usage card, from every chat and group, oldest first (time,
21639
21641
  * then id). Reads the card index only (`CARD_INDEX`): no chat's history is read, however long. The index follows the
21640
21642
  * rows, so an edit's card, a deleted message and a deleted chat or group are in step with no bookkeeping.
21641
21643
  */
21642
21644
  async getCardMessages() {
21643
21645
  const index = (await store(STORES$1.messages, "readonly")).index(CARD_INDEX);
21644
- const [tasks, routines] = await Promise.all([wrap(index.getAll("task")), wrap(index.getAll("routine"))]);
21645
- return [...tasks, ...routines].sort((a, b) => a.timestamp - b.timestamp || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
21646
+ return (await Promise.all([
21647
+ "task",
21648
+ "routine",
21649
+ "usage"
21650
+ ].map((kind) => wrap(index.getAll(kind))))).flat().sort((a, b) => a.timestamp - b.timestamp || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
21646
21651
  },
21647
21652
  /** Returns false when the message was already stored. */
21648
21653
  async addMessage(message) {
@@ -22275,12 +22280,28 @@ var Communities = class {
22275
22280
  now() {
22276
22281
  return this.time || Date.now();
22277
22282
  }
22283
+ /** Files and voice messages (WISP 503), the desk `Groups` keeps for every group. */
22284
+ files;
22278
22285
  has(groupId) {
22279
22286
  return this.stored.has(groupId);
22280
22287
  }
22288
+ ids() {
22289
+ return [...this.stored.keys()];
22290
+ }
22281
22291
  session(groupId) {
22282
22292
  return this.live.get(groupId)?.session;
22283
22293
  }
22294
+ /** Me in a community I am a member of, for its files: who is in it, and who could read one of its messages. */
22295
+ fileMembership(groupId) {
22296
+ const session = this.live.get(groupId)?.session;
22297
+ if (!session?.isMember) return void 0;
22298
+ return {
22299
+ me: session.myKey,
22300
+ community: true,
22301
+ inRoster: (key) => rosterHas(session.roster, key),
22302
+ couldRead: (key, messageId) => session.couldRead(key, messageId)
22303
+ };
22304
+ }
22284
22305
  /** Is this device a hub of the group? (tests and the load harness) */
22285
22306
  isHub(groupId) {
22286
22307
  const live = this.live.get(groupId);
@@ -22472,7 +22493,7 @@ var Communities = class {
22472
22493
  await this.store.putGroup(group);
22473
22494
  this.host.emit();
22474
22495
  }
22475
- async send(groupId, text, mentions = [], reply, forwarded, card) {
22496
+ async send(groupId, text, mentions = [], reply, forwarded, card, file) {
22476
22497
  const live = this.live.get(groupId);
22477
22498
  if (!live) return {
22478
22499
  error: "You are not in this group yet",
@@ -22483,7 +22504,7 @@ var Communities = class {
22483
22504
  refused: true
22484
22505
  };
22485
22506
  const now = this.now();
22486
- const result = card ? await live.session.sendText(text, this.host.myNick?.(), now, mentions, reply, forwarded, card) : await live.session.sendText(text, this.host.myNick?.(), now, mentions, reply, forwarded);
22507
+ const result = card || file ? await live.session.sendText(text, this.host.myNick?.(), now, mentions, reply, forwarded, card, file) : await live.session.sendText(text, this.host.myNick?.(), now, mentions, reply, forwarded);
22487
22508
  return "error" in result ? { error: result.error } : {
22488
22509
  error: null,
22489
22510
  messageId: result.id
@@ -23587,9 +23608,7 @@ var Communities = class {
23587
23608
  await this.store.putGroup(group);
23588
23609
  },
23589
23610
  broadcast: (frame) => this.hearers(id, frame).filter(([, linkId]) => this.sendTo(linkId, frame)).length,
23590
- direct: (to, frame) => {
23591
- if (this.mayHear(this.live.get(id), to, frame)) this.sendTo(this.host.edges(id).get(to), frame);
23592
- },
23611
+ direct: (to, frame) => this.mayHear(this.live.get(id), to, frame) ? this.sendTo(this.host.edges(id).get(to), frame) : void 0,
23593
23612
  addressed: (to, frame) => {
23594
23613
  const edges = this.host.edges(id), live = this.live.get(id);
23595
23614
  const direct = edges.get(to);
@@ -23604,10 +23623,15 @@ var Communities = class {
23604
23623
  message: async (m) => {
23605
23624
  const timestamp = m.timestamp;
23606
23625
  const mentioned = m.sender !== session.myKey && mentionsMember(m.mentions, session.myKey);
23626
+ const file = m.file && this.files ? await this.files.announced(id, {
23627
+ id: m.id,
23628
+ sender: m.sender,
23629
+ file: m.file
23630
+ }, session.myKey) : void 0;
23607
23631
  const stored = await this.host.storeMessage({
23608
23632
  linkId: MESSAGE_LINK$1(id),
23609
23633
  id: m.id,
23610
- text: m.text,
23634
+ text: file ? groupFileText(m.text, m.file) : m.text,
23611
23635
  sender: m.sender === session.myKey ? "me" : "peer",
23612
23636
  member: m.sender,
23613
23637
  timestamp,
@@ -23615,7 +23639,8 @@ var Communities = class {
23615
23639
  ...mentionFields(m.mentions, mentioned),
23616
23640
  ...m.reply && { replyTo: groupReply(m.reply, session.myKey) },
23617
23641
  ...m.forwarded && { forwarded: m.forwarded },
23618
- ...m.card && { card: m.card }
23642
+ ...m.card && { card: m.card },
23643
+ ...file && { file }
23619
23644
  });
23620
23645
  const came = m.sender === session.myKey ? timestamp : cameAt(timestamp, stored, arrivalNow(this.now()));
23621
23646
  this.lastMessageAt.set(id, Math.max(this.lastMessageAt.get(id) ?? 0, came));
@@ -23623,7 +23648,7 @@ var Communities = class {
23623
23648
  if (m.sender !== session.myKey) this.lastPeerMessageAt.set(id, Math.max(this.lastPeerMessageAt.get(id) ?? 0, came));
23624
23649
  noteCame(this.stored.get(id), came, m.sender !== session.myKey, mentioned);
23625
23650
  },
23626
- app: (m) => this.deliver(id, () => this.host.communityApp?.(id, m.sender, m.frame)),
23651
+ app: (m) => this.deliver(id, () => m.frame.t === "group-have" ? this.files?.communityApp(id, m.sender, m.frame) : this.host.communityApp?.(id, m.sender, m.frame)),
23627
23652
  pair: (m) => this.deliver(id, () => this.host.communityPair?.(id, m.sender, m.payload)),
23628
23653
  changed: () => {
23629
23654
  this.membershipChanged(id);
@@ -23642,7 +23667,11 @@ var Communities = class {
23642
23667
  adminTurn: () => this.host.adminTurn?.(id) ?? Promise.resolve(true),
23643
23668
  relay: (frame) => {
23644
23669
  if (this.live.get(id)?.hub) for (const [, linkId] of this.hearers(id, frame)) this.sendTo(linkId, frame);
23645
- }
23670
+ },
23671
+ ...this.host.linkHandled && { handled: (to) => {
23672
+ const edge = this.host.edges(id).get(to);
23673
+ return edge && this.host.linkHandled ? this.host.linkHandled(edge) : Promise.resolve(false);
23674
+ } }
23646
23675
  });
23647
23676
  this.live.set(id, {
23648
23677
  session,
@@ -24829,6 +24858,8 @@ var Groups = class {
24829
24858
  typings;
24830
24859
  /** Edge signaling through members (WISP 902 · Group Mesh § Signaling through members). */
24831
24860
  signals;
24861
+ /** Files and voice messages in every group, private or community (WISP 503 · Group Files). */
24862
+ files;
24832
24863
  constructor(host, store = db, timings = ENTRY_TIMINGS, communityTimings = COMMUNITY_TIMINGS, random, hubTimings = MESH_HUB_TIMINGS) {
24833
24864
  this.host = host;
24834
24865
  this.store = store;
@@ -24846,6 +24877,27 @@ var Groups = class {
24846
24877
  stored: (id) => this.stored.get(id),
24847
24878
  now: () => this.now()
24848
24879
  });
24880
+ const files = host.groupFiles;
24881
+ if (files) {
24882
+ this.files = new GroupFileDesk({
24883
+ ...files,
24884
+ send: (linkId, frame) => {
24885
+ try {
24886
+ host.sendOnLink(linkId, frame);
24887
+ return true;
24888
+ } catch {
24889
+ return false;
24890
+ }
24891
+ },
24892
+ edges: (groupId) => host.edges(groupId),
24893
+ ready: (linkId) => host.linkReady(linkId, 6),
24894
+ membership: (groupId) => this.fileMembership(groupId),
24895
+ sendApp: (groupId, frame) => this.communities.sendApp(groupId, frame),
24896
+ changed: () => host.emit(),
24897
+ now: () => this.now()
24898
+ });
24899
+ this.communities.files = this.files;
24900
+ }
24849
24901
  }
24850
24902
  async load() {
24851
24903
  const all = await this.store.getGroups();
@@ -24863,6 +24915,7 @@ var Groups = class {
24863
24915
  if (mentionAt) this.lastMentionAt.set(group.id, mentionAt);
24864
24916
  }
24865
24917
  for (const id of this.sessions.keys()) this.reconcileEdges(id);
24918
+ await this.files?.load(all.map((g) => g.id));
24866
24919
  for (const group of this.stored.values()) for (const [, linkId] of this.host.entries(group.id)) if (!group.invitation?.entry || group.invitation.linkId !== linkId) await this.host.closeEdge(linkId);
24867
24920
  }
24868
24921
  /** A group's pin now (WISP 400 § Pinned message), the latest one taken, unpinned or not. */
@@ -25107,6 +25160,58 @@ var Groups = class {
25107
25160
  };
25108
25161
  }
25109
25162
  /**
25163
+ * Announces a file or voice message of mine (WISP 503): `text` is its caption, or the line older apps show
25164
+ * (`groupFileFallback`), `file` its description with the digest of the bytes kept under `fileId`, which this device
25165
+ * then holds and serves. `messageId` is the announcement's.
25166
+ */
25167
+ async sendFile(groupId, text, file, fileId, reply, forwarded) {
25168
+ if (!this.files) return {
25169
+ error: "This app takes no group files",
25170
+ refused: true
25171
+ };
25172
+ const paced = this.files.mayAnnounce(groupId);
25173
+ if (paced) return { error: paced };
25174
+ this.files.outgoing(groupId, fileId, file);
25175
+ let result;
25176
+ if (this.isCommunity(groupId)) result = await this.communities.send(groupId, text, [], reply, forwarded, void 0, file);
25177
+ else {
25178
+ const session = this.sessions.get(groupId);
25179
+ const sent = session ? await session.sendText(text, Date.now(), [], reply, forwarded, void 0, file) : { error: "You are not in this group yet" };
25180
+ if (session) this.typings.say(session, false);
25181
+ result = "error" in sent ? { error: sent.error } : {
25182
+ error: null,
25183
+ messageId: sent.id
25184
+ };
25185
+ }
25186
+ if (result.error) this.files.notSent(groupId, file);
25187
+ return result;
25188
+ }
25189
+ /** The person asks for a group file this device did not fetch by itself, or asks again for one that stopped coming. */
25190
+ downloadFile(fileId) {
25191
+ if (!groupOfFile(fileId, [...this.stored.keys(), ...this.communities.ids()]) || !this.files) throw new Error("No such file");
25192
+ return this.files.download(fileId);
25193
+ }
25194
+ /** An edge's files/3 session opened or closed (WISP 503: the bytes of a group's files go over it). */
25195
+ fileSession(groupId, peerKey, linkId, open) {
25196
+ this.files?.session(groupId, peerKey, linkId, open);
25197
+ }
25198
+ /** A files/3 frame on an edge, from the member it is pinned to. */
25199
+ filesFrame(groupId, peerKey, linkId, frame) {
25200
+ return this.files?.filesFrame(groupId, peerKey, linkId, frame) ?? Promise.resolve();
25201
+ }
25202
+ /** Me in a group I am active in, for its files: who is in it, and who could read one of its messages. */
25203
+ fileMembership(groupId) {
25204
+ if (this.isCommunity(groupId)) return this.communities.fileMembership(groupId);
25205
+ const session = this.sessions.get(groupId);
25206
+ if (session?.status !== "active") return void 0;
25207
+ return {
25208
+ me: session.myKey,
25209
+ community: false,
25210
+ inRoster: (key) => rosterHas(session.roster, key),
25211
+ couldRead: (key, messageId) => session.couldRead(key, messageId)
25212
+ };
25213
+ }
25214
+ /**
25110
25215
  * This side is typing in a private group (with what it is doing), or stopped (WISP 902 · Group Mesh § Typing).
25111
25216
  * Nothing for a community: it does not carry typing yet.
25112
25217
  */
@@ -25347,7 +25452,11 @@ var Groups = class {
25347
25452
  await this.session(groupId).rename(name);
25348
25453
  }
25349
25454
  async forget(groupId) {
25350
- if (this.isCommunity(groupId)) return this.communities.forget(groupId);
25455
+ if (this.isCommunity(groupId)) {
25456
+ await this.communities.forget(groupId);
25457
+ await this.files?.drop(groupId);
25458
+ return;
25459
+ }
25351
25460
  if (this.sessions.get(groupId)?.status === "active") try {
25352
25461
  await this.leave(groupId);
25353
25462
  } catch {}
@@ -25374,6 +25483,7 @@ var Groups = class {
25374
25483
  this.signals.forget(groupId);
25375
25484
  for (const linkId of [...this.host.edges(groupId).values(), ...this.host.entries(groupId).values()]) await this.host.closeEdge(linkId);
25376
25485
  await this.store.deleteGroup(groupId);
25486
+ await this.files?.drop(groupId);
25377
25487
  this.host.historyGone?.(groupId);
25378
25488
  this.host.membersChanged?.(groupId);
25379
25489
  this.host.emit();
@@ -25499,6 +25609,7 @@ var Groups = class {
25499
25609
  this.meshTick(now);
25500
25610
  await this.communities.tick(now);
25501
25611
  await this.hubsTick(now);
25612
+ this.files?.tick();
25502
25613
  } finally {
25503
25614
  this.ticking = false;
25504
25615
  }
@@ -25872,6 +25983,12 @@ var Groups = class {
25872
25983
  }
25873
25984
  /** A `group-*` frame on an edge: from the member the edge is pinned to. */
25874
25985
  async handleEdgeFrame(groupId, peerKey, frame) {
25986
+ const t = frame && typeof frame === "object" ? frame.t : void 0;
25987
+ if (t === "group-want" || t === "group-want-no" || t === "group-have") {
25988
+ const linkId = this.host.edges(groupId).get(peerKey);
25989
+ if (linkId && !this.stored.get(groupId)?.left) await this.files?.frame(groupId, peerKey, linkId, frame);
25990
+ return;
25991
+ }
25875
25992
  if (this.isCommunity(groupId)) return this.communities.handleEdgeFrame(groupId, peerKey, frame);
25876
25993
  if (this.stored.get(groupId)?.left) {
25877
25994
  if (this.removesMe(groupId, peerKey, frame)) await this.leaveConfirmed(groupId);
@@ -25900,7 +26017,6 @@ var Groups = class {
25900
26017
  return;
25901
26018
  }
25902
26019
  if (!session) return;
25903
- const t = frame && typeof frame === "object" ? frame.t : void 0;
25904
26020
  if (t === "group-react" || t === "group-reacted") {
25905
26021
  await this.reaction(groupId, session, peerKey, frame);
25906
26022
  return;
@@ -25961,6 +26077,7 @@ var Groups = class {
25961
26077
  edgeReady(groupId, peerKey, linkId) {
25962
26078
  if (this.isCommunity(groupId)) {
25963
26079
  this.communities.edgeReady(groupId, peerKey, linkId);
26080
+ this.files?.edgeReady(groupId, peerKey, linkId);
25964
26081
  return;
25965
26082
  }
25966
26083
  const left = this.stored.get(groupId)?.left;
@@ -26000,6 +26117,7 @@ var Groups = class {
26000
26117
  this.welcomed.get(groupId)?.delete(peerKey);
26001
26118
  this.announceHere(groupId, session, peerKey);
26002
26119
  this.signals.edgeReady(groupId, session, peerKey, linkId);
26120
+ this.files?.edgeReady(groupId, peerKey, linkId);
26003
26121
  this.host.edgeUp?.(groupId, peerKey);
26004
26122
  this.host.emit();
26005
26123
  }
@@ -26201,10 +26319,15 @@ var Groups = class {
26201
26319
  message: async (m) => {
26202
26320
  const timestamp = m.timestamp;
26203
26321
  const mentioned = m.sender !== session.myKey && mentionsMember(m.mentions, session.myKey);
26322
+ const file = m.file && this.files ? await this.files.announced(state.id, {
26323
+ id: m.id,
26324
+ sender: m.sender,
26325
+ file: m.file
26326
+ }, session.myKey) : void 0;
26204
26327
  const message = {
26205
26328
  linkId: MESSAGE_LINK(state.id),
26206
26329
  id: m.id,
26207
- text: m.text,
26330
+ text: file ? groupFileText(m.text, m.file) : m.text,
26208
26331
  sender: m.sender === session.myKey ? "me" : "peer",
26209
26332
  member: m.sender,
26210
26333
  timestamp,
@@ -26212,7 +26335,8 @@ var Groups = class {
26212
26335
  ...mentionFields(m.mentions, mentioned),
26213
26336
  ...m.reply && { replyTo: groupReply(m.reply, session.myKey) },
26214
26337
  ...m.forwarded && { forwarded: m.forwarded },
26215
- ...m.card && { card: m.card }
26338
+ ...m.card && { card: m.card },
26339
+ ...file && { file }
26216
26340
  };
26217
26341
  const stored = m.completes && this.host.completeMessage ? (await this.host.completeMessage(message), false) : await this.host.storeMessage(message);
26218
26342
  const came = m.sender === session.myKey ? timestamp : cameAt(timestamp, stored, arrivalNow(this.now()));
@@ -26229,7 +26353,11 @@ var Groups = class {
26229
26353
  },
26230
26354
  metaChanged: (by, change, at) => {
26231
26355
  this.metaChanged(state.id, session, by, change, at);
26232
- }
26356
+ },
26357
+ ...this.host.linkHandled && { handled: (to) => {
26358
+ const edge = this.host.edges(state.id).get(to);
26359
+ return edge && this.host.linkHandled ? this.host.linkHandled(edge) : Promise.resolve(false);
26360
+ } }
26233
26361
  });
26234
26362
  this.sessions.set(state.id, session);
26235
26363
  this.lastRoster.set(state.id, session.roster);
@@ -26341,16 +26469,34 @@ function edgeView(live, now = Date.now(), noSlot = false) {
26341
26469
  noSlot: true,
26342
26470
  lastSeenAt: Math.max(presenceSeenAt(live.presence), live.lastSyncAt)
26343
26471
  };
26344
- const error = open ? void 0 : live.pairing?.status === "error" ? live.pairing.error : live.discoveryError;
26472
+ const pairingError = live.pairing?.status === "error" ? live.pairing.error : void 0;
26473
+ const error = open ? void 0 : pairingError ?? live.discoveryError;
26345
26474
  const state = open ? "open" : error ? "error" : live.dataLink !== "idle" ? "connecting" : "waiting";
26346
26475
  return {
26347
26476
  linkId: live.stored.id,
26348
26477
  state,
26349
26478
  ...open && live.pairing?.transport ? { transport: live.pairing.transport } : {},
26350
26479
  lastSeenAt: open ? now : Math.max(presenceSeenAt(live.presence), live.lastSyncAt),
26351
- ...error ? { error } : {}
26480
+ ...error ? {
26481
+ error,
26482
+ cause: pairingError ? edgeErrorCause(pairingError) : "relays"
26483
+ } : {}
26352
26484
  };
26353
26485
  }
26486
+ /**
26487
+ * A pairing error the session ends with when nothing is wrong with the member: an attempt dialled again. Their
26488
+ * English is `PairedSession`'s and `GhostLink`'s (packages/core).
26489
+ */
26490
+ var SESSION_DROPS = [
26491
+ /^Session receive queue full$/,
26492
+ /^The peer did not finish authentication\b/,
26493
+ /^Invalid session negotiation$/
26494
+ ];
26495
+ /** What an edge's pairing error comes down to: the relays (its signal could not go out), a session dropped, or else. */
26496
+ function edgeErrorCause(error) {
26497
+ if (error.startsWith("Could not publish connection details:") || /^Could not (publish|read) discovery:/.test(error)) return "relays";
26498
+ return SESSION_DROPS.some((drop) => drop.test(error)) ? "session" : "other";
26499
+ }
26354
26500
  //#endregion
26355
26501
  //#region ../browser/src/engine/groupPayments.ts
26356
26502
  /**
@@ -33441,6 +33587,12 @@ var DEFAULT_SETTINGS = {
33441
33587
  mints: [],
33442
33588
  mintsInitialized: false
33443
33589
  };
33590
+ /**
33591
+ * A card that went on the DHT floor or into a hold goes again live (`restoreCards`) only for the newest this many
33592
+ * messages of a chat, sent within this long: an old chat's cards are not worth a burst of edits.
33593
+ */
33594
+ var CARD_RESTORE_MOST = 50;
33595
+ var CARD_RESTORE_MS = 6048e5;
33444
33596
  /** A reaction said on the live session and not confirmed is said again after this long. */
33445
33597
  var REACTION_RESEND_MS = 3e4;
33446
33598
  function emptyLinkFiles() {
@@ -34407,6 +34559,7 @@ var GhostlyNode = class GhostlyNode {
34407
34559
  live.link.sendGroupFrame(frame);
34408
34560
  },
34409
34561
  linkReady: (linkId, version = 1) => !!this.links.get(linkId)?.link?.supportsGroupVersion(version),
34562
+ linkHandled: (linkId) => this.links.get(linkId)?.link?.handled() ?? Promise.resolve(false),
34410
34563
  linkOpen: (linkId) => !!this.links.get(linkId)?.link?.isDataLinkOpen,
34411
34564
  myNick: () => this.sharedNick,
34412
34565
  seqFloor: () => this.counterFloor,
@@ -34482,7 +34635,17 @@ var GhostlyNode = class GhostlyNode {
34482
34635
  this.groupEdits.resend(groupId, peer).catch(() => {});
34483
34636
  this.sendGroupPinFrame(groupId, peer);
34484
34637
  },
34485
- communityPair: (groupId, sender, payload) => this.communityPay.receivePair(groupId, sender, payload)
34638
+ communityPair: (groupId, sender, payload) => this.communityPay.receivePair(groupId, sender, payload),
34639
+ groupFiles: {
34640
+ store: storedGroupFiles,
34641
+ transfers: this.transfers,
34642
+ settings: () => ({
34643
+ autoDownloads: this.settings.autoDownloads !== false,
34644
+ serveFiles: this.settings.serveFiles !== false
34645
+ }),
34646
+ sendFiles: (linkId, frame) => this.links.get(linkId)?.link?.sendFilesFrame(frame) ?? false,
34647
+ writable: (linkId) => this.links.get(linkId)?.link?.filesWritable()
34648
+ }
34486
34649
  });
34487
34650
  /**
34488
34651
  * Payments in community groups (WISP 903 · Group Community § Payments): the desk pays and requests over a link
@@ -35531,7 +35694,7 @@ var GhostlyNode = class GhostlyNode {
35531
35694
  async statusCardIndex() {
35532
35695
  const rows = [];
35533
35696
  for (const m of await db.getCardMessages()) {
35534
- if (m.card?.kind !== "task" && m.card?.kind !== "routine") continue;
35697
+ if (m.card?.kind !== "task" && m.card?.kind !== "routine" && m.card?.kind !== "usage") continue;
35535
35698
  rows.push({
35536
35699
  linkId: m.linkId,
35537
35700
  id: m.id,
@@ -36448,7 +36611,7 @@ var GhostlyNode = class GhostlyNode {
36448
36611
  delivery: "sending",
36449
36612
  ...preview && { preview },
36450
36613
  ...answers,
36451
- ...card?.kind === "buttons" && { buttonsRestore: "due" }
36614
+ ...card && { cardRestore: "due" }
36452
36615
  });
36453
36616
  this.hold.hold(linkId, {
36454
36617
  kind: "text",
@@ -36660,34 +36823,49 @@ var GhostlyNode = class GhostlyNode {
36660
36823
  return !!live?.caps?.peer?.capabilities.includes(EDIT_CAPABILITY);
36661
36824
  }
36662
36825
  /**
36663
- * A question of mine with buttons went on the DHT floor or into a hold, which carry its text alone (WISP 406 · Message
36664
- * Buttons): its buttons are due to go again live. Once: a row that had them restored already stays so.
36826
+ * A card of mine went on the DHT floor or into a hold, which carry its text alone (WISP 405 · Status Cards, WISP 406 ·
36827
+ * Message Buttons): the card is due to go again live. Once: a row that had it restored already stays so.
36665
36828
  */
36666
- async buttonsWentBare(linkId, message) {
36667
- if (message.card?.kind !== "buttons" || message.buttonsRestore) return;
36668
- await db.patchMessage(linkId, message.id, (current) => current.sender === "me" && current.card?.kind === "buttons" && !current.buttonsRestore ? { buttonsRestore: "due" } : null);
36829
+ async cardWentBare(linkId, message) {
36830
+ if (!message.card || message.cardRestore || message.buttonsRestore) return;
36831
+ await db.patchMessage(linkId, message.id, (current) => current.sender === "me" && current.card && !current.cardRestore && !current.buttonsRestore ? { cardRestore: "due" } : null);
36832
+ }
36833
+ /** The session capability a contact's app lists when it shows a card of this kind, so a restore edit reads as a card there. */
36834
+ static cardCapability(card) {
36835
+ return card.kind === "buttons" ? BUTTONS_CAPABILITY : card.kind === "app" ? APPS_CAPABILITY : STATUS_CARD_CAPABILITY;
36669
36836
  }
36670
36837
  /**
36671
- * Buttons the contact may not have (WISP 406 · Message Buttons): a question whose text went on the DHT floor or into a
36672
- * hold reached the contact as text alone, and a copy under the same id that comes live later is taken as the one
36673
- * already there. Once the chat is live with an app that shows buttons (`buttons/1`) and takes edits, each such
36674
- * question goes again as an edit of its buttons alone: the same text, so no version and no edit mark. Once per
36675
- * message (the row says so, across restarts); the edit queue carries it (a card's edit goes only live, and a later
36676
- * edit of the bot's replaces it, the highest number winning). An app without `buttons/1` (1.0.0 would mark it edited)
36677
- * gets none: the row waits for one that shows buttons. The edit is marked `restore`: the bot's own event stream says
36678
- * nothing of it, while its number counts, so a later edit of the bot's takes the next one.
36838
+ * Cards the contact may not have (WISP 405 · Status Cards, WISP 406 · Message Buttons): a message whose text went on
36839
+ * the DHT floor or into a hold reached the contact as text alone, and a copy under the same id that comes live later
36840
+ * is taken as the one already there. An app card or a finished task gets no later update to carry its card. So once
36841
+ * the chat is live with an app that shows the kind (`buttons/1`, `status-card/1`, `apps/1`) and takes edits, each such
36842
+ * message goes again as an edit of its card alone: the same text, so no version and no edit mark. Once per message (the
36843
+ * row says so, across restarts); the edit queue carries it (a card's edit goes only live, and a later edit of the
36844
+ * author's replaces it, the highest number winning). A contact whose app does not show the kind (it would drop the card
36845
+ * and mark the text edited) gets none: the row waits for one that does. Bounded: the newest `CARD_RESTORE_MOST` such
36846
+ * messages of the last `CARD_RESTORE_MS`; older ones are given up. The edit is marked `restore`: the bot's own event
36847
+ * stream says nothing of it, while its number counts, so a later edit of the bot's takes the next one.
36679
36848
  */
36680
- async restoreButtons(linkId) {
36849
+ async restoreCards(linkId) {
36681
36850
  const live = this.links.get(linkId), link = live?.link;
36682
- if (!live?.stored.profile || live.stored.group || !link?.supportsEdits || !link.sessionOffers.peer?.includes("buttons/1")) return;
36683
- const due = (await db.getMessages(linkId)).filter((m) => m.sender === "me" && m.buttonsRestore === "due");
36851
+ const offers = link?.sessionOffers.peer;
36852
+ if (!live?.stored.profile || live.stored.group || !link?.supportsEdits || !offers) return;
36853
+ const due = (await db.getMessages(linkId)).filter((m) => m.sender === "me" && (m.cardRestore ?? m.buttonsRestore) === "due").sort((a, b) => b.timestamp - a.timestamp);
36854
+ const since = Date.now() - CARD_RESTORE_MS;
36684
36855
  const changed = [];
36685
- for (const message of due) {
36856
+ for (const [index, message] of due.entries()) {
36857
+ const stale = index >= CARD_RESTORE_MOST || message.timestamp < since;
36858
+ if (!stale && message.card && !offers.includes(GhostlyNode.cardCapability(message.card))) continue;
36686
36859
  const at = Date.now();
36687
36860
  if ((await db.patchMessage(linkId, message.id, (current) => {
36688
- if (current.sender !== "me" || current.buttonsRestore !== "due") return null;
36861
+ if (current.sender !== "me" || (current.cardRestore ?? current.buttonsRestore) !== "due") return null;
36862
+ const done = {
36863
+ cardRestore: "sent",
36864
+ buttonsRestore: void 0
36865
+ };
36689
36866
  const seq = (current.edit?.seq ?? 0) + 1;
36690
- if (current.card?.kind !== "buttons" || !current.wireId || seq > STATUS_CARD_LIMITS.edits) return { buttonsRestore: "sent" };
36867
+ if (stale || !current.card || !current.wireId || seq > STATUS_CARD_LIMITS.edits) return done;
36868
+ if (current.edit?.pending) return done;
36691
36869
  return {
36692
36870
  edit: {
36693
36871
  ...withEdit(current, {
@@ -36700,7 +36878,7 @@ var GhostlyNode = class GhostlyNode {
36700
36878
  }).edit,
36701
36879
  restore: true
36702
36880
  },
36703
- buttonsRestore: "sent"
36881
+ ...done
36704
36882
  };
36705
36883
  }))?.edit?.pending) changed.push(message.id);
36706
36884
  }
@@ -36806,7 +36984,7 @@ var GhostlyNode = class GhostlyNode {
36806
36984
  const reply = GhostlyNode.wireReply(message);
36807
36985
  const error = message.card ? await link.sendMessage(message.text, message.timestamp, message.wireId, message.preview, reply, message.forwarded, message.card) : message.forwarded ? await link.sendMessage(message.text, message.timestamp, message.wireId, message.preview, reply, message.forwarded) : reply ? await link.sendMessage(message.text, message.timestamp, message.wireId, message.preview, reply) : await link.sendMessage(message.text, message.timestamp, message.wireId, ...message.preview ? [message.preview] : []);
36808
36986
  await this.noteTextSend(linkId, message, snapshot, at, error, link);
36809
- if (!error && message.via === "pkarr") await this.buttonsWentBare(linkId, message);
36987
+ if (!error && message.via === "pkarr") await this.cardWentBare(linkId, message);
36810
36988
  return error;
36811
36989
  }, (message) => message.via === "pkarr" ? DHT_MESSAGE_TTL : 2e4, (message) => {
36812
36990
  const pending = this.links.get(linkId)?.stored.dhtDeliveryState?.pending;
@@ -36831,7 +37009,7 @@ var GhostlyNode = class GhostlyNode {
36831
37009
  via: "hold",
36832
37010
  resendUntil: void 0
36833
37011
  });
36834
- await this.buttonsWentBare(linkId, message);
37012
+ await this.cardWentBare(linkId, message);
36835
37013
  await this.messagesChanged(linkId, [message.id]);
36836
37014
  this.hold.hold(linkId, {
36837
37015
  kind: "text",
@@ -37264,6 +37442,11 @@ var GhostlyNode = class GhostlyNode {
37264
37442
  return offered;
37265
37443
  }
37266
37444
  fileAction({ linkId, fileId, action }) {
37445
+ if (typeof linkId === "string" && linkId.startsWith("group:")) {
37446
+ if (action !== "accept" && action !== "request") throw new Error("A group's file is downloaded (accept) or asked for again (request)");
37447
+ if (!fileId.startsWith(`group-${linkId.slice(6)}-`)) throw new Error("No such file in this group");
37448
+ return this.groups.downloadFile(fileId);
37449
+ }
37267
37450
  const live = this.links.get(linkId);
37268
37451
  if (!live) throw new Error("No such chat");
37269
37452
  if (![
@@ -37872,6 +38055,58 @@ var GhostlyNode = class GhostlyNode {
37872
38055
  if (!sent.error) this.wakeMentioned(groupId, text, named);
37873
38056
  return sent;
37874
38057
  }
38058
+ /**
38059
+ * Announces a file or voice message in a group (WISP 503 · Group Files): its bytes are in the files store under
38060
+ * `file.id` (`group-<group>-out-…`), put there by the caller as a chat's file is (`sendFile`). The message carries its
38061
+ * description and the SHA-256 of those bytes; its text is `caption`, or the line apps from before show. This device
38062
+ * is its first holder: members fetch it from here, then from each other.
38063
+ */
38064
+ async sendGroupFile({ groupId, file, caption, replyTo, forwarded: hops }) {
38065
+ if (typeof groupId !== "string" || !file || typeof file.id !== "string" || !file.id.startsWith(`group-${groupId}-out-`)) return {
38066
+ error: "Invalid file id",
38067
+ refused: true
38068
+ };
38069
+ if (file.size > GROUP_FILE_LIMITS.maxBytes) return {
38070
+ error: `A group takes files of up to ${formatFileSize(GROUP_FILE_LIMITS.maxBytes)}`,
38071
+ refused: true
38072
+ };
38073
+ const stored = await fileStore.get(file.id);
38074
+ if (!stored?.metadata || stored.linkId !== `group:${groupId}` || stored.direction !== "out") return {
38075
+ error: "The file is gone",
38076
+ refused: true
38077
+ };
38078
+ if (storedSize(stored) !== file.size) return {
38079
+ error: "The file is not all here",
38080
+ refused: true
38081
+ };
38082
+ const digest = stored.digest ?? (stored.blob ? await blobDigest(stored.blob) : await (await fileBytesOf(stored.bytes)).digest(file.id));
38083
+ if (!stored.digest) await fileStore.patch(file.id, { digest });
38084
+ const meta = readGroupFileMeta({
38085
+ name: file.name,
38086
+ mime: file.mime,
38087
+ size: file.size,
38088
+ d: digest,
38089
+ ...file.voice && { voice: file.voice },
38090
+ ...file.video && { video: file.video },
38091
+ ...file.image && { image: file.image }
38092
+ });
38093
+ if (!meta) return {
38094
+ error: "That file cannot go to a group",
38095
+ refused: true
38096
+ };
38097
+ if (file.voice && !meta.voice) return {
38098
+ error: "That recording cannot be sent as a voice message",
38099
+ refused: true
38100
+ };
38101
+ const reply = replyTo === void 0 ? void 0 : await this.replyFor(`group:${groupId}`, replyTo);
38102
+ if (typeof reply === "string") return { error: reply };
38103
+ const text = typeof caption === "string" && caption.trim() ? caption : groupFileFallback(meta);
38104
+ return this.groups.sendFile(groupId, text, meta, file.id, reply && {
38105
+ i: reply.id,
38106
+ s: reply.snippet,
38107
+ f: reply.member
38108
+ }, readForwarded(hops));
38109
+ }
37875
38110
  groupMessages({ groupId }) {
37876
38111
  return this.groups.messages(groupId);
37877
38112
  }
@@ -37986,7 +38221,30 @@ var GhostlyNode = class GhostlyNode {
37986
38221
  continue;
37987
38222
  }
37988
38223
  if (groupId) {
37989
- note("Groups take no files yet");
38224
+ const original = message.file, file = {
38225
+ id: groupFileId(groupId, "out"),
38226
+ name: original.name,
38227
+ size: original.size,
38228
+ mime: original.mime,
38229
+ ...original.voice && { voice: original.voice },
38230
+ ...original.video && { video: original.video },
38231
+ ...original.image && { image: original.image }
38232
+ };
38233
+ await copyForForward(original.id, {
38234
+ linkId: to,
38235
+ wireId: file.id.slice(`group-${groupId}-out-`.length),
38236
+ timestamp: now(),
38237
+ file
38238
+ });
38239
+ const sent = await this.sendGroupFile({
38240
+ groupId,
38241
+ file,
38242
+ forwarded: hops
38243
+ });
38244
+ if (sent.error) {
38245
+ await removeStored(file.id).catch(() => {});
38246
+ note(sent.error);
38247
+ } else if (sent.messageId) result.messageIds.push(sent.messageId);
37990
38248
  continue;
37991
38249
  }
37992
38250
  if (!live.link) {
@@ -39448,6 +39706,12 @@ var GhostlyNode = class GhostlyNode {
39448
39706
  this.groups.stopTyping();
39449
39707
  }
39450
39708
  }
39709
+ for (const key of ["autoDownloads", "serveFiles"]) {
39710
+ if (settings[key] === void 0) continue;
39711
+ if (settings[key] !== false) delete this.settings[key];
39712
+ else this.settings[key] = false;
39713
+ await db.putSettings(this.settings);
39714
+ }
39451
39715
  if (settings.publicProfiles !== void 0) {
39452
39716
  if (settings.publicProfiles !== false) delete this.settings.publicProfiles;
39453
39717
  else this.settings.publicProfiles = false;
@@ -40455,6 +40719,7 @@ var GhostlyNode = class GhostlyNode {
40455
40719
  arkPaymentsSupport: !entry,
40456
40720
  usdtPaymentsSupport: !entry,
40457
40721
  barkPaymentsSupport: !entry,
40722
+ largeFilesSupport: !entry,
40458
40723
  params: stored,
40459
40724
  rtcAvailable: typeof RTCPeerConnection !== "undefined" && !this.edgeRtcOff.has(linkId),
40460
40725
  resume: !entry && stored.edgeLive ? resumeOn : void 0,
@@ -40531,7 +40796,9 @@ var GhostlyNode = class GhostlyNode {
40531
40796
  onPaymentAsk: (ask) => member() ? this.desk.onPaymentAsk(linkId, ask) : void 0,
40532
40797
  onPayment: (payment) => member() ? this.desk.onPayment(linkId, payment) : void 0,
40533
40798
  onPaymentResult: (result) => member() ? this.desk.onPaymentResult(linkId, result) : void 0,
40534
- onPeerNick: (nick) => this.groups.edgeNick(group, peer, nick ?? void 0)
40799
+ onPeerNick: (nick) => this.groups.edgeNick(group, peer, nick ?? void 0),
40800
+ onFilesFrame: (frame) => member() ? this.groups.filesFrame(group, peer, linkId, frame) : void 0,
40801
+ onFilesSession: (open) => this.groups.fileSession(group, peer, linkId, open)
40535
40802
  },
40536
40803
  onPresence: (presence) => {
40537
40804
  if (presence.online && !seen) {
@@ -40833,7 +41100,7 @@ var GhostlyNode = class GhostlyNode {
40833
41100
  onMessageEdit: stored.profile && !stored.group ? (edit) => this.receiveEdit(linkId, edit) : void 0,
40834
41101
  onEditReceipt: stored.profile && !stored.group ? (id, e) => this.editsFor(linkId).received(id, e) : void 0,
40835
41102
  onEditSupport: (supported) => {
40836
- if (supported && stored.profile && !stored.group) this.restoreButtons(linkId).catch(() => {}).then(() => this.editsFor(linkId).flush()).catch(() => {});
41103
+ if (supported && stored.profile && !stored.group) this.restoreCards(linkId).catch(() => {}).then(() => this.editsFor(linkId).flush()).catch(() => {});
40837
41104
  },
40838
41105
  onWakeSupport: (supported) => {
40839
41106
  if (supported && stored.profile && !stored.group) this.shareWake(linkId);
@@ -41101,7 +41368,7 @@ var GhostlyNode = class GhostlyNode {
41101
41368
  get nativeFactories() {
41102
41369
  const relay = this.relaysHyperdht ? this.hyperdhtRelay : "";
41103
41370
  return {
41104
- ...relay ? { "hyperdht/1": async (seedB64) => (await import("./hyperdhtRelay-w9cwVqGZ.js")).createRelayedHyperEndpoint(seedB64, relay) } : {},
41371
+ ...relay ? { "hyperdht/1": async (seedB64) => (await import("./hyperdhtRelay-CnEkFl8R.js")).createRelayedHyperEndpoint(seedB64, relay) } : {},
41105
41372
  ...this.options.nativeTransports,
41106
41373
  ...this.ownIrohRelays ? { "iroh/1": (seedB64) => this.options.nativeTransports["iroh/1"](seedB64, this.settings.irohRelays?.length ? { relays: [...this.settings.irohRelays] } : {}) } : {},
41107
41374
  ...this.options.irohWeb ? { "iroh/1": (seedB64) => createIrohWebEndpoint(seedB64, { relays: this.irohRelays }) } : {}
@@ -41369,12 +41636,16 @@ var GhostlyNode = class GhostlyNode {
41369
41636
  ...whole.replyTo && { replyTo: whole.replyTo },
41370
41637
  ...whole.forwarded && { forwarded: whole.forwarded },
41371
41638
  ...whole.card && { card: whole.card },
41372
- ...whole.press && { press: whole.press }
41639
+ ...whole.press && { press: whole.press },
41640
+ ...whole.file && {
41641
+ file: whole.file,
41642
+ text: whole.text
41643
+ }
41373
41644
  };
41374
41645
  if (!Object.keys(added).length) return;
41375
41646
  if (!await db.patchMessage(message.linkId, message.id, (stored) => {
41376
41647
  if (stored.member !== message.member || stored.sender !== message.sender) return null;
41377
- const { card: _card, ...rest } = added;
41648
+ const { card: _card, text: _text, ...rest } = added;
41378
41649
  return stored.edit ? rest : added;
41379
41650
  })) return;
41380
41651
  await this.messagesChanged(message.linkId, [message.id]);
@@ -41844,4 +42115,4 @@ function signalKind(signal) {
41844
42115
  //#endregion
41845
42116
  export { EngineServer, hyperdhtRelayProblem as n, server_exports as t };
41846
42117
 
41847
- //# sourceMappingURL=server-TamKsfmC.js.map
42118
+ //# sourceMappingURL=server-HMzdsyn_.js.map